diff --git a/.circleci/config.yml b/.circleci/config.yml index c67f572c52..30eec16e5a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -38,67 +38,32 @@ commands: steps: - run: command: | + export DEBIAN_FRONTEND=noninteractive sudo apt-get update && sudo apt-get install -y libssh-dev install-deps: steps: - run: command: | - sudo apt-get update && sudo apt-get install -y libpsl-dev libbrotli-dev libzstd-dev zlib1g-dev python3-pip libpsl-dev - sudo python3 -m pip install impacket - - install-wolfssl: - steps: - - run: - command: | - # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com - WOLFSSL_VERSION=5.8.0 - echo "Installing wolfSSL $WOLFSSL_VERSION" - curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/wolfSSL/wolfssl/archive/v$WOLFSSL_VERSION-stable.tar.gz" | tar -xz - cd wolfssl-$WOLFSSL_VERSION-stable - ./autogen.sh - ./configure --disable-dependency-tracking --enable-tls13 --enable-all --enable-harden --prefix=$HOME/wssl - make install - - install-wolfssh: - steps: - - run: - command: | - # renovate: datasource=github-tags depName=wolfSSL/wolfssh versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com - WOLFSSH_VERSION=1.4.19 - echo "Installing wolfSSH $WOLFSSH_VERSION" - curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/wolfSSL/wolfssh/archive/v$WOLFSSH_VERSION-stable.tar.gz" | tar -xz - cd wolfssh-$WOLFSSH_VERSION-stable - ./autogen.sh - ./configure --disable-dependency-tracking --with-wolfssl=$HOME/wssl --prefix=$HOME/wssh --enable-scp --enable-sftp --disable-term --disable-examples - make install + sudo apt-get update && sudo apt-get install -y libpsl-dev libbrotli-dev libzstd-dev zlib1g-dev python3-pip + python3 -m venv ~/venv + ~/venv/bin/pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary -r tests/requirements.txt configure: steps: - run: command: | autoreconf -fi - ./configure --disable-dependency-tracking --enable-unity --enable-test-bundles --enable-werror --enable-warnings \ + ./configure --disable-dependency-tracking --enable-option-checking=fatal --enable-unity --enable-werror --enable-warnings \ --with-openssl \ || { tail -1000 config.log; false; } - configure-openssl-no-verbose: - steps: - - run: - command: | - autoreconf -fi - ./configure --disable-dependency-tracking --enable-unity --enable-test-bundles --enable-werror \ - --with-openssl --disable-verbose \ - || { tail -1000 config.log; false; } - configure-no-proxy: steps: - run: command: | autoreconf -fi - ./configure --disable-dependency-tracking --enable-unity --enable-test-bundles --enable-werror \ + ./configure --disable-dependency-tracking --enable-option-checking=fatal --enable-unity --enable-werror \ --with-openssl --disable-proxy \ || { tail -1000 config.log; false; } @@ -107,7 +72,7 @@ commands: - run: command: | autoreconf -fi - ./configure --disable-dependency-tracking --enable-unity --enable-test-bundles --enable-werror --enable-warnings \ + ./configure --disable-dependency-tracking --enable-option-checking=fatal --enable-unity --enable-werror --enable-warnings \ --with-openssl --with-libssh \ || { tail -1000 config.log; false; } @@ -116,42 +81,40 @@ commands: - run: command: | autoreconf -fi - ./configure --disable-dependency-tracking --enable-unity --enable-test-bundles --enable-werror --enable-warnings \ + ./configure --disable-dependency-tracking --enable-option-checking=fatal --enable-unity --enable-werror --enable-warnings \ --with-openssl --enable-ares \ || { tail -1000 config.log; false; } - configure-wolfssh: - steps: - - run: - command: | - autoreconf -fi - LDFLAGS="-Wl,-rpath,$HOME/wssh/lib" \ - ./configure --disable-dependency-tracking --enable-unity --enable-test-bundles --enable-werror --enable-warnings \ - --with-wolfssl=$HOME/wssl --with-wolfssh=$HOME/wssh \ - || { tail -1000 config.log; false; } - configure-cares-debug: steps: - run: command: | autoreconf -fi - ./configure --disable-dependency-tracking --enable-unity --enable-test-bundles --enable-werror --enable-debug \ + ./configure --disable-dependency-tracking --enable-option-checking=fatal --enable-unity --enable-werror --enable-debug \ --with-openssl --enable-ares \ || { tail -1000 config.log; false; } build: steps: - run: make -j3 V=1 + - run: src/curl --disable --version - run: make -j3 V=1 examples test: steps: - - run: make -j3 V=1 test-ci TFLAGS='-j14' + - run: + command: | + source ~/venv/bin/activate + # Revert a CircleCI-specific local setting that makes test 1459 + # return 67 (CURLE_LOGIN_DENIED) instead of the + # expected 60 (CURLE_PEER_FAILED_VERIFICATION). + echo 'StrictHostKeyChecking yes' >> ~/.ssh/config + make -j3 V=1 test-ci TFLAGS='-j14' executors: ubuntu: machine: - image: ubuntu-2004:2024.01.1 + image: ubuntu-2204:2025.09.1 jobs: basic: @@ -163,24 +126,6 @@ jobs: - build - test - no-verbose: - executor: ubuntu - steps: - - checkout - - install-deps - - configure-openssl-no-verbose - - build - - wolfssh: - executor: ubuntu - steps: - - checkout - - install-deps - - install-wolfssl - - install-wolfssh - - configure-wolfssh - - build - no-proxy: executor: ubuntu steps: @@ -212,7 +157,7 @@ jobs: arm: machine: - image: ubuntu-2004:2024.01.1 + image: ubuntu-2204:2025.09.1 resource_class: arm.medium steps: - checkout @@ -223,7 +168,7 @@ jobs: arm-cares: machine: - image: ubuntu-2004:2024.01.1 + image: ubuntu-2204:2025.09.1 resource_class: arm.medium steps: - checkout @@ -250,14 +195,6 @@ workflows: jobs: - no-proxy - openssl-no-verbose: - jobs: - - no-verbose - - wolfssl-wolfssh: - jobs: - - wolfssh - arm-openssl: jobs: - arm diff --git a/.clang-tidy.yml b/.clang-tidy.yml new file mode 100644 index 0000000000..5f523fb50b --- /dev/null +++ b/.clang-tidy.yml @@ -0,0 +1,59 @@ +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl +--- +# https://clang.llvm.org/extra/clang-tidy/ + +# https://clang.llvm.org/extra/clang-tidy/checks/list.html +Checks: + - clang-analyzer-* + - -clang-analyzer-optin.performance.Padding + - -clang-analyzer-security.ArrayBound # due to false positives with clang-tidy v21.1.0+ + - -clang-analyzer-security.insecureAPI.bzero # for FD_ZERO() (seen on macOS) + - -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling + - -clang-diagnostic-nullability-extension + - bugprone-assert-side-effect + - bugprone-assignment-in-if-condition + - bugprone-chained-comparison + - bugprone-dynamic-static-initializers + - bugprone-invalid-enum-default-initialization + - bugprone-macro-parentheses + - bugprone-macro-repeated-side-effects + - bugprone-misplaced-operator-in-strlen-in-alloc + - bugprone-misplaced-pointer-arithmetic-in-alloc + - bugprone-not-null-terminated-result + - bugprone-posix-return + - bugprone-redundant-branch-condition + - bugprone-signed-char-misuse + - bugprone-sizeof-expression + - bugprone-suspicious-enum-usage + - bugprone-suspicious-memset-usage + - bugprone-suspicious-missing-comma + - bugprone-suspicious-realloc-usage + - bugprone-suspicious-semicolon + # bugprone-unchecked-string-to-number-conversion # needs converting sscanf to strtol or curlx_str_* + - misc-const-correctness + - misc-header-include-cycle + # misc-redundant-expression # undesired hits due to system macros, e.g. due to POLLIN == POLLRDNORM | POLLRDBAND, then or-ing all three + - portability-* + - readability-duplicate-include + # readability-else-after-return + # readability-enum-initial-value + # readability-function-cognitive-complexity + - readability-inconsistent-declaration-parameter-name + # readability-misleading-indentation # too many false positives and oddball/conditional source + - readability-named-parameter + # readability-redundant-casting # false positives in types that change from platform to platform, even with IgnoreTypeAliases: true + - readability-redundant-control-flow + - readability-redundant-declaration + - readability-redundant-function-ptr-dereference + - readability-redundant-parentheses + - readability-redundant-preprocessor + - readability-suspicious-call-argument + - readability-uppercase-literal-suffix + +CheckOptions: + misc-header-include-cycle.IgnoredFilesList: 'curl/curl.h' + readability-inconsistent-declaration-parameter-name.Strict: true + +HeaderFilterRegex: '.*' # Default in v22.1.0+ diff --git a/.gitattributes b/.gitattributes index 41a5aff8dc..fc4480fb72 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,7 +2,6 @@ # # SPDX-License-Identifier: curl -buildconf eol=lf configure.ac eol=lf *.m4 eol=lf *.in eol=lf diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d71ceb1ef0..6121de5b8b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -4,26 +4,19 @@ Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl --> -How to contribute to curl -========================= +# How to contribute to curl -Join the community ------------------- +## Join the community - 1. Click 'watch' on the GitHub repo +1. Click 'watch' on the GitHub repo +2. Subscribe to the suitable [mailing lists](https://curl.se/mail/) - 2. Subscribe to the suitable [mailing lists](https://curl.se/mail/) +## Read [CONTRIBUTE](/docs/CONTRIBUTE.md) -Read [CONTRIBUTE](/docs/CONTRIBUTE.md) ---------------------------------------- +## Send your suggestions using one of these methods: -Send your suggestions using one of these methods: -------------------------------------------------- - - 1. in a mail to the mailing list - - 2. as a [pull request](https://github.com/curl/curl/pulls) - - 3. as an [issue](https://github.com/curl/curl/issues) +1. in a mail to the mailing list +2. as a [pull request](https://github.com/curl/curl/pulls) +3. as an [issue](https://github.com/curl/curl/issues) / The curl team diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index a857700fe5..c2b79901af 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -13,12 +13,7 @@ body: Only file bugs here! Ask questions on the mailing lists https://curl.se/mail/ - **SECURITY RELATED?** Post it here: https://hackerone.com/curl - - There are collections of known issues to be aware of: - - - https://curl.se/docs/knownbugs.html - - https://curl.se/docs/todo.html + **SECURITY RELATED?** Submit here: https://hackerone.com/curl - type: textarea id: reproducer @@ -40,7 +35,7 @@ body: label: curl/libcurl version description: | Please paste the output of `curl -V` here. - placeholder: 'curl 8.2.0' + placeholder: 'curl 8.18.0' validations: required: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 701a5d01cf..c68d6301ef 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,9 +2,37 @@ # # SPDX-License-Identifier: curl +# https://docs.github.com/code-security/dependabot/working-with-dependabot/dependabot-options-reference + version: 2 updates: - package-ecosystem: 'github-actions' directory: '/' schedule: - interval: 'weekly' + interval: 'monthly' + cooldown: + default-days: 7 + groups: + gha-dependencies: + patterns: + - '*' + commit-message: + prefix: 'GHA:' + + - package-ecosystem: 'pip' + directories: + - '.github/scripts' + - 'tests' + schedule: + interval: 'monthly' + cooldown: + default-days: 7 + semver-major-days: 15 + semver-minor-days: 7 + semver-patch-days: 3 + groups: + pip-dependencies: + patterns: + - '*' + commit-message: + prefix: 'GHA:' diff --git a/.github/labeler.yml b/.github/labeler.yml index 42a891e623..32117e7107 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -11,7 +11,7 @@ # the files fit into the category, and the any-glob-to-any-file ones are added # as long as any file matches. The first ones are for "major" categories (the # PR is all about that one topic, like HTTP/3), while the second ones are -# "addendums" that give useful information about a PR that's really mostly +# "addendums" that give useful information about a PR that is really mostly # something else (e.g. CI if the PR also touches CI jobs). # # N.B. any-glob-to-all-files is misnamed; it acts like one-glob-to-all-files. @@ -28,14 +28,17 @@ appleOS: - any-glob-to-all-files: "{\ .github/workflows/macos.yml,\ lib/config-mac.h,\ - lib/macos*\ + lib/macos*,\ + lib/vtls/apple.*,\ + m4/curl-apple-sectrust.m4\ }" authentication: - all: - changed-files: - any-glob-to-all-files: "{\ - docs/mk-ca-bundle.1,\ + CMake/FindGSS.cmake,\ + CMake/FindLibgsasl.cmake,\ docs/libcurl/opts/CURLINFO_HTTPAUTH*,\ docs/libcurl/opts/CURLINFO_PROXYAUTH*,\ docs/libcurl/opts/CURLOPT_KRB*,\ @@ -45,7 +48,6 @@ authentication: docs/libcurl/opts/CURLOPT_USERPWD*,\ docs/libcurl/opts/CURLOPT_XOAUTH*,\ lib/*gssapi*,\ - lib/*krb5*,\ lib/*ntlm*,\ lib/curl_sasl.*,\ lib/http_aws*,\ @@ -62,20 +64,16 @@ build: **/Makefile.am,\ **/Makefile.inc,\ **/*.m4,\ - **/*.mk,\ *.m4,\ docs/INSTALL-CMAKE.md,\ - lib/curl_config.h.cmake,\ + lib/curl_config-cmake.h.in,\ lib/libcurl*.in,\ CMake/**,\ CMakeLists.txt,\ configure.ac,\ m4/**,\ Makefile.*,\ - packages/**,\ - plan9/**,\ projects/**,\ - winbuild/**,\ lib/libcurl.def,\ tests/cmake/**\ }" @@ -99,7 +97,7 @@ cmake: **/CMakeLists.txt,\ CMake/**,\ docs/INSTALL-CMAKE.md,\ - lib/curl_config.h.cmake,\ + lib/curl_config-cmake.h.in,\ tests/cmake/**\ }" @@ -114,6 +112,9 @@ connecting & proxies: - all: - changed-files: - any-glob-to-all-files: "{\ + docs/cmdline-opts/happy-eyeballs*,\ + docs/cmdline-opts/ipv*,\ + docs/cmdline-opts/*proxy*,\ docs/internals/CONNECTION-FILTERS.md,\ docs/examples/ipv6.c,\ docs/libcurl/opts/CURLINFO_CONNECT*,\ @@ -137,6 +138,8 @@ connecting & proxies: lib/if2ip.*,\ lib/noproxy.*,\ lib/socks.*,\ + src/tool_cb_soc.*,\ + tests/http/*socks*,\ tests/server/socksd.c\ }" @@ -144,6 +147,7 @@ cookies: - all: - changed-files: - any-glob-to-all-files: "{\ + CMake/FindLibpsl.cmake,\ docs/HTTP-COOKIES.md,\ docs/cmdline-opts/cookie*,\ docs/cmdline-opts/junk-session-cookies.md,\ @@ -163,9 +167,9 @@ cryptography: docs/libcurl/opts/CURLOPT_EGDSOCKET*,\ lib/*sha256*,\ lib/*sha512*,\ - lib/curl_des.*,\ lib/curl_hmac.*,\ lib/curl_md?.*,\ + lib/curl_ntlm_core.*,\ lib/md?.*,\ lib/rand.*\ }" @@ -183,11 +187,10 @@ documentation: - changed-files: - any-glob-to-all-files: "{\ .github/workflows/checkdocs.yml,\ - .github/scripts/badwords.*,\ - .github/scripts/cd2cd,\ - .github/scripts/cd2nroff,\ - .github/scripts/cdall.pl,\ - .github/scripts/nroff2cd,\ + .github/scripts/pyspelling*,\ + .github/scripts/requirements-docs.txt,\ + .github/scripts/requirements-proselint.txt,\ + .github/scripts/typos*,\ .github/scripts/verify-examples.pl,\ .github/scripts/verify-synopsis.pl,\ **/*.md,\ @@ -198,7 +201,11 @@ documentation: LICENSES/**,\ README,\ RELEASE-NOTES,\ - scripts/cd*\ + scripts/badwords.*,\ + scripts/cd*,\ + scripts/mdlinkcheck,\ + scripts/nroff2cd,\ + scripts/release-notes.pl\ }" - all-globs-to-all-files: # negative matches @@ -209,6 +216,7 @@ FTP: - all: - changed-files: - any-glob-to-all-files: "{\ + docs/cmdline-opts/ftp*,\ docs/libcurl/opts/CURLINFO_FTP*,\ docs/libcurl/opts/CURLOPT_FTP*,\ docs/libcurl/opts/CURLOPT_WILDCARDMATCH*,\ @@ -216,7 +224,10 @@ FTP: lib/curl_fnmatch.*,\ lib/curl_range.*,\ lib/ftp*,\ - tests/ftp*\ + tests/ftp*,\ + tests/http/*ftpd*,\ + tests/http/testenv/*ftpd*,\ + tests/libtest/*_ftp_*\ }" GOPHER: @@ -230,6 +241,8 @@ HTTP: - all: - changed-files: - any-glob-to-all-files: "{\ + docs/HTTPSRR.md,\ + docs/HSTS.md,\ docs/examples/hsts*,\ docs/examples/http-*,\ docs/examples/httpput*,\ @@ -237,10 +250,10 @@ HTTP: docs/examples/*post*,\ docs/HTTP-COOKIES.md,\ docs/libcurl/opts/CURLINFO_COOKIE*,\ - docs/libcurl/opts/CURLOPT_COOKIE*,\ - docs/libcurl/opts/CURLINFO_HTTP_**,\ + docs/libcurl/opts/CURLINFO_HTTP*,\ docs/libcurl/opts/CURLINFO_REDIRECT*,\ docs/libcurl/opts/CURLINFO_REFER*,\ + docs/libcurl/opts/CURLOPT_COOKIE*,\ docs/libcurl/opts/CURLOPT_FOLLOWLOCATION*,\ docs/libcurl/opts/CURLOPT_HSTS*,\ docs/libcurl/opts/CURLOPT_HTTP*,\ @@ -279,9 +292,7 @@ HTTP/3: - all: - changed-files: - any-glob-to-all-files: "{\ - .github/workflows/ngtcp2*,\ - .github/workflows/quiche*,\ - .github/workflows/osslq*,\ + .github/workflows/http3-linux.yml,\ CMake/FindNGHTTP3.cmake,\ CMake/FindNGTCP2.cmake,\ docs/HTTP3.md,\ @@ -341,6 +352,7 @@ MQTT: - all: - changed-files: - any-glob-to-all-files: "{\ + docs/internals/MQTT.md,\ lib/mqtt*,\ tests/server/mqttd.c\ }" @@ -349,18 +361,28 @@ name lookup: - all: - changed-files: - any-glob-to-all-files: "{\ + CMake/FindLibidn2.cmake,\ + docs/cmdline-opts/doh*,\ + docs/cmdline-opts/dns*,\ docs/examples/resolve.c,\ + docs/internals/THRDPOOL-AND-QUEUE.md,\ docs/libcurl/opts/CURLINFO_NAMELOOKUP*,\ docs/libcurl/opts/CURLOPT_DNS*,\ docs/libcurl/opts/CURLOPT_DOH*,\ docs/libcurl/opts/CURLOPT_RESOLVE*,\ + lib/*addrinfo*,\ lib/asyn*,\ + lib/cf-dns.*,\ lib/curl_gethostname.*,\ + lib/dns*,\ lib/doh*,\ lib/host*,\ lib/idn*,\ - lib/inet_pton.*,\ lib/socketpair*,\ + lib/thrdpool.*,\ + lib/thrdqueue.*,\ + tests/http/*resolve.py,\ + tests/server/dnsd.c,\ tests/server/resolve.c\ }" @@ -372,14 +394,6 @@ POP3: lib/pop3.*\ }" -RTMP: - - all: - - changed-files: - - any-glob-to-all-files: "{\ - CMake/FindLibrtmp.cmake,\ - lib/curl_rtmp.*\ - }" - RTSP: - all: - changed-files: @@ -395,12 +409,17 @@ SCP/SFTP: - all: - changed-files: - any-glob-to-all-files: "{\ + CMake/FindLibssh.cmake,\ CMake/FindLibssh2.cmake,\ + docs/cmdline-opts/knownhosts.md,\ docs/libcurl/opts/CURLOPT_SSH*,\ docs/examples/sftp*,\ lib/vssh/**,\ tests/sshhelp.pm,\ - tests/sshserver.pl\ + tests/sshserver.pl,\ + tests/http/*scp*,\ + tests/http/*sftp*,\ + tests/http/testenv/sshd.py\ }" script: @@ -410,8 +429,9 @@ script: **/*.pl,\ **/*.sh,\ curl-config.in,\ - docs/curl-config.1,\ - docs/mk-ca-bundle.1,\ + docs/curl-config.md,\ + docs/mk-ca-bundle.md,\ + docs/wcurl.md,\ docs/THANKS-filter,\ scripts/**\ }" @@ -428,6 +448,7 @@ SMTP: - all: - changed-files: - any-glob-to-all-files: "{\ + docs/cmdline-opts/mail*,\ docs/examples/smtp-*,\ docs/libcurl/opts/CURLOPT_MAIL*,\ lib/smtp.*\ @@ -452,16 +473,25 @@ TLS: - all: - changed-files: - any-glob-to-all-files: "{\ + CMake/FindGnuTLS.cmake,\ CMake/FindMbedTLS.cmake,\ CMake/FindWolfSSL.cmake,\ CMake/FindRustls.cmake,\ + docs/CIPHERS-TLS12.md,\ + docs/SSL*,\ + docs/cmdline-opts/dump-ca-embed.md,\ + docs/cmdline-opts/*cert*,\ + docs/cmdline-opts/*ssl*,\ + docs/cmdline-opts/*tls*,\ docs/examples/ssl*,\ docs/examples/*ssl.*,\ docs/examples/*tls.*,\ - docs/SSL*,\ + docs/internals/TLS-SESSIONS.md,\ docs/libcurl/curl_global_sslset*,\ + docs/libcurl/curl_easy_ssls*,\ docs/libcurl/opts/CURLINFO_CA*,\ docs/libcurl/opts/CURLINFO_CERT*,\ + docs/libcurl/opts/CURLINFO_EARLYDATA*,\ docs/libcurl/opts/CURLINFO_SSL*,\ docs/libcurl/opts/CURLINFO_TLS*,\ docs/libcurl/opts/CURLOPT_CA*,\ @@ -476,7 +506,8 @@ TLS: m4/curl-openssl.m4,\ m4/curl-rustls.m4,\ m4/curl-schannel.m4,\ - m4/curl-wolfssl.m4\ + m4/curl-wolfssl.m4,\ + src/tool_ssls.*\ }" URL: @@ -494,32 +525,32 @@ WebSocket: - all: - changed-files: - any-glob-to-all-files: "{\ - docs/internals/WEBSOCKET.md*,\ + docs/internals/WEBSOCKET.md,\ docs/examples/websocket*,\ docs/libcurl/curl_ws_*,\ docs/libcurl/libcurl-ws*,\ docs/libcurl/opts/CURLOPT_WS_*,\ include/curl/websockets.h,\ lib/ws.*,\ - tests/http/clients/ws*,\ tests/http/test_20_websockets.py,\ - tests/http/testenv/ws*\ + tests/http/testenv/ws*,\ + tests/libtest/cli_ws*\ }" Windows: - all: - changed-files: - any-glob-to-all-files: "{\ - appveyor.*,\ .github/workflows/windows.yml,\ + appveyor.*,\ CMake/win32-cache.cmake,\ lib/*win32*,\ + lib/curlx/fopen.*,\ lib/curlx/multibyte.*,\ - lib/rename.*,\ + lib/curlx/winapi.*,\ lib/vtls/schannel*,\ m4/curl-schannel.m4,\ - projects/**,\ + projects/Windows/**,\ src/tool_doswin.c,\ - winbuild/**,\ lib/libcurl.def\ }" diff --git a/.github/scripts/badwords.pl b/.github/scripts/badwords.pl deleted file mode 100755 index bd66f33166..0000000000 --- a/.github/scripts/badwords.pl +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env perl -# Copyright (C) Daniel Stenberg, , et al. -# -# SPDX-License-Identifier: curl -# -# bad[:=]correct -# -# If separator is '=', the string will be compared case sensitively. -# If separator is ':', the check is done case insensitively. -# -# To add white listed uses of bad words that are removed before checking for -# the bad ones: -# -# ---(accepted word) -# - -use strict; -use warnings; - -my @whitelist; -my %alt; -my %exactcase; - -my @w; -while() { - chomp; - if($_ =~ /^#/) { - next; - } - if($_ =~ /^---(.*)/) { - push @whitelist, $1; - } - elsif($_ =~ /^([^:=]*)([:=])(.*)/) { - my ($bad, $sep, $better)=($1, $2, $3); - push @w, $bad; - $alt{$bad} = $better; - if($sep eq "=") { - $exactcase{$bad} = 1; - } - } -} - -my $errors = 0; - -sub file { - my ($f) = @_; - my $l = 0; - open(F, "<$f"); - while() { - my $in = $_; - $l++; - chomp $in; - if($in =~ /^ /) { - next; - } - # remove the link part - $in =~ s/(\[.*\])\(.*\)/$1/g; - # remove backticked texts - $in =~ s/\`.*\`//g; - # remove whitelisted patterns - for my $p (@whitelist) { - $in =~ s/$p//g; - } - foreach my $w (@w) { - my $case = $exactcase{$w}; - if(($in =~ /^(.*)$w/i && !$case) || - ($in =~ /^(.*)$w/ && $case) ) { - my $p = $1; - my $c = length($p)+1; - print STDERR "$f:$l:$c: error: found bad word \"$w\"\n"; - printf STDERR " %4d | $in\n", $l; - printf STDERR " | %*s^%s\n", length($p), " ", - "~" x (length($w)-1); - printf STDERR " maybe use \"%s\" instead?\n", $alt{$w}; - $errors++; - } - } - } - close(F); -} - -my @files = @ARGV; - -foreach my $each (@files) { - file($each); -} -exit $errors; diff --git a/.github/scripts/badwords.txt b/.github/scripts/badwords.txt deleted file mode 100644 index 741e5376fc..0000000000 --- a/.github/scripts/badwords.txt +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (C) Daniel Stenberg, , et al. -# -# SPDX-License-Identifier: curl -# -back-end:backend -e-mail:email -run-time:runtime -set-up:setup -tool chain:toolchain -tool-chain:toolchain -wild-card:wildcard -wild card:wildcard -i'm:I am -you've:You have -we've:we have -we're:we are -we'll:we will -we'd:we would -they've:They have -they're:They are -they'll:They will -they'd:They would -you've:you have -you'd:you would -you'll:you will -you're:you are -should've:should have -don't=do not -could've:could have -doesn't:does not -isn't:is not -aren't:are not - a html: an html - a http: an http - a ftp: an ftp - a IPv4: an IPv4 - a IPv6: an IPv6 - url =URL -internet\b=Internet -isation:ization -\bit's:it is -it'd:it would -there's:there is -[^.]\. And: Rewrite it somehow? -^(And|So|But) = Rewrite it somehow? -\. But: Rewrite it somehow? -\. So : Rewrite without "so" ? - dir :directory -can't:cannot -that's:that is -web page:webpage -host name\b:hostname -host names\b:hostnames -file name\b:filename -file names\b:filenames -\buser name\b:username -\buser names\b:usernames -\bpass phrase:passphrase -didn't:did not -doesn't:does not -won't:will not -couldn't:could not -\bwill\b:rewrite to present tense -\b32bit=32-bit -\b64bit=64-bit -32 bit\b=32-bit -64 bit\b=64-bit -64-bits:64 bits or 64-bit -32-bits:32 bits or 32-bit -\bvery\b:rephrase using an alternative word -\bCurl\b=curl -\bLibcurl\b=libcurl ----WWW::Curl ----NET::Curl ----Curl Corporation diff --git a/.github/scripts/cleancmd.pl b/.github/scripts/cleancmd.pl index 7b79c2e65a..988d456204 100755 --- a/.github/scripts/cleancmd.pl +++ b/.github/scripts/cleancmd.pl @@ -16,7 +16,7 @@ use warnings; my @asyms; open(S, "<./docs/libcurl/symbols-in-versions") - || die "can't find symbols-in-versions"; + || die "cannot find symbols-in-versions"; while() { if(/^([^ ]*) /) { push @asyms, $1; @@ -30,7 +30,7 @@ my @aopts = ( ); open(O, "<./docs/options-in-versions") - || die "can't find options-in-versions"; + || die "cannot find options-in-versions"; while() { chomp; if(/^([^ ]+)/) { @@ -50,7 +50,7 @@ while() { close(O); open(C, "<./.github/scripts/spellcheck.curl") - || die "can't find spellcheck.curl"; + || die "cannot find spellcheck.curl"; while() { if(/^\#/) { next; @@ -99,9 +99,11 @@ sub process { # *italics* $l =~ s/\*(\S.*?)\*//g; - # strip out https URLs, we don't want them spellchecked + # strip out https URLs, we do not want them spellchecked $l =~ s!https://[a-z0-9\#_/.-]+!!gi; + # strip links, both name and target + $l =~ s/(\[.*?\])\(.*?\)//g; $out .= $l; } close(F); @@ -119,6 +121,10 @@ sub process { } } -for my $f (@ARGV) { +my @filemasks = @ARGV; +open(my $git_ls_files, '-|', 'git', 'ls-files', '--', @filemasks) or die "Failed running git ls-files: $!"; +while(my $f = <$git_ls_files>) { + chomp $f; process($f); } +close $git_ls_files; diff --git a/.github/scripts/cmp-config.pl b/.github/scripts/cmp-config.pl index 88df37b795..66cb657563 100755 --- a/.github/scripts/cmp-config.pl +++ b/.github/scripts/cmp-config.pl @@ -45,18 +45,18 @@ my %remove = ( '#define HAVE_BROTLI 1' => 1, '#define HAVE_BROTLI_DECODE_H 1' => 1, '#define HAVE_DLFCN_H 1' => 1, + '#define HAVE_GSSAPI_GSSAPI_GENERIC_H 1' => 1, + '#define HAVE_GSSAPI_GSSAPI_H 1' => 1, '#define HAVE_GSSAPI_GSSAPI_KRB5_H 1' => 1, '#define HAVE_INTTYPES_H 1' => 1, '#define HAVE_LDAP_H 1' => 1, '#define HAVE_LDAP_SSL 1' => 1, '#define HAVE_LIBBROTLIDEC 1' => 1, '#define HAVE_LIBPSL_H 1' => 1, - '#define HAVE_LIBRTMP_RTMP_H 1' => 1, '#define HAVE_LIBSOCKET 1' => 1, '#define HAVE_LIBSSH' => 1, '#define HAVE_LIBSSH2 1' => 1, '#define HAVE_LIBSSL 1' => 1, - '#define HAVE_LIBWOLFSSH' => 1, '#define HAVE_LIBZSTD 1' => 1, '#define HAVE_NGHTTP2_NGHTTP2_H 1' => 1, '#define HAVE_NGHTTP3_NGHTTP3_H 1' => 1, @@ -67,7 +67,6 @@ my %remove = ( '#define HAVE_OPENSSL_PEM_H 1' => 1, '#define HAVE_OPENSSL_RSA_H 1' => 1, '#define HAVE_OPENSSL_SSL_H 1' => 1, - '#define HAVE_OPENSSL_X509_H 1' => 1, '#define HAVE_QUICHE_H 1' => 1, '#define HAVE_SSL_SET_QUIC_TLS_CBS 1' => 1, '#define HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT 1' => 1, @@ -78,7 +77,6 @@ my %remove = ( '#define HAVE_SYS_STAT_H 1' => 1, '#define HAVE_SYS_XATTR_H 1' => 1, '#define HAVE_UNICODE_UIDNA_H 1' => 1, - '#define HAVE_WOLFSSH_SSH_H 1' => 1, '#define HAVE_WOLFSSL_SET_QUIC_USE_LEGACY_CODEPOINT 1' => 1, '#define HAVE_ZSTD 1' => 1, '#define HAVE_ZSTD_H 1' => 1, @@ -91,7 +89,6 @@ my %remove = ( '#define PACKAGE_TARNAME "curl"' => 1, '#define PACKAGE_URL ""' => 1, '#define PACKAGE_VERSION "-"' => 1, - '#define SIZEOF_LONG_LONG 8' => 1, '#define VERSION "-"' => 1, '#define _FILE_OFFSET_BITS 64' => 1, ); @@ -136,6 +133,5 @@ foreach my $v (keys %remove) { } } - # return the exit code from diff -exit system("diff -u /tmp/autotools /tmp/cmake") >> 8; +exit system('diff', ('-u', '/tmp/autotools', '/tmp/cmake')) >> 8; diff --git a/.github/scripts/codespell-ignore.txt b/.github/scripts/codespell-ignore.words similarity index 96% rename from .github/scripts/codespell-ignore.txt rename to .github/scripts/codespell-ignore.words index fa8a432b95..1a5e106400 100644 --- a/.github/scripts/codespell-ignore.txt +++ b/.github/scripts/codespell-ignore.words @@ -7,7 +7,7 @@ bu clen CNA hel -htpt +htpts inout PASE passwor diff --git a/.github/scripts/codespell.sh b/.github/scripts/codespell.sh index 766eeeb87c..c5ddf90a47 100755 --- a/.github/scripts/codespell.sh +++ b/.github/scripts/codespell.sh @@ -7,14 +7,16 @@ set -eu cd "$(dirname "${0}")"/../.. -# shellcheck disable=SC2046 +git ls-files -z | xargs -0 -r \ codespell \ - --skip '.github/scripts/spellcheck.words' \ + --skip '.github/scripts/pyspelling.words' \ --skip '.github/scripts/typos.toml' \ --skip 'docs/THANKS' \ - --skip 'packages/*' \ + --skip 'projects/OS400/*' \ + --skip 'projects/vms/*' \ + --skip 'RELEASE-NOTES' \ --skip 'scripts/wcurl' \ - --skip 'winbuild/*' \ + --skip 'tests/unit/unit1625.c' \ --ignore-regex '.*spellchecker:disable-line' \ - --ignore-words '.github/scripts/codespell-ignore.txt' \ - $(git ls-files) + --ignore-words '.github/scripts/codespell-ignore.words' \ + -- diff --git a/.github/scripts/distfiles.sh b/.github/scripts/distfiles.sh index 26370744d7..5eb6a470a8 100755 --- a/.github/scripts/distfiles.sh +++ b/.github/scripts/distfiles.sh @@ -11,7 +11,6 @@ set -eu gitonly=".git* ^.* ^appveyor.* -^buildconf ^GIT-INFO.md ^README.md ^renovate.json @@ -19,17 +18,15 @@ gitonly=".git* ^SECURITY.md ^LICENSES/* ^docs/examples/adddocsref.pl +^docs/tests/CI.md ^docs/THANKS-filter ^projects/Windows/* -^scripts/ciconfig.pl -^scripts/cijobs.pl ^scripts/contributors.sh ^scripts/contrithanks.sh ^scripts/delta ^scripts/installcheck.sh ^scripts/release-notes.pl -^scripts/singleuse.pl -^tests/CI.md" +^scripts/singleuse.pl" tarfiles="$(mktemp)" gitfiles="$(mktemp)" diff --git a/.github/scripts/spellcheck.words b/.github/scripts/pyspelling.words similarity index 96% rename from .github/scripts/spellcheck.words rename to .github/scripts/pyspelling.words index 13b7b2f367..dc6e93c8b4 100644 --- a/.github/scripts/spellcheck.words +++ b/.github/scripts/pyspelling.words @@ -53,6 +53,7 @@ axTLS backend backends backoff +backtick backticks balancers Baratov @@ -97,6 +98,7 @@ changeset CharConv charset charsets +checkdocs checksrc checksums chgrp @@ -122,6 +124,7 @@ CMakeLists CNA CNAME CNAMEs +CodeQL CODESET codeset CodeSonar @@ -147,7 +150,6 @@ CSeq csh cshrc CTRL -cURL CURLcode curldown CURLE @@ -170,8 +172,10 @@ dbg Debian DEBUGBUILD decrypt +decrypted decrypting deepcode +defacto DELE DER dereference @@ -200,6 +204,7 @@ DLLs DNS dns dnsop +DNSSEC DoH DoT doxygen @@ -213,6 +218,7 @@ dynbuf EAGAIN EBCDIC ECC +ECCN ECDHE ECH ECHConfig @@ -230,9 +236,9 @@ else's encodings enctype endianness -enums Engler enum +enums epoll EPRT EPSV @@ -243,6 +249,7 @@ et etag ETag ETags +exa exe executables EXPN @@ -256,6 +263,7 @@ Fedora Feltzing ffi filesize +filesystem FindCURL FLOSS fnmatch @@ -292,9 +300,12 @@ getinfo GETing getpwuid ggcov +GHA Ghedini +giga Gisle Glesys +glibc globbed globbing gmail @@ -355,7 +366,6 @@ HTTPS https HTTPSRR hyper's -Högskolan IANA Icecast ICONV @@ -377,6 +387,7 @@ imap IMAPS imaps impacket +implementers init initializer inlined @@ -415,6 +426,7 @@ kerberos Keychain keychain KiB +kibibyte kickstart Kirei Knauf @@ -423,14 +435,15 @@ Krb krb Kubernetes Kuhrt -Kungliga Largefile LDAP ldap LDAPS ldaps LF +LGPL LGTM +libbacktrace libbrotlidec libc libcurl @@ -460,6 +473,7 @@ libWebSocket libz libzstd LineageOS +linter linux lldb ln @@ -490,6 +504,7 @@ Mavrogiannopoulos Mbed mbedTLS md +mebibyte Meglio memdebug MesaLink @@ -562,6 +577,7 @@ Necko NetBSD netrc netstat +NetWare Netware NFS nghttp @@ -618,6 +634,7 @@ PEM pem perl permafailing +peta PINGs pipelining PKCS @@ -704,6 +721,7 @@ ROADMAP Roadmap Rockbox roffit +RPC RPG RR RRs @@ -742,6 +760,7 @@ scp SDK se SEB +SecTrust SEK selectable Serv @@ -759,6 +778,7 @@ singlecwd SINIX Sintonen sizeof +Slowloris SLE slist sln @@ -790,6 +810,7 @@ sprintf src SRP SRWLOCK +SSI SSL ssl SSLeay @@ -806,6 +827,7 @@ stateful statvfs stderr stdin +stdint stdout Steinar Stenberg @@ -847,7 +869,7 @@ Tatsuhiro TBD TCP tcpdump -Tekniska +tera testability testcurl TFTP @@ -884,7 +906,9 @@ UDP UI UID UIDL +uint Ultrix +umask Unary unassign UNC @@ -929,6 +953,7 @@ VC vcpkg vexxhost Viktor +virtualized Virtuozzo VLAN VM @@ -949,12 +974,13 @@ watchOS WAV WB wcurl -web page WebDAV WebOS webpage +webpages WebSocket WEBSOCKET +Wget WHATWG whitespace Whitespaces @@ -973,7 +999,9 @@ www Xbox XDG xdigit +XHTML Xilinx +xmllint XP Xtensa XYZ diff --git a/.github/scripts/spellcheck.yaml b/.github/scripts/pyspelling.yaml similarity index 87% rename from .github/scripts/spellcheck.yaml rename to .github/scripts/pyspelling.yaml index 05ddf0d937..bb0585ab7a 100644 --- a/.github/scripts/spellcheck.yaml +++ b/.github/scripts/pyspelling.yaml @@ -2,6 +2,7 @@ # # SPDX-License-Identifier: curl # +# Docs: https://facelessuser.github.io/pyspelling/configuration/ # Docs: https://github.com/UnicornGlobal/spellcheck-github-actions matrix: - name: Markdown @@ -29,4 +30,4 @@ matrix: - 'strong' - 'em' sources: - - '**/*.md|!docs/BINDINGS.md|!docs/DISTROS.md|!docs/CIPHERS-TLS12.md|!docs/wcurl.md' + - '**/*.md|!docs/BINDINGS.md|!docs/DISTROS.md|!docs/CIPHERS-TLS12.md|!docs/wcurl.md|!tests/data/data*.md' diff --git a/.github/scripts/randcurl.pl b/.github/scripts/randcurl.pl index b085d2ef13..f9c24d90db 100755 --- a/.github/scripts/randcurl.pl +++ b/.github/scripts/randcurl.pl @@ -116,7 +116,6 @@ my %commonrc = ( '26' => 1, ); - sub runone { my $a; my $nargs = getnum(60) + 1; diff --git a/.github/scripts/requirements-docs.txt b/.github/scripts/requirements-docs.txt new file mode 100644 index 0000000000..6850461de7 --- /dev/null +++ b/.github/scripts/requirements-docs.txt @@ -0,0 +1,5 @@ +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + +pyspelling==2.12.1 diff --git a/winbuild/.gitignore b/.github/scripts/requirements-proselint.txt similarity index 83% rename from winbuild/.gitignore rename to .github/scripts/requirements-proselint.txt index 0d7f2b276f..1896109442 100644 --- a/winbuild/.gitignore +++ b/.github/scripts/requirements-proselint.txt @@ -2,5 +2,4 @@ # # SPDX-License-Identifier: curl -*.idb -*.inc +proselint==0.16.0 diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt new file mode 100644 index 0000000000..1999337141 --- /dev/null +++ b/.github/scripts/requirements.txt @@ -0,0 +1,9 @@ +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + +cmakelang==0.6.13 +codespell==2.4.2 +pytype==2024.10.11 +reuse==6.2.0 +ruff==0.15.10 diff --git a/.github/scripts/shellcheck.sh b/.github/scripts/shellcheck.sh index 66590ec6c7..59b49131ac 100755 --- a/.github/scripts/shellcheck.sh +++ b/.github/scripts/shellcheck.sh @@ -3,7 +3,11 @@ # # SPDX-License-Identifier: curl -# shellcheck disable=SC2046 -shellcheck --exclude=1091 \ +set -eu + +cd "$(dirname "${0}")"/../.. + +git grep -z -l -E '^#!(/usr/bin/env bash|/bin/sh|/bin/bash)' | xargs -0 -r \ +shellcheck --exclude=1091,2248 \ --enable=avoid-nullary-conditions,deprecate-which \ - $(grep -l -E '^#!(/usr/bin/env bash|/bin/sh|/bin/bash)' $(git ls-files)) + -- diff --git a/.github/scripts/spacecheck.pl b/.github/scripts/spacecheck.pl deleted file mode 100755 index e61d30b5b0..0000000000 --- a/.github/scripts/spacecheck.pl +++ /dev/null @@ -1,195 +0,0 @@ -#!/usr/bin/env perl -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) Viktor Szakats -# -# 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 -# -########################################################################### - -use strict; -use warnings; - -my @tabs = ( - "^m4/zz40-xc-ovr.m4", - "Makefile\\.(am|example)\$", - "/mkfile", - "\\.(sln|vc)\$", - "^tests/data/test", -); - -my @mixed_eol = ( - "^tests/data/test", -); - -my @need_crlf = ( - "\\.(bat|sln)\$", - "^winbuild/.+\\.md\$", -); - -my @space_at_eol = ( - "^tests/data/test", -); - -my @non_ascii_allowed = ( - '\xC3\xB6', # UTF-8 for https://codepoints.net/U+00F6 LATIN SMALL LETTER O WITH DIAERESIS -); - -my $non_ascii_allowed = join(', ', @non_ascii_allowed); - -my @non_ascii = ( - ".github/scripts/spellcheck.words", - ".mailmap", - "RELEASE-NOTES", - "docs/BINDINGS.md", - "docs/THANKS", - "docs/THANKS-filter", -); - -sub fn_match { - my ($filename, @masklist) = @_; - - foreach my $mask (@masklist) { - if($filename =~ $mask) { - return 1; - } - } - return 0; -} - -sub eol_detect { - my ($content) = @_; - - my $cr = () = $content =~ /\r/g; - my $lf = () = $content =~ /\n/g; - - if($cr > 0 && $lf == 0) { - return "cr"; - } - elsif($cr == 0 && $lf > 0) { - return "lf"; - } - elsif($cr == 0 && $lf == 0) { - return "bin"; - } - elsif($cr == $lf) { - return "crlf"; - } - - return ""; -} - -my $issues = 0; - -open my $git_ls_files, '-|', 'git ls-files' or die "Failed running git ls-files: $!"; -while(my $filename = <$git_ls_files>) { - chomp $filename; - - open my $fh, '<', $filename or die "Cannot open '$filename': $!"; - my $content = do { local $/; <$fh> }; - close $fh; - - my @err = (); - - if(!fn_match($filename, @tabs) && - $content =~ /\t/) { - push @err, "content: has tab"; - } - - my $eol = eol_detect($content); - - if($eol eq "" && - !fn_match($filename, @mixed_eol)) { - push @err, "content: has mixed EOL types"; - } - - if($eol ne "crlf" && - fn_match($filename, @need_crlf)) { - push @err, "content: must use CRLF EOL for this file type"; - } - - if($eol ne "lf" && $content ne "" && - !fn_match($filename, @need_crlf) && - !fn_match($filename, @mixed_eol)) { - push @err, "content: must use LF EOL for this file type"; - } - - if(!fn_match($filename, @space_at_eol) && - $content =~ /[ \t]\n/) { - my $line; - for my $l (split(/\n/, $content)) { - $line++; - if($l =~ /[ \t]$/) { - push @err, "line $line: trailing whitespace"; - } - } - } - - if($content ne "" && - $content !~ /\n\z/) { - push @err, "content: has no EOL at EOF"; - } - - if($content =~ /\n\n\z/ || - $content =~ /\r\n\r\n\z/) { - push @err, "content: has multiple EOL at EOF"; - } - - if($content =~ /\n\n\n\n/ || - $content =~ /\r\n\r\n\r\n\r\n/) { - push @err, "content: has 3 or more consecutive empty lines"; - } - - if($content =~ /([\x00-\x08\x0b\x0c\x0e-\x1f\x7f])/) { - push @err, "content: has binary contents"; - } - - if($filename !~ /tests\/data/) { - # the tests have no allowed UTF bytes - $content =~ s/[$non_ascii_allowed]//g; - } - - if(!fn_match($filename, @non_ascii) && - ($content =~ /([\x80-\xff]+)/)) { - my $non = $1; - my $hex; - for my $e (split(//, $non)) { - $hex .= sprintf("%s%02x", $hex ? " ": "", ord($e)); - } - my $line; - for my $l (split(/\n/, $content)) { - $line++; - if($l =~ /([\x80-\xff]+)/) { - push @err, "line $line: has non-ASCII: '$non' ($hex)"; - } - } - } - - if(@err) { - $issues++; - foreach my $err (@err) { - print "$filename: $err\n"; - } - } -} -close $git_ls_files; - -if($issues) { - exit 1; -} diff --git a/.github/scripts/spellcheck.curl b/.github/scripts/spellcheck.curl index c24edf2b3b..1d8be5ed3e 100644 --- a/.github/scripts/spellcheck.curl +++ b/.github/scripts/spellcheck.curl @@ -133,9 +133,10 @@ curl_multi_setopt curl_multi_assign curl_multi_get_handles curl_multi_get_offt +curl_multi_notify_disable +curl_multi_notify_enable curl_pushheader_bynum curl_pushheader_byname -curl_multi_waitfds curl_easy_option_by_name curl_easy_option_by_id curl_easy_option_next diff --git a/.github/scripts/typos.toml b/.github/scripts/typos.toml index 46301615cd..8d8511953a 100644 --- a/.github/scripts/typos.toml +++ b/.github/scripts/typos.toml @@ -4,14 +4,16 @@ [default] extend-ignore-identifiers-re = [ - "^(ba|pn|PN|UE)$", - "^(CNA|ser)$", - "^(ECT0|ECT1|HELO|htpt|PASE)$", + "^(ba|fo|pn|PN|UE)$", + "^(CNA|cpy|ser)$", + "^(ECT0|ECT1|HELO|htpts|PASE)$", "^[A-Za-z0-9_-]*(EDE|GOST)[A-Z0-9_-]*$", # ciphers "^0x[0-9a-fA-F]+FUL$", # unsigned long hex literals ending with 'F' - "^[0-9a-zA-Z+]{64,}$", # possibly base64 - "^(Januar|eyeballers|HELO_smtp|kno22|MkTypLibCompatible|optin|passin|perfec|__SecURE|SMTP_HELO|v_alue)$", - "^(clen|req_clen|smtp_perform_helo|smtp_state_helo_resp|_stati64)$", + "^[0-9a-zA-Z+]{64,}$", # possibly base64 + "^(eyeballers|HELO_smtp|Januar|optin|passin|perfec|SMTP_HELO)$", + "^(clen|req_clen|smtp_perform_helo|smtp_state_helo_resp|Tru64|_stati64)$", + "(_ccontains|_controllen|O_WRONLY|secur32)", + "proxys", # this should be limited to tests/http/*. Short for secure proxy. ] extend-ignore-re = [ @@ -20,10 +22,15 @@ extend-ignore-re = [ [files] extend-exclude = [ - ".github/scripts/codespell-ignore.txt", - ".github/scripts/spellcheck.words", + ".github/scripts/codespell-ignore.words", + ".github/scripts/pyspelling.words", "docs/THANKS", - "packages/*", + "projects/OS400/*", + "projects/vms/*", + "projects/Windows/tmpl/curl.vcxproj", + "projects/Windows/tmpl/libcurl.vcxproj", + "RELEASE-NOTES", "scripts/wcurl", - "winbuild/*", + "tests/data/test*", + "tests/unit/unit1625.c", ] diff --git a/.github/scripts/verify-examples.pl b/.github/scripts/verify-examples.pl index 27c4de6db8..007369b4ab 100755 --- a/.github/scripts/verify-examples.pl +++ b/.github/scripts/verify-examples.pl @@ -37,12 +37,14 @@ if(!@files || $files[0] eq "-h") { } sub testcompile { - my $rc = system("gcc -c test.c -DCURL_ALLOW_OLD_MULTI_SOCKET -DCURL_DISABLE_DEPRECATION -Wunused -Werror -Wall -Wno-unused-but-set-variable -I include") >> 8; + my $rc = system('gcc -c test.c -I include -W -Wall -pedantic -Werror ' . + '-Wno-unused-parameter -Wno-unused-but-set-variable ' . + '-DCURL_ALLOW_OLD_MULTI_SOCKET -DCURL_DISABLE_DEPRECATION') >> 8; return $rc; } sub checksrc { - my $rc = system("$check test.c") >> 8; + my $rc = system($check, ('test.c')) >> 8; return $rc; } @@ -63,9 +65,9 @@ sub extract { elsif($syn == 1) { if(/^~~~/) { $syn++; - print O "/* !checksrc! disable UNUSEDIGNORE all */\n"; + print O "/* !checksrc! disable BANNEDFUNC all */\n"; # for fopen() print O "/* !checksrc! disable COPYRIGHT all */\n"; - print O "/* !checksrc! disable FOPENMODE all */\n"; + print O "/* !checksrc! disable UNUSEDIGNORE all */\n"; printf O "#line %d \"$f\"\n", $iline+1; } } diff --git a/.github/scripts/verify-synopsis.pl b/.github/scripts/verify-synopsis.pl index 1c6b00b60b..02918dd65d 100755 --- a/.github/scripts/verify-synopsis.pl +++ b/.github/scripts/verify-synopsis.pl @@ -35,11 +35,11 @@ if(!@files || $files[0] eq "-h") { } sub testcompile { - my $rc = system("gcc -c test.c -DCURL_DISABLE_TYPECHECK -DCURL_ALLOW_OLD_MULTI_SOCKET -I include") >> 8; + my $rc = system('gcc -c test.c -I include -W -Wall -pedantic -Werror ' . + '-DCURL_ALLOW_OLD_MULTI_SOCKET -DCURL_DISABLE_TYPECHECK') >> 8; return $rc; } - sub extract { my($f) = @_; my $syn = 0; diff --git a/.github/scripts/yamlcheck.sh b/.github/scripts/yamlcheck.sh index 2431c97abc..4bdeff45cb 100755 --- a/.github/scripts/yamlcheck.sh +++ b/.github/scripts/yamlcheck.sh @@ -5,9 +5,11 @@ set -eu -# shellcheck disable=SC2046 +cd "$(dirname "${0}")"/../.. + +git ls-files '*.yaml' '*.yml' -z | xargs -0 -r \ yamllint \ --format standard \ --strict \ - --config-data "$(dirname "$0")/yamlcheck.yaml" \ - $(git ls-files '*.yaml' '*.yml') + --config-data .github/scripts/yamlcheck.yaml \ + -- diff --git a/.github/stale.yml b/.github/stale.yml index dc239b56ce..69a822e78b 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -6,7 +6,7 @@ daysUntilStale: 180 # Number of days of inactivity before a stale issue is closed daysUntilClose: 14 -# Issues with these labels will never be considered stale +# Issues with these labels are never considered stale exemptLabels: - pinned - security diff --git a/.github/workflows/appveyor-status.yml b/.github/workflows/appveyor-status.yml index cb7f96b190..197c5f26e5 100644 --- a/.github/workflows/appveyor-status.yml +++ b/.github/workflows/appveyor-status.yml @@ -16,10 +16,10 @@ permissions: {} jobs: split: name: 'split' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm if: ${{ github.event.sender.login == 'appveyor[bot]' }} permissions: - statuses: write + statuses: write # To update build statuses steps: - name: 'Create individual AppVeyor build statuses' if: ${{ github.event.sha && github.event.target_url }} @@ -29,7 +29,7 @@ jobs: APPVEYOR_REPOSITORY: ${{ github.event.repository.full_name }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - echo "${APPVEYOR_TARGET_URL}" | sed 's/\/project\//\/api\/projects\//' | xargs -t -n1 curl -s | \ + echo "${APPVEYOR_TARGET_URL}" | sed 's/\/project\//\/api\/projects\//' | xargs -t -n1 curl -s -- | \ jq -c '.build.jobs[] | {target_url: ($target_url + "/job/" + .jobId), context: (.name | sub("^(Environment: )?"; "AppVeyor / ")), state: (.status | sub("queued"; "pending") diff --git a/.github/workflows/checkdocs.yml b/.github/workflows/checkdocs.yml index 917efc5398..0c2ecf3676 100644 --- a/.github/workflows/checkdocs.yml +++ b/.github/workflows/checkdocs.yml @@ -14,8 +14,8 @@ name: 'Docs' - '*/ci' paths: - '.github/workflows/checkdocs.yml' - - '.github/scripts/mdlinkcheck' - - '/scripts/**' + - '.github/scripts/**' + - 'scripts/**' - '**.md' - 'docs/*' pull_request: @@ -24,7 +24,7 @@ name: 'Docs' paths: - '.github/workflows/checkdocs.yml' - '.github/scripts/**' - - '.github/scripts/mdlinkcheck' + - 'scripts/**' - '**.md' - 'docs/*' @@ -35,127 +35,96 @@ concurrency: permissions: {} jobs: - # proselint: - # name: 'proselint' - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 - # with: - # persist-credentials: false - # - # - name: 'install prereqs' - # run: | - # sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list - # sudo apt-get -o Dpkg::Use-Pty=0 update - # sudo rm -f /var/lib/man-db/auto-update - # sudo apt-get -o Dpkg::Use-Pty=0 install python3-proselint - # - # # config file help: https://github.com/amperser/proselint/ - # - name: 'create proselint config' - # run: | - # cat < ~/.proselintrc.json - # { - # "checks": { - # "typography.diacritical_marks": false, - # "typography.symbols": false, - # "annotations.misc": false, - # "security.password": false, - # "misc.annotations": false - # } - # } - # JSON - # - # - name: 'trim headers off all *.md files' - # run: git ls-files -z '*.md' | xargs -0 -n1 .github/scripts/trimmarkdownheader.pl - # - # - name: 'check prose' - # run: git ls-files -z '*.md' | grep -Evz 'CHECKSRC.md|DISTROS.md|curl_mprintf.md|CURLOPT_INTERFACE.md|interface.md' | xargs -0 proselint README - # - # # This is for CHECKSRC and files with aggressive exclamation mark needs - # - name: 'create second proselint config' - # run: | - # cat < ~/.proselintrc.json - # { - # "checks": { - # "typography.diacritical_marks": false, - # "typography.symbols": false, - # "typography.exclamation": false, - # "lexical_illusions.misc": false, - # "annotations.misc": false - # } - # } - # JSON - # - # - name: 'check special prose' - # run: proselint docs/internals/CHECKSRC.md docs/libcurl/curl_mprintf.md docs/libcurl/opts/CURLOPT_INTERFACE.md docs/cmdline-opts/interface.md - - linkcheck: - name: 'linkcheck' - runs-on: ubuntu-latest + # config file help: https://github.com/amperser/proselint/ + proselint: + name: 'proselint' + runs-on: ubuntu-24.04-arm steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - name: 'mdlinkcheck' - run: ./scripts/mdlinkcheck + - name: 'install prereqs' + run: | + python3 -m venv ~/venv + ~/venv/bin/pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary -r .github/scripts/requirements-proselint.txt - spellcheck: - name: 'spellcheck' + - name: 'trim headers off all *.md files' + run: git ls-files '*.md' -z | xargs -0 -n1 .github/scripts/trimmarkdownheader.pl + + - name: 'check prose' + run: | + cat < ~/.proselintrc.json + { + "checks": { + "annotations.misc": false, + "lexical_illusions": false, + "misc.annotations": false, + "redundancy.misc.garner": false, + "security.password": false, + "spelling.ve_of": false, + "typography.diacritical_marks": false, + "typography.symbols": false + } + } + JSON + source ~/venv/bin/activate + git ls-files README '*.md' -z | grep -Evz '(CHECKSRC|DISTROS|CURLOPT_INTERFACE|interface)\.md' | xargs -0 proselint check -- + + - name: 'check special prose' # For CHECKSRC and files with aggressive exclamation mark needs + run: | + cat < ~/.proselintrc.json + { + "checks": { + "annotations.misc": false, + "lexical_illusions": false, + "typography.diacritical_marks": false, + "typography.punctuation.exclamation": false, + "typography.symbols": false + } + } + JSON + source ~/venv/bin/activate + proselint check docs/internals/CHECKSRC.md docs/libcurl/opts/CURLOPT_INTERFACE.md docs/cmdline-opts/interface.md + + pyspelling: + name: 'pyspelling' runs-on: ubuntu-latest steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: 'trim all *.md files in docs/' + run: .github/scripts/cleancmd.pl 'docs/*.md' + + - name: 'install' run: | - # shellcheck disable=SC2046 - .github/scripts/cleancmd.pl $(find docs -name '*.md') + sudo rm -f /etc/apt/sources.list.d/{azure-cli.sources,microsoft-prod.list,ondrej-ubuntu-php-noble.sources} + sudo apt-get -o Dpkg::Use-Pty=0 update + sudo apt-get -o Dpkg::Use-Pty=0 install aspell aspell-en + python3 -m venv ~/venv + ~/venv/bin/pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary -r .github/scripts/requirements-docs.txt - - name: 'setup the custom wordlist' - run: grep -v '^#' .github/scripts/spellcheck.words > wordlist.txt + - name: 'check spelling' + run: | + source ~/venv/bin/activate + # setup the custom wordlist + grep -v '^#' .github/scripts/pyspelling.words > wordlist.txt + aspell --version + pyspelling --version + pyspelling --verbose --jobs 5 --config .github/scripts/pyspelling.yaml - - name: 'check Spelling' - uses: rojopolis/spellcheck-github-actions@35a02bae020e6999c5c37fabaf447f2eb8822ca7 # v0 - with: - config_path: .github/scripts/spellcheck.yaml - - badwords-synopsis: - name: 'badwords, synopsis' - runs-on: ubuntu-latest + synopsis-man-examples: + name: 'synopsis, man-examples' + runs-on: ubuntu-24.04-arm steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - name: 'badwords' - run: | - # shellcheck disable=SC2046 - .github/scripts/badwords.pl < .github/scripts/badwords.txt $(git ls-files '**.md') docs/TODO docs/KNOWN_BUGS packages/OS400/README.OS400 - - name: 'verify synopsis' run: .github/scripts/verify-synopsis.pl docs/libcurl/curl*.md - man-examples: - name: 'man-examples' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - with: - persist-credentials: false - - name: 'verify examples' run: .github/scripts/verify-examples.pl docs/libcurl/curl*.md docs/libcurl/opts/*.md - - miscchecks: - name: 'spacecheck' - runs-on: ubuntu-24.04-arm - timeout-minutes: 5 - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - with: - persist-credentials: false - - - name: 'spacecheck' - run: .github/scripts/spacecheck.pl diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index 061a192297..5e3e87ca59 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -12,129 +12,153 @@ name: 'Source' - master - '*/ci' paths-ignore: - - '**/*.md' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'plan9/**' - - 'tests/data/**' - - 'winbuild/**' pull_request: branches: - master paths-ignore: - - '**/*.md' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'plan9/**' - - 'tests/data/**' - - 'winbuild/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true permissions: {} jobs: checksrc: name: 'checksrc' - runs-on: ubuntu-latest + runs-on: ubuntu-slim steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: 'check' run: scripts/checksrc-all.pl - spellcheck-cmakelint-pytype-ruff: - name: 'spellcheck, cmakelint, pytype, ruff' - runs-on: ubuntu-latest + linters: + name: 'spellcheck, linters, REUSE' + runs-on: ubuntu-24.04-arm steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - name: 'install' - env: - DEBIAN_FRONTEND: noninteractive + - name: 'install prereqs' run: | - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list - sudo apt-get -o Dpkg::Use-Pty=0 update - sudo rm -f /var/lib/man-db/auto-update - sudo apt-get -o Dpkg::Use-Pty=0 install \ - python3-pip python3-networkx python3-pydot python3-yaml \ - python3-toml python3-markupsafe python3-jinja2 python3-tabulate \ - python3-typing-extensions python3-libcst python3-impacket \ - python3-websockets python3-pytest python3-filelock python3-pytest-xdist - python3 -m pip install --break-system-packages cmakelang==0.6.13 pytype==2024.10.11 ruff==0.11.9 codespell==2.4.1 + python3 -m venv ~/venv + ~/venv/bin/pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary \ + -r .github/scripts/requirements.txt \ + -r tests/http/requirements.txt \ + -r tests/requirements.txt + + - name: 'REUSE check' + run: | + source ~/venv/bin/activate + reuse lint - name: 'codespell' run: | + source ~/venv/bin/activate codespell --version .github/scripts/codespell.sh - name: 'typos' + timeout-minutes: 2 run: | - /home/linuxbrew/.linuxbrew/bin/brew install typos-cli + HOMEBREW_NO_AUTO_UPDATE=1 /home/linuxbrew/.linuxbrew/bin/brew install typos-cli eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" typos --version .github/scripts/typos.sh - name: 'cmakelint' - run: scripts/cmakelint.sh + run: | + source ~/venv/bin/activate + scripts/cmakelint.sh + + - name: 'perlcheck' + run: | + scripts/perlcheck.sh - name: 'pytype' - run: find . -name '*.py' -exec pytype -j auto -k {} + + run: | + source ~/venv/bin/activate + find . -name '*.py' -exec pytype -j auto -k -- {} + - name: 'ruff' - run: scripts/pythonlint.sh - - reuse: - name: 'REUSE' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - with: - persist-credentials: false - - - name: 'check' - uses: fsfe/reuse-action@bb774aa972c2a89ff34781233d275075cbddf542 # v5 + run: | + source ~/venv/bin/activate + scripts/pythonlint.sh complexity: name: 'complexity' - runs-on: ubuntu-latest + runs-on: ubuntu-slim timeout-minutes: 3 steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - with: - persist-credentials: false - - name: 'install pmccabe' run: | - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list + sudo rm -f /etc/apt/sources.list.d/{azure-cli.sources,microsoft-prod.list,ondrej-ubuntu-php-noble.sources} sudo apt-get -o Dpkg::Use-Pty=0 update - sudo rm -f /var/lib/man-db/auto-update sudo apt-get -o Dpkg::Use-Pty=0 install \ pmccabe + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: 'check scores' run: ./scripts/top-complexity + xmllint: + name: 'xmllint' + runs-on: ubuntu-slim + timeout-minutes: 3 + steps: + - name: 'install prereqs' + run: | + sudo rm -f /etc/apt/sources.list.d/{azure-cli.sources,microsoft-prod.list,ondrej-ubuntu-php-noble.sources} + sudo apt-get -o Dpkg::Use-Pty=0 update + sudo apt-get -o Dpkg::Use-Pty=0 install \ + libxml2-utils + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: 'check' + run: git grep -z -i -l -E '^<\?xml' | xargs -0 -r xmllint --output /dev/null + miscchecks: name: 'misc checks' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm timeout-minutes: 5 steps: - name: 'install prereqs' - run: /home/linuxbrew/.linuxbrew/bin/brew install shellcheck zizmor + timeout-minutes: 2 + run: HOMEBREW_NO_AUTO_UPDATE=1 /home/linuxbrew/.linuxbrew/bin/brew install actionlint shellcheck zizmor - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: 'zizmor GHA' + env: + GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}' run: | eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" - zizmor --pedantic .github/workflows/*.yml + zizmor --pedantic .github/workflows/*.yml .github/dependabot.yml + + - name: 'actionlint' + run: | + eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + export SHELLCHECK_OPTS='--exclude=1090,1091,2086,2153 --enable=avoid-nullary-conditions,deprecate-which' + actionlint --version + actionlint --ignore matrix .github/workflows/*.yml - name: 'shellcheck CI' run: | @@ -149,14 +173,10 @@ jobs: .github/scripts/shellcheck.sh - name: 'spacecheck' - run: .github/scripts/spacecheck.pl + run: scripts/spacecheck.pl - name: 'yamlcheck' run: .github/scripts/yamlcheck.sh - # we allow some extra in source code - name: 'badwords' - run: | - # shellcheck disable=SC2046 - grep -Ev '(\\bwill| url | dir )' .github/scripts/badwords.txt | \ - .github/scripts/badwords.pl $(git ls-files -- src lib include) + run: scripts/badwords-all diff --git a/.github/workflows/checkurls.yml b/.github/workflows/checkurls.yml new file mode 100644 index 0000000000..ec9b511205 --- /dev/null +++ b/.github/workflows/checkurls.yml @@ -0,0 +1,40 @@ +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + +name: 'URLs' + +'on': + push: + branches: + - master + - '*/ci' + pull_request: + branches: + - master + schedule: + - cron: '10 5 * * *' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + +permissions: {} + +jobs: + linkcheck: + if: ${{ github.repository_owner == 'curl' || github.event_name != 'schedule' }} + name: 'linkcheck' + runs-on: ubuntu-slim + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: 'mdlinkcheck (dry run)' + if: ${{ github.event_name != 'schedule' }} + run: ./scripts/mdlinkcheck --dry-run + + - name: 'mdlinkcheck' + if: ${{ github.event_name == 'schedule' }} + run: ./scripts/mdlinkcheck diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 25a8f8cbc0..17db4154fc 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -13,12 +13,8 @@ name: 'CodeQL' - '**/*.md' - '.circleci/**' - 'appveyor.*' - - 'docs/**' - - 'packages/**' - - 'plan9/**' - 'projects/**' - 'tests/data/**' - - 'winbuild/**' pull_request: branches: - master @@ -26,36 +22,120 @@ name: 'CodeQL' - '**/*.md' - '.circleci/**' - 'appveyor.*' - - 'docs/**' - - 'packages/**' - - 'plan9/**' - 'projects/**' - 'tests/data/**' - - 'winbuild/**' schedule: - cron: '0 0 * * 4' concurrency: - group: ${{ github.workflow }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true permissions: {} jobs: - codeql: + gha_python: + if: ${{ github.repository_owner == 'curl' || github.event_name != 'schedule' }} name: 'GHA and Python' runs-on: ubuntu-latest permissions: - security-events: write + security-events: write # To create/update security events steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: 'initialize' - uses: github/codeql-action/init@d3678e237b9c32a6c9bffb3315c335f976f3549f # v3 + uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 with: languages: actions, python queries: security-extended - name: 'perform analysis' - uses: github/codeql-action/analyze@d3678e237b9c32a6c9bffb3315c335f976f3549f # v3 + uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + + c: + if: ${{ github.repository_owner == 'curl' || github.event_name != 'schedule' }} + name: 'C' + runs-on: ${{ matrix.platform == 'Linux' && 'ubuntu-latest' || 'windows-2022' }} + permissions: + security-events: write # To create/update security events + strategy: + fail-fast: false + matrix: + platform: [Linux, Windows] + env: + MATRIX_PLATFORM: '${{ matrix.platform }}' + steps: + - name: 'install prereqs' + if: ${{ matrix.platform == 'Linux' }} + timeout-minutes: 5 + run: | + sudo rm -f /etc/apt/sources.list.d/{azure-cli.sources,microsoft-prod.list,ondrej-ubuntu-php-noble.sources} + sudo apt-get -o Dpkg::Use-Pty=0 update + printf "#!/bin/sh + while [ \$? = 0 ]; do for i in 1 2 3; do timeout 60 \"\$@\" && break 2; echo \"Error: slow server, retry \$i\"; sleep 1 + dpkg --configure -a; done; false; done" > "$HOME"/my-apt; chmod +x "$HOME"/my-apt + sudo "$HOME"/my-apt apt-get -o Dpkg::Use-Pty=0 install \ + libpsl-dev libbrotli-dev libidn2-dev libssh2-1-dev libssh-dev \ + libnghttp2-dev libldap-dev libkrb5-dev libgnutls28-dev libwolfssl-dev + HOMEBREW_NO_AUTO_UPDATE=1 /home/linuxbrew/.linuxbrew/bin/brew install c-ares gsasl libnghttp3 libngtcp2 mbedtls rustls-ffi + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: 'delete test input C files' + shell: bash + run: find tests/data -name '*.c' -delete + + - name: 'initialize' + # https://github.com/github/codeql-action/blob/main/init/action.yml + uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + with: + languages: cpp + build-mode: manual + trap-caching: false + + - name: 'build' + timeout-minutes: 10 + shell: bash + run: | + if [ "${MATRIX_PLATFORM}" = 'Windows' ]; then + cmake -B . -DBUILD_SHARED_LIBS=OFF -DCURL_DROP_UNUSED=ON -DCURL_WERROR=ON \ + -DCMAKE_VS_GLOBALS=TrackFileAccess=false \ + -DCURL_USE_SCHANNEL=ON -DCURL_USE_LIBPSL=OFF -DUSE_WIN32_IDN=ON + cmake --build . --verbose + src/Debug/curl.exe --disable --version + else + eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + + export PKG_CONFIG_PATH + + # MultiSSL + PKG_CONFIG_PATH="$(brew --prefix c-ares)/lib/pkgconfig:$(brew --prefix mbedtls)/lib/pkgconfig:$(brew --prefix rustls-ffi)/lib/pkgconfig:$(brew --prefix gsasl)/lib/pkgconfig" + cmake -B _bld1 -G Ninja -DCURL_DISABLE_TYPECHECK=ON -DCURL_WERROR=ON -DENABLE_DEBUG=ON \ + -DCURL_USE_GNUTLS=ON -DCURL_USE_MBEDTLS=ON -DCURL_USE_RUSTLS=ON -DCURL_USE_WOLFSSL=ON \ + -DCURL_USE_GSASL=ON -DCURL_USE_GSSAPI=ON -DUSE_SSLS_EXPORT=ON -DUSE_ECH=ON -DENABLE_ARES=ON \ + -DCURL_DISABLE_VERBOSE_STRINGS=ON + cmake --build _bld1 + cmake --build _bld1 --target testdeps + cmake --build _bld1 --target curl-examples-build + + # HTTP/3 + PKG_CONFIG_PATH="$(brew --prefix libnghttp3)/lib/pkgconfig:$(brew --prefix libngtcp2)/lib/pkgconfig:$(brew --prefix gsasl)/lib/pkgconfig" + cmake -B _bld2 -G Ninja -DCURL_DISABLE_TYPECHECK=ON -DCURL_WERROR=ON \ + -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR="$(brew --prefix openssl)" -DUSE_NGTCP2=ON \ + -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON \ + -DCURL_USE_GSASL=ON -DCURL_USE_GSSAPI=ON -DUSE_SSLS_EXPORT=ON + cmake --build _bld2 + cmake --build _bld2 --target testdeps + cmake --build _bld2 --target curl-examples-build + + _bld1/src/curl --disable --version + _bld2/src/curl --disable --version + fi + + - name: 'perform analysis' + # https://github.com/github/codeql-action/blob/main/analyze/action.yml + uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 diff --git a/.github/workflows/configure-vs-cmake.yml b/.github/workflows/configure-vs-cmake.yml index 984fccd592..8734e1a534 100644 --- a/.github/workflows/configure-vs-cmake.yml +++ b/.github/workflows/configure-vs-cmake.yml @@ -12,7 +12,7 @@ name: 'configure-vs-cmake' - '**/*.m4' - '**/CMakeLists.txt' - 'CMake/**' - - 'lib/curl_config.h.cmake' + - 'lib/curl_config-cmake.h.in' - 'tests/cmake/**' - '.github/scripts/cmp-config.pl' - '.github/workflows/configure-vs-cmake.yml' @@ -25,11 +25,15 @@ name: 'configure-vs-cmake' - '**/*.m4' - '**/CMakeLists.txt' - 'CMake/**' - - 'lib/curl_config.h.cmake' + - 'lib/curl_config-cmake.h.in' - 'tests/cmake/**' - '.github/scripts/cmp-config.pl' - '.github/workflows/configure-vs-cmake.yml' +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + permissions: {} jobs: @@ -37,7 +41,7 @@ jobs: name: 'Linux' runs-on: ubuntu-latest steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -48,7 +52,7 @@ jobs: mkdir bld-am && cd bld-am && ../configure --enable-static=no --with-openssl --without-libpsl - name: 'run cmake' - run: cmake -B bld-cm -DCURL_WERROR=ON -DCURL_USE_LIBPSL=OFF + run: cmake -B bld-cm -DCURL_WERROR=ON -DCURL_USE_CMAKECONFIG=OFF -DCURL_USE_LIBPSL=OFF - name: 'configure log' run: cat bld-am/config.log 2>/dev/null || true @@ -77,14 +81,25 @@ jobs: runs-on: macos-latest steps: - name: 'install packages' + timeout-minutes: 2 run: | - # shellcheck disable=SC2181,SC2034 - while [[ $? == 0 ]]; do for i in 1 2 3; do if brew update && brew install automake libtool; then break 2; else echo Error: wait to try again; sleep 10; fi; done; false Too many retries; done + # shellcheck disable=SC2181 + while [[ $? == 0 ]]; do + for i in 1 2 3; do + if brew update && brew install automake libtool; then + break 2 + else + echo "Error: wait to try again: $i" + sleep 10 + fi + done + false Too many retries + done - name: 'toolchain versions' run: echo '::group::brew packages installed'; ls -l /opt/homebrew/opt; echo '::endgroup::' - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -92,13 +107,13 @@ jobs: run: | autoreconf -fi export PKG_CONFIG_DEBUG_SPEW=1 - mkdir bld-am && cd bld-am && ../configure --enable-static=no --with-openssl --without-libpsl --disable-ldap --with-brotli --with-zstd + mkdir bld-am && cd bld-am && ../configure --enable-static=no --with-openssl --without-libpsl --disable-ldap --with-brotli --with-zstd --with-apple-sectrust - name: 'run cmake' run: | - cmake -B bld-cm -DCURL_WERROR=ON -DCURL_USE_LIBPSL=OFF -DCURL_DISABLE_LDAP=ON \ + cmake -B bld-cm -DCURL_WERROR=ON -DCURL_USE_CMAKECONFIG=OFF -DCURL_USE_LIBPSL=OFF -DCURL_DISABLE_LDAP=ON \ -DCMAKE_C_COMPILER_TARGET="$(uname -m | sed 's/arm64/aarch64/')-apple-darwin$(uname -r)" \ - -DCURL_USE_LIBSSH2=OFF + -DCURL_USE_LIBSSH2=OFF -DUSE_APPLE_SECTRUST=ON - name: 'configure log' run: cat bld-am/config.log 2>/dev/null || true @@ -130,10 +145,12 @@ jobs: steps: - name: 'install packages' run: | - sudo rm -f /var/lib/man-db/auto-update - sudo apt-get -o Dpkg::Use-Pty=0 install mingw-w64 + printf "#!/bin/sh + while [ \$? = 0 ]; do for i in 1 2 3; do timeout 30 \"\$@\" && break 2; echo \"Error: slow server, retry \$i\"; sleep 1 + dpkg --configure -a; done; false; done" > "$HOME"/my-apt; chmod +x "$HOME"/my-apt + sudo "$HOME"/my-apt apt-get -o Dpkg::Use-Pty=0 install gcc-mingw-w64-x86-64-win32 - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -145,7 +162,7 @@ jobs: - name: 'run cmake' run: | - cmake -B bld-cm -DCURL_WERROR=ON -DCURL_USE_SCHANNEL=ON -DCURL_USE_LIBPSL=OFF \ + cmake -B bld-cm -DCURL_WERROR=ON -DCURL_USE_CMAKECONFIG=OFF -DCURL_USE_SCHANNEL=ON -DCURL_USE_LIBPSL=OFF \ -DCMAKE_SYSTEM_NAME=Windows \ -DCMAKE_C_COMPILER_TARGET="${TRIPLET}" \ -DCMAKE_C_COMPILER="${TRIPLET}-gcc" diff --git a/.github/workflows/curl-for-win.yml b/.github/workflows/curl-for-win.yml index 704a78d2cd..74a81f7e55 100644 --- a/.github/workflows/curl-for-win.yml +++ b/.github/workflows/curl-for-win.yml @@ -14,10 +14,7 @@ name: 'curl-for-win' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' pull_request: branches: - master @@ -26,10 +23,7 @@ name: 'curl-for-win' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} @@ -49,7 +43,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false path: 'curl' @@ -58,19 +52,46 @@ jobs: run: | git clone --depth 1 https://github.com/curl/curl-for-win mv curl-for-win/* . - export CW_CONFIG='-main-werror-unitybatch-linux-a64-x64-gcc' + export CW_CONFIG='-main-werror-unitybatch-nocertdata-linux-a64-x64-gcc' export CW_REVISION="${GITHUB_SHA}" - DOCKER_IMAGE='debian:bookworm-slim' + . ./_versions.sh export CW_CCSUFFIX='-15' export CW_GCCSUFFIX='-12' sudo podman image trust set --type reject default sudo podman image trust set --type accept docker.io/library - time podman pull "${DOCKER_IMAGE}" + time podman pull "${OCI_IMAGE_DEBIAN_STABLE}" podman images --digests time podman run --volume "$(pwd):$(pwd)" --workdir "$(pwd)" \ --env-file <(env | grep -a -E \ '^(CW_|GITHUB_)') \ - "${DOCKER_IMAGE}" \ + "${OCI_IMAGE_DEBIAN_STABLE}" \ + sh -c ./_ci-linux-debian.sh + + linux-glibc-gcc-minimal: # use gcc to minimize installed packages + name: 'Linux gcc glibc minimal (amd64)' + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + path: 'curl' + fetch-depth: 8 + - name: 'build' + run: | + git clone --depth 1 https://github.com/curl/curl-for-win + mv curl-for-win/* . + export CW_CONFIG='-main-werror-unitybatch-nocertdata-prefill-zero-osnotls-osnoidn-nohttp-nocurltool-linux-x64-gcc' + export CW_REVISION="${GITHUB_SHA}" + . ./_versions.sh + sudo podman image trust set --type reject default + sudo podman image trust set --type accept docker.io/library + time podman pull "${OCI_IMAGE_DEBIAN}" + podman images --digests + time podman run --volume "$(pwd):$(pwd)" --workdir "$(pwd)" \ + --env-file <(env | grep -a -E \ + '^(CW_|GITHUB_)') \ + "${OCI_IMAGE_DEBIAN}" \ sh -c ./_ci-linux-debian.sh linux-musl-llvm: @@ -78,7 +99,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false path: 'curl' @@ -87,27 +108,27 @@ jobs: run: | git clone --depth 1 https://github.com/curl/curl-for-win mv curl-for-win/* . - export CW_CONFIG='-main-werror-unitybatch-linux-musl-r64-x64' + export CW_CONFIG='-main-werror-unitybatch-nocertdata-linux-musl-r64-x64' export CW_REVISION="${GITHUB_SHA}" . ./_versions.sh sudo podman image trust set --type reject default sudo podman image trust set --type accept docker.io/library - time podman pull "${DOCKER_IMAGE}" + time podman pull "${OCI_IMAGE_DEBIAN}" podman images --digests time podman run --volume "$(pwd):$(pwd)" --workdir "$(pwd)" \ --env-file <(env | grep -a -E \ '^(CW_|GITHUB_)') \ - "${DOCKER_IMAGE}" \ + "${OCI_IMAGE_DEBIAN}" \ sh -c ./_ci-linux-debian.sh mac-clang: - name: 'macOS clang (x86_64)' + name: 'macOS clang cares (x86_64)' runs-on: macos-latest timeout-minutes: 10 env: CW_JOBS: '4' steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false path: 'curl' @@ -116,7 +137,7 @@ jobs: run: | git clone --depth 1 https://github.com/curl/curl-for-win mv curl-for-win/* . - export CW_CONFIG='-main-werror-unitybatch-mac-x64' + export CW_CONFIG='-main-werror-unitybatch-nocertdata-mac-x64-cares' export CW_REVISION="${GITHUB_SHA}" sh -c ./_ci-mac-homebrew.sh @@ -125,7 +146,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false path: 'curl' @@ -134,25 +155,25 @@ jobs: run: | git clone --depth 1 https://github.com/curl/curl-for-win mv curl-for-win/* . - export CW_CONFIG='-main-werror-unitybatch-win-x64' + export CW_CONFIG='-main-werror-unitybatch-nocertdata-win-x64-noWINE' export CW_REVISION="${GITHUB_SHA}" . ./_versions.sh sudo podman image trust set --type reject default sudo podman image trust set --type accept docker.io/library - time podman pull "${DOCKER_IMAGE}" + time podman pull "${OCI_IMAGE_DEBIAN}" podman images --digests time podman run --volume "$(pwd):$(pwd)" --workdir "$(pwd)" \ --env-file <(env | grep -a -E \ '^(CW_|GITHUB_)') \ - "${DOCKER_IMAGE}" \ + "${OCI_IMAGE_DEBIAN}" \ sh -c ./_ci-linux-debian.sh - win-gcc-libssh-zlibold-x64: - name: 'Windows gcc libssh zlib-classic (x64)' + win-gcc-zlibold-x64: + name: 'Windows gcc zlib-classic (x64)' runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false path: 'curl' @@ -161,15 +182,15 @@ jobs: run: | git clone --depth 1 https://github.com/curl/curl-for-win mv curl-for-win/* . - export CW_CONFIG='-main-werror-unitybatch-win-x64-gcc-libssh1-zlibold' + export CW_CONFIG='-main-werror-unitybatch-nocertdata-win-x64-gcc-zlibold-noWINE' export CW_REVISION="${GITHUB_SHA}" . ./_versions.sh sudo podman image trust set --type reject default sudo podman image trust set --type accept docker.io/library - time podman pull "${DOCKER_IMAGE}" + time podman pull "${OCI_IMAGE_DEBIAN}" podman images --digests time podman run --volume "$(pwd):$(pwd)" --workdir "$(pwd)" \ --env-file <(env | grep -a -E \ '^(CW_|GITHUB_)') \ - "${DOCKER_IMAGE}" \ + "${OCI_IMAGE_DEBIAN}" \ sh -c ./_ci-linux-debian.sh diff --git a/.github/workflows/distcheck.yml b/.github/workflows/distcheck.yml index bf6c69bfcf..b9c4c71988 100644 --- a/.github/workflows/distcheck.yml +++ b/.github/workflows/distcheck.yml @@ -20,6 +20,7 @@ concurrency: permissions: {} env: + CURL_TEST_MIN: 1450 MAKEFLAGS: -j 5 jobs: @@ -28,7 +29,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -47,7 +48,7 @@ jobs: - name: 'maketgz' run: SOURCE_DATE_EPOCH=1711526400 ./scripts/maketgz 99.98.97 - - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4 + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: name: 'release-tgz' path: 'curl-99.98.97.tar.gz' @@ -58,7 +59,7 @@ jobs: echo "::stop-commands::$(uuidgen)" tar xvf curl-99.98.97.tar.gz pushd curl-99.98.97 - ./configure --prefix="$HOME"/temp --enable-werror --without-ssl --without-libpsl + ./configure --prefix="$HOME"/temp --enable-option-checking=fatal --enable-werror --without-ssl --without-libpsl make make test-ci make install @@ -73,7 +74,7 @@ jobs: timeout-minutes: 10 needs: maketgz-and-verify-in-tree steps: - - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: 'release-tgz' @@ -84,7 +85,7 @@ jobs: touch curl-99.98.97/docs/{cmdline-opts,libcurl}/Makefile.inc mkdir build pushd build - ../curl-99.98.97/configure --enable-werror --without-ssl --without-libpsl + ../curl-99.98.97/configure --enable-option-checking=fatal --enable-werror --without-ssl --without-libpsl make make test-ci popd @@ -97,7 +98,7 @@ jobs: timeout-minutes: 10 needs: maketgz-and-verify-in-tree steps: - - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: 'release-tgz' @@ -108,7 +109,7 @@ jobs: pushd curl-99.98.97 mkdir build pushd build - ../configure --prefix="$PWD"/curl-install --enable-werror --without-ssl --enable-debug --without-libpsl + ../configure --prefix="$PWD"/curl-install --enable-option-checking=fatal --enable-werror --without-ssl --enable-debug --without-libpsl make make test-ci make install @@ -123,7 +124,7 @@ jobs: timeout-minutes: 10 needs: maketgz-and-verify-in-tree steps: - - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: 'release-tgz' @@ -134,7 +135,7 @@ jobs: pushd curl-99.98.97 mkdir build pushd build - ../configure --prefix="$PWD"/curl-install --enable-werror --without-ssl --without-libpsl ac_cv_path_PERL= + ../configure --prefix="$PWD"/curl-install --enable-option-checking=fatal --enable-werror --without-ssl --without-libpsl ac_cv_path_PERL= make make install curl-install/bin/curl --disable --version @@ -147,7 +148,7 @@ jobs: timeout-minutes: 10 needs: maketgz-and-verify-in-tree steps: - - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: 'release-tgz' @@ -156,7 +157,7 @@ jobs: echo "::stop-commands::$(uuidgen)" tar xvf curl-99.98.97.tar.gz pushd curl-99.98.97 - ./configure --prefix="$PWD"/curl-install --enable-werror --without-ssl --without-libpsl ac_cv_path_PERL= + ./configure --prefix="$PWD"/curl-install --enable-option-checking=fatal --enable-werror --without-ssl --without-libpsl ac_cv_path_PERL= make make install curl-install/bin/curl --disable --version @@ -168,7 +169,7 @@ jobs: timeout-minutes: 5 needs: maketgz-and-verify-in-tree steps: - - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: 'release-tgz' @@ -190,7 +191,7 @@ jobs: timeout-minutes: 5 needs: maketgz-and-verify-in-tree steps: - - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: 'release-tgz' @@ -208,15 +209,15 @@ jobs: missing-files: name: 'missing files' - runs-on: ubuntu-latest + runs-on: ubuntu-slim timeout-minutes: 5 needs: maketgz-and-verify-in-tree steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: 'release-tgz' @@ -228,7 +229,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -248,21 +249,25 @@ jobs: cmake-integration: name: 'CM integration ${{ matrix.image }}' runs-on: ${{ matrix.image }} - timeout-minutes: 10 + timeout-minutes: 15 defaults: run: shell: ${{ contains(matrix.image, 'windows') && 'msys2 {0}' || 'bash' }} env: CC: ${{ !contains(matrix.image, 'windows') && 'clang' || '' }} + MAKEFLAGS: ${{ contains(matrix.image, 'macos') && '-j 4' || '-j 5' }} MATRIX_IMAGE: '${{ matrix.image }}' TESTOPTS: ${{ contains(matrix.image, 'macos') && '-D_CURL_PREFILL=ON' || '' }} ${{ contains(matrix.image, 'windows') && '-DCMAKE_UNITY_BUILD_BATCH_SIZE=30' || '' }} - OLD_CMAKE_VERSION: 3.11.4 + OLD_CMAKE_VERSION: 3.19.8 + OLD_CMAKE_SHA256_LINUX_ARM: 807f5afb2a560e00af9640e496d5673afefc2888bf0ed076412884a5ebb547a1 + OLD_CMAKE_SHA256_MACOS_UNI: 0976d23d982af05dcbfb3aa34fcb62ead43bea27f0e3bb95222f2a78161423f2 + OLD_CMAKE_SHA256_WIN_INTEL: 2a30877a3d6b50da305b289f4d1c03befdfaeb2edba02a563c681e883d810380 strategy: fail-fast: false matrix: - image: [ubuntu-latest, macos-latest, windows-2022] + image: [ubuntu-24.04-arm, macos-latest, windows-2022] steps: - - uses: msys2/setup-msys2@fb197b72ce45fb24f17bf3f807a388985654d1f2 # v2 + - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 if: ${{ contains(matrix.image, 'windows') }} with: msystem: mingw64 @@ -274,30 +279,34 @@ jobs: mingw-w64-x86_64-zlib mingw-w64-x86_64-zstd mingw-w64-x86_64-libpsl mingw-w64-x86_64-libssh2 mingw-w64-x86_64-nghttp2 mingw-w64-x86_64-openssl - name: 'install prereqs' + timeout-minutes: 3 run: | if [[ "${MATRIX_IMAGE}" = *'windows'* ]]; then cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/Kitware/CMake/releases/download/v${OLD_CMAKE_VERSION}/cmake-${OLD_CMAKE_VERSION}-win64-x64.zip" --output bin.zip - unzip -q bin.zip - rm -f bin.zip + --location "https://github.com/Kitware/CMake/releases/download/v${OLD_CMAKE_VERSION}/cmake-${OLD_CMAKE_VERSION}-win64-x64.zip" --output pkg.bin + sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${OLD_CMAKE_SHA256_WIN_INTEL}" && unzip -q pkg.bin && rm -f pkg.bin printf '%s' ~/cmake-"${OLD_CMAKE_VERSION}"-win64-x64/bin/cmake.exe > ~/old-cmake-path.txt elif [[ "${MATRIX_IMAGE}" = *'ubuntu'* ]]; then - sudo rm -f /var/lib/man-db/auto-update - sudo apt-get -o Dpkg::Use-Pty=0 install libpsl-dev libssl-dev + printf "#!/bin/sh + while [ \$? = 0 ]; do for i in 1 2 3; do timeout 30 \"\$@\" && break 2; echo \"Error: slow server, retry \$i\"; sleep 1 + dpkg --configure -a; done; false; done" > "$HOME"/my-apt; chmod +x "$HOME"/my-apt + sudo "$HOME"/my-apt apt-get -o Dpkg::Use-Pty=0 install libpsl-dev libssl-dev cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/Kitware/CMake/releases/download/v${OLD_CMAKE_VERSION}/cmake-${OLD_CMAKE_VERSION}-Linux-x86_64.tar.gz" | tar -xz - printf '%s' ~/cmake-"${OLD_CMAKE_VERSION}"-Linux-x86_64/bin/cmake > ~/old-cmake-path.txt + --location "https://github.com/Kitware/CMake/releases/download/v${OLD_CMAKE_VERSION}/cmake-${OLD_CMAKE_VERSION}-Linux-aarch64.tar.gz" --output pkg.bin + sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${OLD_CMAKE_SHA256_LINUX_ARM}" && tar -xzf pkg.bin && rm -f pkg.bin + printf '%s' ~/cmake-"${OLD_CMAKE_VERSION}"-Linux-aarch64/bin/cmake > ~/old-cmake-path.txt else brew install libpsl openssl cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/Kitware/CMake/releases/download/v${OLD_CMAKE_VERSION}/cmake-${OLD_CMAKE_VERSION}-Darwin-x86_64.tar.gz" | tar -xz - printf '%s' ~/cmake-"${OLD_CMAKE_VERSION}"-Darwin-x86_64/CMake.app/Contents/bin/cmake > ~/old-cmake-path.txt + --location "https://github.com/Kitware/CMake/releases/download/v${OLD_CMAKE_VERSION}/cmake-${OLD_CMAKE_VERSION}-macos-universal.tar.gz" --output pkg.bin + sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${OLD_CMAKE_SHA256_MACOS_UNI}" && tar -xzf pkg.bin && rm -f pkg.bin + printf '%s' ~/cmake-"${OLD_CMAKE_VERSION}"-macos-universal/CMake.app/Contents/bin/cmake > ~/old-cmake-path.txt fi - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -310,6 +319,16 @@ jobs: run: ./tests/cmake/test.sh add_subdirectory ${TESTOPTS} -DCURL_USE_OPENSSL=ON - name: 'via find_package' run: ./tests/cmake/test.sh find_package ${TESTOPTS} -DCURL_USE_OPENSSL=ON + - name: 'via find_package (C++)' + if: ${{ contains(matrix.image, 'ubuntu') }} + run: TEST_CMAKE_FLAGS=-DTEST_CPP=ON ./tests/cmake/test.sh find_package ${TESTOPTS} -DCURL_USE_OPENSSL=ON + - name: 'via find_package (PREFER_CONFIG=ON)' + if: ${{ contains(matrix.image, 'windows') }} + run: | + export TEST_CMAKE_FLAGS_PROVIDER='-DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON -DCURL_ZSTD=OFF' + TEST_CMAKE_FLAGS_PROVIDER+=' -DNGHTTP2_INCLUDE_DIR=C:/msys64/mingw64/include -DNGHTTP2_LIBRARY=C:/msys64/mingw64/lib/libnghttp2.dll.a' + export TEST_CMAKE_FLAGS_CONSUMER="${TEST_CMAKE_FLAGS_PROVIDER}" + ./tests/cmake/test.sh find_package ${TESTOPTS} -DCURL_USE_OPENSSL=ON - name: 'via ExternalProject (old cmake)' if: ${{ contains(matrix.image, 'ubuntu') }} diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index d9cc43169e..45884515e8 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -16,11 +16,8 @@ name: 'Fuzzer' - 'appveyor.*' - 'CMake/**' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - 'tests/data/**' - - 'winbuild/**' pull_request: branches: - master @@ -31,14 +28,16 @@ name: 'Fuzzer' - 'appveyor.*' - 'CMake/**' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - 'tests/data/**' - - 'winbuild/**' + +concurrency: + # Hard-coded workflow name to avoid colliding with curl-fuzzer's group + group: curl-fuzz-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true permissions: {} jobs: Fuzzing: - uses: curl/curl-fuzzer/.github/workflows/ci.yml@master + uses: curl/curl-fuzzer/.github/workflows/ci.yml@master # zizmor: ignore[unpinned-uses] diff --git a/.github/workflows/hacktoberfest-accepted.yml b/.github/workflows/hacktoberfest-accepted.yml deleted file mode 100644 index 916b354481..0000000000 --- a/.github/workflows/hacktoberfest-accepted.yml +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (C) Daniel Stenberg, , et al. -# -# SPDX-License-Identifier: curl - -name: 'Hacktoberfest' - -'on': - # this must not ever run on any other branch than master - push: - branches: - - master - -concurrency: - # this should not run in parallel, so just run one at a time - group: ${{ github.workflow }} - -permissions: {} - -jobs: - # add hacktoberfest-accepted label to PRs opened starting from September 30th - # till November 1st which are closed via commit reference from master branch. - merged: - name: 'Add hacktoberfest-accepted label' - runs-on: ubuntu-latest - permissions: - # requires issues AND pull-requests write permissions to edit labels on PRs! - issues: write - pull-requests: write - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - with: - persist-credentials: false - fetch-depth: 100 - - - name: 'Check whether repo participates in Hacktoberfest' - id: check - env: - GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}' - run: | - gh config set prompt disabled && echo "label=$( - gh repo view --json repositoryTopics --jq '.repositoryTopics[].name' | grep '^hacktoberfest$')" >> "$GITHUB_OUTPUT" - - - name: 'Search relevant commit message lines starting with Closes/Merges' - if: ${{ steps.check.outputs.label == 'hacktoberfest' }} - env: - GITHUB_EVENT_BEFORE: '${{ github.event.before }}' - GITHUB_EVENT_AFTER: '${{ github.event.after }}' - run: | - git log --format=email "${GITHUB_EVENT_BEFORE}..${GITHUB_EVENT_AFTER}" | \ - grep -Ei '^Close[sd]? ' | sort | uniq | tee log - - - name: 'Search for number-based PR references' - if: ${{ steps.check.outputs.label == 'hacktoberfest' }} - env: - GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}' - run: | - grep -Eo '#([0-9]+)' log | cut -d# -f2 | sort | uniq | xargs -t -n1 -I{} \ - gh pr view {} --json number,createdAt \ - --jq '{number, opened: .createdAt} | [.number, .opened] | join(":")' | tee /dev/stderr | \ - grep -Eo '^([0-9]+):[0-9]{4}-(09-30T|10-|11-01T)' | cut -d: -f1 | sort | uniq | xargs -t -n1 -I {} \ - gh pr edit {} --add-label 'hacktoberfest-accepted' - - - name: 'Search for URL-based PR references' - if: ${{ steps.check.outputs.label == 'hacktoberfest' }} - env: - GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}' - run: | - grep -Eo 'github.com/(.+)/(.+)/pull/([0-9]+)' log | sort | uniq | xargs -t -n1 -I{} \ - gh pr view 'https://{}' --json number,createdAt \ - --jq '{number, opened: .createdAt} | [.number, .opened] | join(":")' | tee /dev/stderr | \ - grep -Eo '^([0-9]+):[0-9]{4}-(09-30T|10-|11-01T)' | cut -d: -f1 | sort | uniq | xargs -t -n1 -I {} \ - gh pr edit {} --add-label 'hacktoberfest-accepted' diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index a317f99783..39c19823fd 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -14,10 +14,7 @@ name: 'Linux HTTP/3' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' pull_request: branches: - master @@ -26,14 +23,10 @@ name: 'Linux HTTP/3' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' concurrency: - # Hardcoded workflow filename as workflow name above is just Linux again - group: http3-${{ github.event.pull_request.number || github.sha }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} cancel-in-progress: true permissions: {} @@ -41,28 +34,31 @@ permissions: {} env: MAKEFLAGS: -j 5 CURL_CI: github + CURL_TEST_MIN: 1600 # handled in renovate.json - OPENSSL_VERSION: 3.5.2 - # handled in renovate.json - QUICTLS_VERSION: 3.3.0 + OPENSSL_VERSION: 3.6.2 + # manually bumped + OPENSSL4_VERSION: 4.0.0 # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com - LIBRESSL_VERSION: 4.1.0 + LIBRESSL_VERSION: 4.2.1 # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com - AWSLC_VERSION: 1.60.0 + AWSLC_VERSION: 1.71.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20250818.0 - # renovate: datasource=github-tags depName=gnutls/gnutls versioning=semver registryUrl=https://github.com - GNUTLS_VERSION: 3.8.10 + BORINGSSL_VERSION: 0.20260413.0 + # renovate: datasource=github-tags depName=gnutls/nettle versioning=semver registryUrl=https://github.com + NETTLE_VERSION: 3.10.2 + # renovate: datasource=github-tags depName=gnutls/gnutls versioning=semver extractVersion=^nettle_?(?.+)_release_.+$ registryUrl=https://github.com + GNUTLS_VERSION: 3.8.11 # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com - WOLFSSL_VERSION: 5.8.2 + WOLFSSL_VERSION: 5.9.1 # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com - NGHTTP3_VERSION: 1.11.0 + NGHTTP3_VERSION: 1.15.0 # renovate: datasource=github-tags depName=ngtcp2/ngtcp2 versioning=semver registryUrl=https://github.com - NGTCP2_VERSION: 1.15.1 + NGTCP2_VERSION: 1.22.0 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com - NGHTTP2_VERSION: 1.67.0 + NGHTTP2_VERSION: 1.68.1 # renovate: datasource=github-tags depName=cloudflare/quiche versioning=semver registryUrl=https://github.com - QUICHE_VERSION: 0.24.6 + QUICHE_VERSION: 0.24.7 jobs: build-cache: @@ -71,16 +67,25 @@ jobs: steps: - name: 'cache openssl' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-openssl-http3 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-openssl-http3-no-deprecated env: - cache-name: cache-openssl-http3 + cache-name: cache-openssl-http3-no-deprecated with: path: ~/openssl/build key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_VERSION }} + - name: 'cache openssl4' + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-openssl4-http3-no-deprecated + env: + cache-name: cache-openssl4-http3-no-deprecated + with: + path: ~/openssl4/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL4_VERSION }} + - name: 'cache libressl' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-libressl env: cache-name: cache-libressl @@ -89,7 +94,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} - name: 'cache awslc' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-awslc env: cache-name: cache-awslc @@ -98,7 +103,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.AWSLC_VERSION }} - name: 'cache boringssl' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-boringssl env: cache-name: cache-boringssl @@ -106,26 +111,26 @@ jobs: path: ~/boringssl/build key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.BORINGSSL_VERSION }} - - name: 'cache quictls' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-quictls-no-deprecated + - name: 'cache nettle' + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-nettle env: - cache-name: cache-quictls-no-deprecated + cache-name: cache-nettle with: - path: ~/quictls/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.QUICTLS_VERSION }}-quic1 + path: ~/nettle/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NETTLE_VERSION }} - name: 'cache gnutls' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-gnutls env: cache-name: cache-gnutls with: path: ~/gnutls/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.GNUTLS_VERSION }} + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.GNUTLS_VERSION }}-${{ env.NETTLE_VERSION }} - name: 'cache wolfssl' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-wolfssl env: cache-name: cache-wolfssl @@ -134,7 +139,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.WOLFSSL_VERSION }} - name: 'cache nghttp3' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-nghttp3 env: cache-name: cache-nghttp3 @@ -143,16 +148,26 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGHTTP3_VERSION }} - name: 'cache ngtcp2' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-ngtcp2 env: cache-name: cache-ngtcp2 with: path: ~/ngtcp2/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_VERSION }}-${{ env.LIBRESSL_VERSION }}-${{ env.AWSLC_VERSION }}-${{ env.QUICTLS_VERSION }}-${{ env.GNUTLS_VERSION }}-${{ env.WOLFSSL_VERSION }} + key: "${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_VERSION }}-\ + ${{ env.LIBRESSL_VERSION }}-${{ env.AWSLC_VERSION }}-${{ env.NETTLE_VERSION }}-${{ env.GNUTLS_VERSION }}-${{ env.WOLFSSL_VERSION }}" + + - name: 'cache ngtcp2 openssl4' + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-ngtcp2-openssl4 + env: + cache-name: cache-ngtcp2-openssl4 + with: + path: ~/ngtcp2-openssl4/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL4_VERSION }} - name: 'cache ngtcp2 boringssl' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-ngtcp2-boringssl env: cache-name: cache-ngtcp2-boringssl @@ -161,25 +176,28 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.BORINGSSL_VERSION }} - name: 'cache nghttp2' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-nghttp2 env: cache-name: cache-nghttp2 with: path: ~/nghttp2/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGHTTP2_VERSION }}-${{ env.QUICTLS_VERSION }}-${{ env.NGTCP2_VERSION }}-${{ env.NGHTTP3_VERSION }} + key: "${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGHTTP2_VERSION }}-${{ env.OPENSSL_VERSION }}-\ + ${{ env.NGTCP2_VERSION }}-${{ env.NGHTTP3_VERSION }}" - id: settings if: >- - ${{ steps.cache-openssl-http3.outputs.cache-hit != 'true' || + ${{ steps.cache-openssl-http3-no-deprecated.outputs.cache-hit != 'true' || + steps.cache-openssl4-http3-no-deprecated.outputs.cache-hit != 'true' || steps.cache-libressl.outputs.cache-hit != 'true' || steps.cache-awslc.outputs.cache-hit != 'true' || steps.cache-boringssl.outputs.cache-hit != 'true' || - steps.cache-quictls-no-deprecated.outputs.cache-hit != 'true' || + steps.cache-nettle.outputs.cache-hit != 'true' || steps.cache-gnutls.outputs.cache-hit != 'true' || steps.cache-wolfssl.outputs.cache-hit != 'true' || steps.cache-nghttp3.outputs.cache-hit != 'true' || steps.cache-ngtcp2.outputs.cache-hit != 'true' || + steps.cache-ngtcp2-openssl4.outputs.cache-hit != 'true' || steps.cache-ngtcp2-boringssl.outputs.cache-hit != 'true' || steps.cache-nghttp2.outputs.cache-hit != 'true' }} @@ -188,26 +206,37 @@ jobs: - name: 'install build prereqs' if: ${{ steps.settings.outputs.needs-build == 'true' }} run: | - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list + sudo rm -f /etc/apt/sources.list.d/{azure-cli.sources,microsoft-prod.list,ondrej-ubuntu-php-noble.sources} sudo apt-get -o Dpkg::Use-Pty=0 update - sudo rm -f /var/lib/man-db/auto-update - sudo apt-get -o Dpkg::Use-Pty=0 install \ - libtool autoconf automake pkgconf stunnel4 \ - libpsl-dev libbrotli-dev libzstd-dev zlib1g-dev libev-dev libc-ares-dev \ - nettle-dev libp11-kit-dev libtspi-dev libunistring-dev guile-2.2-dev libtasn1-bin \ - libtasn1-6-dev libidn2-0-dev gawk gperf libtss2-dev dns-root-data bison gtk-doc-tools \ - texinfo texlive texlive-extra-utils autopoint libev-dev \ - apache2 apache2-dev libnghttp2-dev dante-server + printf "#!/bin/sh + while [ \$? = 0 ]; do for i in 1 2 3; do timeout 60 \"\$@\" && break 2; echo \"Error: slow server, retry \$i\"; sleep 1 + dpkg --configure -a; done; false; done" > "$HOME"/my-apt; chmod +x "$HOME"/my-apt + sudo "$HOME"/my-apt apt-get -o Dpkg::Use-Pty=0 install \ + libtool autoconf automake pkgconf \ + libbrotli-dev libzstd-dev zlib1g-dev \ + libev-dev \ + libc-ares-dev \ + libp11-kit-dev autopoint bison gperf gtk-doc-tools libtasn1-bin # for GnuTLS echo 'CC=gcc-12' >> "$GITHUB_ENV" echo 'CXX=g++-12' >> "$GITHUB_ENV" - name: 'build openssl' - if: ${{ steps.cache-openssl-http3.outputs.cache-hit != 'true' }} + if: ${{ steps.cache-openssl-http3-no-deprecated.outputs.cache-hit != 'true' }} run: | cd ~ - git clone --quiet --depth=1 -b "openssl-${OPENSSL_VERSION}" https://github.com/openssl/openssl + git clone --quiet --depth 1 -b "openssl-${OPENSSL_VERSION}" https://github.com/openssl/openssl cd openssl - ./config --prefix="$PWD"/build --libdir=lib no-makedepend no-apps no-docs no-tests + ./config --prefix="$PWD"/build --libdir=lib no-makedepend no-apps no-docs no-tests no-deprecated + make + make -j1 install_sw + + - name: 'build openssl4' + if: ${{ steps.cache-openssl4-http3-no-deprecated.outputs.cache-hit != 'true' }} + run: | + cd ~ + git clone --quiet --depth 1 -b "openssl-${OPENSSL4_VERSION}" https://github.com/openssl/openssl openssl4 + cd openssl4 + ./config --prefix="$PWD"/build --libdir=lib no-makedepend no-apps no-docs no-tests no-deprecated make make -j1 install_sw @@ -216,7 +245,8 @@ jobs: run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" | tar -xz + --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "libressl-${LIBRESSL_VERSION}" cmake -B . -G Ninja -DLIBRESSL_APPS=OFF -DLIBRESSL_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/libressl/build cmake --build . @@ -227,7 +257,8 @@ jobs: run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/awslabs/aws-lc/archive/refs/tags/v${AWSLC_VERSION}.tar.gz" | tar -xz + --location "https://github.com/awslabs/aws-lc/archive/refs/tags/v${AWSLC_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "aws-lc-${AWSLC_VERSION}" cmake -B . -G Ninja -DBUILD_SHARED_LIBS=ON -DBUILD_TOOL=OFF -DBUILD_TESTING=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/awslc/build cmake --build . @@ -239,44 +270,51 @@ jobs: mkdir boringssl-src cd boringssl-src curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - "https://boringssl.googlesource.com/boringssl/+archive/${BORINGSSL_VERSION}.tar.gz" | tar -xz + "https://boringssl.googlesource.com/boringssl/+archive/${BORINGSSL_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cmake -B . -G Ninja -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/boringssl/build cmake --build . cmake --install . - - name: 'build quictls' - if: ${{ steps.cache-quictls-no-deprecated.outputs.cache-hit != 'true' }} + - name: 'build nettle' + if: ${{ steps.cache-nettle.outputs.cache-hit != 'true' }} run: | cd ~ - git clone --quiet --depth=1 -b "openssl-${QUICTLS_VERSION}-quic1" https://github.com/quictls/openssl quictls - cd quictls - ./config no-deprecated --prefix="$PWD"/build --libdir=lib no-makedepend no-apps no-docs no-tests - make - make -j1 install_sw + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ + --location "https://ftpmirror.gnu.org/nettle/nettle-${NETTLE_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin + cd "nettle-${NETTLE_VERSION}" + autoreconf -fi + ./configure --disable-dependency-tracking --prefix=/home/runner/nettle/build \ + --disable-static --disable-openssl --disable-documentation + make install - name: 'build gnutls' if: ${{ steps.cache-gnutls.outputs.cache-hit != 'true' }} run: | cd ~ - git clone --quiet --depth=1 -b "${GNUTLS_VERSION}" https://github.com/gnutls/gnutls.git - cd gnutls - ./bootstrap - ./configure --disable-dependency-tracking --prefix="$PWD"/build \ - LDFLAGS="-Wl,-rpath,$PWD/build/lib -L$PWD/build/lib" \ + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ + "https://www.gnupg.org/ftp/gcrypt/gnutls/v${GNUTLS_VERSION%.*}/gnutls-${GNUTLS_VERSION}.tar.xz" --output pkg.bin + sha256sum pkg.bin && tar -xJf pkg.bin && rm -f pkg.bin + cd "gnutls-${GNUTLS_VERSION}" + autoreconf -fi + # required: libp11-kit-dev libev-dev autopoint bison gperf gtk-doc-tools libtasn1-bin + ./configure --disable-dependency-tracking --prefix=/home/runner/gnutls/build \ + PKG_CONFIG_PATH=/home/runner/nettle/build/lib64/pkgconfig \ + LDFLAGS=-Wl,-rpath,/home/runner/nettle/build/lib64 \ --with-included-libtasn1 --with-included-unistring \ --disable-guile --disable-doc --disable-tests --disable-tools - make make install - name: 'build wolfssl' if: ${{ steps.cache-wolfssl.outputs.cache-hit != 'true' }} run: | cd ~ - git clone --quiet --depth=1 -b "v${WOLFSSL_VERSION}-stable" https://github.com/wolfSSL/wolfssl.git + git clone --quiet --depth 1 -b "v${WOLFSSL_VERSION}-stable" https://github.com/wolfSSL/wolfssl cd wolfssl ./autogen.sh - ./configure --disable-dependency-tracking --enable-all --enable-quic \ - --disable-benchmark --disable-crypttests --disable-examples --prefix="$PWD"/build + ./configure --disable-dependency-tracking --prefix="$PWD"/build --enable-all --enable-quic \ + --disable-benchmark --disable-crypttests --disable-examples make make install @@ -284,9 +322,9 @@ jobs: if: ${{ steps.cache-nghttp3.outputs.cache-hit != 'true' }} run: | cd ~ - git clone --quiet --depth=1 -b "v${NGHTTP3_VERSION}" https://github.com/ngtcp2/nghttp3 + git clone --quiet --depth 1 -b "v${NGHTTP3_VERSION}" https://github.com/ngtcp2/nghttp3 cd nghttp3 - git submodule update --init --depth=1 + git submodule update --init --depth 1 autoreconf -fi ./configure --disable-dependency-tracking --prefix="$PWD"/build --enable-lib-only make @@ -294,36 +332,48 @@ jobs: - name: 'build ngtcp2' if: ${{ steps.cache-ngtcp2.outputs.cache-hit != 'true' }} - # building 3 times to get crypto libs for ossl, libressl, quictls and awslc installed + # building twice to get crypto libs for ossl, libressl and awslc installed run: | cd ~ - git clone --quiet --depth=1 -b "v${NGTCP2_VERSION}" https://github.com/ngtcp2/ngtcp2 + git clone --quiet --depth 1 -b "v${NGTCP2_VERSION}" https://github.com/ngtcp2/ngtcp2 cd ngtcp2 autoreconf -fi - ./configure --disable-dependency-tracking --prefix="$PWD"/build \ - PKG_CONFIG_PATH=/home/runner/libressl/build/lib/pkgconfig --enable-lib-only --with-openssl + ./configure --disable-dependency-tracking --prefix="$PWD"/build --enable-lib-only \ + PKG_CONFIG_PATH=/home/runner/libressl/build/lib/pkgconfig \ + --with-openssl make install make clean - ./configure --disable-dependency-tracking --prefix="$PWD"/build \ - PKG_CONFIG_PATH=/home/runner/quictls/build/lib/pkgconfig --enable-lib-only --with-openssl - make install - make clean - ./configure --disable-dependency-tracking --prefix="$PWD"/build \ - PKG_CONFIG_PATH=/home/runner/openssl/build/lib/pkgconfig:/home/runner/gnutls/build/lib/pkgconfig:/home/runner/wolfssl/build/lib/pkgconfig \ - --enable-lib-only --with-openssl --with-gnutls --with-wolfssl --with-boringssl \ + export PKG_CONFIG_PATH=/home/runner/openssl/build/lib/pkgconfig + PKG_CONFIG_PATH+=:/home/runner/nettle/build/lib64/pkgconfig + PKG_CONFIG_PATH+=:/home/runner/gnutls/build/lib/pkgconfig + PKG_CONFIG_PATH+=:/home/runner/wolfssl/build/lib/pkgconfig + ./configure --disable-dependency-tracking --prefix="$PWD"/build --enable-lib-only \ + --with-openssl --with-gnutls --with-wolfssl --with-boringssl \ BORINGSSL_LIBS='-L/home/runner/awslc/build/lib -lssl -lcrypto' \ BORINGSSL_CFLAGS='-I/home/runner/awslc/build/include' make install + - name: 'build ngtcp2 openssl4' + if: ${{ steps.cache-ngtcp2-openssl4.outputs.cache-hit != 'true' }} + run: | + cd ~ + git clone --quiet --depth 1 -b "v${NGTCP2_VERSION}" https://github.com/ngtcp2/ngtcp2 ngtcp2-openssl4 + cd ngtcp2-openssl4 + autoreconf -fi + ./configure --disable-dependency-tracking --prefix="$PWD"/build --enable-lib-only \ + PKG_CONFIG_PATH=/home/runner/openssl4/build/lib/pkgconfig \ + --with-openssl + make install + - name: 'build ngtcp2 boringssl' if: ${{ steps.cache-ngtcp2-boringssl.outputs.cache-hit != 'true' }} run: | cd ~ - git clone --quiet --depth=1 -b "v${NGTCP2_VERSION}" https://github.com/ngtcp2/ngtcp2 ngtcp2-boringssl + git clone --quiet --depth 1 -b "v${NGTCP2_VERSION}" https://github.com/ngtcp2/ngtcp2 ngtcp2-boringssl cd ngtcp2-boringssl autoreconf -fi - ./configure --disable-dependency-tracking --prefix="$PWD"/build \ - --enable-lib-only --with-openssl=no --with-boringssl \ + ./configure --disable-dependency-tracking --prefix="$PWD"/build --enable-lib-only \ + --with-openssl=no --with-boringssl \ BORINGSSL_LIBS='-L/home/runner/boringssl/build/lib -lssl -lcrypto' \ BORINGSSL_CFLAGS='-I/home/runner/boringssl/build/include' make install @@ -332,160 +382,170 @@ jobs: if: ${{ steps.cache-nghttp2.outputs.cache-hit != 'true' }} run: | cd ~ - git clone --quiet --depth=1 -b "v${NGHTTP2_VERSION}" https://github.com/nghttp2/nghttp2 + git clone --quiet --depth 1 -b "v${NGHTTP2_VERSION}" https://github.com/nghttp2/nghttp2 cd nghttp2 - git submodule update --init --depth=1 + git submodule update --init --depth 1 autoreconf -fi - ./configure --disable-dependency-tracking --prefix="$PWD"/build \ - PKG_CONFIG_PATH=/home/runner/quictls/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig \ - LDFLAGS=-Wl,-rpath,/home/runner/quictls/build/lib \ - --enable-http3 + # required (for nghttpx application): libc-ares-dev libev-dev zlib1g-dev + # optional (for nghttpx application): libbrotli-dev + export PKG_CONFIG_PATH=/home/runner/openssl/build/lib/pkgconfig + PKG_CONFIG_PATH+=:/home/runner/nghttp3/build/lib/pkgconfig + PKG_CONFIG_PATH+=:/home/runner/ngtcp2/build/lib/pkgconfig + ./configure --disable-dependency-tracking --prefix="$PWD"/build --enable-app --enable-http3 \ + LDFLAGS=-Wl,-rpath,/home/runner/openssl/build/lib \ + --with-libbrotlienc --with-libbrotlidec make install linux: name: ${{ matrix.build.generate && 'CM' || 'AM' }} ${{ matrix.build.name }} - needs: - - build-cache - runs-on: 'ubuntu-latest' - timeout-minutes: 45 + needs: build-cache + runs-on: ubuntu-latest + timeout-minutes: 10 env: + CURL_TRACE_PKG_CONFIG: '1' MATRIX_BUILD: ${{ matrix.build.generate && 'cmake' || 'autotools' }} + MATRIX_INSTALL_PACKAGES: '${{ matrix.build.install_packages }}' strategy: fail-fast: false matrix: build: - name: 'openssl' - PKG_CONFIG_PATH: /home/runner/openssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + tflags: '--min=1640' + LDFLAGS: -Wl,-rpath,/home/runner/openssl/build/lib + PKG_CONFIG_PATH: /home/runner/openssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig configure: >- - LDFLAGS=-Wl,-rpath,/home/runner/openssl/build/lib - --with-ngtcp2 --disable-ntlm - --with-openssl=/home/runner/openssl/build --enable-ssls-export + --with-openssl=/home/runner/openssl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ssls-export - name: 'openssl' install_steps: skipall PKG_CONFIG_PATH: /home/runner/openssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig generate: >- - -DOPENSSL_ROOT_DIR=/home/runner/openssl/build - -DUSE_NGTCP2=ON -DCURL_DISABLE_NTLM=ON + -DOPENSSL_ROOT_DIR=/home/runner/openssl/build -DUSE_NGTCP2=ON + -DCURL_DISABLE_LDAP=ON -DCMAKE_UNITY_BUILD=ON + - name: 'openssl4' + install_steps: skipall + LDFLAGS: -Wl,-rpath,/home/runner/openssl4/build/lib + PKG_CONFIG_PATH: "\ + /home/runner/openssl4/build/lib/pkgconfig:\ + /home/runner/nghttp3/build/lib/pkgconfig:\ + /home/runner/nghttp2-openssl4/build/lib/pkgconfig" + configure: >- + --with-openssl=/home/runner/openssl4/build --with-ngtcp2=/home/runner/ngtcp2-openssl4/build --enable-ech --enable-ssls-export + + - name: 'openssl4' + tflags: '--min=1640' + PKG_CONFIG_PATH: "\ + /home/runner/openssl4/build/lib/pkgconfig:\ + /home/runner/nghttp3/build/lib/pkgconfig:\ + /home/runner/ngtcp2-openssl4/build/lib/pkgconfig:\ + /home/runner/nghttp2/build/lib/pkgconfig" + generate: >- + -DOPENSSL_ROOT_DIR=/home/runner/openssl4/build -DUSE_NGTCP2=ON + -DCURL_DISABLE_LDAP=ON + -DUSE_ECH=ON + - name: 'libressl' install_steps: skipall - PKG_CONFIG_PATH: /home/runner/libressl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + LDFLAGS: -Wl,-rpath,/home/runner/libressl/build/lib + PKG_CONFIG_PATH: /home/runner/libressl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + # Intentionally using '--with-ngtcp2=' to test this way of configuration, in addition to bare '--with-ngtcp2' + 'PKG_CONFIG_PATH' in other jobs. configure: >- - LDFLAGS=-Wl,-rpath,/home/runner/libressl/build/lib - --with-ngtcp2 --disable-ntlm - --with-openssl=/home/runner/libressl/build --enable-ssls-export + --with-openssl=/home/runner/libressl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ssls-export --enable-unity - name: 'libressl' + tflags: '--min=1790' PKG_CONFIG_PATH: /home/runner/libressl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig generate: >- - -DOPENSSL_ROOT_DIR=/home/runner/libressl/build - -DUSE_NGTCP2=ON -DCURL_DISABLE_NTLM=ON + -DOPENSSL_ROOT_DIR=/home/runner/libressl/build -DUSE_NGTCP2=ON - name: 'awslc' install_steps: skipall + LDFLAGS: -Wl,-rpath,/home/runner/awslc/build/lib PKG_CONFIG_PATH: /home/runner/awslc/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + # Intentionally using bare '--with-ngtcp2' + 'PKG_CONFIG_PATH' to test this way of configuration, in addition to '--with-ngtcp2=' in other jobs. configure: >- - LDFLAGS=-Wl,-rpath,/home/runner/awslc/build/lib - --with-ngtcp2 --disable-ntlm - --with-openssl=/home/runner/awslc/build --enable-ssls-export + --with-openssl=/home/runner/awslc/build --with-ngtcp2 --enable-ssls-export - name: 'awslc' + tflags: '--min=1790' PKG_CONFIG_PATH: /home/runner/awslc/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig generate: >- - -DOPENSSL_ROOT_DIR=/home/runner/awslc/build -DBUILD_SHARED_LIBS=OFF - -DUSE_NGTCP2=ON -DCURL_DISABLE_NTLM=ON - -DCMAKE_UNITY_BUILD=ON + -DOPENSSL_ROOT_DIR=/home/runner/awslc/build -DUSE_NGTCP2=ON -DBUILD_SHARED_LIBS=OFF + -DCMAKE_UNITY_BUILD=ON -DCURL_DROP_UNUSED=ON - name: 'boringssl' install_steps: skipall - PKG_CONFIG_PATH: /home/runner/boringssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2-boringssl/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + LDFLAGS: -Wl,-rpath,/home/runner/boringssl/build/lib + PKG_CONFIG_PATH: /home/runner/boringssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig configure: >- - LDFLAGS=-Wl,-rpath,/home/runner/boringssl/build/lib - --with-ngtcp2 --disable-ntlm - --with-openssl=/home/runner/boringssl/build --enable-ssls-export + --with-openssl=/home/runner/boringssl/build --with-ngtcp2=/home/runner/ngtcp2-boringssl/build --enable-ssls-export - name: 'boringssl' - PKG_CONFIG_PATH: /home/runner/boringssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2-boringssl/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + tflags: '--min=1790' + PKG_CONFIG_PATH: "\ + /home/runner/boringssl/build/lib/pkgconfig:\ + /home/runner/nghttp3/build/lib/pkgconfig:\ + /home/runner/ngtcp2-boringssl/build/lib/pkgconfig:\ + /home/runner/nghttp2/build/lib/pkgconfig" generate: >- - -DOPENSSL_ROOT_DIR=/home/runner/boringssl/build -DBUILD_SHARED_LIBS=OFF - -DUSE_NGTCP2=ON -DCURL_DISABLE_NTLM=ON + -DOPENSSL_ROOT_DIR=/home/runner/boringssl/build -DUSE_NGTCP2=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_UNITY_BUILD=ON - - name: 'quictls' + - name: 'gnutls' + install_packages: libp11-kit-dev libssh-dev install_steps: skipall - PKG_CONFIG_PATH: /home/runner/quictls/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + LDFLAGS: -Wl,-rpath,/home/runner/gnutls/build/lib -Wl,-rpath,/home/runner/nettle/build/lib64 -Wl,-rpath,/home/runner/ngtcp2/build/lib + PKG_CONFIG_PATH: /home/runner/nettle/build/lib64/pkgconfig:/home/runner/gnutls/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig configure: >- - LDFLAGS=-Wl,-rpath,/home/runner/quictls/build/lib - --with-ngtcp2 --disable-ntlm - --with-openssl=/home/runner/quictls/build --enable-ssls-export - --enable-unity - - - name: 'quictls' - PKG_CONFIG_PATH: /home/runner/quictls/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig - generate: >- - -DOPENSSL_ROOT_DIR=/home/runner/quictls/build - -DUSE_NGTCP2=ON -DCURL_DISABLE_NTLM=ON + --with-gnutls=/home/runner/gnutls/build --with-ngtcp2=/home/runner/ngtcp2/build --with-libssh --enable-ssls-export - name: 'gnutls' - install_steps: skipall - PKG_CONFIG_PATH: /home/runner/gnutls/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig - configure: >- - LDFLAGS=-Wl,-rpath,/home/runner/gnutls/build/lib - --with-ngtcp2 - --with-gnutls=/home/runner/gnutls/build --enable-ssls-export - - - name: 'gnutls' - PKG_CONFIG_PATH: /home/runner/gnutls/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + install_packages: libp11-kit-dev libssh-dev + tflags: '--min=1840' + LDFLAGS: -Wl,-rpath,/home/runner/gnutls/build/lib + PKG_CONFIG_PATH: "\ + /home/runner/nettle/build/lib64/pkgconfig:\ + /home/runner/gnutls/build/lib/pkgconfig:\ + /home/runner/nghttp3/build/lib/pkgconfig:\ + /home/runner/ngtcp2/build/lib/pkgconfig:\ + /home/runner/nghttp2/build/lib/pkgconfig" generate: >- - -DCURL_USE_GNUTLS=ON - -DUSE_NGTCP2=ON -DCURL_DISABLE_NTLM=ON + -DCURL_USE_GNUTLS=ON -DUSE_NGTCP2=ON -DCURL_USE_LIBSSH=ON -DCMAKE_UNITY_BUILD=ON - name: 'wolfssl' + install_packages: libssh2-1-dev install_steps: skipall - PKG_CONFIG_PATH: /home/runner/wolfssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig + LDFLAGS: -Wl,-rpath,/home/runner/wolfssl/build/lib + PKG_CONFIG_PATH: /home/runner/wolfssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig configure: >- - LDFLAGS=-Wl,-rpath,/home/runner/wolfssl/build/lib - --with-ngtcp2 - --with-wolfssl=/home/runner/wolfssl/build - --enable-ech --enable-ssls-export + --with-wolfssl=/home/runner/wolfssl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ech --with-libssh2 --enable-ssls-export --enable-unity - name: 'wolfssl' + install_packages: libssh2-1-dev + tflags: '--min=1840' PKG_CONFIG_PATH: /home/runner/wolfssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/ngtcp2/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig generate: >- -DCURL_USE_WOLFSSL=ON -DUSE_NGTCP2=ON -DUSE_ECH=ON - - name: 'openssl-quic' - install_steps: skipall - PKG_CONFIG_PATH: /home/runner/openssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig - configure: >- - LDFLAGS=-Wl,-rpath,/home/runner/openssl/build/lib - --disable-ntlm - --with-openssl=/home/runner/openssl/build --with-openssl-quic - - - name: 'openssl-quic' - PKG_CONFIG_PATH: /home/runner/openssl/build/lib/pkgconfig:/home/runner/nghttp3/build/lib/pkgconfig:/home/runner/nghttp2/build/lib/pkgconfig - generate: >- - -DOPENSSL_ROOT_DIR=/home/runner/openssl/build -DUSE_OPENSSL_QUIC=ON - -DCURL_DISABLE_NTLM=ON - -DCMAKE_UNITY_BUILD=ON - - name: 'quiche' install_steps: skipall + LDFLAGS: -Wl,-rpath,/home/runner/quiche/target/release + PKG_CONFIG_PATH: /home/runner/nghttp2/build/lib/pkgconfig configure: >- - LDFLAGS=-Wl,-rpath,/home/runner/quiche/target/release --with-openssl=/home/runner/quiche/quiche/deps/boringssl/src --with-quiche=/home/runner/quiche/target/release --with-ca-fallback --enable-unity - name: 'quiche' - PKG_CONFIG_PATH: /home/runner/quiche/target/release + tflags: '--min=1790' + PKG_CONFIG_PATH: /home/runner/nghttp2/build/lib/pkgconfig:/home/runner/quiche/target/release generate: >- -DOPENSSL_ROOT_DIR=/home/runner/quiche/quiche/deps/boringssl/src -DUSE_QUICHE=ON @@ -493,34 +553,50 @@ jobs: steps: - name: 'install prereqs' + env: + INSTALL_PACKAGES: >- + ${{ !contains(matrix.build.install_steps, 'skipall') && !contains(matrix.build.install_steps, 'skiprun') && 'stunnel4 ' || '' }} + ${{ !contains(matrix.build.install_steps, 'skipall') && !contains(matrix.build.install_steps, 'skiprun') && + 'apache2 apache2-dev libnghttp2-dev vsftpd dante-server libev-dev' || '' }} + run: | - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list + sudo rm -f /etc/apt/sources.list.d/{azure-cli.sources,microsoft-prod.list,ondrej-ubuntu-php-noble.sources} sudo apt-get -o Dpkg::Use-Pty=0 update - sudo rm -f /var/lib/man-db/auto-update - sudo apt-get -o Dpkg::Use-Pty=0 install \ - libtool autoconf automake pkgconf stunnel4 \ - libpsl-dev libbrotli-dev libzstd-dev zlib1g-dev libev-dev libc-ares-dev \ - nettle-dev libp11-kit-dev libtspi-dev libunistring-dev guile-2.2-dev libtasn1-bin \ - libtasn1-6-dev libidn2-0-dev gawk gperf libtss2-dev dns-root-data bison gtk-doc-tools \ - texinfo texlive texlive-extra-utils autopoint libev-dev libuv1-dev \ - apache2 apache2-dev libnghttp2-dev vsftpd dante-server - python3 -m venv ~/venv + printf "#!/bin/sh + while [ \$? = 0 ]; do for i in 1 2 3; do timeout 45 \"\$@\" && break 2; echo \"Error: slow server, retry \$i\"; sleep 1 + dpkg --configure -a; done; false; done" > "$HOME"/my-apt; chmod +x "$HOME"/my-apt + sudo "$HOME"/my-apt apt-get -o Dpkg::Use-Pty=0 install \ + libtool autoconf automake pkgconf \ + libpsl-dev libbrotli-dev libzstd-dev zlib1g-dev libidn2-0-dev libldap-dev libuv1-dev valgrind \ + ${INSTALL_PACKAGES} \ + ${MATRIX_INSTALL_PACKAGES} echo 'CC=gcc-12' >> "$GITHUB_ENV" echo 'CXX=g++-12' >> "$GITHUB_ENV" - name: 'cache openssl' - if: ${{ matrix.build.name == 'openssl' || matrix.build.name == 'openssl-quic' }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-openssl-http3 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-openssl-http3-no-deprecated env: - cache-name: cache-openssl-http3 + cache-name: cache-openssl-http3-no-deprecated with: path: ~/openssl/build key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_VERSION }} fail-on-cache-miss: true + - name: 'cache openssl4' + if: ${{ contains(matrix.build.name, 'openssl4') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-openssl4-http3-no-deprecated + env: + cache-name: cache-openssl4-http3-no-deprecated + with: + path: ~/openssl4/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL4_VERSION }} + fail-on-cache-miss: true + - name: 'cache libressl' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + if: ${{ contains(matrix.build.name, 'libressl') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-libressl env: cache-name: cache-libressl @@ -530,7 +606,8 @@ jobs: fail-on-cache-miss: true - name: 'cache awslc' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + if: ${{ contains(matrix.build.name, 'awslc') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-awslc env: cache-name: cache-awslc @@ -540,7 +617,8 @@ jobs: fail-on-cache-miss: true - name: 'cache boringssl' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + if: ${{ contains(matrix.build.name, 'boringssl') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-boringssl env: cache-name: cache-boringssl @@ -549,30 +627,31 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.BORINGSSL_VERSION }} fail-on-cache-miss: true - - name: 'cache quictls' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-quictls-no-deprecated + - name: 'cache nettle' + if: ${{ contains(matrix.build.name, 'gnutls') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-nettle env: - cache-name: cache-quictls-no-deprecated + cache-name: cache-nettle with: - path: ~/quictls/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.QUICTLS_VERSION }}-quic1 + path: ~/nettle/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NETTLE_VERSION }} fail-on-cache-miss: true - name: 'cache gnutls' - if: ${{ matrix.build.name == 'gnutls' }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + if: ${{ contains(matrix.build.name, 'gnutls') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-gnutls env: cache-name: cache-gnutls with: path: ~/gnutls/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.GNUTLS_VERSION }} + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.GNUTLS_VERSION }}-${{ env.NETTLE_VERSION }} fail-on-cache-miss: true - name: 'cache wolfssl' - if: ${{ matrix.build.name == 'wolfssl' }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + if: ${{ contains(matrix.build.name, 'wolfssl') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-wolfssl env: cache-name: cache-wolfssl @@ -582,7 +661,7 @@ jobs: fail-on-cache-miss: true - name: 'cache nghttp3' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-nghttp3 env: cache-name: cache-nghttp3 @@ -592,17 +671,30 @@ jobs: fail-on-cache-miss: true - name: 'cache ngtcp2' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-ngtcp2 env: cache-name: cache-ngtcp2 with: path: ~/ngtcp2/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_VERSION }}-${{ env.LIBRESSL_VERSION }}-${{ env.AWSLC_VERSION }}-${{ env.QUICTLS_VERSION }}-${{ env.GNUTLS_VERSION }}-${{ env.WOLFSSL_VERSION }} + key: "${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_VERSION }}-\ + ${{ env.LIBRESSL_VERSION }}-${{ env.AWSLC_VERSION }}-${{ env.NETTLE_VERSION }}-${{ env.GNUTLS_VERSION }}-${{ env.WOLFSSL_VERSION }}" + fail-on-cache-miss: true + + - name: 'cache ngtcp2 openssl4' + if: ${{ contains(matrix.build.name, 'openssl4') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-ngtcp2-openssl4 + env: + cache-name: cache-ngtcp2-openssl4 + with: + path: ~/ngtcp2-openssl4/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL4_VERSION }} fail-on-cache-miss: true - name: 'cache ngtcp2 boringssl' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + if: ${{ contains(matrix.build.name, 'boringssl') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-ngtcp2-boringssl env: cache-name: cache-ngtcp2-boringssl @@ -612,18 +704,18 @@ jobs: fail-on-cache-miss: true - name: 'cache nghttp2' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-nghttp2 env: cache-name: cache-nghttp2 with: path: ~/nghttp2/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGHTTP2_VERSION }}-${{ env.QUICTLS_VERSION }}-${{ env.NGTCP2_VERSION }}-${{ env.NGHTTP3_VERSION }} + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGHTTP2_VERSION }}-${{ env.OPENSSL_VERSION }}-${{ env.NGTCP2_VERSION }}-${{ env.NGHTTP3_VERSION }} fail-on-cache-miss: true - name: 'cache quiche' - if: ${{ matrix.build.name == 'quiche' }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + if: ${{ contains(matrix.build.name, 'quiche') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-quiche env: cache-name: cache-quiche @@ -632,10 +724,10 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.QUICHE_VERSION }} - name: 'build quiche and boringssl' - if: ${{ matrix.build.name == 'quiche' && steps.cache-quiche.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.name, 'quiche') && steps.cache-quiche.outputs.cache-hit != 'true' }} run: | cd ~ - git clone --quiet --depth=1 -b "${QUICHE_VERSION}" --recursive https://github.com/cloudflare/quiche.git + git clone --quiet --depth 1 -b "${QUICHE_VERSION}" --recursive https://github.com/cloudflare/quiche cd quiche #### Work-around https://github.com/curl/curl/issues/7927 ####### #### See https://github.com/alexcrichton/cmake-rs/issues/131 #### @@ -644,15 +736,14 @@ jobs: cargo build -v --package quiche --release --features ffi,pkg-config-meta,qlog --verbose ln -s libquiche.so target/release/libquiche.so.0 mkdir -v quiche/deps/boringssl/src/lib - # shellcheck disable=SC2046 - ln -vnf $(find target/release -name libcrypto.a -o -name libssl.a) quiche/deps/boringssl/src/lib/ + find target/release \( -name libcrypto.a -o -name libssl.a \) -exec ln -vnf -- '{}' quiche/deps/boringssl/src/lib \; # include dir # /home/runner/quiche/quiche/deps/boringssl/src/include # lib dir # /home/runner/quiche/quiche/deps/boringssl/src/lib - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -662,24 +753,27 @@ jobs: - name: 'configure' env: + LDFLAGS: '${{ matrix.build.LDFLAGS }}' MATRIX_CONFIGURE: '${{ matrix.build.configure }}' MATRIX_GENERATE: '${{ matrix.build.generate }}' MATRIX_PKG_CONFIG_PATH: '${{ matrix.build.PKG_CONFIG_PATH }}' run: | [ -n "${MATRIX_PKG_CONFIG_PATH}" ] && export PKG_CONFIG_PATH="${MATRIX_PKG_CONFIG_PATH}" if [ "${MATRIX_BUILD}" = 'cmake' ]; then + [[ "${MATRIX_GENERATE}" = *'boringssl'* ]] && options=" -DBORINGSSL_VERSION=${BORINGSSL_VERSION}" cmake -B bld -G Ninja \ -DCMAKE_C_COMPILER_TARGET="$(uname -m)-pc-linux-gnu" -DBUILD_STATIC_LIBS=ON \ -DCURL_WERROR=ON -DENABLE_DEBUG=ON \ - -DCURL_USE_LIBUV=ON \ + -DCURL_USE_LIBUV=ON -DCURL_ENABLE_NTLM=ON \ -DTEST_NGHTTPX=/home/runner/nghttp2/build/bin/nghttpx \ -DHTTPD_NGHTTPX=/home/runner/nghttp2/build/bin/nghttpx \ - ${MATRIX_GENERATE} + ${MATRIX_GENERATE} ${options} else - mkdir bld && cd bld && ../configure --enable-warnings --enable-werror --enable-debug \ - --with-libuv \ + [[ "${MATRIX_CONFIGURE}" = *'boringssl'* ]] && export CPPFLAGS="-DCURL_BORINGSSL_VERSION=\\\"${BORINGSSL_VERSION}\\\"" + mkdir bld && cd bld && ../configure --enable-warnings --enable-werror --enable-debug --disable-static \ + --disable-dependency-tracking --enable-option-checking=fatal \ + --with-libuv --enable-ntlm \ --with-test-nghttpx=/home/runner/nghttp2/build/bin/nghttpx \ - --disable-dependency-tracking \ ${MATRIX_CONFIGURE} fi @@ -703,8 +797,11 @@ jobs: make -C bld V=1 fi - - name: 'check curl -V output' - run: bld/src/curl -V + - name: 'curl -V' + run: | + find . -type f \( -name curl -o -name '*.so.*' -o -name '*.a' \) -print0 | xargs -0 file -- + find . -type f \( -name curl -o -name '*.so.*' -o -name '*.a' \) -print0 | xargs -0 stat -c '%10s bytes: %n' -- + bld/src/curl --disable -V - name: 'build tests' if: ${{ !contains(matrix.build.install_steps, 'skipall') }} @@ -718,14 +815,28 @@ jobs: - name: 'install test prereqs' if: ${{ !contains(matrix.build.install_steps, 'skipall') && !contains(matrix.build.install_steps, 'skiprun') }} run: | - source ~/venv/bin/activate - python3 -m pip install -r tests/requirements.txt + python3 -m venv ~/venv + if bld/src/curl --disable -V 2>/dev/null | grep smb; then + ~/venv/bin/pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary -r tests/requirements.txt + fi - name: 'run tests' if: ${{ !contains(matrix.build.install_steps, 'skipall') && !contains(matrix.build.install_steps, 'skiprun') }} env: TFLAGS: '${{ matrix.build.tflags }}' run: | + TFLAGS+=' -n' + source ~/venv/bin/activate + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --build bld --verbose --target test-ci + else + make -C bld V=1 test-ci + fi + + - name: 'run tests (valgrind)' + if: ${{ !contains(matrix.build.install_steps, 'skipall') && !contains(matrix.build.install_steps, 'skiprun') }} + run: | + export TFLAGS='-j6 --min=4 HTTP/3' source ~/venv/bin/activate if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake --build bld --verbose --target test-ci @@ -736,10 +847,10 @@ jobs: - name: 'install pytest prereqs' if: ${{ !contains(matrix.build.install_steps, 'skipall') && !contains(matrix.build.install_steps, 'skiprun') }} run: | - source ~/venv/bin/activate - python3 -m pip install -r tests/http/requirements.txt + [ -d ~/venv ] || python3 -m venv ~/venv + ~/venv/bin/pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary -r tests/http/requirements.txt - - name: 'run pytest event based' + - name: 'run pytest (event based)' if: ${{ !contains(matrix.build.install_steps, 'skipall') && !contains(matrix.build.install_steps, 'skiprun') }} env: CURL_TEST_EVENT: 1 diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index b84702a8a1..7e0ae094ee 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -10,19 +10,24 @@ # https://github.com/actions/labeler name: 'Labeler' + 'on': [pull_request_target] # zizmor: ignore[dangerous-triggers] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + permissions: {} jobs: label: name: 'Labeler' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: - contents: read - pull-requests: write + contents: read # To comply with https://github.com/actions/labeler documentation + pull-requests: write # To edit labels on PRs steps: - - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6 + - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' diff --git a/.github/workflows/linux-old.yml b/.github/workflows/linux-old.yml index 352986eb04..c96da5c7f0 100644 --- a/.github/workflows/linux-old.yml +++ b/.github/workflows/linux-old.yml @@ -11,12 +11,12 @@ # is still supported (as of this writing). # stretch has ELTS support from Freexian until 2027-06-30 # For ELTS info see https://www.freexian.com/lts/extended/docs/how-to-use-extended-lts/ -# The Debian key will expire 2025-05-20, after which package signature +# The Debian key expires 2025-05-20, after which package signature # verification may need to be disabled. # httrack is one of the smallest downloaders, needed to bootstrap ELTS, -# and won't conflict with the curl we're building. +# and doesn not conflict with the curl we are building. -name: 'Old Linux' +name: 'Linux Old' 'on': push: @@ -28,10 +28,7 @@ name: 'Old Linux' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' pull_request: branches: - master @@ -40,22 +37,24 @@ name: 'Old Linux' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true permissions: {} env: MAKEFLAGS: -j 5 CURL_CI: github + CURL_TEST_MIN: 1500 DEBIAN_FRONTEND: noninteractive jobs: cmake-autotools: - name: 'cmake & autotools' - runs-on: 'ubuntu-latest' + name: 'autotools & cmake' + runs-on: ubuntu-latest container: 'debian:stretch' steps: @@ -67,90 +66,121 @@ jobs: # See comment above if this fails after 2025-05-20 apt-get -o Dpkg::Use-Pty=0 install -y --no-install-suggests --no-install-recommends httrack httrack --get https://deb.freexian.com/extended-lts/pool/main/f/freexian-archive-keyring/freexian-archive-keyring_2022.06.08_all.deb - dpkg -i freexian-archive-keyring_2022.06.08_all.deb + sha256sum freexian-archive-keyring_2022.06.08_all.deb && dpkg -i freexian-archive-keyring_2022.06.08_all.deb echo 'deb http://deb.freexian.com/extended-lts stretch-lts main contrib non-free' | tee /etc/apt/sources.list.d/extended-lts.list apt-get -o Dpkg::Use-Pty=0 update - apt-get -o Dpkg::Use-Pty=0 install -y --no-install-suggests --no-install-recommends cmake make automake autoconf libtool gcc pkg-config libpsl-dev libzstd-dev zlib1g-dev libgnutls28-dev libssh-dev libssh2-1-dev libc-ares-dev heimdal-dev libldap2-dev librtmp-dev stunnel4 groff + apt-get -o Dpkg::Use-Pty=0 install -y --no-install-suggests --no-install-recommends \ + make automake autoconf libtool ninja-build gcc pkg-config libpsl-dev libzstd-dev zlib1g-dev libkrb5-dev libldap2-dev stunnel4 # GitHub's actions/checkout needs newer glibc and libstdc++. The latter also depends on - # gcc-8-base, but it doesn't actually seem used in our situation and isn't available in + # gcc-8-base, but it does not actually seem used in our situation and is not available in # the main repo, so force the install. httrack --get https://deb.freexian.com/extended-lts/pool/main/g/glibc/libc6_2.28-10+deb10u5_amd64.deb httrack --get https://deb.freexian.com/extended-lts/pool/main/g/gcc-8/libstdc++6_8.3.0-6_amd64.deb - dpkg -i --force-depends libc6_*_amd64.deb libstdc++6_*_amd64.deb + sha256sum libc6_*_amd64.deb libstdc++6_*_amd64.deb && dpkg -i --force-depends libc6_*_amd64.deb libstdc++6_*_amd64.deb - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - name: 'install prereqs (cmake)' + env: + CMAKE_VERSION: 3.18.0 # Earliest version supported by curl + CMAKE_SHA256: 4d9a9d3351161073a67e49366d701b6fa4b0343781982dc5eef08a02a750d403 + run: | + cd ~ + fn="cmake-${CMAKE_VERSION}-linux-x86_64" + httrack --get "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${fn}.tar.gz" + sha256sum "${fn}".tar*.gz | tee /dev/stderr | grep -qwF -- "${CMAKE_SHA256}" && tar -xf "${fn}".tar*.gz && rm -f "${fn}".tar*.gz + mv "cmake-${CMAKE_VERSION}-Linux-x86_64" cmake + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - name: 'cmake build-only (out-of-tree, libssh2)' + - name: 'CM build-only configure (out-of-tree)' run: | - mkdir bld-1 - cd bld-1 - cmake .. -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON -DBUILD_SHARED_LIBS=ON \ - -DCURL_USE_GNUTLS=ON -DENABLE_ARES=OFF -DCURL_ZSTD=OFF -DCURL_USE_GSSAPI=OFF -DCURL_USE_LIBSSH2=ON -DCURL_USE_LIBSSH=OFF -DUSE_LIBRTMP=ON - make install - src/curl --disable --version + ~/cmake/bin/cmake -B bld-1 -G Ninja -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON -DBUILD_SHARED_LIBS=ON \ + -DCURL_ENABLE_SSL=OFF -DENABLE_ARES=OFF -DCURL_ZSTD=OFF -DCURL_USE_GSSAPI=OFF -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=OFF - - name: 'cmake build-only curl_config.h' + - name: 'CM build-only build' + run: | + ~/cmake/bin/cmake --build bld-1 --verbose + ~/cmake/bin/cmake --install bld-1 --verbose + + - name: 'CM build-only curl -V' + run: bld-1/src/curl --disable --version + + - name: 'CM build-only configure log' + if: ${{ !cancelled() }} + run: cat bld-1/CMakeFiles/CMake*.log 2>/dev/null || true + + - name: 'CM build-only curl_config.h' run: | echo '::group::raw'; cat bld-1/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld-1/lib/curl_config.h | sort || true # when this job can get a libssh version 0.9.0 or later, this should get # that enabled again - - name: 'cmake generate (out-of-tree, c-ares, zstd, gssapi)' + # when this job can get c-ares 1.16.0 or later, we can enable that + # again + + - name: 'CM configure (out-of-tree, zstd, gssapi)' run: | - mkdir bld-cares - cd bld-cares - cmake .. -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON -DBUILD_SHARED_LIBS=ON \ - -DCURL_USE_GNUTLS=ON -DENABLE_ARES=ON -DCURL_USE_GSSAPI=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=OFF -DUSE_LIBRTMP=ON \ + ~/cmake/bin/cmake -B bld-oldie -G Ninja -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON -DBUILD_SHARED_LIBS=ON \ + -DCURL_ENABLE_SSL=OFF -DENABLE_ARES=OFF -DCURL_USE_GSSAPI=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=OFF \ -DCURL_LIBCURL_VERSIONED_SYMBOLS=ON - - name: 'cmake curl_config.h' + - name: 'CM configure log' + if: ${{ !cancelled() }} + run: cat bld-oldie/CMakeFiles/CMake*.log 2>/dev/null || true + + - name: 'CM curl_config.h' run: | - echo '::group::raw'; cat bld-cares/lib/curl_config.h || true; echo '::endgroup::' - grep -F '#define' bld-cares/lib/curl_config.h | sort || true + echo '::group::raw'; cat bld-oldie/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld-oldie/lib/curl_config.h | sort || true - - name: 'cmake build' - run: | - make -C bld-cares - bld-cares/src/curl --disable --version + - name: 'CM build' + run: ~/cmake/bin/cmake --build bld-oldie - - name: 'cmake install' - run: make -C bld-cares install + - name: 'CM curl -V' + run: bld-oldie/src/curl --disable --version - - name: 'cmake build tests' - run: make -C bld-cares testdeps + - name: 'CM install' + run: ~/cmake/bin/cmake --install bld-oldie - - name: 'cmake run tests' - run: make -C bld-cares test-ci + - name: 'CM build tests' + run: ~/cmake/bin/cmake --build bld-oldie --target testdeps - - name: 'cmake build examples' - run: make -C bld-cares curl-examples-build + - name: 'CM run tests' + run: ~/cmake/bin/cmake --build bld-oldie --target test-ci - - name: 'autoreconf' - run: autoreconf -if + - name: 'CM build examples' + run: ~/cmake/bin/cmake --build bld-oldie --target curl-examples-build - - name: 'configure (out-of-tree, c-ares, libssh2, zstd, gssapi)' + - name: 'AM autoreconf' + run: autoreconf -fi + + - name: 'AM configure (out-of-tree, zstd, gssapi)' run: | mkdir bld-am cd bld-am - ../configure --disable-dependency-tracking --enable-unity --enable-warnings --enable-werror \ - --with-gnutls --enable-ares --with-libssh2 --with-zstd --with-gssapi --with-librtmp \ - --prefix="$PWD"/../curl-install-am + ../configure --prefix="$PWD"/../curl-install-am --enable-unity --enable-warnings --enable-werror --disable-shared \ + --disable-dependency-tracking --enable-option-checking=fatal \ + --without-ssl --disable-ares --without-libssh2 --with-zstd --with-gssapi - - name: 'autotools curl_config.h' + - name: 'AM configure log' + if: ${{ !cancelled() }} + run: cat bld-am/config.log 2>/dev/null || true + + - name: 'AM curl_config.h' run: | echo '::group::raw'; cat bld-am/lib/curl_config.h || true; echo '::endgroup::' grep -F '#define' bld-am/lib/curl_config.h | sort || true - - name: 'autotools build' - run: | - make -C bld-am - bld-am/src/curl --disable --version + - name: 'AM build' + run: make -C bld-am - - name: 'autotools install' + - name: 'AM curl -V' + run: bld-am/src/curl --disable --version + + - name: 'AM install' run: make -C bld-am install - - name: 'autotools build tests' + - name: 'AM build tests' run: make -C bld-am/tests all diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index ed206ec03c..44a52dca6a 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -13,10 +13,7 @@ name: 'Linux' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' pull_request: branches: - master @@ -24,10 +21,7 @@ name: 'Linux' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} @@ -38,92 +32,130 @@ permissions: {} env: MAKEFLAGS: -j 5 CURL_CI: github - CURL_CLANG_TIDYFLAGS: '-checks=-clang-analyzer-security.insecureAPI.bzero,-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-security.ArrayBound,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,-clang-analyzer-valist.Uninitialized' - # renovate: datasource=github-tags depName=libressl-portable/portable versioning=semver registryUrl=https://github.com - LIBRESSL_VERSION: 4.1.0 + CURL_TEST_MIN: 1600 + # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com + LIBRESSL_VERSION: 4.2.1 # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com - WOLFSSL_VERSION: 5.8.2 - # renovate: datasource=github-tags depName=wolfSSL/wolfssh versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com - WOLFSSH_VERSION: 1.4.19 + WOLFSSL_VERSION: 5.9.1 # renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com - MBEDTLS_VERSION: 3.6.4 + MBEDTLS_VERSION: 4.0.0 + # manually bumped + MBEDTLS_PREV_VERSION: 3.6.5 + MBEDTLS_PREV_SHA256: 4a11f1777bb95bf4ad96721cac945a26e04bf19f57d905f241fe77ebeddf46d8 # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com - AWSLC_VERSION: 1.60.0 + AWSLC_VERSION: 1.71.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20250818.0 + BORINGSSL_VERSION: 0.20260413.0 # handled in renovate.json - OPENSSL_VERSION: 3.5.2 - # handled in renovate.json - QUICTLS_VERSION: 3.3.0 + OPENSSL_VERSION: 3.6.2 # renovate: datasource=github-tags depName=rustls/rustls-ffi versioning=semver registryUrl=https://github.com - RUSTLS_VERSION: 0.15.0 + RUSTLS_VERSION: 0.15.1 # handled in renovate.json OPENLDAP_VERSION: 2.6.10 + # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com + NGHTTP2_VERSION: 1.68.1 + # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com + FIL_C_VERSION: 0.678 jobs: linux: name: ${{ matrix.build.generate && 'CM' || 'AM' }} ${{ matrix.build.name }} runs-on: ${{ matrix.build.image || 'ubuntu-latest' }} container: ${{ matrix.build.container }} - timeout-minutes: 45 + timeout-minutes: 15 env: MATRIX_BUILD: ${{ matrix.build.generate && 'cmake' || 'autotools' }} MATRIX_INSTALL_PACKAGES: '${{ matrix.build.install_packages }}' MATRIX_INSTALL_STEPS: '${{ matrix.build.install_steps }}' - MATRIX_MAKE_PREFIX: '${{ matrix.build.make-prefix }}' strategy: fail-fast: false matrix: build: - - name: 'libressl heimdal' - install_packages: zlib1g-dev libidn2-dev libnghttp2-dev libldap-dev heimdal-dev - install_steps: libressl pytest codeset-test - configure: LDFLAGS=-Wl,-rpath,/home/runner/libressl/lib --with-openssl=/home/runner/libressl --with-gssapi --enable-debug + - name: 'libressl krb5' + image: ubuntu-24.04-arm + install_packages: libidn2-dev libnghttp2-dev libldap-dev libkrb5-dev + install_steps: libressl-c-arm pytest codeset-test + LDFLAGS: -Wl,-rpath,/home/runner/libressl/lib + configure: --with-openssl=/home/runner/libressl --with-gssapi --enable-debug - - name: 'libressl heimdal valgrind' - install_packages: zlib1g-dev libnghttp2-dev libldap-dev heimdal-dev valgrind - install_steps: libressl - generate: -DOPENSSL_ROOT_DIR=/home/runner/libressl -DCURL_USE_GSSAPI=ON -DENABLE_DEBUG=ON -DCURL_LIBCURL_VERSIONED_SYMBOLS=ON + - name: 'libressl krb5 valgrind 1' + image: ubuntu-24.04-arm + install_packages: libnghttp2-dev libldap-dev libkrb5-dev valgrind + install_steps: libressl-c-arm + tflags: '--min=870 1 to 950' + generate: -DOPENSSL_ROOT_DIR=/home/runner/libressl -DCURL_USE_GSSAPI=ON -DENABLE_DEBUG=ON -DCURL_LIBCURL_VERSIONED_SYMBOLS=ON -DCURL_ENABLE_NTLM=ON + + - name: 'libressl krb5 valgrind 2' + image: ubuntu-24.04-arm + install_packages: libnghttp2-dev libldap-dev libkrb5-dev valgrind + install_steps: libressl-c-arm + tflags: '--min=900 951 to 9999' + generate: -DOPENSSL_ROOT_DIR=/home/runner/libressl -DCURL_USE_GSSAPI=ON -DENABLE_DEBUG=ON -DCURL_LIBCURL_VERSIONED_SYMBOLS=ON -DCURL_ENABLE_NTLM=ON - name: 'libressl clang' - install_packages: zlib1g-dev clang - install_steps: libressl - configure: CC=clang LDFLAGS=-Wl,-rpath,/home/runner/libressl/lib --with-openssl=/home/runner/libressl --enable-debug + image: ubuntu-24.04-arm + install_packages: clang + install_steps: libressl-c-arm + CC: clang + LDFLAGS: -Wl,-rpath,/home/runner/libressl/lib + configure: --with-openssl=/home/runner/libressl --enable-debug - name: 'wolfssl-all' - install_packages: zlib1g-dev - install_steps: wolfssl-all - configure: LDFLAGS=-Wl,-rpath,/home/runner/wolfssl-all/lib --with-wolfssl=/home/runner/wolfssl-all --enable-ech --enable-debug + image: ubuntu-24.04-arm + install_steps: wolfssl-all-arm + LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-all/lib + configure: --with-wolfssl=/home/runner/wolfssl-all --enable-ech --enable-debug - - name: 'wolfssl-opensslextra valgrind' - install_packages: zlib1g-dev valgrind - install_steps: wolfssl-opensslextra wolfssh - configure: LDFLAGS=-Wl,-rpath,/home/runner/wolfssl-opensslextra/lib --with-wolfssl=/home/runner/wolfssl-opensslextra --with-wolfssh=/home/runner/wolfssh --enable-ech --enable-debug + - name: 'wolfssl-opensslextra valgrind 1' + image: ubuntu-24.04-arm + install_packages: valgrind + install_steps: wolfssl-opensslextra-arm + tflags: '--min=780 1 to 950' + LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib + configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug - - name: 'mbedtls valgrind' - install_packages: libnghttp2-dev libidn2-dev libldap-dev valgrind - install_steps: mbedtls - 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: 'wolfssl-opensslextra valgrind 2' + image: ubuntu-24.04-arm + install_packages: valgrind + install_steps: wolfssl-opensslextra-arm + tflags: '--min=800 951 to 9999' + LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib + configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug + + - name: 'mbedtls gss valgrind 1' + image: ubuntu-24.04-arm + install_packages: libnghttp2-dev libidn2-dev libldap-dev libgss-dev valgrind + install_steps: mbedtls-latest-arm + tflags: '--min=830 1 to 950' + LDFLAGS: -Wl,-rpath,/home/runner/mbedtls/lib + PKG_CONFIG_PATH: /home/runner/mbedtls/lib/pkgconfig + generate: -DCURL_USE_MBEDTLS=ON -DENABLE_DEBUG=ON -DCURL_USE_GSSAPI=ON -DCURL_DROP_UNUSED=ON + + - name: 'mbedtls gss valgrind 2' + image: ubuntu-24.04-arm + install_packages: libnghttp2-dev libidn2-dev libldap-dev libgss-dev valgrind + install_steps: mbedtls-latest-arm + tflags: '--min=800 951 to 9999' + LDFLAGS: -Wl,-rpath,/home/runner/mbedtls/lib + PKG_CONFIG_PATH: /home/runner/mbedtls/lib/pkgconfig + generate: -DCURL_USE_MBEDTLS=ON -DENABLE_DEBUG=ON -DCURL_USE_GSSAPI=ON - name: 'mbedtls clang' - install_packages: libnghttp2-dev libldap-dev clang - install_steps: mbedtls pytest - configure: CC=clang LDFLAGS=-Wl,-rpath,/home/runner/mbedtls/lib --with-mbedtls=/home/runner/mbedtls --enable-debug --with-fish-functions-dir --with-zsh-functions-dir + install_packages: libssh-dev libnghttp2-dev libldap-dev clang + install_steps: mbedtls-latest-intel pytest + CC: clang + LDFLAGS: -Wl,-rpath,/home/runner/mbedtls/lib + configure: --with-mbedtls=/home/runner/mbedtls --with-libssh --enable-debug --with-fish-functions-dir --with-zsh-functions-dir - - name: 'mbedtls' - install_packages: libnghttp2-dev - install_steps: mbedtls - PKG_CONFIG_PATH: /home/runner/mbedtls/lib/pkgconfig # Requires v3.6.0 - generate: -DCURL_USE_MBEDTLS=ON -DENABLE_DEBUG=ON + - name: 'mbedtls-prev' + install_packages: libssh2-1-dev libnghttp2-dev libuv1-dev + install_steps: mbedtls-prev pytest + PKG_CONFIG_PATH: /home/runner/mbedtls-prev/lib/pkgconfig # Requires v3.6.0 + generate: -DCURL_USE_MBEDTLS=ON -DCURL_USE_LIBUV=ON -DENABLE_DEBUG=ON - name: 'mbedtls-pkg MultiSSL !pc' install_packages: libnghttp2-dev libmbedtls-dev - install_steps: mbedtls skipall + install_steps: mbedtls-latest-intel skipall generate: >- -DCURL_USE_MBEDTLS=ON -DENABLE_DEBUG=ON -DCURL_DEFAULT_SSL_BACKEND=mbedtls -DMBEDTLS_INCLUDE_DIR=/home/runner/mbedtls/include @@ -135,142 +167,241 @@ jobs: -DCURL_COMPLETION_FISH=ON -DCURL_COMPLETION_ZSH=ON - name: 'awslc' - install_packages: zlib1g-dev install_steps: awslc pytest - configure: LDFLAGS=-Wl,-rpath,/home/runner/awslc/lib --with-openssl=/home/runner/awslc --enable-ech + LDFLAGS: -Wl,-rpath,/home/runner/awslc/lib + configure: --with-openssl=/home/runner/awslc --enable-ech --enable-ntlm - name: 'awslc' - install_packages: zlib1g-dev libidn2-dev + install_packages: libidn2-dev install_steps: awslc - generate: -DOPENSSL_ROOT_DIR=/home/runner/awslc -DUSE_ECH=ON -DCMAKE_UNITY_BUILD=OFF + generate: -DOPENSSL_ROOT_DIR=/home/runner/awslc -DUSE_ECH=ON -DCMAKE_UNITY_BUILD=OFF -DCURL_DROP_UNUSED=ON -DCURL_PATCHSTAMP=test-patch -DCURL_ENABLE_NTLM=ON - name: 'boringssl' - install_packages: zlib1g-dev install_steps: boringssl pytest - generate: -DOPENSSL_ROOT_DIR=/home/runner/boringssl -DUSE_ECH=ON + generate: -DOPENSSL_ROOT_DIR=/home/runner/boringssl -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON - name: 'openssl default' install_steps: pytest configure: --with-openssl --enable-debug --disable-unity - - name: 'openssl libssh2 sync-resolver valgrind' - install_packages: zlib1g-dev libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev valgrind - generate: -DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_USE_LIBSSH2=ON + - name: 'openssl libssh2 sync-resolver valgrind 1 +analyzer' + image: ubuntu-24.04-arm + install_packages: libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev valgrind + tflags: '--min=920 1 to 950' + generate: -DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_GCC_ANALYZER=ON -DCURL_ENABLE_NTLM=ON - - name: 'openssl' - install_packages: zlib1g-dev + - name: 'openssl libssh2 sync-resolver valgrind 2' + image: ubuntu-24.04-arm + install_packages: libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev valgrind + tflags: '--min=910 951 to 9999' + generate: -DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_ENABLE_NTLM=ON + + - name: 'openssl intel C89' + install_packages: libssh-dev install_steps: pytest - configure: CFLAGS=-std=gnu89 --with-openssl --enable-debug + configure: CFLAGS=-std=gnu89 --with-openssl --with-libssh --enable-debug - - name: 'openssl arm' - install_packages: zlib1g-dev + - name: 'openssl arm C89' + image: ubuntu-24.04-arm + install_packages: libssh2-1-dev install_steps: pytest - configure: CFLAGS=-std=gnu89 --with-openssl --enable-debug - image: 'ubuntu-24.04-arm' + configure: CFLAGS=-std=gnu89 --with-openssl --with-libssh2 --enable-debug --disable-verbose - - name: 'openssl -O3 libssh valgrind' - install_packages: zlib1g-dev libssh-dev valgrind + - name: 'openssl -O3 libssh valgrind 1' + install_packages: libssh-dev valgrind CFLAGS: -O3 - generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH=ON -DCMAKE_UNITY_BUILD_BATCH_SIZE=50 + tflags: '--min=920 1 to 950' + generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH=ON -DCMAKE_UNITY_BUILD_BATCH_SIZE=50 -DCURL_ENABLE_NTLM=ON + + - name: 'openssl -O3 libssh valgrind 2' + install_packages: libssh-dev valgrind + CFLAGS: -O3 + tflags: '--min=890 951 to 9999' + generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH=ON -DCMAKE_UNITY_BUILD_BATCH_SIZE=50 -DCURL_ENABLE_NTLM=ON - name: 'openssl clang krb5 openldap static' install_steps: openldap-static - install_packages: zlib1g-dev libidn2-dev libkrb5-dev clang libssl-dev - configure: CC=clang --disable-shared --with-openssl --with-gssapi --enable-debug --disable-docs --disable-manual --with-ldap=/home/runner/openldap-static --with-ldap-lib=ldap --with-lber-lib=lber + install_packages: libidn2-dev libkrb5-dev clang libssl-dev + CC: clang + configure: >- + --enable-static --disable-shared --with-openssl --with-gssapi --enable-debug --disable-docs + --disable-manual --with-ldap=/home/runner/openldap-static --with-ldap-lib=ldap --with-lber-lib=lber - name: 'openssl clang krb5 LTO' - install_packages: zlib1g-dev libkrb5-dev clang - install_steps: skiprun + image: ubuntu-24.04-arm + install_packages: libssh2-1-dev libkrb5-dev clang + install_steps: skiprun pytest + CC: clang generate: -DCURL_USE_OPENSSL=ON -DCURL_USE_GSSAPI=ON -DENABLE_DEBUG=ON -DCURL_LTO=ON - name: 'openssl !ipv6 !--libcurl !--digest-auth' - configure: --with-openssl --disable-ipv6 --enable-debug --disable-unity --disable-libcurl-option --disable-digest-auth + image: ubuntu-24.04-arm + configure: --with-openssl --disable-ipv6 --enable-debug --disable-unity --disable-libcurl-option --disable-digest-auth --enable-ntlm + + - name: 'curl_global_init_mem debug valgrind' + image: ubuntu-24.04-arm + install_packages: valgrind + tflags: '--min=1150' + configure: >- + --enable-init-mem-debug + --with-openssl --disable-debug --enable-unity + --enable-ntlm - name: 'openssl https-only' + image: ubuntu-24.04-arm + tflags: '--min=1150' configure: >- --with-openssl --enable-debug --disable-unity --disable-dict --disable-gopher --disable-ldap --disable-telnet --disable-imap --disable-pop3 --disable-smtp - --without-librtmp --disable-rtsp - --without-libssh2 --without-libssh --without-wolfssh + --disable-rtsp + --without-libssh2 --without-libssh --disable-tftp --disable-ftp --disable-file --disable-smb + --enable-ntlm - - name: 'openssl torture !FTP' - install_packages: zlib1g-dev libnghttp2-dev libssh2-1-dev libc-ares-dev - generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON - tflags: -t --shallow=25 !FTP + - name: 'openssl torture 1' + install_packages: libnghttp2-dev libssh2-1-dev libc-ares-dev + tflags: '-t --shallow=25 --min=920 1 to 950' torture: true + generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON -DCURL_ENABLE_NTLM=ON - - name: 'openssl torture FTP' - install_packages: zlib1g-dev libnghttp2-dev libssh2-1-dev libc-ares-dev - generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON - tflags: -t --shallow=20 FTP + - name: 'openssl torture 2' + install_packages: libnghttp2-dev libssh2-1-dev libc-ares-dev + tflags: '-t --shallow=25 --min=900 951 to 9999' torture: true + generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON -DCURL_ENABLE_NTLM=ON - name: 'openssl i686' - install_packages: gcc-14-i686-linux-gnu libssl-dev:i386 librtmp-dev:i386 libssh2-1-dev:i386 libidn2-dev:i386 libc-ares-dev:i386 zlib1g-dev:i386 + install_packages: gcc-14-i686-linux-gnu libssl-dev:i386 libssh2-1-dev:i386 libidn2-dev:i386 libc-ares-dev:i386 zlib1g-dev:i386 + CC: i686-linux-gnu-gcc-14 + LDFLAGS: -L/usr/lib/i386-linux-gnu + PKG_CONFIG_PATH: /usr/lib/i386-linux-gnu/pkgconfig configure: >- - PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig - CC=i686-linux-gnu-gcc-14 CPPFLAGS=-I/usr/include/i386-linux-gnu - LDFLAGS=-L/usr/lib/i386-linux-gnu --host=i686-linux-gnu - --with-openssl --with-librtmp --with-libssh2 --with-libidn2 --enable-ares --enable-debug + --with-openssl --with-libssh2 --with-libidn2 --enable-ares --enable-debug - name: '!ssl !http !smtp !imap' + image: ubuntu-24.04-arm + tflags: '--min=475' configure: --without-ssl --enable-debug --disable-http --disable-smtp --disable-imap --disable-unity - - name: 'clang-tidy' - install_packages: clang-tidy zlib1g-dev libssl-dev libkrb5-dev - install_steps: skipall wolfssl-opensslextra wolfssh - configure: LDFLAGS=-Wl,-rpath,/home/runner/wolfssl-opensslextra/lib --with-wolfssl=/home/runner/wolfssl-opensslextra --with-wolfssh=/home/runner/wolfssh --with-openssl --enable-ech --with-gssapi --enable-ssls-export - make-custom-target: tidy + - name: 'libressl Fil-C' + install_steps: filc libressl-filc nghttp2-filc pytest + tflags: '!776' # adds 1-9 minutes to the test run step, and fails consistently + CC: /home/runner/filc/build/bin/filcc + PKG_CONFIG_PATH: /home/runner/nghttp2/lib/pkgconfig + generate: >- + -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_UNITY_BUILD=OFF -DCURL_DISABLE_TYPECHECK=ON + -DOPENSSL_ROOT_DIR=/home/runner/libressl -DCURL_USE_LIBPSL=OFF + -DCURL_ZLIB=OFF -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF + -DCURL_DISABLE_LDAP=ON -DUSE_LIBIDN2=OFF -DCURL_USE_LIBSSH2=OFF + -DCURL_ENABLE_NTLM=ON - - name: 'scanbuild' - install_packages: clang-tools clang libssl-dev libssh2-1-dev - install_steps: skipall - configure: --with-openssl --enable-debug --with-libssh2 --disable-unity - CC: clang - configure-prefix: scan-build - make-prefix: scan-build --status-bugs + - name: 'clang-tidy' + install_packages: clang-20 clang-tidy-20 libssl-dev libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev libkrb5-dev libgnutls28-dev + install_steps: skiprun mbedtls-latest-intel rustls wolfssl-opensslextra-intel + install_steps_brew: gsasl + CC: clang-20 + LDFLAGS: >- + -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib + -Wl,-rpath,/home/runner/mbedtls/lib + -Wl,-rpath,/home/runner/rustls/lib + -Wl,-rpath,/home/linuxbrew/.linuxbrew/opt/gsasl/lib + + PKG_CONFIG_PATH: "\ + /home/runner/wolfssl-opensslextra/lib/pkgconfig:\ + /home/runner/mbedtls/lib/pkgconfig:\ + /home/runner/rustls/lib/pkgconfig:\ + /home/linuxbrew/.linuxbrew/opt/gsasl/lib/pkgconfig" + generate: >- + -DCURL_USE_OPENSSL=ON -DCURL_USE_WOLFSSL=ON -DCURL_USE_GNUTLS=ON -DCURL_USE_MBEDTLS=ON -DCURL_USE_RUSTLS=ON + -DCURL_USE_GSASL=ON + -DUSE_ECH=ON -DCURL_USE_GSSAPI=ON -DUSE_SSLS_EXPORT=ON + -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-20 + + - name: 'clang-tidy H3 c-ares !examples' + install_packages: clang-20 clang-tidy-20 libidn2-dev libssh-dev libnghttp2-dev + install_steps: skiprun + install_steps_brew: openssl libngtcp2 libnghttp3 c-ares + CC: clang-20 + LDFLAGS: >- + -Wl,-rpath,/home/linuxbrew/.linuxbrew/opt/openssl/lib + -Wl,-rpath,/home/linuxbrew/.linuxbrew/opt/libngtcp2/lib + -Wl,-rpath,/home/linuxbrew/.linuxbrew/opt/libnghttp3/lib + -Wl,-rpath,/home/linuxbrew/.linuxbrew/opt/c-ares/lib + + PKG_CONFIG_PATH: "\ + /home/linuxbrew/.linuxbrew/opt/libngtcp2/lib/pkgconfig:\ + /home/linuxbrew/.linuxbrew/opt/libnghttp3/lib/pkgconfig:\ + /home/linuxbrew/.linuxbrew/opt/c-ares/lib/pkgconfig" + generate: >- + -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/home/linuxbrew/.linuxbrew/opt/openssl -DUSE_NGTCP2=ON + -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON -DUSE_HTTPSRR=ON -DENABLE_ARES=ON + -DCURL_DISABLE_VERBOSE_STRINGS=ON + -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-20 - name: 'address-sanitizer' - install_packages: zlib1g-dev libssh2-1-dev clang libssl-dev libubsan1 libasan8 libtsan2 + install_packages: clang-20 libssl-dev libssh-dev libidn2-dev libnghttp2-dev libubsan1 libasan8 libtsan2 install_steps: pytest randcurl + CC: clang-20 CFLAGS: -fsanitize=address,undefined,signed-integer-overflow -fno-sanitize-recover=undefined,integer -Wformat -Werror=format-security -Werror=array-bounds -g - LDFLAGS: -fsanitize=address,undefined -fno-sanitize-recover=undefined,integer - LIBS: -ldl -lubsan - configure: CC=clang --with-openssl --enable-debug + LDFLAGS: -fsanitize=address,undefined -fno-sanitize-recover=undefined,integer -ldl -lubsan + generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH=ON + + - name: 'address-sanitizer H3 c-ares' + install_packages: clang-20 libubsan1 libasan8 libtsan2 + install_steps: pytest + install_steps_brew: openssl libssh2 libngtcp2 libnghttp3 c-ares + CC: clang-20 + CFLAGS: -fsanitize=address,undefined,signed-integer-overflow -fno-sanitize-recover=undefined,integer -Wformat -Werror=format-security -Werror=array-bounds -g + LDFLAGS: -fsanitize=address,undefined -fno-sanitize-recover=undefined,integer -ldl -lubsan -Wl,-rpath,/home/linuxbrew/.linuxbrew/opt/c-ares/lib + PKG_CONFIG_PATH: "\ + /home/linuxbrew/.linuxbrew/opt/libssh2/lib/pkgconfig:\ + /home/linuxbrew/.linuxbrew/opt/libngtcp2/lib/pkgconfig:\ + /home/linuxbrew/.linuxbrew/opt/libnghttp3/lib/pkgconfig:\ + /home/linuxbrew/.linuxbrew/opt/c-ares/lib/pkgconfig" + generate: -DENABLE_DEBUG=ON -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/home/linuxbrew/.linuxbrew/opt/openssl -DUSE_NGTCP2=ON -DUSE_SSLS_EXPORT=ON -DENABLE_ARES=ON - name: 'thread-sanitizer' - install_packages: zlib1g-dev clang libtsan2 + install_packages: clang-20 libtsan2 install_steps: pytest openssl-tsan + CC: clang-20 CFLAGS: -fsanitize=thread -g LDFLAGS: -fsanitize=thread - CC: clang - generate: -DOPENSSL_ROOT_DIR=/home/runner/openssl -DENABLE_DEBUG=ON -DENABLE_CURLDEBUG=OFF + generate: -DOPENSSL_ROOT_DIR=/home/runner/openssl -DENABLE_DEBUG=ON - name: 'memory-sanitizer' - install_packages: clang + install_packages: clang-20 install_steps: randcurl + CC: clang-20 CFLAGS: -fsanitize=memory -Wformat -Werror=format-security -Werror=array-bounds -g LDFLAGS: -fsanitize=memory LIBS: -ldl - configure: CC=clang --without-ssl --without-zlib --without-brotli --without-zstd --without-libpsl --without-nghttp2 --enable-debug + configure: --without-ssl --without-zlib --without-brotli --without-zstd --without-libpsl --without-nghttp2 --enable-debug + tflags: '--min=1480' - name: 'event-based' install_packages: libssh-dev - configure: --enable-debug --disable-shared --disable-threaded-resolver --with-libssh --with-openssl - tflags: -n --test-event '!TLS-SRP' + configure: --enable-debug --enable-static --disable-shared --disable-threaded-resolver --with-libssh --with-openssl --enable-ntlm + tflags: '-n --test-event --min=1350' - name: 'duphandle' + image: ubuntu-24.04-arm install_packages: libssh-dev - configure: --enable-debug --disable-shared --disable-threaded-resolver --with-libssh --with-openssl - tflags: -n --test-duphandle '!TLS-SRP' + configure: --enable-debug --enable-static --disable-shared --disable-threaded-resolver --with-libssh --with-openssl + tflags: '-n --test-duphandle' - - name: 'rustls valgrind' + - name: 'rustls valgrind 1' install_packages: libnghttp2-dev libldap-dev valgrind install_steps: rust rustls + tflags: '--min=780 1 to 950' + generate: -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DENABLE_DEBUG=ON + + - name: 'rustls valgrind 2' + install_packages: libnghttp2-dev libldap-dev valgrind + install_steps: rust rustls + tflags: '--min=820 951 to 9999' generate: -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DENABLE_DEBUG=ON - name: 'rustls' @@ -279,14 +410,15 @@ jobs: configure: --with-rustls --enable-ech --enable-debug - name: 'IntelC openssl' - install_packages: zlib1g-dev libssl-dev - install_steps: intel - configure: CC=icc --enable-debug --with-openssl + install_packages: libssl-dev + install_steps: intelc + CC: icc + configure: --enable-debug --with-openssl - - name: 'Slackware openssl gssapi gcc' - # These are essentially the same flags used to build the curl Slackware package + - name: 'Slackware !ssl gssapi gcc' + # Flags used to build the curl Slackware package, except OpenSSL 1.1.0: # https://ftpmirror.infania.net/slackware/slackware64-current/source/n/curl/curl.SlackBuild - configure: --enable-debug --with-openssl --with-libssh2 --with-gssapi --enable-ares --enable-static=no --without-ca-bundle --with-ca-path=/etc/ssl/certs + configure: --enable-debug --without-ssl --with-libssh2 --with-gssapi --enable-ares --without-ca-bundle --with-ca-path=/etc/ssl/certs # Docker Hub image that `container-job` executes in container: 'andy5995/slackware-build-essential:15.0' @@ -302,140 +434,213 @@ jobs: - name: 'install prereqs' if: ${{ matrix.build.container == null && !contains(matrix.build.name, 'i686') }} env: + INSTALL_PACKAGES_BREW: '${{ matrix.build.install_steps_brew }}' INSTALL_PACKAGES: >- ${{ !contains(matrix.build.install_steps, 'skipall') && !contains(matrix.build.install_steps, 'skiprun') && 'stunnel4' || '' }} ${{ contains(matrix.build.install_steps, 'pytest') && 'apache2 apache2-dev libnghttp2-dev vsftpd dante-server' || '' }} run: | - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list + sudo rm -f /etc/apt/sources.list.d/{azure-cli.sources,microsoft-prod.list,ondrej-ubuntu-php-noble.sources} sudo apt-get -o Dpkg::Use-Pty=0 update - sudo rm -f /var/lib/man-db/auto-update - sudo apt-get -o Dpkg::Use-Pty=0 install \ + printf "#!/bin/sh + while [ \$? = 0 ]; do for i in 1 2 3; do timeout 45 \"\$@\" && break 2; echo \"Error: slow server, retry \$i\"; sleep 1 + dpkg --configure -a; done; false; done" > "$HOME"/my-apt; chmod +x "$HOME"/my-apt + sudo "$HOME"/my-apt apt-get -o Dpkg::Use-Pty=0 install \ libtool autoconf automake pkgconf \ - libpsl-dev libbrotli-dev libzstd-dev \ + libpsl-dev zlib1g-dev libbrotli-dev libzstd-dev \ ${INSTALL_PACKAGES} \ ${MATRIX_INSTALL_PACKAGES} - python3 -m venv ~/venv + if [ -n "${INSTALL_PACKAGES_BREW}" ]; then + HOMEBREW_NO_AUTO_UPDATE=1 /home/linuxbrew/.linuxbrew/bin/brew install ${INSTALL_PACKAGES_BREW} + fi + # Workaround for ubuntu-24.04-arm images having 0777 for /home/runner, + # which breaks the test sshd server used in pytest. + if [[ "$(uname -m)" = *'aarch64'* ]]; then + ls -l /home + chmod 0755 /home/runner + fi - - name: 'install prereqs' + - name: 'install prereqs (i686)' if: ${{ contains(matrix.build.name, 'i686') }} run: | - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list + sudo rm -f /etc/apt/sources.list.d/{azure-cli.sources,microsoft-prod.list,ondrej-ubuntu-php-noble.sources} sudo dpkg --add-architecture i386 sudo apt-get -o Dpkg::Use-Pty=0 update - sudo rm -f /var/lib/man-db/auto-update - sudo apt-get -o Dpkg::Use-Pty=0 install \ + printf "#!/bin/sh + while [ \$? = 0 ]; do for i in 1 2 3; do timeout 45 \"\$@\" && break 2; echo \"Error: slow server, retry \$i\"; sleep 1 + dpkg --configure -a; done; false; done" > "$HOME"/my-apt; chmod +x "$HOME"/my-apt + sudo "$HOME"/my-apt apt-get -o Dpkg::Use-Pty=0 install \ libtool autoconf automake pkgconf stunnel4 \ libpsl-dev:i386 libbrotli-dev:i386 libzstd-dev:i386 \ ${MATRIX_INSTALL_PACKAGES} - python3 -m venv ~/venv - - name: 'install dependencies' + - name: 'install prereqs (alpine)' if: ${{ startsWith(matrix.build.container, 'alpine') }} run: | apk add --no-cache build-base autoconf automake libtool perl openssl-dev \ libssh2-dev zlib-dev brotli-dev zstd-dev libidn2-dev openldap-dev \ - heimdal-dev libpsl-dev c-ares-dev \ + krb5-dev libpsl-dev c-ares-dev \ py3-impacket py3-asn1 py3-six py3-pycryptodomex \ perl-time-hires openssh stunnel sudo git openssl - - name: 'cache libressl' - if: ${{ contains(matrix.build.install_steps, 'libressl') }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-libressl + - name: 'install Fil-C' + if: ${{ contains(matrix.build.install_steps, 'filc') }} + run: | + cd /home/runner + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ + --location "https://github.com/pizlonator/fil-c/releases/download/v${FIL_C_VERSION}/filc-${FIL_C_VERSION}-linux-x86_64.tar.xz" --output pkg.bin + sha256sum pkg.bin && tar -xJf pkg.bin && rm -f pkg.bin && mv "filc-${FIL_C_VERSION}-linux-x86_64" filc + cd filc + ./setup.sh + + - name: 'cache libressl (c-arm)' + if: ${{ contains(matrix.build.install_steps, 'libressl-c-arm') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-libressl-c-arm env: - cache-name: cache-libressl + cache-name: cache-libressl-c-arm with: path: ~/libressl key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} - - name: 'build libressl' - if: ${{ contains(matrix.build.install_steps, 'libressl') && steps.cache-libressl.outputs.cache-hit != 'true' }} + - name: 'build libressl (c-arm)' + if: ${{ contains(matrix.build.install_steps, 'libressl-c-arm') && steps.cache-libressl-c-arm.outputs.cache-hit != 'true' }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" | tar -xz + --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "libressl-${LIBRESSL_VERSION}" - ./configure --disable-dependency-tracking --prefix=/home/runner/libressl - make install + cmake -B . -G Ninja -DLIBRESSL_APPS=OFF -DLIBRESSL_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/libressl -DCURL_ENABLE_NTLM=ON + cmake --build . + cmake --install . - - name: 'cache wolfssl (all)' - if: ${{ contains(matrix.build.install_steps, 'wolfssl-all') }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-wolfssl-all + - name: 'cache libressl (filc)' + if: ${{ contains(matrix.build.install_steps, 'libressl-filc') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-libressl-filc env: - cache-name: cache-wolfssl-all + cache-name: cache-libressl-filc + with: + path: ~/libressl + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }}-${{ env.FIL_C_VERSION }} + + - name: 'build libressl (filc)' + if: ${{ contains(matrix.build.install_steps, 'libressl-filc') && steps.cache-libressl-filc.outputs.cache-hit != 'true' }} + run: | + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ + --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin + cd "libressl-${LIBRESSL_VERSION}" + cmake -B . -G Ninja -DLIBRESSL_APPS=OFF -DLIBRESSL_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/libressl \ + -DCMAKE_C_COMPILER=/home/runner/filc/build/bin/filcc -DENABLE_ASM=OFF + cmake --build . + cmake --install . + + - name: 'cache nghttp2 (filc)' + if: ${{ contains(matrix.build.install_steps, 'nghttp2-filc') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-nghttp2-filc + env: + cache-name: cache-nghttp2-filc + with: + path: ~/nghttp2 + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.NGHTTP2_VERSION }}-${{ env.FIL_C_VERSION }} + + - name: 'build nghttp2 (filc)' + if: ${{ contains(matrix.build.install_steps, 'nghttp2-filc') && steps.cache-nghttp2-filc.outputs.cache-hit != 'true' }} + run: | + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ + --location "https://github.com/nghttp2/nghttp2/releases/download/v${NGHTTP2_VERSION}/nghttp2-${NGHTTP2_VERSION}.tar.xz" --output pkg.bin + sha256sum pkg.bin && tar -xJf pkg.bin && rm -f pkg.bin + cd "nghttp2-${NGHTTP2_VERSION}" + cmake -B . -G Ninja -DENABLE_LIB_ONLY=ON -DBUILD_TESTING=OFF -DENABLE_DOC=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/nghttp2 \ + -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF \ + -DCMAKE_C_COMPILER=/home/runner/filc/build/bin/filcc + cmake --build . + cmake --install . + + - name: 'cache wolfssl (all-arm)' + if: ${{ contains(matrix.build.install_steps, 'wolfssl-all-arm') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-wolfssl-all-arm + env: + cache-name: cache-wolfssl-all-arm with: path: ~/wolfssl-all key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.WOLFSSL_VERSION }} - - name: 'build wolfssl (all)' # does not support `OPENSSL_COEXIST` - if: ${{ contains(matrix.build.install_steps, 'wolfssl-all') && steps.cache-wolfssl-all.outputs.cache-hit != 'true' }} + - name: 'build wolfssl (all-arm)' # does not support `OPENSSL_COEXIST` + if: ${{ contains(matrix.build.install_steps, 'wolfssl-all-arm') && steps.cache-wolfssl-all-arm.outputs.cache-hit != 'true' }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" | tar -xz + --location "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" ./autogen.sh - ./configure --disable-dependency-tracking --enable-tls13 --enable-harden --enable-all \ - --disable-benchmark --disable-crypttests --disable-examples --prefix=/home/runner/wolfssl-all + ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-all --enable-tls13 --enable-harden --enable-all \ + --disable-benchmark --disable-crypttests --disable-examples make install - - name: 'cache wolfssl (opensslextra)' # does support `OPENSSL_COEXIST` - if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra') }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-wolfssl-opensslextra + - name: 'cache wolfssl (opensslextra-intel)' # does support `OPENSSL_COEXIST` + if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-intel') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-wolfssl-opensslextra-intel env: - cache-name: cache-wolfssl-opensslextra + cache-name: cache-wolfssl-opensslextra-intel with: path: ~/wolfssl-opensslextra key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.WOLFSSL_VERSION }} - - name: 'build wolfssl (opensslextra)' - if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra') && steps.cache-wolfssl-opensslextra.outputs.cache-hit != 'true' }} + - name: 'build wolfssl (opensslextra-intel)' + if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-intel') && steps.cache-wolfssl-opensslextra-intel.outputs.cache-hit != 'true' }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" | tar -xz + --location "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "wolfssl-${WOLFSSL_VERSION}-stable" ./autogen.sh - ./configure --disable-dependency-tracking --enable-tls13 --enable-harden --enable-wolfssh --enable-ech --enable-opensslextra \ - --disable-benchmark --disable-crypttests --disable-examples --prefix=/home/runner/wolfssl-opensslextra + ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-opensslextra --enable-tls13 --enable-harden --enable-ech --enable-opensslextra \ + --disable-benchmark --disable-crypttests --disable-examples make install - - name: 'cache wolfssh' - if: ${{ contains(matrix.build.install_steps, 'wolfssh') }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-wolfssh + - name: 'cache wolfssl (opensslextra-arm)' # does support `OPENSSL_COEXIST` + if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-arm') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-wolfssl-opensslextra-arm env: - cache-name: cache-wolfssh + cache-name: cache-wolfssl-opensslextra-arm with: - path: ~/wolfssh - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.WOLFSSH_VERSION }}-${{ env.WOLFSSL_VERSION }} + path: ~/wolfssl-opensslextra + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.WOLFSSL_VERSION }} - - name: 'build wolfssh' - if: ${{ contains(matrix.build.install_steps, 'wolfssh') && steps.cache-wolfssh.outputs.cache-hit != 'true' }} + - name: 'build wolfssl (opensslextra-arm)' + if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-arm') && steps.cache-wolfssl-opensslextra-arm.outputs.cache-hit != 'true' }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/wolfSSL/wolfssh/archive/v${WOLFSSH_VERSION}-stable.tar.gz" | tar -xz - cd "wolfssh-${WOLFSSH_VERSION}-stable" + --location "https://github.com/wolfSSL/wolfssl/archive/v${WOLFSSL_VERSION}-stable.tar.gz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin + cd "wolfssl-${WOLFSSL_VERSION}-stable" ./autogen.sh - ./configure --disable-dependency-tracking --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-scp --enable-sftp --disable-term \ - --disable-examples --prefix=/home/runner/wolfssh + ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-opensslextra --enable-tls13 --enable-harden --enable-ech --enable-opensslextra \ + --disable-benchmark --disable-crypttests --disable-examples make install - - name: 'cache mbedtls' - if: ${{ contains(matrix.build.install_steps, 'mbedtls') }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-mbedtls + - name: 'cache mbedtls (latest-intel)' + if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-intel') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-mbedtls-latest-intel env: - cache-name: cache-mbedtls-threadsafe + cache-name: cache-mbedtls-latest-intel with: path: ~/mbedtls key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.MBEDTLS_VERSION }} - - name: 'build mbedtls' - if: ${{ contains(matrix.build.install_steps, 'mbedtls') && steps.cache-mbedtls.outputs.cache-hit != 'true' }} + - name: 'build mbedtls (latest-intel)' + if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-intel') && steps.cache-mbedtls-latest-intel.outputs.cache-hit != 'true' }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_VERSION}/mbedtls-${MBEDTLS_VERSION}.tar.bz2" | tar -xj + --location "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_VERSION}/mbedtls-${MBEDTLS_VERSION}.tar.bz2" --output pkg.bin + sha256sum pkg.bin && tar -xjf pkg.bin && rm -f pkg.bin cd "mbedtls-${MBEDTLS_VERSION}" ./scripts/config.py set MBEDTLS_THREADING_C ./scripts/config.py set MBEDTLS_THREADING_PTHREAD @@ -444,9 +649,57 @@ jobs: cmake --build . cmake --install . - - name: 'cache openldap-static' + - name: 'cache mbedtls (latest-arm)' + if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-arm') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-mbedtls-latest-arm + env: + cache-name: cache-mbedtls-latest-arm + with: + path: ~/mbedtls + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.MBEDTLS_VERSION }} + + - name: 'build mbedtls (latest-arm)' + if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-arm') && steps.cache-mbedtls-latest-arm.outputs.cache-hit != 'true' }} + run: | + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ + --location "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_VERSION}/mbedtls-${MBEDTLS_VERSION}.tar.bz2" --output pkg.bin + sha256sum pkg.bin && tar -xjf pkg.bin && rm -f pkg.bin + cd "mbedtls-${MBEDTLS_VERSION}" + ./scripts/config.py set MBEDTLS_THREADING_C + ./scripts/config.py set MBEDTLS_THREADING_PTHREAD + cmake -B . -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_PREFIX=/home/runner/mbedtls \ + -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF + cmake --build . + cmake --install . + + - name: 'cache mbedtls (prev)' + if: ${{ contains(matrix.build.install_steps, 'mbedtls-prev') }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-mbedtls-prev + env: + cache-name: cache-mbedtls-prev + with: + path: ~/mbedtls-prev + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.MBEDTLS_PREV_VERSION }} + + - name: 'build mbedtls (prev)' + if: ${{ contains(matrix.build.install_steps, 'mbedtls-prev') && steps.cache-mbedtls-prev.outputs.cache-hit != 'true' }} + run: | + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ + --location "https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-${MBEDTLS_PREV_VERSION}/mbedtls-${MBEDTLS_PREV_VERSION}.tar.bz2" --output pkg.bin + sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${MBEDTLS_PREV_SHA256}" && tar -xjf pkg.bin && rm -f pkg.bin + cd "mbedtls-${MBEDTLS_PREV_VERSION}" + ./scripts/config.py set MBEDTLS_THREADING_C + ./scripts/config.py set MBEDTLS_THREADING_PTHREAD + cmake -B . -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_PREFIX=/home/runner/mbedtls-prev \ + -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF + cmake --build . + cmake --install . + + - name: 'cache openldap (static)' if: ${{ contains(matrix.build.install_steps, 'openldap-static') }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-openldap-static env: cache-name: cache-openldap-static @@ -458,15 +711,16 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'openldap-static') && steps.cache-openldap-static.outputs.cache-hit != 'true' }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-${OPENLDAP_VERSION}.tgz" | tar -xz + --location "https://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-${OPENLDAP_VERSION}.tgz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "openldap-${OPENLDAP_VERSION}" - ./configure --enable-static --disable-shared --prefix=/home/runner/openldap-static - make + autoreconf -fi + ./configure --prefix=/home/runner/openldap-static --enable-static --disable-shared --disable-slapd make install - name: 'cache openssl (thread sanitizer)' if: ${{ contains(matrix.build.install_steps, 'openssl-tsan') }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-openssl-tsan env: cache-name: cache-openssl-tsan @@ -477,34 +731,15 @@ jobs: - name: 'build openssl (thread sanitizer)' if: ${{ contains(matrix.build.install_steps, 'openssl-tsan') && steps.cache-openssl-tsan.outputs.cache-hit != 'true' }} run: | - git clone --quiet --depth=1 -b "openssl-${OPENSSL_VERSION}" https://github.com/openssl/openssl + git clone --quiet --depth 1 -b "openssl-${OPENSSL_VERSION}" https://github.com/openssl/openssl cd openssl CC=clang CFLAGS='-fsanitize=thread' LDFLAGS='-fsanitize=thread' ./config --prefix=/home/runner/openssl --libdir=lib no-makedepend no-apps no-docs no-tests make make -j1 install_sw - - name: 'cache quictls' - if: ${{ contains(matrix.build.install_steps, 'quictls') }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-quictls - env: - cache-name: cache-quictls - with: - path: ~/quictls - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.QUICTLS_VERSION }}-quic1 - - - name: 'build quictls' - if: ${{ contains(matrix.build.install_steps, 'quictls') && steps.cache-quictls.outputs.cache-hit != 'true' }} - run: | - git clone --quiet --depth=1 -b "openssl-${QUICTLS_VERSION}-quic1" https://github.com/quictls/openssl - cd openssl - ./config --prefix=/home/runner/quictls --libdir=lib no-makedepend no-apps no-docs no-tests - make - make -j1 install_sw - - name: 'cache awslc' if: ${{ contains(matrix.build.install_steps, 'awslc') }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-awslc env: cache-name: cache-awslc @@ -516,16 +751,16 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'awslc') && steps.cache-awslc.outputs.cache-hit != 'true' }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/awslabs/aws-lc/archive/refs/tags/v${AWSLC_VERSION}.tar.gz" | tar -xz - mkdir "aws-lc-${AWSLC_VERSION}-build" - cd "aws-lc-${AWSLC_VERSION}-build" - cmake -G Ninja -DCMAKE_INSTALL_PREFIX=/home/runner/awslc "../aws-lc-${AWSLC_VERSION}" -DBUILD_TOOL=OFF -DBUILD_TESTING=OFF + --location "https://github.com/awslabs/aws-lc/archive/refs/tags/v${AWSLC_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin + cd "aws-lc-${AWSLC_VERSION}" + cmake -B . -G Ninja -DCMAKE_INSTALL_PREFIX=/home/runner/awslc -DBUILD_TOOL=OFF -DBUILD_TESTING=OFF cmake --build . cmake --install . - name: 'cache boringssl' if: ${{ contains(matrix.build.install_steps, 'boringssl') }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-boringssl env: cache-name: cache-boringssl @@ -539,14 +774,15 @@ jobs: mkdir boringssl-src cd boringssl-src curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - "https://boringssl.googlesource.com/boringssl/+archive/${BORINGSSL_VERSION}.tar.gz" | tar -xz + "https://boringssl.googlesource.com/boringssl/+archive/${BORINGSSL_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cmake -B . -G Ninja -DCMAKE_INSTALL_PREFIX=/home/runner/boringssl -DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=ON cmake --build . cmake --install . - name: 'cache rustls' if: ${{ contains(matrix.build.install_steps, 'rustls') }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-rustls env: cache-name: cache-rustls @@ -557,29 +793,28 @@ jobs: - name: 'fetch rustls deb' if: ${{ contains(matrix.build.install_steps, 'rustls') && steps.cache-rustls.outputs.cache-hit != 'true' }} run: | - mkdir -p ~/rustls + cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/rustls/rustls-ffi/releases/download/v${RUSTLS_VERSION}/librustls_${RUSTLS_VERSION}_amd64.deb.zip" --output ~/rustls/librustls.zip - unzip ~/rustls/librustls.zip -d ~/rustls - rm ~/rustls/librustls.zip + --location "https://github.com/rustls/rustls-ffi/releases/download/v${RUSTLS_VERSION}/librustls_${RUSTLS_VERSION}_amd64.deb.zip" --output pkg.bin + sha256sum pkg.bin && unzip pkg.bin -d rustls && rm -f pkg.bin - name: 'build rustls' - # Note: we don't check cache-hit here. If the cache is hit, we still need to dpkg install the deb. + # Note: we do not check cache-hit here. If the cache is hit, we need to dpkg install the deb. if: ${{ contains(matrix.build.install_steps, 'rustls') }} run: sudo dpkg -i ~/rustls/"librustls_${RUSTLS_VERSION}_amd64.deb" - name: 'install Intel compilers' - if: ${{ contains(matrix.build.install_steps, 'intel') }} + if: ${{ contains(matrix.build.install_steps, 'intelc') }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ --compressed https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | \ sudo tee /etc/apt/trusted.gpg.d/intel-sw.asc >/dev/null - sudo add-apt-repository "deb https://apt.repos.intel.com/oneapi all main" + sudo add-apt-repository 'deb https://apt.repos.intel.com/oneapi all main' sudo apt-get -o Dpkg::Use-Pty=0 install intel-oneapi-compiler-dpcpp-cpp-and-cpp-classic source /opt/intel/oneapi/setvars.sh printenv >> "$GITHUB_ENV" - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -594,7 +829,6 @@ jobs: LDFLAGS: '${{ matrix.build.LDFLAGS }}' LIBS: '${{ matrix.build.LIBS }}' MATRIX_CONFIGURE: '${{ matrix.build.configure }}' - MATRIX_CONFIGURE_PREFIX: '${{ matrix.build.configure-prefix }}' MATRIX_GENERATE: '${{ matrix.build.generate }}' MATRIX_PKG_CONFIG_PATH: '${{ matrix.build.PKG_CONFIG_PATH }}' run: | @@ -608,9 +842,8 @@ jobs: ${MATRIX_GENERATE} else mkdir bld && cd bld && \ - ${MATRIX_CONFIGURE_PREFIX} \ - ../configure --prefix="$HOME"/curl-install --enable-unity --enable-warnings --enable-werror \ - --disable-dependency-tracking \ + ../configure --prefix="$HOME"/curl-install --enable-unity --enable-warnings --enable-werror --disable-static \ + --disable-dependency-tracking --enable-option-checking=fatal \ ${MATRIX_CONFIGURE} fi @@ -627,17 +860,15 @@ jobs: run: grep -H -v '^#' bld/tests/config bld/tests/http/config.ini || true - name: 'build' - env: - MATRIX_MAKE_CUSTOM_TARGET: '${{ matrix.build.make-custom-target }}' run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then - ${MATRIX_MAKE_PREFIX} cmake --build bld --verbose + cmake --build bld --verbose else - ${MATRIX_MAKE_PREFIX} make -C bld V=1 ${MATRIX_MAKE_CUSTOM_TARGET} + make -C bld V=1 fi - name: 'single-use function check' - if: ${{ contains(matrix.build.configure, '--disable-unity') || contains(matrix.build.generate, '-DCMAKE_UNITY_BUILD=OFF') }} + if: ${{ (contains(matrix.build.configure, '--disable-unity') || contains(matrix.build.generate, '-DCMAKE_UNITY_BUILD=OFF')) && !contains(matrix.build.install_steps, 'filc') }} run: | git config --global --add safe.directory "*" if [ "${MATRIX_BUILD}" = 'cmake' ]; then @@ -647,9 +878,11 @@ jobs: fi ./scripts/singleuse.pl --unit "${libcurla}" - - name: 'check curl -V output' - if: ${{ matrix.build.make-custom-target != 'tidy' }} - run: bld/src/curl -V + - name: 'curl -V' + run: | + find . -type f \( -name curl -o -name '*.so.*' -o -name '*.a' \) -print0 | xargs -0 file -- + find . -type f \( -name curl -o -name '*.so.*' -o -name '*.a' \) -print0 | xargs -0 stat -c '%10s bytes: %n' -- + bld/src/curl --disable -V - name: 'curl install' run: | @@ -671,28 +904,28 @@ jobs: - name: 'install test prereqs' if: ${{ !contains(matrix.build.install_steps, 'skipall') && !contains(matrix.build.install_steps, 'skiprun') && matrix.build.container == null }} run: | - [ -x ~/venv/bin/activate ] && source ~/venv/bin/activate - python3 -m pip install -r tests/requirements.txt + python3 -m venv ~/venv + if bld/src/curl --disable -V 2>/dev/null | grep smb; then + ~/venv/bin/pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary -r tests/requirements.txt + fi - name: 'run tests' if: ${{ !contains(matrix.build.install_steps, 'skipall') && !contains(matrix.build.install_steps, 'skiprun') }} - timeout-minutes: ${{ contains(matrix.build.install_packages, 'valgrind') && 30 || 15 }} + timeout-minutes: ${{ contains(matrix.build.install_packages, 'valgrind') && 20 || 10 }} env: TEST_TARGET: ${{ matrix.build.torture && 'test-torture' || 'test-ci' }} TFLAGS: '${{ matrix.build.tflags }}' run: | - if [ "${TEST_TARGET}" = 'test-ci' ]; then - if [[ "${MATRIX_INSTALL_STEPS}" = *'wolfssh'* ]]; then - TFLAGS+=' ~SFTP' # curl: (79) wolfssh SFTP connect error -1051 / WS_MATCH_KEY_ALGO_E / cannot match key algo with peer - fi - if [[ "${MATRIX_INSTALL_PACKAGES}" = *'valgrind'* ]]; then - TFLAGS+=' -j6' - if [[ "${MATRIX_INSTALL_PACKAGES}" = *'heimdal-dev'* ]]; then - TFLAGS+=' ~2056 ~2057 ~2077 ~2078' # memory leaks from Curl_auth_decode_spnego_message() -> gss_import_name() - fi + if [ "${TEST_TARGET}" = 'test-ci' ] && [[ "${MATRIX_INSTALL_PACKAGES}" = *'valgrind'* ]]; then + TFLAGS+=' -j6' + TFLAGS+=' !776' # skip long-running flaky test + if [[ "${MATRIX_INSTALL_PACKAGES}" = *'libgss-dev'* ]]; then + TFLAGS+=' ~2077 ~2078' # memory leaks from Curl_auth_decode_spnego_message() -> gss_init_sec_context() fi + elif [ "${TEST_TARGET}" != 'test-ci' ]; then + TFLAGS+=' --buildinfo' # only test-ci sets this by default, set it manually for test-torture fi - [ -x ~/venv/bin/activate ] && source ~/venv/bin/activate + [ -f ~/venv/bin/activate ] && source ~/venv/bin/activate if [[ "${MATRIX_INSTALL_STEPS}" = *'codeset-test'* ]]; then locale || true export LC_ALL=C @@ -708,8 +941,8 @@ jobs: - name: 'install pytest prereqs' if: ${{ contains(matrix.build.install_steps, 'pytest') }} run: | - [ -x ~/venv/bin/activate ] && source ~/venv/bin/activate - python3 -m pip install -r tests/http/requirements.txt + [ -d ~/venv ] || python3 -m venv ~/venv + ~/venv/bin/pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary -r tests/http/requirements.txt - name: 'run pytest' if: ${{ contains(matrix.build.install_steps, 'pytest') }} @@ -717,7 +950,7 @@ jobs: PYTEST_ADDOPTS: '--color=yes' PYTEST_XDIST_AUTO_NUM_WORKERS: 4 run: | - [ -x ~/venv/bin/activate ] && source ~/venv/bin/activate + [ -f ~/venv/bin/activate ] && source ~/venv/bin/activate if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake --build bld --verbose --target curl-pytest-ci else @@ -732,10 +965,10 @@ jobs: ../.github/scripts/randcurl.pl 60 ../bld/src/curl - name: 'build examples' - if: ${{ !contains(matrix.build.install_packages, 'valgrind') && matrix.build.make-custom-target != 'tidy' }} + if: ${{ !contains(matrix.build.install_packages, 'valgrind') && !contains(matrix.build.name, '!examples') }} run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then - ${MATRIX_MAKE_PREFIX} cmake --build bld --verbose --target curl-examples-build + cmake --build bld --verbose --target curl-examples-build else - ${MATRIX_MAKE_PREFIX} make -C bld V=1 examples + make -C bld V=1 examples fi diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 5e817dd4bd..54c50e50a3 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -14,10 +14,7 @@ name: 'macOS' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' pull_request: branches: - master @@ -26,10 +23,7 @@ name: 'macOS' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} @@ -37,31 +31,35 @@ concurrency: permissions: {} -# Deprecated Apple APIs and the macos-version-min value required to avoid -# deprecation warnings with llvm/clang: -# -# - 10.7 Lion (2011) - GSS -# - 10.9 Mavericks (2013) - LDAP +# Apple APIs and the macos-version-min value required to avoid deprecation +# warnings with llvm/clang, and/or the feature getting enabled at build-time +# or runtime: # +# - 10.7 Lion (2011) - GSS (build-time, deprecated MIT Kerberos shim) +# - 10.9 Mavericks (2013) - LDAP (build-time, deprecated), OCSP (runtime) +# - 10.11 El Capitan (2015) - connectx() (runtime) +# - 10.12 Sierra (2016) - clock_gettime() (build-time, runtime) +# - 10.14 Mojave (2018) - SecTrustEvaluateWithError() (runtime) env: CURL_CI: github + CURL_TEST_MIN: 1700 MAKEFLAGS: -j 4 LDFLAGS: -w # suppress 'object file was built for newer macOS version than being linked' warnings jobs: ios: name: "iOS, ${{ (matrix.build.generator && format('CM-{0}', matrix.build.generator)) || (matrix.build.generate && 'CM' || 'AM' )}} ${{ matrix.build.name }} arm64" - runs-on: 'macos-latest' + runs-on: macos-latest timeout-minutes: 10 env: DEVELOPER_DIR: "/Applications/Xcode${{ matrix.build.xcode && format('_{0}', matrix.build.xcode) || '' }}.app/Contents/Developer" - CC: ${{ matrix.build.compiler || 'clang' }} + CC: 'clang' LDFLAGS: '' MATRIX_BUILD: ${{ matrix.build.generate && 'cmake' || 'autotools' }} MATRIX_OPTIONS: ${{ matrix.build.options }} - # renovate: datasource=github-tags depName=libressl-portable/portable versioning=semver registryUrl=https://github.com - LIBRESSL_VERSION: 4.1.0 + # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com + LIBRESSL_VERSION: 4.2.1 strategy: fail-fast: false matrix: @@ -73,16 +71,8 @@ jobs: - name: 'libressl' install_steps: libressl # FIXME: Could not make OPENSSL_ROOT_DIR work. CMake seems to prepend sysroot to it. - generate: >- - -DCMAKE_BUILD_TYPE=Release -DCMAKE_UNITY_BUILD_BATCH_SIZE=50 - -DOPENSSL_INCLUDE_DIR=/Users/runner/libressl/include - -DOPENSSL_SSL_LIBRARY=/Users/runner/libressl/lib/libssl.a - -DOPENSSL_CRYPTO_LIBRARY=/Users/runner/libressl/lib/libcrypto.a - -DCURL_USE_LIBPSL=OFF - - - name: 'libressl' - install_steps: libressl generator: Xcode + xcode: '' # default Xcode. Set it once to silence actionlint. options: --config Debug generate: >- -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=OFF @@ -90,14 +80,25 @@ jobs: -DOPENSSL_INCLUDE_DIR=/Users/runner/libressl/include -DOPENSSL_SSL_LIBRARY=/Users/runner/libressl/lib/libssl.a -DOPENSSL_CRYPTO_LIBRARY=/Users/runner/libressl/lib/libcrypto.a - -DCURL_USE_LIBPSL=OFF + -DCURL_USE_LIBPSL=OFF -DCURL_ENABLE_NTLM=ON steps: - name: 'brew install' if: ${{ matrix.build.configure }} + timeout-minutes: 5 run: | - # shellcheck disable=SC2181,SC2034 - while [[ $? == 0 ]]; do for i in 1 2 3; do if brew update && brew install automake libtool; then break 2; else echo Error: wait to try again; sleep 10; fi; done; false Too many retries; done + # shellcheck disable=SC2181 + while [[ $? == 0 ]]; do + for i in 1 2 3; do + if brew update && brew install automake libtool; then + break 2 + else + echo "Error: wait to try again: $i" + sleep 10 + fi + done + false Too many retries + done - name: 'toolchain versions' run: | @@ -106,12 +107,13 @@ jobs: xcodebuild -sdk -version | grep '^Path:' || true xcrun --sdk iphoneos --show-sdk-path 2>/dev/null || true xcrun --sdk iphoneos --show-sdk-version || true + echo '::group::compiler defaults'; echo 'int main(void) {}' | "${CC}" -v -x c -; echo '::endgroup::' echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::' - echo '::group::brew packages installed'; ls -l /opt/homebrew/opt; echo '::endgroup::' + echo '::group::brew packages installed'; ls -l "$(brew --prefix)"/opt; echo '::endgroup::' - name: 'cache libressl' if: ${{ contains(matrix.build.install_steps, 'libressl') }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-libressl env: cache-name: cache-libressl @@ -123,11 +125,10 @@ jobs: if: ${{ contains(matrix.build.install_steps, 'libressl') && steps.cache-libressl.outputs.cache-hit != 'true' }} run: | curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" | tar -x + --location "https://github.com/libressl/portable/releases/download/v${LIBRESSL_VERSION}/libressl-${LIBRESSL_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin && tar -xzf pkg.bin && rm -f pkg.bin cd "libressl-${LIBRESSL_VERSION}" - # FIXME: on the 4.0.1 release, delete '-DHAVE_ENDIAN_H=0' cmake -B . -G Ninja \ - -DHAVE_ENDIAN_H=0 \ -DCMAKE_INSTALL_PREFIX=/Users/runner/libressl \ -DCMAKE_SYSTEM_NAME=iOS \ -DCMAKE_SYSTEM_PROCESSOR=aarch64 \ @@ -137,7 +138,7 @@ jobs: cmake --build . cmake --install . --verbose - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -155,13 +156,13 @@ jobs: # https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-ios-tvos-visionos-or-watchos [ -n "${MATRIX_GENERATOR}" ] && options="-G ${MATRIX_GENERATOR}" cmake -B bld -G Ninja -D_CURL_PREFILL=ON \ - -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON \ + -DCMAKE_UNITY_BUILD=ON -DCURL_DROP_UNUSED=ON -DCURL_WERROR=ON \ -DCMAKE_SYSTEM_NAME=iOS \ -DUSE_APPLE_IDN=ON \ ${MATRIX_GENERATE} ${options} else mkdir bld && cd bld && ../configure --enable-unity --enable-warnings --enable-werror \ - --disable-dependency-tracking \ + --disable-dependency-tracking --enable-option-checking=fatal \ CFLAGS="-isysroot $(xcrun --sdk iphoneos --show-sdk-path 2>/dev/null)" \ --host=aarch64-apple-darwin \ --with-apple-idn \ @@ -186,9 +187,12 @@ jobs: fi - name: 'curl info' - run: find . -type f \( -name curl -o -name '*.dylib' -o -name '*.a' \) -exec file '{}' \; + run: | + find . -type f \( -name curl -o -name '*.dylib' -o -name '*.a' \) -print0 | xargs -0 file -- + find . -type f \( -name curl -o -name '*.dylib' -o -name '*.a' \) -print0 | xargs -0 stat -f '%10z bytes: %N' -- - name: 'build tests' + if: ${{ matrix.build.generate }} # skip for autotools to save time run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake --build bld ${MATRIX_OPTIONS} --parallel 4 --target testdeps --verbose @@ -197,6 +201,7 @@ jobs: fi - name: 'build examples' + if: ${{ matrix.build.generate }} # skip for autotools to save time run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake --build bld ${MATRIX_OPTIONS} --parallel 4 --target curl-examples-build --verbose @@ -205,155 +210,215 @@ jobs: fi macos: - name: "${{ matrix.build.generate && 'CM' || 'AM' }} ${{ matrix.compiler }} ${{ matrix.build.name }}" - runs-on: 'macos-15' - timeout-minutes: 45 + name: "${{ matrix.build.generate && 'CM' || 'AM' }} ${{ matrix.build.compiler }} ${{ matrix.build.name }}" + runs-on: ${{ matrix.build.image || 'macos-15' }} + timeout-minutes: 15 env: DEVELOPER_DIR: "/Applications/Xcode${{ matrix.build.xcode && format('_{0}', matrix.build.xcode) || '' }}.app/Contents/Developer" - CC: '${{ matrix.compiler }}' + CC: '${{ matrix.build.compiler }}' MATRIX_BUILD: ${{ matrix.build.generate && 'cmake' || 'autotools' }} - MATRIX_COMPILER: '${{ matrix.compiler }}' + MATRIX_COMPILER: '${{ matrix.build.compiler }}' MATRIX_INSTALL: '${{ matrix.build.install }}' MATRIX_INSTALL_STEPS: '${{ matrix.build.install_steps }}' MATRIX_MACOS_VERSION_MIN: '${{ matrix.build.macos-version-min }}' strategy: fail-fast: false matrix: - compiler: [clang, llvm@18, gcc-12] build: - # autotools - name: '!ssl !debug brotli zstd' - compiler: clang - install: brotli zstd - configure: --without-ssl --with-brotli --with-zstd - - name: '!ssl !debug' - compiler: gcc-12 - configure: --without-ssl - - name: '!ssl' - compiler: clang - configure: --enable-debug --without-ssl + compiler: gcc-13 + configure: --without-ssl --with-brotli --with-zstd --with-apple-idn + tflags: '--min=1450' + xcode: '' # default Xcode. Set it once to silence actionlint. + - name: '!ssl libssh2 AppleIDN' compiler: clang - configure: --enable-debug --with-libssh2=/opt/homebrew/opt/libssh2 --without-ssl --with-apple-idn + generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH2=ON -DUSE_APPLE_IDN=ON -DCURL_ENABLE_SSL=OFF -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF + tflags: '--min=1550' + - name: 'OpenSSL libssh c-ares' compiler: clang install: libssh configure: --enable-debug --with-libssh --with-openssl=/opt/homebrew/opt/openssl --enable-ares --with-fish-functions-dir --with-zsh-functions-dir + - name: 'OpenSSL libssh' compiler: llvm@18 install: libssh libnghttp3 - configure: --enable-debug --with-libssh --with-openssl=/opt/homebrew/opt/openssl --with-openssl-quic - - name: '!ssl c-ares' - compiler: clang - configure: --enable-debug --enable-ares --without-ssl - - name: '!ssl HTTP-only' - compiler: clang - configure: >- - --enable-debug - --disable-alt-svc --disable-dict --disable-file --disable-ftp --disable-gopher --disable-imap - --disable-ldap --disable-pop3 --without-librtmp --disable-rtsp - --disable-shared --disable-smb --disable-smtp --disable-telnet --disable-tftp --disable-unix-sockets - --without-brotli --without-gssapi --without-libidn2 --without-libpsl --without-librtmp - --without-libssh2 --without-libssh --without-wolfssh - --without-nghttp2 --disable-ntlm --without-ssl --without-zlib --without-zstd + generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF + - name: '!ssl HTTP-only c-ares' macos-version-min: '10.15' # Catalina (2019) - - name: 'LibreSSL +examples' + compiler: clang + tflags: '--min=930' + generate: >- + -DENABLE_DEBUG=ON -DENABLE_ARES=ON + -DCURL_ENABLE_SSL=OFF -DHTTP_ONLY=ON + -DCURL_DISABLE_ALTSVC=ON -DENABLE_UNIX_SOCKETS=OFF + -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=OFF -DUSE_NGHTTP2=OFF + -DCURL_USE_GSSAPI=OFF -DUSE_LIBIDN2=OFF -DCURL_USE_LIBPSL=OFF + -DCURL_BROTLI=OFF -DCURL_ZLIB=OFF -DCURL_ZSTD=OFF + -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=OFF + + - name: 'LibreSSL !ldap +examples' compiler: clang install: libressl install_steps: pytest - configure: --enable-debug --with-openssl=/opt/homebrew/opt/libressl - - name: 'OpenSSL' + generate: >- + -DENABLE_DEBUG=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/libressl -DCURL_DISABLE_LDAP=ON -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF + -DCURL_USE_LIBSSH2=OFF -DCURL_ENABLE_NTLM=ON + + - name: 'OpenSSL 10.15 C89' + macos-version-min: '10.15' compiler: clang install: libnghttp3 libngtcp2 install_steps: pytest - configure: --enable-debug --with-openssl=/opt/homebrew/opt/openssl --with-ngtcp2 + generate: >- + -DENABLE_DEBUG=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DUSE_NGTCP2=ON -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF -DCURL_USE_LIBSSH2=OFF + -DCMAKE_C_STANDARD=90 -DCURL_ENABLE_NTLM=ON + + - name: 'OpenSSL SecTrust' + compiler: clang + install: libnghttp3 libngtcp2 + install_steps: pytest + configure: --enable-debug --with-openssl=/opt/homebrew/opt/openssl --with-ngtcp2 --with-apple-sectrust --enable-ntlm + - name: 'OpenSSL event-based' compiler: clang - configure: --enable-debug --with-openssl=/opt/homebrew/opt/openssl - tflags: --test-event - - name: 'quictls libssh2 !ldap 10.15' + generate: -DENABLE_DEBUG=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF -DCURL_USE_LIBSSH2=OFF -DCURL_ENABLE_NTLM=ON + tflags: '--test-event --min=1300' + + - name: 'OpenSSL gsasl AppleIDN SecTrust +examples' compiler: clang - install: quictls - configure: --enable-debug --disable-ldap --with-openssl=/opt/homebrew/opt/quictls LDFLAGS=-L/opt/homebrew/opt/quictls/lib - macos-version-min: '10.15' - # cmake - - name: 'OpenSSL gsasl rtmp AppleIDN' - install: libnghttp3 libngtcp2 gsasl rtmpdump - generate: -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DCURL_USE_GSASL=ON -DUSE_LIBRTMP=ON -DUSE_APPLE_IDN=ON -DUSE_NGTCP2=ON + install: libnghttp3 libngtcp2 gsasl + generate: >- + -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DCURL_USE_GSASL=ON -DUSE_APPLE_IDN=ON -DUSE_NGTCP2=ON -DCURL_DISABLE_VERBOSE_STRINGS=ON + -DUSE_APPLE_SECTRUST=ON -DCURL_ENABLE_NTLM=ON + - name: 'MultiSSL AppleIDN clang-tidy +examples' + image: macos-26 compiler: clang - install: llvm brotli zstd gnutls nettle mbedtls gsasl rtmpdump fish - install_steps: clang-tidy - generate: -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DCURL_DEFAULT_SSL_BACKEND=openssl -DCURL_USE_GNUTLS=ON -DCURL_USE_MBEDTLS=ON -DENABLE_ARES=ON -DCURL_USE_GSASL=ON -DUSE_LIBRTMP=ON -DUSE_APPLE_IDN=ON -DUSE_SSLS_EXPORT=ON -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/opt/homebrew/opt/llvm/bin/clang-tidy -DCURL_COMPLETION_FISH=ON -DCURL_COMPLETION_ZSH=ON + install: llvm gnutls nettle libressl krb5 mbedtls gsasl rustls-ffi libssh fish + install_steps: skiprun chkprefill: _chkprefill - - name: 'quictls +static libssh +examples' - install: quictls libssh - generate: -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/quictls -DBUILD_STATIC_LIBS=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON - - name: 'LibreSSL openldap heimdal c-ares +examples' - install: libressl heimdal openldap - generate: -DENABLE_DEBUG=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/libressl -DENABLE_ARES=ON -DCURL_USE_GSSAPI=ON -DGSS_ROOT_DIR=/opt/homebrew/opt/heimdal -DLDAP_INCLUDE_DIR=/opt/homebrew/opt/openldap/include -DLDAP_LIBRARY=/opt/homebrew/opt/openldap/lib/libldap.dylib -DLDAP_LBER_LIBRARY=/opt/homebrew/opt/openldap/lib/liblber.dylib + generate: >- + -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/libressl -DCURL_DEFAULT_SSL_BACKEND=openssl + -DCURL_USE_GNUTLS=ON -DCURL_USE_MBEDTLS=ON -DCURL_USE_RUSTLS=ON -DENABLE_ARES=ON -DCURL_USE_GSASL=ON + -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON -DUSE_APPLE_IDN=ON -DUSE_SSLS_EXPORT=ON + -DCURL_USE_GSSAPI=ON -DGSS_ROOT_DIR=/opt/homebrew/opt/krb5 + -DCURL_BROTLI=ON -DCURL_ZSTD=ON + -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/opt/homebrew/opt/llvm/bin/clang-tidy + -DCURL_COMPLETION_FISH=ON -DCURL_COMPLETION_ZSH=ON + -DCURL_ENABLE_NTLM=ON + + - name: 'HTTP/3 clang-tidy' + image: macos-26 + compiler: clang + install: llvm libnghttp3 libngtcp2 openldap krb5 + install_steps: skipall + generate: >- + -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DUSE_NGTCP2=ON + -DLDAP_INCLUDE_DIR=/opt/homebrew/opt/openldap/include + -DLDAP_LIBRARY=/opt/homebrew/opt/openldap/lib/libldap.dylib + -DLDAP_LBER_LIBRARY=/opt/homebrew/opt/openldap/lib/liblber.dylib + -DCURL_USE_GSSAPI=ON -DGSS_ROOT_DIR=/opt/homebrew/opt/krb5 + -DCURL_BROTLI=ON -DCURL_ZSTD=ON + -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/opt/homebrew/opt/llvm/bin/clang-tidy + -DCURL_ENABLE_NTLM=ON + + - name: 'LibreSSL openldap krb5 c-ares +examples' + compiler: clang + install: libressl krb5 openldap + generate: >- + -DENABLE_DEBUG=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/libressl -DENABLE_ARES=ON -DCURL_USE_GSSAPI=ON + -DGSS_ROOT_DIR=/opt/homebrew/opt/krb5 + -DLDAP_INCLUDE_DIR=/opt/homebrew/opt/openldap/include + -DLDAP_LIBRARY=/opt/homebrew/opt/openldap/lib/libldap.dylib + -DLDAP_LBER_LIBRARY=/opt/homebrew/opt/openldap/lib/liblber.dylib + - name: 'wolfSSL !ldap brotli zstd' + compiler: clang install: brotli wolfssl zstd install_steps: pytest - generate: -DCURL_USE_WOLFSSL=ON -DCURL_DISABLE_LDAP=ON -DUSE_ECH=ON + generate: -DCURL_USE_WOLFSSL=ON -DCURL_DISABLE_LDAP=ON -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON + - name: 'mbedTLS !ldap brotli zstd MultiSSL AppleIDN' compiler: llvm@18 install: brotli mbedtls zstd install_steps: codeset-test - generate: -DCURL_USE_MBEDTLS=ON -DCURL_DISABLE_LDAP=ON -DCURL_DEFAULT_SSL_BACKEND=mbedtls -DCURL_USE_OPENSSL=ON -DUSE_APPLE_IDN=ON - - name: 'GnuTLS !ldap krb5' + generate: -DCURL_USE_MBEDTLS=ON -DCURL_DISABLE_LDAP=ON -DCURL_DEFAULT_SSL_BACKEND=mbedtls -DCURL_USE_OPENSSL=ON -DUSE_APPLE_IDN=ON -DCURL_ENABLE_NTLM=ON + + - name: 'GnuTLS !ldap krb5 +examples' + compiler: clang install: gnutls nettle krb5 - generate: -DENABLE_DEBUG=ON -DCURL_USE_GNUTLS=ON -DCURL_USE_OPENSSL=OFF -DCURL_USE_GSSAPI=ON -DGSS_ROOT_DIR=/opt/homebrew/opt/krb5 -DCURL_DISABLE_LDAP=ON -DUSE_SSLS_EXPORT=ON - - name: 'aws-lc' - compiler: gcc-12 + generate: >- + -DENABLE_DEBUG=ON -DCURL_USE_GNUTLS=ON -DCURL_USE_OPENSSL=OFF + -DCURL_USE_GSSAPI=ON -DGSS_ROOT_DIR=/opt/homebrew/opt/krb5 + -DCURL_DISABLE_LDAP=ON -DUSE_SSLS_EXPORT=ON -DCURL_ENABLE_NTLM=ON + + - name: 'aws-lc +analyzer' + compiler: gcc-15 install: aws-lc - generate: -DENABLE_DEBUG=ON -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/aws-lc -DUSE_ECH=ON -DCURL_DISABLE_LDAP=ON -DUSE_SSLS_EXPORT=ON + generate: >- + -DENABLE_DEBUG=ON -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/aws-lc -DUSE_ECH=ON + -DCURL_DISABLE_LDAP=ON -DUSE_SSLS_EXPORT=ON -DCURL_GCC_ANALYZER=ON + - name: 'Rustls' compiler: clang install: rustls-ffi - generate: -DENABLE_DEBUG=ON -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DCURL_DISABLE_LDAP=ON - - name: 'OpenSSL torture !FTP' + generate: -DENABLE_DEBUG=ON -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DCURL_DISABLE_LDAP=ON -DCURL_ENABLE_NTLM=ON + tflags: '--min=1650' + + - name: 'OpenSSL torture 1' compiler: clang install: libnghttp3 install_steps: torture - generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DUSE_OPENSSL_QUIC=ON - tflags: -t --shallow=25 !FTP - - name: 'OpenSSL torture FTP' + generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DCURL_ENABLE_NTLM=ON + tflags: '-t --shallow=25 --min=480 1 to 500' + + - name: 'OpenSSL torture 2' compiler: clang install: libnghttp3 install_steps: torture - generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DUSE_OPENSSL_QUIC=ON - tflags: -t --shallow=20 FTP - exclude: - # opt out jobs from combinations that have the compiler set manually - - { compiler: llvm@18, build: { compiler: 'clang' } } - - { compiler: llvm@18, build: { compiler: 'gcc-12' } } - - { compiler: gcc-12, build: { compiler: 'clang' } } - - { compiler: gcc-12, build: { compiler: 'llvm@18' } } - - { compiler: clang, build: { compiler: 'gcc-12' } } - - { compiler: clang, build: { compiler: 'llvm@18' } } + generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DCURL_ENABLE_NTLM=ON + tflags: '-t --shallow=25 --min=730 501 to 1250' + + - name: 'OpenSSL torture 3' + compiler: clang + install: libnghttp3 + install_steps: torture + generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DCURL_ENABLE_NTLM=ON + tflags: '-t --shallow=25 --min=628 1251 to 9999' steps: - name: 'brew install' + timeout-minutes: 5 # Run this command with retries because of spurious failures seen # while running the tests, for example # https://github.com/curl/curl/runs/4095721123?check_suite_focus=true env: INSTALL_PACKAGES: >- ${{ matrix.build.generate && 'ninja' || 'automake libtool' }} - ${{ !contains(matrix.build.install_steps, 'clang-tidy') && 'nghttp2 stunnel' || '' }} + ${{ !contains(matrix.build.install_steps, 'skipall') && !contains(matrix.build.install_steps, 'skiprun') && 'nghttp2 stunnel' || '' }} ${{ contains(matrix.build.install_steps, 'pytest') && 'caddy httpd vsftpd' || '' }} run: | - echo pkgconf libpsl libssh2 ${INSTALL_PACKAGES} ${MATRIX_INSTALL} | xargs -Ix -n1 echo brew '"x"' > /tmp/Brewfile - # shellcheck disable=SC2181,SC2034 - while [[ $? == 0 ]]; do for i in 1 2 3; do if brew update && brew bundle install --file /tmp/Brewfile; then break 2; else echo Error: wait to try again; sleep 10; fi; done; false Too many retries; done + # shellcheck disable=SC2181 + while [[ $? == 0 ]]; do + for i in 1 2 3; do + if brew update && brew install pkgconf libpsl libssh2 ${INSTALL_PACKAGES} ${MATRIX_INSTALL}; then + break 2 + else + echo "Error: wait to try again: $i" + sleep 10 + fi + done + false Too many retries + done - name: 'brew unlink openssl' - if: ${{ contains(matrix.build.install, 'aws-lc') || contains(matrix.build.install, 'libressl') || contains(matrix.build.install, 'quictls') }} + if: ${{ contains(matrix.build.install, 'aws-lc') || contains(matrix.build.install, 'libressl') }} run: | - if [ -d /opt/homebrew/include/openssl ]; then + if [ -d "$(brew --prefix)"/include/openssl ]; then brew unlink openssl fi @@ -367,9 +432,9 @@ jobs: xcrun --sdk macosx --show-sdk-version || true ls -l /Library/Developer/CommandLineTools/SDKs || true echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::' - echo '::group::brew packages installed'; ls -l /opt/homebrew/opt; echo '::endgroup::' + echo '::group::brew packages installed'; ls -l "$(brew --prefix)"/opt; echo '::endgroup::' - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -403,7 +468,7 @@ jobs: [ "${_chkprefill}" = '_chkprefill' ] && options+=' -D_CURL_PREFILL=OFF' cmake -B "bld${_chkprefill}" -G Ninja -D_CURL_PREFILL=ON \ -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ - -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON \ + -DCMAKE_UNITY_BUILD=ON -DCURL_DROP_UNUSED=ON -DCURL_WERROR=ON \ -DCMAKE_OSX_SYSROOT="${sysroot}" \ -DCMAKE_C_COMPILER_TARGET="$(uname -m | sed 's/arm64/aarch64e/')-apple-darwin$(uname -r)" \ ${MATRIX_GENERATE} ${options} @@ -423,9 +488,9 @@ jobs: fi [ -n "${MATRIX_MACOS_VERSION_MIN}" ] && CFLAGS+=" -mmacosx-version-min=${MATRIX_MACOS_VERSION_MIN}" [[ "${MATRIX_INSTALL_STEPS}" = *'pytest'* ]] && options+=' --with-test-vsftpd=no' # Skip ~20 tests that stretch run time by 7x on macOS - mkdir bld && cd bld && ../configure --prefix="$PWD"/curl-install --enable-unity --enable-warnings --enable-werror \ - --disable-dependency-tracking \ - --with-libpsl=/opt/homebrew/opt/libpsl \ + mkdir bld && cd bld && ../configure --prefix="$PWD"/curl-install --enable-unity --enable-warnings --enable-werror --disable-static \ + --disable-dependency-tracking --enable-option-checking=fatal \ + --with-libpsl="$(brew --prefix libpsl)" \ ${MATRIX_CONFIGURE} ${options} fi @@ -449,8 +514,11 @@ jobs: make -C bld V=1 fi - - name: 'curl version' - run: bld/src/curl --disable --version + - name: 'curl -V' + run: | + find . -type f \( -name curl -o -name '*.dylib' -o -name '*.a' \) -print0 | xargs -0 file -- + find . -type f \( -name curl -o -name '*.dylib' -o -name '*.a' \) -print0 | xargs -0 stat -f '%10z bytes: %N' -- + bld/src/curl --disable --version - name: 'curl install' run: | @@ -461,6 +529,7 @@ jobs: fi - name: 'build tests' + if: ${{ !contains(matrix.build.install_steps, 'skipall') }} run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake --build bld --verbose --target testdeps @@ -469,20 +538,24 @@ jobs: fi - name: 'install test prereqs' - if: ${{ !contains(matrix.build.install_steps, 'clang-tidy') }} + if: ${{ !contains(matrix.build.install_steps, 'skipall') && !contains(matrix.build.install_steps, 'skiprun') }} run: | python3 -m venv ~/venv - source ~/venv/bin/activate - python3 -m pip install -r tests/requirements.txt + if bld/src/curl --disable -V 2>/dev/null | grep smb; then + ~/venv/bin/pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary -r tests/requirements.txt + fi - name: 'run tests' - if: ${{ !contains(matrix.build.install_steps, 'clang-tidy') }} + if: ${{ !contains(matrix.build.install_steps, 'skipall') && !contains(matrix.build.install_steps, 'skiprun') }} timeout-minutes: ${{ contains(matrix.build.install_steps, 'torture') && 20 || 10 }} env: TEST_TARGET: ${{ contains(matrix.build.install_steps, 'torture') && 'test-torture' || 'test-ci' }} TFLAGS: '${{ matrix.build.tflags }}' run: | TFLAGS="-j20 ${TFLAGS}" + if [ "${TEST_TARGET}" != 'test-ci' ]; then + TFLAGS+=' --buildinfo' # only test-ci sets this by default, set it manually for test-torture + fi source ~/venv/bin/activate if [[ "${MATRIX_INSTALL_STEPS}" = *'codeset-test'* ]]; then locale || true @@ -498,13 +571,13 @@ jobs: fi - name: 'install pytest prereqs' - if: ${{ !contains(matrix.build.install_steps, 'clang-tidy') && contains(matrix.build.install_steps, 'pytest') }} + if: ${{ contains(matrix.build.install_steps, 'pytest') }} run: | - source ~/venv/bin/activate - python3 -m pip install -r tests/http/requirements.txt + [ -d ~/venv ] || python3 -m venv ~/venv + ~/venv/bin/pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary -r tests/http/requirements.txt - name: 'run pytest' - if: ${{ !contains(matrix.build.install_steps, 'clang-tidy') && contains(matrix.build.install_steps, 'pytest') }} + if: ${{ contains(matrix.build.install_steps, 'pytest') }} env: PYTEST_ADDOPTS: '--color=yes' PYTEST_XDIST_AUTO_NUM_WORKERS: 4 @@ -526,7 +599,7 @@ jobs: fi combinations: # Test buildability with host OS, Xcode / SDK, compiler, target-OS, built tool, combinations - name: "${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.compiler }} ${{ matrix.image }} ${{ matrix.xcode }} ${{ matrix.config }}" + name: "${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.compiler }} ${{ matrix.image }} ${{ matrix.xcode }}" runs-on: ${{ matrix.image }} timeout-minutes: 10 env: @@ -539,58 +612,64 @@ jobs: strategy: fail-fast: false matrix: - compiler: [gcc-12, gcc-13, gcc-14, llvm@15, llvm@18, clang] - # Xcode support matrix as of 2024-07, with default macOS SDK versions and OS names, years: + # Sources: + # https://github.com/actions/runner-images/blob/main/images/macos/macos-14-arm64-Readme.md + # https://github.com/actions/runner-images/blob/main/images/macos/macos-15-arm64-Readme.md + # https://github.com/actions/runner-images/blob/main/images/macos/macos-26-arm64-Readme.md + compiler: [gcc-13, gcc-14, gcc-15, llvm@15, llvm@18, llvm@20, clang] + # Xcode support matrix as of 2025-10, with default macOS SDK versions and OS names, years: # * = default Xcode on the runner. - # macos-13: 14.1, 14.2, 14.3.1, 15.0.1, 15.1,*15.2 - # macos-14: 15.0.1, 15.1, 15.2, 15.3,*15.4 - # macos-15: *16.0, 16.1 - # macOSSDK: 13.0, 13.1, 13.3, 14.0, 14.2, 14.2, 14.4, 14.5, 15.0, 15.1 - # Ventura (2022) Sonoma (2023) Sequoia (2024) + # macos-14: 15.0.1, 15.1, 15.2, 15.3,*15.4 + # macos-15: 16.0, 16.1, 16.2, 16.3,*16.4, 26.0 + # macos-26: 16.4 *26.0 + # macOSSDK: 14.0, 14.2, 14.2, 14.4, 14.5, 15.0, 15.1, 15.2, 15.4, 15.5, 26.0 + # Sonoma (2023) Sequoia (2024) Tahoe (2025) # https://github.com/actions/runner-images/tree/main/images/macos # https://en.wikipedia.org/wiki/MacOS_version_history - # TODO when dropping macos-13: replace '$(brew --prefix ...' with /opt/homebrew - image: [macos-13, macos-14, macos-15] - # Can skip these to reduce jobs: - # 15.1 has the same default macOS SDK as 15.2 and identical test results. - # 14.1, 15.4 not revealing new fallouts. - #xcode: ['14.1', '14.2', '14.3.1', '15.0.1', '15.1', '15.2', '15.3', '15.4', '16.0', '16.1'] # all Xcode - #xcode: ['14.1', '14.2', '14.3.1', '15.0.1' , '15.2', '15.3', '15.4', '16.0', '16.1'] # all SDK - #xcode: [ '14.2', '14.3.1', '15.0.1' , '15.2', '15.3' , '16.0' ] # coverage + image: [macos-14, macos-15, macos-26] xcode: [''] # default Xcodes macos-version-min: [''] build: [autotools, cmake] exclude: # Combinations not covered by runner images: - - { image: macos-13, xcode: '15.3' } - - { image: macos-13, xcode: '15.4' } - - { image: macos-13, xcode: '16.0' } - - { image: macos-13, xcode: '16.1' } - - { image: macos-14, xcode: '14.1' } - - { image: macos-14, xcode: '14.2' } - - { image: macos-14, xcode: '14.3.1' } - - { image: macos-14, xcode: '16.0' } - - { image: macos-14, xcode: '16.1' } - - { image: macos-15, xcode: '14.1' } - - { image: macos-15, xcode: '14.2' } - - { image: macos-15, xcode: '14.3.1' } - - { image: macos-15, xcode: '15.0.1' } - - { image: macos-15, xcode: '15.1' } - - { image: macos-15, xcode: '15.2' } - - { image: macos-15, xcode: '15.3' } - - { image: macos-15, xcode: '15.4' } - - { image: macos-13, compiler: 'llvm@18' } - { image: macos-14, compiler: 'llvm@18' } + - { image: macos-14, compiler: 'llvm@20' } - { image: macos-15, compiler: 'llvm@15' } + - { image: macos-15, compiler: 'llvm@20' } + - { image: macos-26, compiler: 'llvm@15' } + - { image: macos-26, compiler: 'llvm@18' } + # Covered by the main workflow + - { image: macos-15, compiler: 'gcc-13' } + - { image: macos-15, compiler: 'llvm@18' } + - { image: macos-15, compiler: 'clang' } # Reduce build combinations, by dropping less interesting ones - - { compiler: gcc-13, build: cmake } - - { compiler: gcc-14, build: autotools } + - { image: macos-26, compiler: 'gcc-13' } + - { compiler: 'gcc-14' , build: cmake } + # Reduce autotools to just one job that is also build with cmake + - { compiler: 'gcc-13' , build: autotools } + - { compiler: 'gcc-14' , build: autotools } + - { compiler: 'gcc-15' , build: autotools } + - { compiler: 'llvm@15', build: autotools } + - { compiler: 'llvm@18', build: autotools } + - { compiler: 'llvm@20', build: autotools } + - { image: macos-14, build: autotools } + - { image: macos-15, build: autotools } steps: - name: 'install autotools' if: ${{ matrix.build == 'autotools' }} run: | - # shellcheck disable=SC2181,SC2034 - while [[ $? == 0 ]]; do for i in 1 2 3; do if brew update && brew install automake libtool; then break 2; else echo Error: wait to try again; sleep 10; fi; done; false Too many retries; done + # shellcheck disable=SC2181 + while [[ $? == 0 ]]; do + for i in 1 2 3; do + if brew update && brew install automake libtool; then + break 2 + else + echo "Error: wait to try again: $i" + sleep 10 + fi + done + false Too many retries + done - name: 'toolchain versions' run: | @@ -601,10 +680,11 @@ jobs: xcrun --sdk macosx --show-sdk-path 2>/dev/null || true xcrun --sdk macosx --show-sdk-version || true ls -l /Library/Developer/CommandLineTools/SDKs || true + echo '::group::compiler defaults'; echo 'int main(void) {}' | "${CC}" -v -x c -; echo '::endgroup::' echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::' - echo '::group::brew packages preinstalled'; ls -l "$(brew --prefix)/opt"; echo '::endgroup::' + echo '::group::brew packages preinstalled'; ls -l "$(brew --prefix)"/opt; echo '::endgroup::' - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -614,9 +694,9 @@ jobs: - name: 'configure / ${{ matrix.build }}' run: | - if [ "${MATRIX_COMPILER}" = 'gcc-13' ] && [ "${MATRIX_IMAGE}" = 'macos-15' ] ; then + if [ "${MATRIX_COMPILER}" = 'gcc-13' ] && [ "${MATRIX_IMAGE}" = 'macos-15' ]; then # Ref: https://github.com/Homebrew/homebrew-core/issues/194778#issuecomment-2793243409 - /opt/homebrew/opt/gcc@13/libexec/gcc/aarch64-apple-darwin24/13/install-tools/mkheaders + "$(brew --prefix gcc@13)"/libexec/gcc/aarch64-apple-darwin24/13/install-tools/mkheaders fi if [[ "${MATRIX_COMPILER}" = 'gcc'* ]]; then @@ -635,7 +715,7 @@ jobs: [ -n "${MATRIX_MACOS_VERSION_MIN}" ] && options+=" -DCMAKE_OSX_DEPLOYMENT_TARGET=${MATRIX_MACOS_VERSION_MIN}" # would pick up nghttp2, libidn2, and libssh2 cmake -B bld -G Ninja -D_CURL_PREFILL=ON \ - -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON \ + -DCMAKE_UNITY_BUILD=ON -DCURL_DROP_UNUSED=ON -DCURL_WERROR=ON \ -DCMAKE_OSX_SYSROOT="${sysroot}" \ -DCMAKE_C_COMPILER_TARGET="$(uname -m | sed 's/arm64/aarch64e/')-apple-darwin$(uname -r)" \ -DCMAKE_IGNORE_PREFIX_PATH="$(brew --prefix)" \ @@ -643,6 +723,7 @@ jobs: -DCURL_USE_OPENSSL=ON \ -DUSE_NGHTTP2=OFF -DUSE_LIBIDN2=OFF \ -DCURL_USE_LIBPSL=OFF -DCURL_USE_LIBSSH2=OFF \ + -DUSE_APPLE_IDN=ON -DUSE_APPLE_SECTRUST=ON \ ${options} else export CFLAGS @@ -655,12 +736,13 @@ jobs: fi [ -n "${MATRIX_MACOS_VERSION_MIN}" ] && CFLAGS+=" -mmacosx-version-min=${MATRIX_MACOS_VERSION_MIN}" # would pick up nghttp2, libidn2, but libssh2 is disabled by default - mkdir bld && cd bld && ../configure --enable-unity --enable-warnings --enable-werror \ - --disable-dependency-tracking \ + mkdir bld && cd bld && ../configure --enable-unity --enable-warnings --enable-werror --disable-static \ + --disable-dependency-tracking --enable-option-checking=fatal \ --disable-docs --disable-manual \ --with-openssl="$(brew --prefix openssl)" \ --without-nghttp2 --without-libidn2 \ --without-libpsl \ + --with-apple-idn --with-apple-sectrust \ ${options} fi @@ -681,5 +763,8 @@ jobs: make -C bld V=1 fi - - name: 'curl version' - run: bld/src/curl --disable --version + - name: 'curl -V' + run: | + find . -type f \( -name curl -o -name '*.dylib' -o -name '*.a' \) -print0 | xargs -0 file -- + find . -type f \( -name curl -o -name '*.dylib' -o -name '*.a' \) -print0 | xargs -0 stat -f '%10z bytes: %N' -- + bld/src/curl --disable --version diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index 3c76ed9de8..a482431ecc 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -14,10 +14,7 @@ name: 'non-native' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' pull_request: branches: - master @@ -26,10 +23,7 @@ name: 'non-native' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} @@ -39,6 +33,7 @@ permissions: {} env: CURL_CI: github + CURL_TEST_MIN: 1750 jobs: netbsd: @@ -49,21 +44,21 @@ jobs: matrix: arch: ['x86_64'] steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: 'cmake' - uses: cross-platform-actions/action@e8a7b572196ff79ded1979dc2bb9ee67d1ddb252 # v0.29.0 + uses: cross-platform-actions/action@492b0c80085400348c599edace11141a4ee73524 # v0.32.0 env: MATRIX_ARCH: '${{ matrix.arch }}' with: - environment_variables: MATRIX_ARCH + environment_variables: CURL_CI CURL_TEST_MIN MATRIX_ARCH operating_system: 'netbsd' version: '10.1' architecture: ${{ matrix.arch }} run: | # https://pkgsrc.se/ - time sudo pkgin -y install cmake ninja-build pkg-config perl brotli heimdal openldap-client libssh2 libidn2 libpsl nghttp2 py311-impacket + time sudo pkgin -y install cmake ninja-build pkg-config perl brotli mit-krb5 openldap-client libssh2 libidn2 libpsl nghttp2 py311-impacket time cmake -B bld -G Ninja \ -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ -DCMAKE_UNITY_BUILD=ON \ @@ -71,6 +66,7 @@ jobs: -DENABLE_DEBUG=ON -DCMAKE_BUILD_TYPE=Debug \ -DCURL_USE_OPENSSL=ON \ -DCURL_USE_GSSAPI=ON \ + -DCURL_ENABLE_NTLM=ON \ || { cat bld/CMakeFiles/CMake*.yaml; false; } echo '::group::curl_config.h (raw)'; cat bld/lib/curl_config.h || true; echo '::endgroup::' echo '::group::curl_config.h'; grep -F '#define' bld/lib/curl_config.h | sort || true; echo '::endgroup::' @@ -94,15 +90,15 @@ jobs: matrix: arch: ['x86_64'] steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: 'cmake' - uses: cross-platform-actions/action@e8a7b572196ff79ded1979dc2bb9ee67d1ddb252 # v0.29.0 + uses: cross-platform-actions/action@492b0c80085400348c599edace11141a4ee73524 # v0.32.0 env: MATRIX_ARCH: '${{ matrix.arch }}' with: - environment_variables: MATRIX_ARCH + environment_variables: CURL_CI CURL_TEST_MIN MATRIX_ARCH operating_system: 'openbsd' version: '7.7' architecture: ${{ matrix.arch }} @@ -116,6 +112,7 @@ jobs: -DCURL_WERROR=ON \ -DENABLE_DEBUG=ON -DCMAKE_BUILD_TYPE=Debug \ -DCURL_USE_OPENSSL=ON \ + -DCURL_ENABLE_NTLM=ON \ || { cat bld/CMakeFiles/CMake*.yaml; false; } echo '::group::curl_config.h (raw)'; cat bld/lib/curl_config.h || true; echo '::endgroup::' echo '::group::curl_config.h'; grep -F '#define' bld/lib/curl_config.h | sort || true; echo '::endgroup::' @@ -134,29 +131,30 @@ jobs: freebsd: name: "FreeBSD, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.compiler }} openssl${{ matrix.desc }} ${{ matrix.arch }}" runs-on: ubuntu-latest - timeout-minutes: 20 + timeout-minutes: 15 strategy: matrix: include: - { build: 'autotools', arch: 'x86_64', compiler: 'clang' } - { build: 'cmake' , arch: 'x86_64', compiler: 'clang', options: '-DCMAKE_UNITY_BUILD=OFF', desc: ' !unity !runtests !examples' } - - { build: 'autotools', arch: 'arm64', compiler: 'clang' } - - { build: 'cmake' , arch: 'arm64', compiler: 'clang' } + - { build: 'autotools', arch: 'arm64' , compiler: 'clang', desc: ' !examples' } + - { build: 'cmake' , arch: 'arm64' , compiler: 'clang' } fail-fast: false steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: '${{ matrix.build }}' - uses: cross-platform-actions/action@e8a7b572196ff79ded1979dc2bb9ee67d1ddb252 # v0.29.0 + uses: cross-platform-actions/action@492b0c80085400348c599edace11141a4ee73524 # v0.32.0 env: CC: '${{ matrix.compiler }}' + CURL_TEST_MIN: 1800 MATRIX_ARCH: '${{ matrix.arch }}' MATRIX_BUILD: '${{ matrix.build }}' MATRIX_DESC: '${{ matrix.desc }}' MATRIX_OPTIONS: '${{ matrix.options }}' with: - environment_variables: CC MATRIX_ARCH MATRIX_BUILD MATRIX_DESC MATRIX_OPTIONS + environment_variables: CC CURL_CI CURL_TEST_MIN MATRIX_ARCH MATRIX_BUILD MATRIX_DESC MATRIX_OPTIONS operating_system: 'freebsd' version: '14.3' architecture: ${{ matrix.arch }} @@ -166,10 +164,10 @@ jobs: # https://ports.freebsd.org/ if [ "${MATRIX_BUILD}" = 'cmake' ]; then time sudo pkg install -y cmake-core ninja perl5 \ - pkgconf brotli openldap26-client libidn2 libnghttp2 stunnel py311-impacket + pkgconf brotli krb5-devel openldap26-client libidn2 libnghttp2 stunnel py311-impacket else time sudo pkg install -y autoconf automake libtool \ - pkgconf brotli openldap26-client libidn2 libnghttp2 stunnel py311-impacket + pkgconf brotli krb5-devel openldap26-client libidn2 libnghttp2 stunnel py311-impacket export MAKEFLAGS=-j3 fi @@ -190,11 +188,10 @@ jobs: options='--disable-manual --disable-docs' # Slow with autotools, skip on emulated CPU fi mkdir bld && cd bld - time ../configure --enable-unity --enable-debug --enable-warnings --enable-werror \ - --prefix="$HOME"/curl-install \ + time ../configure --prefix="$HOME"/curl-install --enable-unity --enable-debug --enable-warnings --enable-werror --disable-static \ + --disable-dependency-tracking --enable-option-checking=fatal \ --with-openssl \ --with-brotli --enable-ldap --enable-ldaps --with-libidn2 --with-libssh2 --with-nghttp2 --with-gssapi \ - --disable-dependency-tracking \ ${options} \ ${MATRIX_OPTIONS} \ || { tail -n 1000 config.log; false; } @@ -241,9 +238,10 @@ jobs: android: name: "Android ${{ matrix.platform }}, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.name }} arm64" - runs-on: 'ubuntu-latest' - timeout-minutes: 25 + runs-on: ubuntu-latest + timeout-minutes: 5 env: + LDFLAGS: -s MAKEFLAGS: -j 5 MATRIX_BUILD: '${{ matrix.build }}' strategy: @@ -263,7 +261,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -286,7 +284,8 @@ jobs: ${MATRIX_OPTIONS} else TOOLCHAIN="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64" - mkdir bld && cd bld && ../configure --disable-dependency-tracking --enable-unity --enable-warnings --enable-werror \ + mkdir bld && cd bld && ../configure --enable-unity --enable-warnings --enable-werror --disable-shared \ + --disable-dependency-tracking --enable-option-checking=fatal \ CC="$TOOLCHAIN/bin/aarch64-linux-android${MATRIX_PLATFORM}-clang" \ AR="$TOOLCHAIN/bin/llvm-ar" \ RANLIB="$TOOLCHAIN/bin/llvm-ranlib" \ @@ -312,7 +311,9 @@ jobs: fi - name: 'curl info' - run: find . -type f \( -name curl -o -name '*.so' -o -name '*.a' \) -exec file '{}' \; + run: | + find . -type f \( -name curl -o -name '*.so' -o -name '*.a' \) -print0 | xargs -0 file -- + find . -type f \( -name curl -o -name '*.so' -o -name '*.a' \) -print0 | xargs -0 stat -c '%10s bytes: %n' -- - name: 'build tests' run: | @@ -329,3 +330,119 @@ jobs: else make -C bld examples fi + + msdos: + name: "MS-DOS, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} djgpp !ssl i586" + runs-on: ubuntu-latest + timeout-minutes: 5 + env: + LDFLAGS: -s + MAKEFLAGS: -j 5 + MATRIX_BUILD: '${{ matrix.build }}' + # renovate: datasource=github-releases depName=andrewwutw/build-djgpp versioning=semver-coerced registryUrl=https://github.com + TOOLCHAIN_VERSION: 3.4 + TOOLCHAIN_SHA256: 8464f17017d6ab1b2bb2df4ed82357b5bf692e6e2b7fee37e315638f3d505f00 + strategy: + matrix: + build: [autotools, cmake] + fail-fast: false + steps: + - name: 'install packages' + run: | + printf "#!/bin/sh + while [ \$? = 0 ]; do for i in 1 2 3; do timeout 30 \"\$@\" && break 2; echo \"Error: slow server, retry \$i\"; sleep 1 + dpkg --configure -a; done; false; done" > "$HOME"/my-apt; chmod +x "$HOME"/my-apt + sudo "$HOME"/my-apt apt-get -o Dpkg::Use-Pty=0 install libfl2 + + - name: 'cache compiler (djgpp)' + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-compiler + with: + path: ~/djgpp + key: ${{ runner.os }}-djgpp-${{ env.TOOLCHAIN_VERSION }}-amd64 + + - name: 'install compiler (djgpp)' + if: ${{ steps.cache-compiler.outputs.cache-hit != 'true' }} + run: | + cd ~ + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 3 --retry-connrefused \ + --location "https://github.com/andrewwutw/build-djgpp/releases/download/v${TOOLCHAIN_VERSION}/djgpp-linux64-gcc1220.tar.bz2" --output pkg.bin + sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${TOOLCHAIN_SHA256}" && tar -xjf pkg.bin && rm -f pkg.bin + cd djgpp + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ + https://www.delorie.com/pub/djgpp/current/v2tk/wat3211b.zip --output pkg.bin + sha256sum pkg.bin | tee /dev/stderr | grep -qwF faa2222ab5deb2c2aac229c760bf4d45aca5379f5af97865c308a0467046b67a && unzip -q pkg.bin && rm -f pkg.bin + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ + https://www.delorie.com/pub/djgpp/current/v2tk/zlb13b.zip --output pkg.bin + sha256sum pkg.bin | tee /dev/stderr | grep -qwF f3d2fa8129e7591c7e79074306d8ab91a70ec172cc01baedeae74992285dd3a3 && unzip -q pkg.bin && rm -f pkg.bin + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: 'configure' + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake -B bld -G Ninja \ + -DCMAKE_SYSTEM_NAME=DOS \ + -DCMAKE_SYSTEM_PROCESSOR=x86 \ + -DCMAKE_C_COMPILER_TARGET=i586-pc-msdosdjgpp \ + -DCMAKE_C_COMPILER="$HOME"/djgpp/bin/i586-pc-msdosdjgpp-gcc \ + -DCMAKE_UNITY_BUILD=ON \ + -DCURL_WERROR=ON \ + -DCURL_ENABLE_SSL=OFF -DCURL_USE_LIBPSL=OFF \ + -DZLIB_INCLUDE_DIR="$HOME"/djgpp/include \ + -DZLIB_LIBRARY="$HOME"/djgpp/lib/libz.a \ + -DWATT_ROOT="$HOME"/djgpp/net/watt + else + autoreconf -fi + mkdir bld && cd bld && ../configure --enable-unity --enable-warnings --enable-werror --disable-shared \ + --disable-dependency-tracking --enable-option-checking=fatal \ + CC="$HOME"/djgpp/bin/i586-pc-msdosdjgpp-gcc \ + AR="$HOME"/djgpp/bin/i586-pc-msdosdjgpp-ar \ + RANLIB="$HOME"/djgpp/bin/i586-pc-msdosdjgpp-ranlib \ + WATT_ROOT="$HOME"/djgpp/net/watt \ + --host=i586-pc-msdosdjgpp \ + --without-ssl --without-libpsl \ + --with-zlib="$HOME"/djgpp + fi + + - name: 'configure log' + if: ${{ !cancelled() }} + run: cat bld/config.log bld/CMakeFiles/CMake*.yaml 2>/dev/null || true + + - name: 'curl_config.h' + run: | + echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld/lib/curl_config.h | sort || true + + - name: 'build' + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --build bld + else + make -C bld + fi + + - name: 'curl info' + run: | + find . \( -name '*.exe' -o -name '*.a' \) -print0 | xargs -0 file -- + find . \( -name '*.exe' -o -name '*.a' \) -print0 | xargs -0 stat -c '%10s bytes: %n' -- + + - name: 'build tests' + if: ${{ matrix.build == 'cmake' }} # skip for autotools to save time + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --build bld --target testdeps + else + make -C bld -C tests + fi + + - name: 'build examples' + if: ${{ matrix.build == 'cmake' }} # skip for autotools to save time + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --build bld --target curl-examples-build + else + make -C bld examples + fi diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 27b29ce429..3e326acf01 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -14,10 +14,7 @@ name: 'Windows' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' pull_request: branches: - master @@ -26,10 +23,7 @@ name: 'Windows' - '.circleci/**' - 'appveyor.*' - 'Dockerfile' - - 'packages/**' - - 'plan9/**' - 'projects/**' - - 'winbuild/**' concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} @@ -39,94 +33,79 @@ permissions: {} env: CURL_CI: github + CURL_TEST_MIN: 1700 + CURL_TEST_SSH_KEYALGO: ed25519 + OPENSSH_WINDOWS_VERSION: 10.0.0.0p2-Preview + OPENSSH_WINDOWS_SHA256_ARM64: 698c6aec31c1dd0fb996206e8741f4531a97355686b5431ef347d531b07fcd42 + OPENSSH_WINDOWS_SHA256_WIN64: 23f50f3458c4c5d0b12217c6a5ddfde0137210a30fa870e98b29827f7b43aba5 + STUNNEL_VERSION: 5.77 + STUNNEL_SHA256: 59ba0c5330de85ef474164b40cc559b2eb4e2b8d788c48b564e2257efa236384 jobs: build-cache: name: 'Build caches' runs-on: ${{ matrix.image }} - timeout-minutes: 15 - defaults: - run: - shell: msys2 {0} strategy: - fail-fast: false matrix: - image: [windows-2022, windows-11-arm] + image: [windows-11-arm, windows-2022] # Cannot share cache between arm and intel: https://github.com/actions/cache/issues/1622 steps: - - name: 'install build prereqs' - if: ${{ steps.cache-perl-win32-pkgs.outputs.cache-hit != 'true' }} - uses: msys2/setup-msys2@fb197b72ce45fb24f17bf3f807a388985654d1f2 # v2 + - name: 'cache test prereqs (stunnel)' + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-stunnel with: - msystem: msys - install: gcc make + path: C:\my-stunnel + key: ${{ runner.os }}-stunnel-${{ env.STUNNEL_VERSION }}-amd64 + lookup-only: true - - name: 'perl version' - run: perl --version | tee "$GITHUB_WORKSPACE"/perlversion - - - name: 'cache perl packages' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-perl-win32-pkgs - env: - cache-name: cache-perl-win32-pkgs - with: - path: C:\perl-win32-pkgs - key: ${{ runner.os }}-${{ runner.arch }}-build-${{ env.cache-name }}-${{ hashFiles('perlversion') }} - - - name: 'build perl packages' - if: ${{ steps.cache-perl-win32-pkgs.outputs.cache-hit != 'true' }} + - name: 'install test prereqs (stunnel)' + if: ${{ steps.cache-stunnel.outputs.cache-hit != 'true' }} + timeout-minutes: 2 + shell: bash run: | - cd /c - mkdir perl-win32-pkgs - cd perl-win32-pkgs - sed -i.bak 's/#define I_CRYPT//g' /usr/lib/perl5/core_perl/CORE/config.h - - # https://metacpan.org/pod/Win32::Process - curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://cpan.metacpan.org/authors/id/J/JD/JDB/Win32-Process-0.17.tar.gz" | tar -xz - cd Win32-Process-0.17 - perl Makefile.PL - sed -i.bak 's/-lcrypt//g' Makefile - make - cd .. - - # https://metacpan.org/pod/Win32::Process::List - curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://cpan.metacpan.org/authors/id/R/RP/RPAGITSCH/Win32-Process-List-0.09.tar.gz" | tar -xz - cd Win32-Process-List-0.09 - perl Makefile.PL - sed -i.bak 's/-lcrypt//g' Makefile - make - cd .. + cd /c && mkdir my-stunnel && cd my-stunnel + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 240 --retry 3 --retry-connrefused \ + "https://www.stunnel.org/archive/5.x/stunnel-${STUNNEL_VERSION}-win64-installer.exe" --output pkg.bin + sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${STUNNEL_SHA256}" && 7z x -y pkg.bin >/dev/null && rm -f pkg.bin && ls -l && bin/tstunnel -version cygwin: name: "cygwin, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.platform }} ${{ matrix.name }}" + needs: build-cache runs-on: windows-2022 - timeout-minutes: 15 + timeout-minutes: 10 defaults: run: - shell: D:\cygwin\bin\bash.exe '{0}' + shell: D:\cygwin\bin\bash.exe '{0}' # zizmor: ignore[misfeature] env: + CURL_TEST_MIN: 1800 + LDFLAGS: -s MAKEFLAGS: -j 5 SHELLOPTS: 'igncr' MATRIX_BUILD: '${{ matrix.build }}' strategy: matrix: include: - - { build: 'automake', platform: 'x86_64', tflags: 'skiprun', config: '--with-openssl', install: 'libssl-devel libssh2-devel', name: 'openssl R' } - - { build: 'cmake' , platform: 'x86_64', tflags: '' , config: '-DENABLE_DEBUG=ON -DCURL_USE_OPENSSL=ON -DENABLE_THREADED_RESOLVER=OFF', install: 'libssl-devel libssh2-devel', name: 'openssl' } + - { name: 'openssl R', + build: 'autotools', platform: 'x86_64', tflags: 'skiprun', + config: '--with-openssl', + install: 'libssl-devel libssh2-devel' } + - { name: 'openssl', + build: 'cmake', platform: 'x86_64', tflags: '', + config: '-DENABLE_DEBUG=ON -DCURL_USE_OPENSSL=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_ENABLE_NTLM=ON', + install: 'libssl-devel libssh2-devel' } + fail-fast: false steps: - - run: git config --global core.autocrlf input - shell: pwsh - - uses: cygwin/cygwin-install-action@f2009323764960f80959895c7bc3bb30210afe4d # v6 + - uses: cygwin/cygwin-install-action@711d29f3da23c9f4a1798e369a6f01198c13b11a # v6.1 with: platform: ${{ matrix.platform }} - site: https://mirrors.kernel.org/sourceware/cygwin/ work-vol: 'D:' + # https://cygwin.com/mirrors.html + # Main mirror status: https://archlinux.org/mirrors/kernel.org/ + site: https://mirrors.kernel.org/sourceware/cygwin/ # https://cygwin.com/cgi-bin2/package-grep.cgi packages: >- - autoconf libtool gcc-core gcc-g++ binutils - ${{ matrix.build }} make ninja + ${{ matrix.build == 'autotools' && 'autoconf automake libtool make' || 'cmake ninja' }} + gcc-core binutils perl openssh libpsl-devel zlib-devel @@ -135,12 +114,12 @@ jobs: libnghttp2-devel ${{ matrix.install }} - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: 'autoreconf' - if: ${{ matrix.build == 'automake' }} + if: ${{ matrix.build == 'autotools' }} timeout-minutes: 2 run: | PATH=/usr/bin @@ -159,10 +138,9 @@ jobs: -DCURL_WERROR=ON \ ${MATRIX_CONFIG} else - mkdir bld && cd bld && ../configure --enable-unity --enable-warnings --enable-werror \ - --prefix="$HOME"/curl-install \ + mkdir bld && cd bld && ../configure --prefix="$HOME"/curl-install --enable-unity --enable-warnings --enable-werror --disable-static \ + --disable-dependency-tracking --enable-option-checking=fatal \ --with-libssh2 \ - --disable-dependency-tracking \ ${MATRIX_CONFIG} fi @@ -189,11 +167,12 @@ jobs: make -C bld V=1 install fi - - name: 'curl version' + - name: 'curl -V' timeout-minutes: 1 run: | PATH=/usr/bin - find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -exec file '{}' \; + find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -print0 | grep -z curl | xargs -0 file -- + find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -print0 | grep -z curl | xargs -0 stat -c '%10s bytes: %n' -- if [ "${MATRIX_BUILD}" = 'cmake' ]; then PATH="$PWD/bld/lib:$PATH" fi @@ -210,13 +189,22 @@ jobs: make -C bld V=1 -C tests fi + - name: 'cache test prereqs (stunnel)' + if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-stunnel + with: + path: C:\my-stunnel + key: ${{ runner.os }}-stunnel-${{ env.STUNNEL_VERSION }}-amd64 + fail-on-cache-miss: true + - name: 'run tests' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} timeout-minutes: 15 env: TFLAGS: '${{ matrix.tflags }}' run: | - PATH=/usr/bin + PATH=/usr/bin:/cygdrive/c/my-stunnel/bin TFLAGS="-j8 ${TFLAGS}" if [ -x "$(cygpath "${SYSTEMROOT}/System32/curl.exe")" ]; then TFLAGS+=" -ac $(cygpath "${SYSTEMROOT}/System32/curl.exe")" @@ -244,47 +232,96 @@ jobs: msys2: # both msys and mingw-w64 name: "${{ matrix.sys == 'msys' && 'msys2' || 'mingw' }}, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.env }} ${{ matrix.name }} ${{ matrix.test }}" - needs: - - build-cache + needs: build-cache runs-on: ${{ matrix.image || 'windows-2022' }} - timeout-minutes: 15 + timeout-minutes: ${{ contains(matrix.tflags, '-t') && 14 || 10 }} defaults: run: - shell: msys2 {0} + shell: msys2 {0} # zizmor: ignore[misfeature] env: + LDFLAGS: -s MAKEFLAGS: -j 5 MATRIX_BUILD: '${{ matrix.build }}' + MATRIX_OPENSSH: '${{ matrix.openssh }}' MATRIX_SYS: '${{ matrix.sys }}' MATRIX_TEST: '${{ matrix.test }}' strategy: matrix: include: # MSYS - - { build: 'autotools', sys: 'msys' , env: 'x86_64' , tflags: '' , config: '--enable-debug --with-openssl --disable-threaded-resolver --disable-proxy', install: 'openssl-devel libssh2-devel', name: '!proxy' } - - { build: 'autotools', sys: 'msys' , env: 'x86_64' , tflags: 'skiprun', config: '--enable-debug --with-openssl --disable-threaded-resolver', install: 'openssl-devel libssh2-devel', name: 'default' } - - { build: 'cmake' , sys: 'msys' , env: 'x86_64' , tflags: '' , config: '-DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF', install: 'openssl-devel libssh2-devel', name: 'default' } - - { build: 'autotools', sys: 'msys' , env: 'x86_64' , tflags: '' , config: '--with-openssl', install: 'openssl-devel libssh2-devel', name: 'default R' } + - { name: '!proxy', + build: 'autotools', sys: 'msys' , env: 'x86_64' , tflags: '--min=1550', + config: '--enable-debug --with-openssl --disable-threaded-resolver --disable-proxy --enable-ntlm', + install: 'openssl-devel libssh2-devel' } + - { name: 'default', + build: 'autotools', sys: 'msys' , env: 'x86_64' , tflags: 'skiprun' , + config: '--enable-debug --with-openssl --disable-threaded-resolver --enable-ntlm', + install: 'openssl-devel libssh2-devel' } + - { name: 'default', + build: 'cmake' , sys: 'msys' , env: 'x86_64' , tflags: '' , + config: '-DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_ENABLE_NTLM=ON', + install: 'openssl-devel libssh2-devel' } + - { name: 'default R', + build: 'autotools', sys: 'msys' , env: 'x86_64' , tflags: '' , + config: '--with-openssl --enable-ntlm', install: 'openssl-devel libssh2-devel' } # MinGW - - { build: 'autotools', sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun', config: '--enable-debug --with-openssl --disable-threaded-resolver --disable-curldebug --enable-static=no --without-zlib CPPFLAGS=-D_WIN32_WINNT=0x0501', install: 'mingw-w64-x86_64-openssl mingw-w64-x86_64-libssh2', name: 'default XP' } - - { build: 'autotools', sys: 'mingw64' , env: 'x86_64' , tflags: '' , config: '--enable-debug --with-openssl --enable-windows-unicode --enable-ares --with-openssl-quic --enable-shared=no', install: 'mingw-w64-x86_64-openssl mingw-w64-x86_64-nghttp3 mingw-w64-x86_64-libssh2', name: 'c-ares U' } - - { build: 'cmake' , sys: 'mingw64' , env: 'x86_64' , tflags: '' , config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_ARES=ON', install: 'mingw-w64-x86_64-libssh2', type: 'Debug', name: 'schannel c-ares U' } - # WARNING: libssh uses hard-coded world-writable paths (/etc/..., ~/.ssh/) to - # read its configuration from, making it vulnerable to attacks on + - { name: 'default', + build: 'autotools', sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun' , + config: '--enable-debug --with-openssl --disable-threaded-resolver --enable-static --without-zlib', + install: 'mingw-w64-x86_64-openssl mingw-w64-x86_64-libssh2' } + - { name: 'c-ares U', + build: 'autotools', sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: '' , + config: '--enable-debug --with-openssl --enable-windows-unicode --enable-ares --enable-static --disable-shared --enable-ca-native --enable-ntlm', + install: 'mingw-w64-ucrt-x86_64-c-ares mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-nghttp3 mingw-w64-ucrt-x86_64-libssh2' } + - { name: 'schannel c-ares U', type: 'Debug', + build: 'cmake' , sys: 'mingw64' , env: 'x86_64' , tflags: '--min=1650', + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_ARES=ON -DCURL_DROP_UNUSED=ON', + install: 'mingw-w64-x86_64-c-ares mingw-w64-x86_64-libssh2' } + # MinGW torture + - { name: 'schannel U torture 1', type: 'Debug', + build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: '-t --shallow=13 --min=700 1 to 950' , + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_ARES=ON', + install: 'mingw-w64-ucrt-x86_64-c-ares mingw-w64-ucrt-x86_64-libssh2' } + - { name: 'schannel U torture 2', type: 'Debug', + build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: '-t --shallow=13 --min=700 951 to 9999', + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_ARES=ON', + install: 'mingw-w64-ucrt-x86_64-c-ares mingw-w64-ucrt-x86_64-libssh2' } + # WARNING: libssh uses hard-coded world-writable paths (C:ProgramData/, /etc/..., ~/.ssh/) + # to read its configuration from, making it vulnerable to attacks on # Windows. Do not use this component till there is a fix for these. - # https://github.com/curl/curl-for-win/blob/3951808deb04df9489ee17430f236ed54436f81a/libssh.sh#L6-L8 - - { build: 'cmake' , sys: 'clang64' , env: 'clang-x86_64' , tflags: '' , config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_GNUTLS=ON -DENABLE_UNICODE=OFF -DUSE_NGTCP2=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON', install: 'mingw-w64-clang-x86_64-gnutls mingw-w64-clang-x86_64-nghttp3 mingw-w64-clang-x86_64-ngtcp2 mingw-w64-clang-x86_64-libssh', type: 'Debug', name: 'gnutls libssh' } - - { build: 'cmake' , sys: 'clangarm64', env: 'clang-aarch64', tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_CURLDEBUG=ON', install: 'mingw-w64-clang-aarch64-libssh2', type: 'Release', name: 'schannel R TrackMemory', image: 'windows-11-arm' } - - { build: 'cmake' , sys: 'clang64' , env: 'clang-x86_64' , tflags: 'skiprun', config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_OPENSSL=ON -DENABLE_UNICODE=OFF -DUSE_NGTCP2=ON', install: 'mingw-w64-clang-x86_64-openssl mingw-w64-clang-x86_64-nghttp3 mingw-w64-clang-x86_64-ngtcp2 mingw-w64-clang-x86_64-libssh2', type: 'Release', name: 'openssl', chkprefill: '_chkprefill' } - - { build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_OPENSSL=ON', install: 'mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-libssh2', type: 'Release', test: 'uwp', name: 'schannel' } - # { build: 'autotools', sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun', config: '--without-debug --with-schannel --enable-shared', install: 'mingw-w64-ucrt-x86_64-libssh2', type: 'Release', test: 'uwp', name: 'schannel' } - - { build: 'cmake' , sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun', config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCMAKE_VERBOSE_MAKEFILE=ON', install: 'mingw-w64-x86_64-libssh2', type: 'Debug', cflags: '-DCURL_SCHANNEL_DEV_DEBUG', name: 'schannel dev debug', image: 'windows-2025' } - - { build: 'cmake' , sys: 'mingw32' , env: 'i686' , tflags: 'skiprun', config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON', install: 'mingw-w64-i686-libssh2', type: 'Release', name: 'schannel R' } + # Holds true after CVE-2025-14821 mitigations in 0.12.0. + # https://github.com/curl/curl-for-win/blob/471a065705a16c61a343b15d3e4ef195e2df2f9e/libssh.sh#L6-L94 + - { name: 'gnutls libssh', type: 'Debug', openssh: 'OpenSSH-Windows', + build: 'cmake' , sys: 'clang64' , env: 'clang-x86_64' , tflags: '' , + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_GNUTLS=ON -DENABLE_UNICODE=OFF -DUSE_NGTCP2=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON -DCURL_ENABLE_NTLM=ON', + install: 'mingw-w64-clang-x86_64-gnutls mingw-w64-clang-x86_64-nghttp3 mingw-w64-clang-x86_64-ngtcp2 mingw-w64-clang-x86_64-libssh unzip' } + - { name: 'schannel R', type: 'Release', image: 'windows-11-arm', + build: 'cmake' , sys: 'clangarm64', env: 'clang-aarch64', tflags: 'skiprun' , + config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCURL_DROP_UNUSED=ON', + install: 'mingw-w64-clang-aarch64-libssh2' } + - { name: 'openssl', type: 'Release', chkprefill: '_chkprefill', + build: 'cmake' , sys: 'clang64' , env: 'clang-x86_64' , tflags: 'skiprun' , + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_OPENSSL=ON -DENABLE_UNICODE=OFF -DUSE_NGTCP2=ON', + install: 'mingw-w64-clang-x86_64-openssl mingw-w64-clang-x86_64-nghttp3 mingw-w64-clang-x86_64-ngtcp2 mingw-w64-clang-x86_64-libssh2' } + - { name: 'schannel', type: 'Release', test: 'uwp', + build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun' , + config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_OPENSSL=ON', + install: 'mingw-w64-ucrt-x86_64-openssl mingw-w64-ucrt-x86_64-libssh2' } + # { name: 'schannel', type: 'Release', test: 'uwp', + # build: 'autotools', sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun' , + # config: '--without-debug --with-schannel --disable-static', + # install: 'mingw-w64-ucrt-x86_64-libssh2' } + - { name: 'schannel dev debug', type: 'Debug', cppflags: '-DCURL_SCHANNEL_DEV_DEBUG', image: 'windows-2025', + build: 'cmake' , sys: 'mingw64' , env: 'x86_64' , tflags: 'skiprun' , + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCMAKE_VERBOSE_MAKEFILE=ON', + install: 'mingw-w64-x86_64-libssh2' } + - { name: 'MultiSSL R', type: 'Release', + build: 'cmake' , sys: 'mingw32' , env: 'i686' , tflags: 'skiprun' , + config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_GNUTLS=ON -DCURL_USE_OPENSSL=ON -DCURL_USE_SCHANNEL=ON -DENABLE_ARES=ON -DENABLE_UNICODE=ON', + install: 'mingw-w64-i686-c-ares mingw-w64-i686-gnutls mingw-w64-i686-libssh2 mingw-w64-i686-openssl' } fail-fast: false steps: - - run: git config --global core.autocrlf input - shell: pwsh - - - uses: msys2/setup-msys2@fb197b72ce45fb24f17bf3f807a388985654d1f2 # v2 + - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 if: ${{ matrix.sys == 'msys' }} with: msystem: ${{ matrix.sys }} @@ -300,20 +337,18 @@ jobs: libpsl-devel ${{ matrix.install }} - - uses: msys2/setup-msys2@fb197b72ce45fb24f17bf3f807a388985654d1f2 # v2 + - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 if: ${{ matrix.sys != 'msys' }} with: msystem: ${{ matrix.sys }} - update: ${{ matrix.sys == 'clangarm64' }} # delete this line on next msys2/setup-msys2 bump install: >- mingw-w64-${{ matrix.env }}-cc mingw-w64-${{ matrix.env }}-${{ matrix.build }} ${{ matrix.build == 'autotools' && 'make' || '' }} mingw-w64-${{ matrix.env }}-diffutils mingw-w64-${{ matrix.env }}-libpsl - mingw-w64-${{ matrix.env }}-c-ares ${{ matrix.install }} - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -325,7 +360,7 @@ jobs: - name: 'configure' timeout-minutes: 5 env: - CFLAGS: '${{ matrix.cflags }}' + CPPFLAGS: '${{ matrix.cppflags }}' MATRIX_CHKPREFILL: '${{ matrix.chkprefill }}' MATRIX_CONFIG: '${{ matrix.config }}' MATRIX_ENV: '${{ matrix.env }}' @@ -333,7 +368,7 @@ jobs: TFLAGS: '${{ matrix.tflags }}' run: | if [ "${MATRIX_TEST}" = 'uwp' ]; then - CPPFLAGS='-DWINSTORECOMPAT -DWINAPI_FAMILY=WINAPI_FAMILY_APP' + CPPFLAGS+=' -DWINSTORECOMPAT -DWINAPI_FAMILY=WINAPI_FAMILY_APP -D_WIN32_WINNT=0x0a00' if [[ "${MATRIX_ENV}" != 'clang'* ]]; then specs="$(realpath gcc-specs-uwp)" gcc -dumpspecs | sed -e 's/-lmingwex/-lwindowsapp -lmingwex -lwindowsapp/' -e 's/-lmsvcrt/-lucrtapp/' > "${specs}" @@ -349,7 +384,7 @@ jobs: options='-DCMAKE_C_COMPILER=gcc' fi [ "${MATRIX_SYS}" = 'msys' ] && options+=' -D_CURL_PREFILL=ON' - [ "${MATRIX_TEST}" = 'uwp' ] && options+=' -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0' + [ "${MATRIX_TEST}" = 'uwp' ] && options+=' -DCMAKE_SYSTEM_NAME=WindowsStore' [ "${TFLAGS}" = 'skiprun' ] && options+=' -D_CURL_SKIP_BUILD_CERTS=ON' [ "${_chkprefill}" = '_chkprefill' ] && options+=' -D_CURL_PREFILL=OFF' cmake -B "bld${_chkprefill}" -G Ninja ${options} \ @@ -365,11 +400,10 @@ jobs: false fi else - export CFLAGS CPPFLAGS - mkdir bld && cd bld && ../configure --enable-unity --enable-warnings --enable-werror \ - --prefix="$HOME"/curl-install \ + export CFLAGS + mkdir bld && cd bld && ../configure --prefix="$HOME"/curl-install --enable-unity --enable-warnings --enable-werror --disable-static \ + --disable-dependency-tracking --enable-option-checking=fatal \ --with-libssh2 \ - --disable-dependency-tracking \ ${MATRIX_CONFIG} fi @@ -393,7 +427,7 @@ jobs: make -C bld V=1 install fi - - name: 'curl version' + - name: 'curl -V' timeout-minutes: 1 run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then @@ -403,7 +437,8 @@ jobs: # avoid libtool's curl.exe wrapper for shared builds mv bld/src/.libs/curl.exe bld/src/curl.exe || true fi - find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -exec file '{}' \; + find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -print0 | grep -z curl | xargs -0 file -- + find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -print0 | grep -z curl | xargs -0 stat -c '%10s bytes: %n' -- if [ "${MATRIX_TEST}" != 'uwp' ]; then # curl: error initializing curl library bld/src/curl.exe --disable --version fi @@ -425,63 +460,83 @@ jobs: mv bld/tests/unit/.libs/*.exe bld/tests/unit || true fi + - name: 'cache test prereqs (stunnel)' + if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-stunnel + with: + path: C:\my-stunnel + key: ${{ runner.os }}-stunnel-${{ env.STUNNEL_VERSION }}-amd64 + fail-on-cache-miss: true + - name: 'install test prereqs' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - timeout-minutes: 5 + timeout-minutes: 2 run: | - /usr/bin/pacman --noconfirm --noprogressbar --sync --needed openssh - /c/ProgramData/chocolatey/choco.exe install --yes --no-progress --limit-output --timeout 180 --force stunnel || true - perl --version | tee "$GITHUB_WORKSPACE"/perlversion - - - name: 'cache perl packages' - if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' && matrix.sys != 'msys' }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-perl-win32-pkgs - env: - cache-name: cache-perl-win32-pkgs - with: - path: C:\perl-win32-pkgs - key: ${{ runner.os }}-${{ runner.arch }}-build-${{ env.cache-name }}-${{ hashFiles('perlversion') }} - - - name: 'install test prereqs perl' - if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - timeout-minutes: 5 - run: &perl-win32-pkgs-install | - perl --version - if [ -d /c/perl-win32-pkgs ]; then - pushd /c/perl-win32-pkgs - pushd Win32-Process-0.17 - install -D blib/arch/auto/Win32/Process/Process.dll /usr/lib/perl5/site_perl/auto/Win32/Process/Process.dll - install -D blib/lib/Win32/Process.pm /usr/lib/perl5/site_perl/Win32/Process.pm - popd - pushd Win32-Process-List-0.09 - install -D blib/arch/auto/Win32/Process/List/List.dll /usr/lib/perl5/site_perl/auto/Win32/Process/List/List.dll - install -D blib/lib/auto/Win32/Process/List/autosplit.ix /usr/lib/perl5/site_perl/auto/Win32/Process/List/autosplit.ix - install -D blib/lib/Win32/Process/List.pm /usr/lib/perl5/site_perl/Win32/Process/List.pm - install -D blib/lib/Win32/Process/processes.pl /usr/lib/perl5/site_perl/Win32/Process/processes.pl - popd - popd + if [ -z "${MATRIX_OPENSSH}" ]; then # MSYS2 openssh + /usr/bin/pacman --noconfirm --noprogressbar --sync --needed openssh + elif [ "${MATRIX_OPENSSH}" = 'OpenSSH-Windows-builtin' ]; then + # https://learn.microsoft.com/windows-server/administration/openssh/openssh_install_firstuse + if [[ "${MATRIX_IMAGE}" = *'windows-2025'* ]]; then + pwsh -Command 'Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0' + pwsh -Command 'Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0' + fi + else # OpenSSH-Windows + cd /c # no D: drive on windows-11-arm runners + if [[ "${MATRIX_IMAGE}" = *'-arm'* ]]; then + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ + --location "https://github.com/PowerShell/Win32-OpenSSH/releases/download/${OPENSSH_WINDOWS_VERSION}/OpenSSH-ARM64.zip" --output pkg.bin + sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${OPENSSH_WINDOWS_SHA256_ARM64}" && unzip pkg.bin && rm -f pkg.bin + else + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ + --location "https://github.com/PowerShell/Win32-OpenSSH/releases/download/${OPENSSH_WINDOWS_VERSION}/OpenSSH-Win64.zip" --output pkg.bin + sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${OPENSSH_WINDOWS_SHA256_WIN64}" && unzip pkg.bin && rm -f pkg.bin + fi fi - perl -MWin32::Process -MWin32::Process::List -e 1 && echo '! Modules loading OK.' || echo '! Failed to load modules.' - name: 'run tests' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - timeout-minutes: 10 + timeout-minutes: ${{ contains(matrix.tflags, '-t') && 15 || 10 }} env: + MATRIX_ENV: '${{ matrix.env }}' MATRIX_INSTALL: '${{ matrix.install }}' TFLAGS: '${{ matrix.tflags }}' run: | TFLAGS="-j8 ${TFLAGS}" if [ "${MATRIX_SYS}" != 'msys' ]; then TFLAGS+=' !498' # 'Reject too large HTTP response headers on endless redirects' HTTP, HTTP GET (runtests detecting result code 2009 instead of 56 returned by curl) + TFLAGS+=' ~3000 ~3001 ~3023 ~3024' # 'HTTPS localhost, first/last subject alt name matches, CN does not match' HTTPS, HTTP GET, PEM certificate (returning 56) if [[ "${MATRIX_INSTALL}" = *'libssh2-wincng'* ]]; then TFLAGS+=' ~SCP ~SFTP' # Flaky: `-8, Unable to exchange encryption keys`. https://github.com/libssh2/libssh2/issues/804 fi + if [[ "${TFLAGS}" = *'-t'* ]]; then + TFLAGS+=' !2300' # Leaks memory and file handle via tool_doswin.c / win32_stdin_read_thread() + export CURL_TEST_NO_TASKKILL=1 # experiment to see if it reduces flaky failures + fi + if [[ "${MATRIX_INSTALL} " = *'-libssh '* && \ + "${MATRIX_ENV}" = *'x86_64'* ]]; then + export CURL_TEST_SSH_DISABLE_KEX=mlkem768x25519-sha256 # broken with libssh 0.12.0 Windows x64 + fi + fi + if [ -n "${MATRIX_OPENSSH}" ]; then # OpenSSH-Windows + TFLAGS+=' ~601 ~603 ~617 ~619 ~621 ~641 ~665 ~2004' # SCP + if [[ "${MATRIX_INSTALL} " = *'libssh '* ]]; then + TFLAGS+=' ~614' # 'SFTP pre-quote chmod' SFTP, pre-quote, directory + else + TFLAGS+=' ~3022' # 'SCP correct sha256 host key' SCP, server sha256 key check + fi + fi + if [ "${MATRIX_OPENSSH}" = 'OpenSSH-Windows' ]; then + if [[ "${MATRIX_IMAGE}" = *'-arm'* ]]; then + PATH="/c/OpenSSH-ARM64:$PATH" + else + PATH="/c/OpenSSH-Win64:$PATH" + fi fi if [ -x "$(cygpath "${SYSTEMROOT}/System32/curl.exe")" ]; then TFLAGS+=" -ac $(cygpath "${SYSTEMROOT}/System32/curl.exe")" fi - PATH="$PATH:/c/Program Files (x86)/stunnel/bin" + PATH="$PATH:/c/my-stunnel/bin" if [ "${MATRIX_BUILD}" = 'cmake' ]; then PATH="$PWD/bld/lib:$PATH" cmake --build bld --verbose --target test-ci @@ -505,64 +560,77 @@ jobs: mingw-w64-standalone-downloads: name: 'dl-mingw, CM ${{ matrix.ver }}-${{ matrix.env }} ${{ matrix.name }}' - needs: - - build-cache + needs: build-cache runs-on: windows-2022 - timeout-minutes: 15 + timeout-minutes: 10 defaults: run: - shell: msys2 {0} + shell: msys2 {0} # zizmor: ignore[misfeature] env: + CURL_TEST_MIN: 1550 + LDFLAGS: -s MAKEFLAGS: -j 5 MATRIX_DIR: '${{ matrix.dir }}' strategy: matrix: include: - - name: 'schannel' # mingw-w64 12.0 - dir: 'mingw64' + - name: 'schannel +analyzer' # mingw-w64 12.0 + sys: 'mingw64' + dir: 'w64devkit' env: 'x86_64' - ver: '15.0.1' - url: 'https://github.com/brechtsanders/winlibs_mingw/releases/download/15.0.1-snapshot20250406posix-12.0.0-ucrt-r1/winlibs-x86_64-posix-seh-gcc-15.0.1-snapshot20250406-mingw-w64ucrt-12.0.0-r1.7z' - config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF' + ver: '15.1.0' + url: 'https://github.com/skeeto/w64devkit/releases/download/v2.2.0/w64devkit-x64-2.2.0.7z.exe' + SHA256: e02de30b97196329662007d64bc4509fbd7f5e14339d344075c7f1223dead4a2 + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DENABLE_UNIX_SOCKETS=OFF -DCURL_GCC_ANALYZER=ON' type: 'Release' - tflags: 'skiprun' - name: 'schannel' # mingw-w64 10.0 + sys: 'mingw64' dir: 'mingw64' env: 'x86_64' ver: '9.5.0' url: 'https://github.com/brechtsanders/winlibs_mingw/releases/download/9.5.0-10.0.0-msvcrt-r1/winlibs-x86_64-posix-seh-gcc-9.5.0-mingw-w64msvcrt-10.0.0-r1.7z' - config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF' + SHA256: 41637132ea7dc36a7f86a1961eaa334c380b5a3423d36aecb481cabcd006e3fe + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DCURL_DISABLE_VERBOSE_STRINGS=ON' type: 'Release' + tflags: 'skiprun' - name: 'schannel mbedtls U' # mingw-w64 6.0 + sys: 'mingw64' dir: 'mingw64' env: 'x86_64' ver: '7.3.0' url: 'https://downloads.sourceforge.net/mingw-w64/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/7.3.0/threads-win32/seh/x86_64-7.3.0-release-win32-seh-rt_v5-rev0.7z' - config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCURL_USE_MBEDTLS=ON' + SHA256: 9dc08c9c2bdd5d8173f87791bed644f6e290624f739de474f117b590dfd8a721 + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCURL_USE_MBEDTLS=ON -DCURL_TARGET_WINDOWS_VERSION=0x0600' install: mingw-w64-x86_64-mbedtls type: 'Release' tflags: 'skiprun' - name: 'schannel !unity' # mingw-w64 5.0 + sys: 'mingw32' dir: 'mingw32' env: 'i686' ver: '6.4.0' url: 'https://downloads.sourceforge.net/mingw-w64/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/6.4.0/threads-win32/dwarf/i686-6.4.0-release-win32-dwarf-rt_v5-rev0.7z' - config: '-DENABLE_DEBUG=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=OFF -DCMAKE_UNITY_BUILD=OFF' + SHA256: 12d2c62ad4527ec8a52275ea8485678dcbe20bec4716a3c7ba274f225d696085 + config: '-DENABLE_DEBUG=ON -DCURL_USE_SCHANNEL=ON -DCURL_USE_LIBSSH=ON -DENABLE_UNICODE=OFF -DCMAKE_UNITY_BUILD=OFF -DCURL_TARGET_WINDOWS_VERSION=0x0600' + install: mingw-w64-i686-libssh type: 'Debug' tflags: 'skiprun' - name: 'schannel !examples' # mingw-w64 3.0 + sys: 'mingw64' dir: 'mingw64' env: 'x86_64' ver: '4.8.1' url: 'https://downloads.sourceforge.net/mingw-w64/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.8.1/threads-win32/seh/x86_64-4.8.1-release-win32-seh-rt_v3-rev2.7z' - config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON' + SHA256: 1353d997e85bb4494ebbebb432d824848d66b32c6045900da9a38a767b3c4ab4 + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=ON -DCURL_TARGET_WINDOWS_VERSION=0x0600' type: 'Debug' tflags: 'skipall' + chkprefill: '' # Set it once to silence actionlint fail-fast: false steps: - - uses: msys2/setup-msys2@fb197b72ce45fb24f17bf3f807a388985654d1f2 # v2 + - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 with: - msystem: ${{ matrix.dir }} + msystem: ${{ matrix.sys }} release: false update: false cache: false @@ -572,7 +640,7 @@ jobs: ${{ matrix.install }} - name: 'cache compiler (gcc ${{ matrix.ver }}-${{ matrix.env }})' - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 id: cache-compiler with: path: D:\my-cache @@ -583,20 +651,17 @@ jobs: timeout-minutes: 5 env: MATRIX_URL: '${{ matrix.url }}' + MATRIX_SHA256: '${{ matrix.SHA256 }}' run: | cd /d mkdir my-cache cd my-cache curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 240 --retry 3 --retry-connrefused \ - --location --proto-redir =https "${MATRIX_URL}" --output pack.bin + --location --proto-redir =https "${MATRIX_URL}" --output pkg.bin pwd - 7z x -y pack.bin >/dev/null - rm -r -f pack.bin - ls -l + sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${MATRIX_SHA256}" && 7z x -y pkg.bin >/dev/null && rm -f pkg.bin && ls -l - - run: git config --global core.autocrlf input - - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -617,6 +682,7 @@ jobs: -DCMAKE_C_COMPILER=gcc \ -DCMAKE_BUILD_TYPE="${MATRIX_TYPE}" \ -DCMAKE_UNITY_BUILD=ON -DCMAKE_UNITY_BUILD_BATCH_SIZE=30 \ + -DCURL_DROP_UNUSED=ON \ -DCURL_WERROR=ON \ -DUSE_LIBIDN2=OFF \ ${MATRIX_CONFIG} @@ -641,10 +707,11 @@ jobs: PATH="/d/my-cache/${MATRIX_DIR}/bin:$PATH" cmake --build bld - - name: 'curl version' + - name: 'curl -V' timeout-minutes: 1 run: | - /usr/bin/find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -exec file '{}' \; + /usr/bin/find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -print0 | grep -z curl | xargs -0 file -- + /usr/bin/find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -print0 | grep -z curl | xargs -0 stat -c '%10s bytes: %n' -- PATH="$PWD/bld/lib:$PATH" bld/src/curl.exe --disable --version @@ -655,28 +722,22 @@ jobs: PATH="/d/my-cache/${MATRIX_DIR}/bin:$PATH" cmake --build bld --target testdeps + - name: 'cache test prereqs (stunnel)' + if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-stunnel + with: + path: C:\my-stunnel + key: ${{ runner.os }}-stunnel-${{ env.STUNNEL_VERSION }}-amd64 + fail-on-cache-miss: true + - name: 'install test prereqs' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - timeout-minutes: 5 + timeout-minutes: 2 run: | - /c/ProgramData/chocolatey/choco.exe install --yes --no-progress --limit-output --timeout 180 --force stunnel || true - python3 -m pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary impacket - perl --version | tee "$GITHUB_WORKSPACE"/perlversion - - - name: 'cache perl packages' - if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-perl-win32-pkgs - env: - cache-name: cache-perl-win32-pkgs - with: - path: C:\perl-win32-pkgs - key: ${{ runner.os }}-${{ runner.arch }}-build-${{ env.cache-name }}-${{ hashFiles('perlversion') }} - - - name: 'install test prereqs perl' - if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - timeout-minutes: 5 - run: *perl-win32-pkgs-install + if "bld/src/curl.exe" --disable -V 2>/dev/null | grep smb; then + python3 -m pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary -r tests/requirements.txt + fi - name: 'run tests' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} @@ -687,10 +748,11 @@ jobs: PATH="/d/my-cache/${MATRIX_DIR}/bin:$PATH" TFLAGS="-j8 ${TFLAGS}" TFLAGS+=' !498' # 'Reject too large HTTP response headers on endless redirects' HTTP, HTTP GET (runtests detecting result code 2009 instead of 56 returned by curl) + TFLAGS+=' ~3000 ~3001 ~3023 ~3024' # 'HTTPS localhost, last subject alt name matches, CN does not match' HTTPS, HTTP GET, PEM certificate (returning 56) if [ -x "$(cygpath "${SYSTEMROOT}/System32/curl.exe")" ]; then TFLAGS+=" -ac $(cygpath "${SYSTEMROOT}/System32/curl.exe")" fi - PATH="$PWD/bld/lib:$PATH:/c/Program Files (x86)/stunnel/bin" + PATH="$PWD/bld/lib:$PATH:/c/my-stunnel/bin" cmake --build bld --target test-ci - name: 'build examples' @@ -706,8 +768,9 @@ jobs: linux-cross-mingw-w64: name: "linux-mingw, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.compiler }}" runs-on: ubuntu-latest - timeout-minutes: 45 + timeout-minutes: 10 env: + LDFLAGS: -s MAKEFLAGS: -j 5 TRIPLET: 'x86_64-w64-mingw32' MATRIX_BUILD: '${{ matrix.build }}' @@ -718,16 +781,18 @@ jobs: include: - { build: 'autotools', compiler: 'gcc' } - { build: 'cmake' , compiler: 'gcc' } - - { build: 'cmake' , compiler: 'clang-tidy' } + - { build: 'cmake' , compiler: 'clang-tidy', install_packages: 'clang-20 clang-tidy-20' } steps: - name: 'install packages' env: - INSTALL_PACKAGES: ${{ matrix.compiler == 'clang-tidy' && 'clang' || '' }} + MATRIX_INSTALL_PACKAGES: '${{ matrix.install_packages }}' run: | - sudo rm -f /var/lib/man-db/auto-update - sudo apt-get -o Dpkg::Use-Pty=0 install mingw-w64 ${INSTALL_PACKAGES} + printf "#!/bin/sh + while [ \$? = 0 ]; do for i in 1 2 3; do timeout 30 \"\$@\" && break 2; echo \"Error: slow server, retry \$i\"; sleep 1 + dpkg --configure -a; done; false; done" > "$HOME"/my-apt; chmod +x "$HOME"/my-apt + sudo "$HOME"/my-apt apt-get -o Dpkg::Use-Pty=0 install gcc-mingw-w64-x86-64-win32 ${MATRIX_INSTALL_PACKAGES} - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -739,28 +804,29 @@ jobs: run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then if [ "${MATRIX_COMPILER}" = 'clang-tidy' ]; then - options+=' -DCURL_CLANG_TIDY=ON' + options+=' -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-20' options+=' -DENABLE_UNICODE=ON -DUSE_SSLS_EXPORT=ON' - options+=' -DCMAKE_C_COMPILER=clang' - options+=" -DCMAKE_RC_COMPILER=llvm-windres-$(clang -dumpversion | cut -d '.' -f 1)" + options+=' -DCMAKE_C_COMPILER=clang-20' + options+=" -DCMAKE_RC_COMPILER=llvm-windres-$(clang-20 -dumpversion | cut -d '.' -f 1)" else options+=" -DCMAKE_C_COMPILER=${TRIPLET}-gcc" fi cmake -B bld -G Ninja \ -DCMAKE_SYSTEM_NAME=Windows \ -DCMAKE_C_COMPILER_TARGET="${TRIPLET}" \ - -DCMAKE_UNITY_BUILD=ON \ + -DCMAKE_UNITY_BUILD=ON -D_CURL_TESTS_CONCAT=ON \ -DCURL_WERROR=ON \ -DCURL_USE_SCHANNEL=ON -DUSE_WIN32_IDN=ON \ -DCURL_USE_LIBPSL=OFF \ + -DCURL_ENABLE_NTLM=ON \ -D_CURL_SKIP_BUILD_CERTS=ON \ ${options} else - mkdir bld && cd bld && ../configure --enable-unity --enable-warnings --enable-werror \ + mkdir bld && cd bld && ../configure --enable-unity --enable-warnings --enable-werror --disable-static \ + --disable-dependency-tracking --enable-option-checking=fatal \ --host="${TRIPLET}" \ --with-schannel --with-winidn \ - --without-libpsl \ - --disable-dependency-tracking + --without-libpsl fi - name: 'configure log' @@ -781,10 +847,12 @@ jobs: fi - name: 'curl info' - run: find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -exec file '{}' \; + run: | + find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -print0 | grep -z curl | xargs -0 file -- + find . \( -name '*.exe' -o -name '*.dll' -o -name '*.a' \) -print0 | grep -z curl | xargs -0 stat -c '%10s bytes: %n' -- - name: 'build tests' - if: ${{ matrix.build == 'cmake' && matrix.compiler != 'clang-tidy' }} # Save time by skipping this for autotools and clang-tidy + if: ${{ matrix.build == 'cmake' }} # Save time by skipping this for autotools and clang-tidy run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake --build bld --target testdeps @@ -806,13 +874,12 @@ jobs: msvc: name: 'msvc, CM ${{ matrix.arch }}-${{ matrix.plat }} ${{ matrix.name }}' - needs: - - build-cache + needs: build-cache runs-on: ${{ matrix.image || 'windows-2022' }} - timeout-minutes: 15 + timeout-minutes: ${{ matrix.arch == 'arm64' && 12 || 10 }} defaults: run: - shell: msys2 {0} + shell: msys2 {0} # zizmor: ignore[misfeature] env: MATRIX_ARCH: '${{ matrix.arch }}' MATRIX_IMAGE: '${{ matrix.image }}' @@ -821,7 +888,6 @@ jobs: MATRIX_OPENSSH: '${{ matrix.openssh }}' MATRIX_PLAT: '${{ matrix.plat }}' MATRIX_TYPE: '${{ matrix.type }}' - OPENSSH_WINDOWS_VERSION: 'v9.8.1.0p1-Preview' VCPKG_DISABLE_METRICS: '1' strategy: matrix: @@ -854,6 +920,7 @@ jobs: env: 'ucrt-x86_64' plat: 'windows' type: 'Debug' + image: 'windows-2025-vs2026' chkprefill: '_chkprefill' config: >- -DENABLE_DEBUG=ON @@ -880,6 +947,8 @@ jobs: -DNGTCP2_INCLUDE_DIR=/ucrt64/include -DNGTCP2_LIBRARY=/ucrt64/lib/libngtcp2.dll.a -DNGTCP2_CRYPTO_OSSL_LIBRARY=/ucrt64/lib/libngtcp2_crypto_ossl.dll.a + -DCURL_CA_NATIVE=ON + -DCURL_ENABLE_NTLM=ON - name: 'schannel U' install-vcpkg: 'zlib libssh2[core,zlib]' @@ -889,6 +958,9 @@ jobs: type: 'Debug' image: 'windows-11-arm' openssh: 'OpenSSH-Windows' + tflags: '--min=1650' + # leave SMB disabled to save 30-60 seconds by omitting prereqs, + # to counteract the slower test run step config: >- -DENABLE_DEBUG=ON -DCURL_USE_SCHANNEL=ON @@ -896,7 +968,7 @@ jobs: fail-fast: false steps: - - uses: msys2/setup-msys2@fb197b72ce45fb24f17bf3f807a388985654d1f2 # v2 + - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 with: msystem: ${{ matrix.arch == 'arm64' && 'clangarm64' || 'ucrt64' }} release: ${{ contains(matrix.image, 'arm') }} @@ -918,7 +990,7 @@ jobs: timeout-minutes: 45 run: vcpkg x-set-installed ${MATRIX_INSTALL_VCPKG} --triplet="${MATRIX_ARCH}-${MATRIX_PLAT}" - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -952,12 +1024,13 @@ jobs: # Use Ninja when running tests to avoid MSBuild heuristics picking # up "error messages" in the test log output and making the job fail. # Officially this requires the vcvarsall.bat MS-DOS batch file (as of - # VS2022). Since it integrates badly with CI steps and shell scripts - # scripts, reproduce the necessary build configuration manually, and - # without envs. + # VS2022). Since it integrates badly with CI steps and shell scripts, + # reproduce the necessary build configuration manually, without envs. + MSVC_EDITION='2022/Enterprise/vc/tools/msvc' + [[ "${MATRIX_IMAGE}" = *'vs2026'* ]] && MSVC_EDITION='18/Enterprise/vc/tools/msvc' [[ "$(uname -s)" = *'ARM64'* ]] && MSVC_HOST='arm64' || MSVC_HOST='x64' # x86 MSVC_ROOTD="$(cygpath --mixed --short-name "$PROGRAMFILES/Microsoft Visual Studio")" # to avoid spaces in directory names - MSVC_ROOTU="$(/usr/bin/find "$(cygpath --unix "$MSVC_ROOTD/2022/Enterprise/vc/tools/msvc")" -mindepth 1 -maxdepth 1 -type d -name '*.*' | sort | tail -n 1)" + MSVC_ROOTU="$(/usr/bin/find "$(cygpath --unix "$MSVC_ROOTD/$MSVC_EDITION")" -mindepth 1 -maxdepth 1 -type d -name '*.*' | sort | tail -n 1)" MSVC_ROOTW="$(cygpath --mixed "$MSVC_ROOTU")" MSVC_ROOTU="$(cygpath --unix "$MSVC_ROOTW")" MSVC_BINU="$MSVC_ROOTU/bin/Host$MSVC_HOST/$MATRIX_ARCH" @@ -983,6 +1056,8 @@ jobs: options+=" -DCMAKE_MT=$MSDK_BINU/mt.exe" options+=" -DCMAKE_C_COMPILER=$MSVC_BINU/cl.exe" export CMAKE_GENERATOR='Ninja Multi-Config' # pass it via env to avoid space issues + echo "Using MSVC: ${MSVC_ROOTW}" + echo "Using Windows SDK: ${MSDK_VER}" fi [ "${_chkprefill}" = '_chkprefill' ] && options+=' -D_CURL_PREFILL=OFF' if [ -n "${MATRIX_INSTALL_VCPKG}" ]; then @@ -997,6 +1072,7 @@ jobs: -DCMAKE_EXE_LINKER_FLAGS="-INCREMENTAL:NO ${ldflags}" \ -DCMAKE_SHARED_LINKER_FLAGS="-INCREMENTAL:NO ${ldflags}" \ -DCMAKE_UNITY_BUILD=ON \ + -DCURL_DROP_UNUSED=ON \ -DCURL_WERROR=ON \ -DLIBPSL_INCLUDE_DIR="${MINGW_PREFIX}/include" \ -DLIBPSL_LIBRARY="${MINGW_PREFIX}/lib/libpsl.dll.a" \ @@ -1021,10 +1097,11 @@ jobs: timeout-minutes: 5 run: cmake --build bld --config "${MATRIX_TYPE}" --parallel 5 - - name: 'curl version' + - name: 'curl -V' timeout-minutes: 1 run: | - /usr/bin/find . \( -name '*.exe' -o -name '*.dll' -o -name '*.lib' -o -name '*.pdb' \) -exec file '{}' \; + /usr/bin/find . \( -name '*.exe' -o -name '*.dll' -o -name '*.lib' -o -name '*.pdb' \) -print0 | grep -z curl | xargs -0 file -- + /usr/bin/find . \( -name '*.exe' -o -name '*.dll' -o -name '*.lib' -o -name '*.pdb' \) -print0 | grep -z curl | xargs -0 stat -c '%10s bytes: %n' -- if [ "${MATRIX_PLAT}" != 'uwp' ]; then # Missing: ucrtbased.dll, VCRUNTIME140D.dll, VCRUNTIME140D_APP.dll PATH="$PWD/bld/lib/${MATRIX_TYPE}:$PATH" "bld/src/${MATRIX_TYPE}/curl.exe" --disable --version @@ -1035,45 +1112,42 @@ jobs: timeout-minutes: 10 run: cmake --build bld --config "${MATRIX_TYPE}" --parallel 5 --target testdeps + - name: 'cache test prereqs (stunnel)' + if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + id: cache-stunnel + with: + path: C:\my-stunnel + key: ${{ runner.os }}-stunnel-${{ env.STUNNEL_VERSION }}-amd64 + fail-on-cache-miss: true + - name: 'install test prereqs' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - timeout-minutes: 5 + timeout-minutes: 2 run: | if [ -z "${MATRIX_OPENSSH}" ]; then # MSYS2 openssh /usr/bin/pacman --noconfirm --noprogressbar --sync --needed openssh elif [ "${MATRIX_OPENSSH}" = 'OpenSSH-Windows-builtin' ]; then # https://learn.microsoft.com/windows-server/administration/openssh/openssh_install_firstuse - if [ "${MATRIX_IMAGE}" != 'windows-2025' ]; then + if [[ "${MATRIX_IMAGE}" = *'windows-2025'* ]]; then pwsh -Command 'Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0' pwsh -Command 'Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0' fi else # OpenSSH-Windows cd /c # no D: drive on windows-11-arm runners - curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ - --location "https://github.com/PowerShell/Win32-OpenSSH/releases/download/${OPENSSH_WINDOWS_VERSION}/OpenSSH-Win64.zip" --output bin.zip - unzip bin.zip - rm -f bin.zip + if [[ "${MATRIX_IMAGE}" = *'-arm'* ]]; then + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ + --location "https://github.com/PowerShell/Win32-OpenSSH/releases/download/${OPENSSH_WINDOWS_VERSION}/OpenSSH-ARM64.zip" --output pkg.bin + sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${OPENSSH_WINDOWS_SHA256_ARM64}" && unzip pkg.bin && rm -f pkg.bin + else + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ + --location "https://github.com/PowerShell/Win32-OpenSSH/releases/download/${OPENSSH_WINDOWS_VERSION}/OpenSSH-Win64.zip" --output pkg.bin + sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${OPENSSH_WINDOWS_SHA256_WIN64}" && unzip pkg.bin && rm -f pkg.bin + fi fi - /c/ProgramData/chocolatey/choco.exe install --yes --no-progress --limit-output --timeout 180 --force stunnel || true - if [ "${MATRIX_IMAGE}" != 'windows-11-arm' ]; then # save 30-60 seconds, to counteract the slower test run step - python3 -m pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary impacket + if "bld/src/${MATRIX_TYPE}/curl.exe" --disable -V 2>/dev/null | grep smb; then + python3 -m pip --disable-pip-version-check --no-input --no-cache-dir install --progress-bar off --prefer-binary -r tests/requirements.txt fi - perl --version | tee "$GITHUB_WORKSPACE"/perlversion - - - name: 'cache perl packages' - if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 - id: cache-perl-win32-pkgs - env: - cache-name: cache-perl-win32-pkgs - with: - path: C:\perl-win32-pkgs - key: ${{ runner.os }}-${{ runner.arch }}-build-${{ env.cache-name }}-${{ hashFiles('perlversion') }} - - - name: 'install test prereqs perl' - if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - timeout-minutes: 5 - run: *perl-win32-pkgs-install - name: 'run tests' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} @@ -1083,6 +1157,7 @@ jobs: run: | TFLAGS="-j8 ${TFLAGS}" TFLAGS+=' !498' # 'Reject too large HTTP response headers on endless redirects' HTTP, HTTP GET (runtests detecting result code 2009 instead of 56 returned by curl) + TFLAGS+=' ~3000 ~3001 ~3023 ~3024' # 'HTTPS localhost, last subject alt name matches, CN does not match' HTTPS, HTTP GET, PEM certificate (returning 56) if [[ "${MATRIX_INSTALL_MSYS2}" = *'libssh2-wincng'* || \ "${MATRIX_INSTALL_VCPKG}" = *'libssh2[core,zlib]'* ]]; then TFLAGS+=' ~SCP ~SFTP' # Flaky: `-8, Unable to exchange encryption keys`. https://github.com/libssh2/libssh2/issues/804 @@ -1095,9 +1170,15 @@ jobs: else TFLAGS+=' ~3022' # 'SCP correct sha256 host key' SCP, server sha256 key check fi - PATH="/c/OpenSSH-Win64:$PATH" fi - PATH="$PWD/bld/lib/${MATRIX_TYPE}:$PATH:/c/Program Files (x86)/stunnel/bin" + if [ "${MATRIX_OPENSSH}" = 'OpenSSH-Windows' ]; then + if [[ "${MATRIX_IMAGE}" = *'-arm'* ]]; then + PATH="/c/OpenSSH-ARM64:$PATH" + else + PATH="/c/OpenSSH-Win64:$PATH" + fi + fi + PATH="$PWD/bld/lib/${MATRIX_TYPE}:$PATH:/c/my-stunnel/bin" cmake --build bld --config "${MATRIX_TYPE}" --target test-ci - name: 'build examples' diff --git a/.gitignore b/.gitignore index 9c901f9fff..0ac1002956 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,6 @@ /.vs /bld/ /build/ -/builds/ /stats/ __pycache__ Debug diff --git a/.mailmap b/.mailmap index e89e386056..d8558a2ec3 100644 --- a/.mailmap +++ b/.mailmap @@ -1,3 +1,7 @@ +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + Guenter Knauf Gisle Vanem Gisle Vanem @@ -116,3 +120,5 @@ Sinkevich Artem Andrew Kirillov Stephen Farrell Calvin Ruocco +Hamza Bensliman +Kaixuan Li diff --git a/CHANGES.md b/CHANGES.md index 6e2f7c6bcc..3eabec9cb8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,7 +4,7 @@ Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl --> -In a release tarball, check the RELEASES-NOTES file for what was done in the +In a release tarball, check the RELEASE-NOTES file for what was done in the most recent release. In a git check-out, that file mentions changes that have been done since the previous release. diff --git a/CMake/CMakeConfigurableFile.in b/CMake/CMakeConfigurableFile.in deleted file mode 100644 index a3d2bc4a28..0000000000 --- a/CMake/CMakeConfigurableFile.in +++ /dev/null @@ -1,24 +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 -# -########################################################################### -@CMAKE_CONFIGURABLE_FILE_CONTENT@ diff --git a/CMake/CurlSymbolHiding.cmake b/CMake/CurlSymbolHiding.cmake index 217c8832c9..51a2d94de1 100644 --- a/CMake/CurlSymbolHiding.cmake +++ b/CMake/CurlSymbolHiding.cmake @@ -24,12 +24,12 @@ option(CURL_HIDDEN_SYMBOLS "Hide libcurl internal symbols (=hide all symbols that are not officially external)" ON) mark_as_advanced(CURL_HIDDEN_SYMBOLS) -if(WIN32 AND (ENABLE_DEBUG OR ENABLE_CURLDEBUG)) +if(WIN32 AND ENABLE_DEBUG) # We need to export internal debug functions, # e.g. curl_easy_perform_ev() or curl_dbg_*(), # so disable symbol hiding for debug builds and for memory tracking. set(CURL_HIDDEN_SYMBOLS OFF) -elseif(DOS OR AMIGA OR MINGW32CE) +elseif(DOS OR AMIGA) set(CURL_HIDDEN_SYMBOLS OFF) endif() diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index 40fa50fe83..2cf306b588 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -27,9 +27,9 @@ #include #include #include -/* */ + #if defined(sun) || defined(__sun__) || \ - defined(__SUNPRO_C) || defined(__SUNPRO_CC) + defined(__SUNPRO_C) || defined(__SUNPRO_CC) # if defined(__SVR4) || defined(__srv4__) # define PLATFORM_SOLARIS # else @@ -39,7 +39,7 @@ #if (defined(_AIX) || defined(__xlC__)) && !defined(_AIX41) # define PLATFORM_AIX_V3 #endif -/* */ + #if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3) #error "O_NONBLOCK does not work on this platform" #endif @@ -119,16 +119,19 @@ int main(void) #include #include #include -int main(void) { return 0; } +int main(void) +{ + return 0; +} #endif #ifdef HAVE_FILE_OFFSET_BITS #include /* Check that off_t can represent 2**63 - 1 correctly. - We cannot simply define LARGE_OFF_T to be 9223372036854775807, + We cannot define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T (((off_t)1 << 62) - 1 + ((off_t)1 << 62)) static int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -272,7 +275,10 @@ int main(void) #include #include -static void check(char c) { (void)c; } +static void check(char c) +{ + (void)c; +} int main(void) { @@ -289,7 +295,10 @@ int main(void) #include /* Float, because a pointer cannot be implicitly cast to float */ -static void check(float f) { (void)f; } +static void check(float f) +{ + (void)f; +} int main(void) { diff --git a/CMake/FindBrotli.cmake b/CMake/FindBrotli.cmake index 690b5a9c27..bd3363cce0 100644 --- a/CMake/FindBrotli.cmake +++ b/CMake/FindBrotli.cmake @@ -25,39 +25,47 @@ # # Input variables: # -# - `BROTLI_INCLUDE_DIR`: The brotli include directory. -# - `BROTLICOMMON_LIBRARY`: Path to `brotlicommon` library. -# - `BROTLIDEC_LIBRARY`: Path to `brotlidec` library. +# - `BROTLI_INCLUDE_DIR`: Absolute path to brotli include directory. +# - `BROTLICOMMON_LIBRARY`: Absolute path to `brotlicommon` library. +# - `BROTLIDEC_LIBRARY`: Absolute path to `brotlidec` library. +# - `BROTLI_USE_STATIC_LIBS`: Configure for static brotli libraries. # -# Result variables: +# Defines: # -# - `BROTLI_FOUND`: System has brotli. -# - `BROTLI_INCLUDE_DIRS`: The brotli include directories. -# - `BROTLI_LIBRARIES`: The brotli library names. -# - `BROTLI_LIBRARY_DIRS`: The brotli library directories. -# - `BROTLI_PC_REQUIRES`: The brotli pkg-config packages. -# - `BROTLI_CFLAGS`: Required compiler flags. -# - `BROTLI_VERSION`: Version of brotli. +# - `BROTLI_FOUND`: System has brotli. +# - `BROTLI_VERSION`: Version of brotli. +# - `CURL::brotli`: brotli library target. -set(BROTLI_PC_REQUIRES "libbrotlidec" "libbrotlicommon") # order is significant: brotlidec then brotlicommon +set(_brotli_pc_requires "libbrotlidec" "libbrotlicommon") # order is significant: brotlidec then brotlicommon if(CURL_USE_PKGCONFIG AND NOT DEFINED BROTLI_INCLUDE_DIR AND NOT DEFINED BROTLICOMMON_LIBRARY AND NOT DEFINED BROTLIDEC_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(BROTLI ${BROTLI_PC_REQUIRES}) + pkg_check_modules(_brotli ${_brotli_pc_requires}) endif() -if(BROTLI_FOUND) +if(_brotli_FOUND) set(Brotli_FOUND TRUE) - set(BROTLI_VERSION "${BROTLI_libbrotlicommon_VERSION}") - string(REPLACE ";" " " BROTLI_CFLAGS "${BROTLI_CFLAGS}") - message(STATUS "Found Brotli (via pkg-config): ${BROTLI_INCLUDE_DIRS} (found version \"${BROTLI_VERSION}\")") + set(BROTLI_FOUND TRUE) + set(BROTLI_VERSION ${_brotli_libbrotlicommon_VERSION}) + if(BROTLI_USE_STATIC_LIBS) + set(_brotli_CFLAGS "${_brotli_STATIC_CFLAGS}") + set(_brotli_INCLUDE_DIRS "${_brotli_STATIC_INCLUDE_DIRS}") + set(_brotli_LIBRARY_DIRS "${_brotli_STATIC_LIBRARY_DIRS}") + set(_brotli_LIBRARIES "${_brotli_STATIC_LIBRARIES}") + endif() + message(STATUS "Found Brotli (via pkg-config): ${_brotli_INCLUDE_DIRS} (found version \"${BROTLI_VERSION}\")") else() find_path(BROTLI_INCLUDE_DIR "brotli/decode.h") - find_library(BROTLICOMMON_LIBRARY NAMES "brotlicommon") - find_library(BROTLIDEC_LIBRARY NAMES "brotlidec") + if(BROTLI_USE_STATIC_LIBS) + find_library(BROTLICOMMON_LIBRARY NAMES "brotlicommon-static" "brotlicommon") + find_library(BROTLIDEC_LIBRARY NAMES "brotlidec-static" "brotlidec") + else() + find_library(BROTLICOMMON_LIBRARY NAMES "brotlicommon") + find_library(BROTLIDEC_LIBRARY NAMES "brotlidec") + endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Brotli @@ -68,9 +76,21 @@ else() ) if(BROTLI_FOUND) - set(BROTLI_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR}) - set(BROTLI_LIBRARIES ${BROTLIDEC_LIBRARY} ${BROTLICOMMON_LIBRARY}) + set(_brotli_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR}) + set(_brotli_LIBRARIES ${BROTLIDEC_LIBRARY} ${BROTLICOMMON_LIBRARY}) endif() mark_as_advanced(BROTLI_INCLUDE_DIR BROTLIDEC_LIBRARY BROTLICOMMON_LIBRARY) endif() + +if(BROTLI_FOUND) + if(NOT TARGET CURL::brotli) + add_library(CURL::brotli INTERFACE IMPORTED) + set_target_properties(CURL::brotli PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_brotli_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_brotli_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_brotli_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_brotli_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_brotli_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindCares.cmake b/CMake/FindCares.cmake index cc47e2d331..26efdf87ff 100644 --- a/CMake/FindCares.cmake +++ b/CMake/FindCares.cmake @@ -25,35 +25,58 @@ # # Input variables: # -# - `CARES_INCLUDE_DIR`: The c-ares include directory. -# - `CARES_LIBRARY`: Path to `cares` library. +# - `CARES_INCLUDE_DIR`: Absolute path to c-ares include directory. +# - `CARES_LIBRARY`: Absolute path to `cares` library. +# - `CARES_USE_STATIC_LIBS`: Configure for static c-ares libraries. # -# Result variables: +# Defines: # -# - `CARES_FOUND`: System has c-ares. -# - `CARES_INCLUDE_DIRS`: The c-ares include directories. -# - `CARES_LIBRARIES`: The c-ares library names. -# - `CARES_LIBRARY_DIRS`: The c-ares library directories. -# - `CARES_PC_REQUIRES`: The c-ares pkg-config packages. -# - `CARES_CFLAGS`: Required compiler flags. -# - `CARES_VERSION`: Version of c-ares. +# - `CARES_FOUND`: System has c-ares. +# - `CARES_VERSION`: Version of c-ares. +# - `CURL::cares`: c-ares library target. -set(CARES_PC_REQUIRES "libcares") +set(_cares_pc_requires "libcares") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED CARES_INCLUDE_DIR AND +if(NOT DEFINED CARES_INCLUDE_DIR AND NOT DEFINED CARES_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(CARES ${CARES_PC_REQUIRES}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_cares ${_cares_pc_requires}) + endif() + if(NOT _cares_FOUND AND CURL_USE_CMAKECONFIG) + find_package(c-ares CONFIG QUIET) + endif() endif() -if(CARES_FOUND) +if(_cares_FOUND) set(Cares_FOUND TRUE) - string(REPLACE ";" " " CARES_CFLAGS "${CARES_CFLAGS}") - message(STATUS "Found Cares (via pkg-config): ${CARES_INCLUDE_DIRS} (found version \"${CARES_VERSION}\")") + set(CARES_FOUND TRUE) + set(CARES_VERSION ${_cares_VERSION}) + if(CARES_USE_STATIC_LIBS) + set(_cares_CFLAGS "${_cares_STATIC_CFLAGS}") + set(_cares_INCLUDE_DIRS "${_cares_STATIC_INCLUDE_DIRS}") + set(_cares_LIBRARY_DIRS "${_cares_STATIC_LIBRARY_DIRS}") + set(_cares_LIBRARIES "${_cares_STATIC_LIBRARIES}") + endif() + message(STATUS "Found Cares (via pkg-config): ${_cares_INCLUDE_DIRS} (found version \"${CARES_VERSION}\")") +elseif(c-ares_CONFIG) + set(Cares_FOUND TRUE) + set(CARES_FOUND TRUE) + set(CARES_VERSION ${c-ares_VERSION}) + if(CARES_USE_STATIC_LIBS) + set(_cares_LIBRARIES c-ares::cares_static) + else() + set(_cares_LIBRARIES c-ares::cares) + endif() + message(STATUS "Found Cares (via CMake Config): ${c-ares_CONFIG} (found version \"${CARES_VERSION}\")") else() find_path(CARES_INCLUDE_DIR NAMES "ares.h") - find_library(CARES_LIBRARY NAMES ${CARES_NAMES} "cares") + if(CARES_USE_STATIC_LIBS) + set(_cares_CFLAGS "-DCARES_STATICLIB") + find_library(CARES_LIBRARY NAMES ${CARES_NAMES} "cares_static" "cares") + else() + find_library(CARES_LIBRARY NAMES ${CARES_NAMES} "cares") + endif() unset(CARES_VERSION CACHE) if(CARES_INCLUDE_DIR AND EXISTS "${CARES_INCLUDE_DIR}/ares_version.h") @@ -85,13 +108,25 @@ else() ) if(CARES_FOUND) - set(CARES_INCLUDE_DIRS ${CARES_INCLUDE_DIR}) - set(CARES_LIBRARIES ${CARES_LIBRARY}) + set(_cares_INCLUDE_DIRS ${CARES_INCLUDE_DIR}) + set(_cares_LIBRARIES ${CARES_LIBRARY}) endif() mark_as_advanced(CARES_INCLUDE_DIR CARES_LIBRARY) endif() -if(CARES_FOUND AND WIN32) - list(APPEND CARES_LIBRARIES "iphlpapi") # for if_indextoname and others +if(CARES_FOUND) + if(WIN32) + list(APPEND _cares_LIBRARIES "iphlpapi") # for if_indextoname and others + endif() + + if(NOT TARGET CURL::cares) + add_library(CURL::cares INTERFACE IMPORTED) + set_target_properties(CURL::cares PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_cares_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_cares_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_cares_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_cares_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_cares_LIBRARIES}") + endif() endif() diff --git a/CMake/FindGSS.cmake b/CMake/FindGSS.cmake index 9000445acb..9237fb30b1 100644 --- a/CMake/FindGSS.cmake +++ b/CMake/FindGSS.cmake @@ -25,32 +25,24 @@ # # Input variables: # -# - `GSS_ROOT_DIR`: Set this variable to the root installation of GSS. (also supported as environment) +# - `GSS_ROOT_DIR`: Absolute path to the root installation of GSS. (also supported as environment) # -# Result variables: +# Defines: # -# - `GSS_FOUND`: System has the Heimdal library. -# - `GSS_FLAVOUR`: "GNU", "MIT" or "Heimdal" if anything found. -# - `GSS_INCLUDE_DIRS`: The GSS include directories. -# - `GSS_LIBRARIES`: The GSS library names. -# - `GSS_LIBRARY_DIRS`: The GSS library directories. -# - `GSS_PC_REQUIRES`: The GSS pkg-config packages. -# - `GSS_CFLAGS`: Required compiler flags. -# - `GSS_VERSION`: This is set to version advertised by pkg-config or read from manifest. -# In case the library is found but no version info available it is set to "unknown" +# - `GSS_FOUND`: System has a GSS library. +# - `GSS_VERSION`: This is set to version advertised by pkg-config or read from manifest. +# In case the library is found but no version info available it is set to "unknown" +# - `CURL::gss`: GSS library target. +# - `CURL_GSS_FLAVOUR`: Custom property. "GNU" or "MIT" if detected. set(_gnu_modname "gss") set(_mit_modname "mit-krb5-gssapi") -set(_heimdal_modname "heimdal-gssapi") include(CheckIncludeFile) include(CheckIncludeFiles) include(CheckTypeSize) -set(_gss_root_hints - "${GSS_ROOT_DIR}" - "$ENV{GSS_ROOT_DIR}" -) +set(_gss_root_hints "${GSS_ROOT_DIR}" "$ENV{GSS_ROOT_DIR}") set(_gss_CFLAGS "") set(_gss_LIBRARY_DIRS "") @@ -59,7 +51,7 @@ set(_gss_LIBRARY_DIRS "") if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}") if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) - pkg_search_module(_gss ${_gnu_modname} ${_mit_modname} ${_heimdal_modname}) + pkg_search_module(_gss ${_gnu_modname} ${_mit_modname}) list(APPEND _gss_root_hints "${_gss_PREFIX}") set(_gss_version "${_gss_VERSION}") endif() @@ -69,37 +61,22 @@ if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}") endif() if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional approach. - find_file(_gss_configure_script - NAMES - "krb5-config" - HINTS - ${_gss_root_hints} - PATH_SUFFIXES - "bin" - NO_CMAKE_PATH - NO_CMAKE_ENVIRONMENT_PATH - ) - + find_file(_gss_configure_script NAMES "krb5-config" PATH_SUFFIXES "bin" HINTS ${_gss_root_hints} + NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH) # If not found in user-supplied directories, maybe system knows better - find_file(_gss_configure_script - NAMES - "krb5-config" - PATH_SUFFIXES - "bin" - ) + find_file(_gss_configure_script NAMES "krb5-config" PATH_SUFFIXES "bin") if(_gss_configure_script) set(_gss_INCLUDE_DIRS "") set(_gss_LIBRARIES "") - execute_process( - COMMAND ${_gss_configure_script} "--cflags" "gssapi" + execute_process(COMMAND ${_gss_configure_script} "--cflags" "gssapi" OUTPUT_VARIABLE _gss_cflags_raw RESULT_VARIABLE _gss_configure_failed - OUTPUT_STRIP_TRAILING_WHITESPACE - ) + OUTPUT_STRIP_TRAILING_WHITESPACE) message(STATUS "FindGSS krb5-config --cflags: ${_gss_cflags_raw}") + if(NOT _gss_configure_failed) # 0 means success # Should also work in an odd case when multiple directories are given. string(STRIP "${_gss_cflags_raw}" _gss_cflags_raw) @@ -116,12 +93,10 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr endforeach() endif() - execute_process( - COMMAND ${_gss_configure_script} "--libs" "gssapi" + execute_process(COMMAND ${_gss_configure_script} "--libs" "gssapi" OUTPUT_VARIABLE _gss_lib_flags RESULT_VARIABLE _gss_configure_failed - OUTPUT_STRIP_TRAILING_WHITESPACE - ) + OUTPUT_STRIP_TRAILING_WHITESPACE) message(STATUS "FindGSS krb5-config --libs: ${_gss_lib_flags}") if(NOT _gss_configure_failed) # 0 means success @@ -141,199 +116,117 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr endforeach() endif() - execute_process( - COMMAND ${_gss_configure_script} "--version" + execute_process(COMMAND ${_gss_configure_script} "--version" OUTPUT_VARIABLE _gss_version RESULT_VARIABLE _gss_configure_failed - OUTPUT_STRIP_TRAILING_WHITESPACE - ) + OUTPUT_STRIP_TRAILING_WHITESPACE) # Older versions may not have the "--version" parameter. In this case we just do not care. if(_gss_configure_failed) set(_gss_version 0) + else() + # Strip prefix string to leave the version number only + string(REPLACE "Kerberos 5 release " "" _gss_version "${_gss_version}") endif() - execute_process( - COMMAND ${_gss_configure_script} "--vendor" + execute_process(COMMAND ${_gss_configure_script} "--vendor" OUTPUT_VARIABLE _gss_vendor RESULT_VARIABLE _gss_configure_failed - OUTPUT_STRIP_TRAILING_WHITESPACE - ) + OUTPUT_STRIP_TRAILING_WHITESPACE) # Older versions may not have the "--vendor" parameter. In this case we just do not care. - if(_gss_configure_failed) - set(GSS_FLAVOUR "Heimdal") # most probably, should not really matter - else() - if(_gss_vendor MATCHES "H|heimdal") - set(GSS_FLAVOUR "Heimdal") - else() - set(GSS_FLAVOUR "MIT") - endif() + if(NOT _gss_configure_failed AND NOT _gss_vendor MATCHES "Heimdal|heimdal") + set(_gss_flavour "MIT") # assume a default, should not really matter endif() else() # Either there is no config script or we are on a platform that does not provide one (Windows?) - find_path(_gss_INCLUDE_DIRS NAMES "gssapi/gssapi.h" - HINTS - ${_gss_root_hints} - PATH_SUFFIXES - "include" - "inc" - ) + find_path(_gss_INCLUDE_DIRS NAMES "gssapi/gssapi.h" HINTS ${_gss_root_hints} PATH_SUFFIXES "include" "inc") + + if(_gss_INCLUDE_DIRS) # We have found something + set(_gss_libdir_suffixes "") - if(_gss_INCLUDE_DIRS) # jay, we have found something cmake_push_check_state() list(APPEND CMAKE_REQUIRED_INCLUDES "${_gss_INCLUDE_DIRS}") check_include_files("gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" _gss_have_mit_headers) + cmake_pop_check_state() if(_gss_have_mit_headers) - set(GSS_FLAVOUR "MIT") - else() - # Prevent compiling the header - just check if we can include it - list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D__ROKEN_H__") - check_include_file("roken.h" _gss_have_roken_h) - - check_include_file("heimdal/roken.h" _gss_have_heimdal_roken_h) - if(_gss_have_roken_h OR _gss_have_heimdal_roken_h) - set(GSS_FLAVOUR "Heimdal") - endif() - endif() - cmake_pop_check_state() - else() - # I am not convinced if this is the right way but this is what autotools do at the moment - find_path(_gss_INCLUDE_DIRS NAMES "gssapi.h" - HINTS - ${_gss_root_hints} - PATH_SUFFIXES - "include" - "inc" - ) - - if(_gss_INCLUDE_DIRS) - set(GSS_FLAVOUR "Heimdal") - else() - find_path(_gss_INCLUDE_DIRS NAMES "gss.h" - HINTS - ${_gss_root_hints} - PATH_SUFFIXES - "include" - ) - - if(_gss_INCLUDE_DIRS) - set(GSS_FLAVOUR "GNU") - set(GSS_PC_REQUIRES "gss") - endif() - endif() - endif() - - # If we have headers, check if we can link libraries - if(GSS_FLAVOUR) - set(_gss_libdir_suffixes "") - set(_gss_libdir_hints ${_gss_root_hints}) - get_filename_component(_gss_calculated_potential_root "${_gss_INCLUDE_DIRS}" DIRECTORY) - list(APPEND _gss_libdir_hints ${_gss_calculated_potential_root}) - - if(WIN32) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - list(APPEND _gss_libdir_suffixes "lib/AMD64") - if(GSS_FLAVOUR STREQUAL "GNU") - set(_gss_libname "gss") - elseif(GSS_FLAVOUR STREQUAL "MIT") + set(_gss_flavour "MIT") + if(WIN32) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + list(APPEND _gss_libdir_suffixes "lib/AMD64") set(_gss_libname "gssapi64") else() - set(_gss_libname "libgssapi") - endif() - else() - list(APPEND _gss_libdir_suffixes "lib/i386") - if(GSS_FLAVOUR STREQUAL "GNU") - set(_gss_libname "gss") - elseif(GSS_FLAVOUR STREQUAL "MIT") + list(APPEND _gss_libdir_suffixes "lib/i386") set(_gss_libname "gssapi32") - else() - set(_gss_libname "libgssapi") endif() - endif() - else() - list(APPEND _gss_libdir_suffixes "lib;lib64") # those suffixes are not checked for HINTS - if(GSS_FLAVOUR STREQUAL "GNU") - set(_gss_libname "gss") - elseif(GSS_FLAVOUR STREQUAL "MIT") - set(_gss_libname "gssapi_krb5") else() - set(_gss_libname "gssapi") + list(APPEND _gss_libdir_suffixes "lib" "lib64") # those suffixes are not checked for HINTS + set(_gss_libname "gssapi_krb5") endif() endif() + else() + find_path(_gss_INCLUDE_DIRS NAMES "gss.h" HINTS ${_gss_root_hints} PATH_SUFFIXES "include") - find_library(_gss_LIBRARIES NAMES ${_gss_libname} - HINTS - ${_gss_libdir_hints} - PATH_SUFFIXES - ${_gss_libdir_suffixes} - ) + if(_gss_INCLUDE_DIRS) + set(_gss_flavour "GNU") + set(_gss_pc_requires ${_gnu_modname}) + set(_gss_libname "gss") + endif() endif() + + # If we have headers, look up libraries + if(_gss_flavour) + set(_gss_libdir_hints ${_gss_root_hints}) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) + cmake_path(GET _gss_INCLUDE_DIRS PARENT_PATH _gss_calculated_potential_root) + else() + get_filename_component(_gss_calculated_potential_root "${_gss_INCLUDE_DIRS}" DIRECTORY) + endif() + list(APPEND _gss_libdir_hints ${_gss_calculated_potential_root}) + + find_library(_gss_LIBRARIES NAMES ${_gss_libname} HINTS ${_gss_libdir_hints} PATH_SUFFIXES ${_gss_libdir_suffixes}) + endif() + endif() + if(NOT _gss_flavour) + message(FATAL_ERROR "GNU or MIT GSS is required") endif() else() - # _gss_MODULE_NAME set since CMake 3.16 - if(_gss_MODULE_NAME STREQUAL _gnu_modname OR _gss_${_gnu_modname}_VERSION) - set(GSS_FLAVOUR "GNU") - set(GSS_PC_REQUIRES "gss") - if(NOT _gss_version) # for old CMake versions? - set(_gss_version ${_gss_${_gnu_modname}_VERSION}) - endif() - elseif(_gss_MODULE_NAME STREQUAL _mit_modname OR _gss_${_mit_modname}_VERSION) - set(GSS_FLAVOUR "MIT") - set(GSS_PC_REQUIRES "mit-krb5-gssapi") - if(NOT _gss_version) # for old CMake versions? - set(_gss_version ${_gss_${_mit_modname}_VERSION}) - endif() + # _gss_MODULE_NAME set since CMake 3.16. + # _pkg_check_modules_pkg_name is undocumented and used as a fallback for CMake <3.16 versions. + if(_gss_MODULE_NAME STREQUAL _gnu_modname OR _pkg_check_modules_pkg_name STREQUAL _gnu_modname) + set(_gss_flavour "GNU") + set(_gss_pc_requires ${_gnu_modname}) + elseif(_gss_MODULE_NAME STREQUAL _mit_modname OR _pkg_check_modules_pkg_name STREQUAL _mit_modname) + set(_gss_flavour "MIT") + set(_gss_pc_requires ${_mit_modname}) else() - set(GSS_FLAVOUR "Heimdal") - set(GSS_PC_REQUIRES "heimdal-gssapi") - if(NOT _gss_version) # for old CMake versions? - set(_gss_version ${_gss_${_heimdal_modname}_VERSION}) - endif() + message(FATAL_ERROR "GNU or MIT GSS is required") endif() - message(STATUS "Found GSS/${GSS_FLAVOUR} (via pkg-config): ${_gss_INCLUDE_DIRS} (found version \"${_gss_version}\")") + message(STATUS "Found GSS/${_gss_flavour} (via pkg-config): ${_gss_INCLUDE_DIRS} (found version \"${_gss_version}\")") endif() -string(REPLACE ";" " " _gss_CFLAGS "${_gss_CFLAGS}") - -set(GSS_INCLUDE_DIRS ${_gss_INCLUDE_DIRS}) -set(GSS_LIBRARIES ${_gss_LIBRARIES}) -set(GSS_LIBRARY_DIRS ${_gss_LIBRARY_DIRS}) -set(GSS_CFLAGS ${_gss_CFLAGS}) set(GSS_VERSION ${_gss_version}) -if(GSS_FLAVOUR) - if(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "Heimdal") - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_heimdal_manifest_file "Heimdal.Application.amd64.manifest") +if(NOT GSS_VERSION) + if(_gss_flavour STREQUAL "MIT") + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) + cmake_host_system_information(RESULT _mit_version QUERY WINDOWS_REGISTRY + "HKLM/SOFTWARE/MIT/Kerberos/SDK/CurrentVersion" VALUE "VersionString") else() - set(_heimdal_manifest_file "Heimdal.Application.x86.manifest") + get_filename_component(_mit_version + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos\\SDK\\CurrentVersion;VersionString]" NAME CACHE) endif() - - if(EXISTS "${GSS_INCLUDE_DIRS}/${_heimdal_manifest_file}") - file(STRINGS "${GSS_INCLUDE_DIRS}/${_heimdal_manifest_file}" _heimdal_version_str - REGEX "^.*version=\"[0-9]\\.[^\"]+\".*$") - - string(REGEX MATCH "[0-9]\\.[^\"]+" GSS_VERSION "${_heimdal_version_str}") - endif() - - if(NOT GSS_VERSION) - set(GSS_VERSION "Heimdal Unknown") - endif() - elseif(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "MIT") - get_filename_component(_mit_version "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos\\SDK\\CurrentVersion;VersionString]" NAME - CACHE) if(WIN32 AND _mit_version) set(GSS_VERSION "${_mit_version}") else() set(GSS_VERSION "MIT Unknown") endif() - elseif(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "GNU") - if(GSS_INCLUDE_DIRS AND EXISTS "${GSS_INCLUDE_DIRS}/gss.h") + else() # GNU + if(_gss_INCLUDE_DIRS AND EXISTS "${_gss_INCLUDE_DIRS}/gss.h") set(_version_regex "#[\t ]*define[\t ]+GSS_VERSION[\t ]+\"([^\"]*)\"") - file(STRINGS "${GSS_INCLUDE_DIRS}/gss.h" _version_str REGEX "${_version_regex}") + file(STRINGS "${_gss_INCLUDE_DIRS}/gss.h" _version_str REGEX "${_version_regex}") string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") set(GSS_VERSION "${_version_str}") unset(_version_regex) @@ -345,12 +238,12 @@ endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GSS REQUIRED_VARS - GSS_FLAVOUR - GSS_LIBRARIES + _gss_flavour + _gss_LIBRARIES VERSION_VAR GSS_VERSION FAIL_MESSAGE - "Could NOT find GSS, try to set the path to GSS root folder in the system variable GSS_ROOT_DIR" + "Could NOT find GSS, try to set the absolute path to GSS installation root directory in the environment variable GSS_ROOT_DIR" ) mark_as_advanced( @@ -363,3 +256,16 @@ mark_as_advanced( _gss_PREFIX _gss_version ) + +if(GSS_FOUND) + if(NOT TARGET CURL::gss) + add_library(CURL::gss INTERFACE IMPORTED) + set_target_properties(CURL::gss PROPERTIES + INTERFACE_CURL_GSS_FLAVOUR "${_gss_flavour}" + INTERFACE_LIBCURL_PC_MODULES "${_gss_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_gss_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_gss_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_gss_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_gss_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindGnuTLS.cmake b/CMake/FindGnuTLS.cmake new file mode 100644 index 0000000000..7f5b227dc2 --- /dev/null +++ b/CMake/FindGnuTLS.cmake @@ -0,0 +1,92 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# Find the GnuTLS library +# +# Input variables: +# +# - `GNUTLS_INCLUDE_DIR`: Absolute path to GnuTLS include directory. +# - `GNUTLS_LIBRARY`: Absolute path to `gnutls` library. +# +# Defines: +# +# - `GNUTLS_FOUND`: System has GnuTLS. +# - `GNUTLS_VERSION`: Version of GnuTLS. +# - `CURL::gnutls`: GnuTLS library target. + +set(_gnutls_pc_requires "gnutls") + +if(CURL_USE_PKGCONFIG AND + NOT DEFINED GNUTLS_INCLUDE_DIR AND + NOT DEFINED GNUTLS_LIBRARY) + find_package(PkgConfig QUIET) + pkg_check_modules(_gnutls ${_gnutls_pc_requires}) +endif() + +if(_gnutls_FOUND) + set(GnuTLS_FOUND TRUE) + set(GNUTLS_FOUND TRUE) + set(GNUTLS_VERSION ${_gnutls_VERSION}) + message(STATUS "Found GnuTLS (via pkg-config): ${_gnutls_INCLUDE_DIRS} (found version \"${GNUTLS_VERSION}\")") +else() + find_path(GNUTLS_INCLUDE_DIR NAMES "gnutls/gnutls.h") + find_library(GNUTLS_LIBRARY NAMES "gnutls" "libgnutls") + + unset(GNUTLS_VERSION CACHE) + if(GNUTLS_INCLUDE_DIR AND EXISTS "${GNUTLS_INCLUDE_DIR}/gnutls/gnutls.h") + set(_version_regex "#[\t ]*define[\t ]+GNUTLS_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${GNUTLS_INCLUDE_DIR}/gnutls/gnutls.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(GNUTLS_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) + endif() + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(GnuTLS + REQUIRED_VARS + GNUTLS_INCLUDE_DIR + GNUTLS_LIBRARY + VERSION_VAR + GNUTLS_VERSION + ) + + if(GNUTLS_FOUND) + set(_gnutls_INCLUDE_DIRS ${GNUTLS_INCLUDE_DIR}) + set(_gnutls_LIBRARIES ${GNUTLS_LIBRARY}) + endif() + + mark_as_advanced(GNUTLS_INCLUDE_DIR GNUTLS_LIBRARY) +endif() + +if(GNUTLS_FOUND) + if(NOT TARGET CURL::gnutls) + add_library(CURL::gnutls INTERFACE IMPORTED) + set_target_properties(CURL::gnutls PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_gnutls_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_gnutls_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_gnutls_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_gnutls_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_gnutls_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindLDAP.cmake b/CMake/FindLDAP.cmake index fdc6d7be94..8902b23f8d 100644 --- a/CMake/FindLDAP.cmake +++ b/CMake/FindLDAP.cmake @@ -25,36 +25,32 @@ # # Input variables: # -# - `LDAP_INCLUDE_DIR`: The ldap include directory. -# - `LDAP_LIBRARY`: Path to `ldap` library. -# - `LDAP_LBER_LIBRARY`: Path to `lber` library. +# - `LDAP_INCLUDE_DIR`: Absolute path to ldap include directory. +# - `LDAP_LIBRARY`: Absolute path to `ldap` library. +# - `LDAP_LBER_LIBRARY`: Absolute path to `lber` library. # -# Result variables: +# Defines: # # - `LDAP_FOUND`: System has ldap. -# - `LDAP_INCLUDE_DIRS`: The ldap include directories. -# - `LDAP_LIBRARIES`: The ldap library names. -# - `LDAP_LIBRARY_DIRS`: The ldap library directories. -# - `LDAP_PC_REQUIRES`: The ldap pkg-config packages. -# - `LDAP_CFLAGS`: Required compiler flags. # - `LDAP_VERSION`: Version of ldap. +# - `CURL::ldap`: ldap library target. -set(LDAP_PC_REQUIRES "ldap" "lber") +set(_ldap_pc_requires "ldap" "lber") if(CURL_USE_PKGCONFIG AND NOT DEFINED LDAP_INCLUDE_DIR AND NOT DEFINED LDAP_LIBRARY AND NOT DEFINED LDAP_LBER_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(LDAP ${LDAP_PC_REQUIRES}) + pkg_check_modules(_ldap ${_ldap_pc_requires}) endif() -if(LDAP_FOUND) - set(LDAP_VERSION "${LDAP_ldap_VERSION}") - string(REPLACE ";" " " LDAP_CFLAGS "${LDAP_CFLAGS}") - message(STATUS "Found LDAP (via pkg-config): ${LDAP_INCLUDE_DIRS} (found version \"${LDAP_VERSION}\")") +if(_ldap_FOUND) + set(LDAP_FOUND TRUE) + set(LDAP_VERSION ${_ldap_ldap_VERSION}) + message(STATUS "Found LDAP (via pkg-config): ${_ldap_INCLUDE_DIRS} (found version \"${LDAP_VERSION}\")") else() - set(LDAP_PC_REQUIRES "") # Depend on pkg-config only when found via pkg-config + set(_ldap_pc_requires "") # Depend on pkg-config only when found via pkg-config # On Apple the SDK LDAP gets picked up from # 'MacOSX.sdk/System/Library/Frameworks/LDAP.framework/Headers', which contains @@ -99,9 +95,21 @@ else() ) if(LDAP_FOUND) - set(LDAP_INCLUDE_DIRS ${LDAP_INCLUDE_DIR}) - set(LDAP_LIBRARIES ${LDAP_LIBRARY} ${LDAP_LBER_LIBRARY}) + set(_ldap_INCLUDE_DIRS ${LDAP_INCLUDE_DIR}) + set(_ldap_LIBRARIES ${LDAP_LIBRARY} ${LDAP_LBER_LIBRARY}) endif() mark_as_advanced(LDAP_INCLUDE_DIR LDAP_LIBRARY LDAP_LBER_LIBRARY) endif() + +if(LDAP_FOUND) + if(NOT TARGET CURL::ldap) + add_library(CURL::ldap INTERFACE IMPORTED) + set_target_properties(CURL::ldap PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_ldap_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_ldap_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_ldap_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_ldap_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_ldap_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindLibbacktrace.cmake b/CMake/FindLibbacktrace.cmake new file mode 100644 index 0000000000..59da7fdf19 --- /dev/null +++ b/CMake/FindLibbacktrace.cmake @@ -0,0 +1,61 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Daniel Stenberg, , et al. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +# SPDX-License-Identifier: curl +# +########################################################################### +# Find the libbacktrace library +# +# Input variables: +# +# - `LIBBACKTRACE_INCLUDE_DIR`: Absolute path to libbacktrace include directory. +# - `LIBBACKTRACE_LIBRARY`: Absolute path to `libbacktrace` library. +# +# Defines: +# +# - `LIBBACKTRACE_FOUND`: System has libbacktrace. +# - `CURL::libbacktrace`: libbacktrace library target. + +find_path(LIBBACKTRACE_INCLUDE_DIR NAMES "backtrace.h") +find_library(LIBBACKTRACE_LIBRARY NAMES "backtrace" "libbacktrace") + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libbacktrace + REQUIRED_VARS + LIBBACKTRACE_INCLUDE_DIR + LIBBACKTRACE_LIBRARY +) + +if(LIBBACKTRACE_FOUND) + set(_libbacktrace_INCLUDE_DIRS ${LIBBACKTRACE_INCLUDE_DIR}) + set(_libbacktrace_LIBRARIES ${LIBBACKTRACE_LIBRARY}) + + if(NOT TARGET CURL::libbacktrace) + add_library(CURL::libbacktrace INTERFACE IMPORTED) + set_target_properties(CURL::libbacktrace PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_libbacktrace_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_libbacktrace_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_libbacktrace_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_libbacktrace_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_libbacktrace_LIBRARIES}") + endif() +endif() + +mark_as_advanced(LIBBACKTRACE_INCLUDE_DIR LIBBACKTRACE_LIBRARY) diff --git a/CMake/FindLibgsasl.cmake b/CMake/FindLibgsasl.cmake index 878d651883..7fbaa7128f 100644 --- a/CMake/FindLibgsasl.cmake +++ b/CMake/FindLibgsasl.cmake @@ -25,32 +25,28 @@ # # Input variables: # -# - `LIBGSASL_INCLUDE_DIR`: The libgsasl include directory. -# - `LIBGSASL_LIBRARY`: Path to `libgsasl` library. +# - `LIBGSASL_INCLUDE_DIR`: Absolute path to libgsasl include directory. +# - `LIBGSASL_LIBRARY`: Absolute path to `libgsasl` library. # -# Result variables: +# Defines: # -# - `LIBGSASL_FOUND`: System has libgsasl. -# - `LIBGSASL_INCLUDE_DIRS`: The libgsasl include directories. -# - `LIBGSASL_LIBRARIES`: The libgsasl library names. -# - `LIBGSASL_LIBRARY_DIRS`: The libgsasl library directories. -# - `LIBGSASL_PC_REQUIRES`: The libgsasl pkg-config packages. -# - `LIBGSASL_CFLAGS`: Required compiler flags. -# - `LIBGSASL_VERSION`: Version of libgsasl. +# - `LIBGSASL_FOUND`: System has libgsasl. +# - `LIBGSASL_VERSION`: Version of libgsasl. +# - `CURL::libgsasl`: libgsasl library target. -set(LIBGSASL_PC_REQUIRES "libgsasl") +set(_libgsasl_pc_requires "libgsasl") if(CURL_USE_PKGCONFIG AND NOT DEFINED LIBGSASL_INCLUDE_DIR AND NOT DEFINED LIBGSASL_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(LIBGSASL ${LIBGSASL_PC_REQUIRES}) + pkg_check_modules(_libgsasl ${_libgsasl_pc_requires}) endif() -if(LIBGSASL_FOUND) +if(_libgsasl_FOUND) set(Libgsasl_FOUND TRUE) - string(REPLACE ";" " " LIBGSASL_CFLAGS "${LIBGSASL_CFLAGS}") - message(STATUS "Found Libgsasl (via pkg-config): ${LIBGSASL_INCLUDE_DIRS} (found version \"${LIBGSASL_VERSION}\")") + set(LIBGSASL_FOUND TRUE) + message(STATUS "Found Libgsasl (via pkg-config): ${_libgsasl_INCLUDE_DIRS} (found version \"${LIBGSASL_VERSION}\")") else() find_path(LIBGSASL_INCLUDE_DIR NAMES "gsasl.h") find_library(LIBGSASL_LIBRARY NAMES "gsasl" "libgsasl") @@ -75,9 +71,21 @@ else() ) if(LIBGSASL_FOUND) - set(LIBGSASL_INCLUDE_DIRS ${LIBGSASL_INCLUDE_DIR}) - set(LIBGSASL_LIBRARIES ${LIBGSASL_LIBRARY}) + set(_libgsasl_INCLUDE_DIRS ${LIBGSASL_INCLUDE_DIR}) + set(_libgsasl_LIBRARIES ${LIBGSASL_LIBRARY}) endif() mark_as_advanced(LIBGSASL_INCLUDE_DIR LIBGSASL_LIBRARY) endif() + +if(LIBGSASL_FOUND) + if(NOT TARGET CURL::libgsasl) + add_library(CURL::libgsasl INTERFACE IMPORTED) + set_target_properties(CURL::libgsasl PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_libgsasl_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_libgsasl_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_libgsasl_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_libgsasl_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_libgsasl_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindLibidn2.cmake b/CMake/FindLibidn2.cmake index f8f00f0c79..dc7873489a 100644 --- a/CMake/FindLibidn2.cmake +++ b/CMake/FindLibidn2.cmake @@ -25,32 +25,29 @@ # # Input variables: # -# - `LIBIDN2_INCLUDE_DIR`: The libidn2 include directory. -# - `LIBIDN2_LIBRARY`: Path to `libidn2` library. +# - `LIBIDN2_INCLUDE_DIR`: Absolute path to libidn2 include directory. +# - `LIBIDN2_LIBRARY`: Absolute path to `libidn2` library. # -# Result variables: +# Defines: # -# - `LIBIDN2_FOUND`: System has libidn2. -# - `LIBIDN2_INCLUDE_DIRS`: The libidn2 include directories. -# - `LIBIDN2_LIBRARIES`: The libidn2 library names. -# - `LIBIDN2_LIBRARY_DIRS`: The libidn2 library directories. -# - `LIBIDN2_PC_REQUIRES`: The libidn2 pkg-config packages. -# - `LIBIDN2_CFLAGS`: Required compiler flags. -# - `LIBIDN2_VERSION`: Version of libidn2. +# - `LIBIDN2_FOUND`: System has libidn2. +# - `LIBIDN2_VERSION`: Version of libidn2. +# - `CURL::libidn2`: libidn2 library target. -set(LIBIDN2_PC_REQUIRES "libidn2") +set(_libidn2_pc_requires "libidn2") if(CURL_USE_PKGCONFIG AND NOT DEFINED LIBIDN2_INCLUDE_DIR AND NOT DEFINED LIBIDN2_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(LIBIDN2 ${LIBIDN2_PC_REQUIRES}) + pkg_check_modules(_libidn2 ${_libidn2_pc_requires}) endif() -if(LIBIDN2_FOUND) +if(_libidn2_FOUND) set(Libidn2_FOUND TRUE) - string(REPLACE ";" " " LIBIDN2_CFLAGS "${LIBIDN2_CFLAGS}") - message(STATUS "Found Libidn2 (via pkg-config): ${LIBIDN2_INCLUDE_DIRS} (found version \"${LIBIDN2_VERSION}\")") + set(LIBIDN2_FOUND TRUE) + set(LIBIDN2_VERSION ${_libidn2_VERSION}) + message(STATUS "Found Libidn2 (via pkg-config): ${_libidn2_INCLUDE_DIRS} (found version \"${LIBIDN2_VERSION}\")") else() find_path(LIBIDN2_INCLUDE_DIR NAMES "idn2.h") find_library(LIBIDN2_LIBRARY NAMES "idn2" "libidn2") @@ -75,9 +72,21 @@ else() ) if(LIBIDN2_FOUND) - set(LIBIDN2_INCLUDE_DIRS ${LIBIDN2_INCLUDE_DIR}) - set(LIBIDN2_LIBRARIES ${LIBIDN2_LIBRARY}) + set(_libidn2_INCLUDE_DIRS ${LIBIDN2_INCLUDE_DIR}) + set(_libidn2_LIBRARIES ${LIBIDN2_LIBRARY}) endif() mark_as_advanced(LIBIDN2_INCLUDE_DIR LIBIDN2_LIBRARY) endif() + +if(LIBIDN2_FOUND) + if(NOT TARGET CURL::libidn2) + add_library(CURL::libidn2 INTERFACE IMPORTED) + set_target_properties(CURL::libidn2 PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_libidn2_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_libidn2_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_libidn2_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_libidn2_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_libidn2_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindLibpsl.cmake b/CMake/FindLibpsl.cmake index d6fde4b2d0..2287e676d3 100644 --- a/CMake/FindLibpsl.cmake +++ b/CMake/FindLibpsl.cmake @@ -25,32 +25,29 @@ # # Input variables: # -# - `LIBPSL_INCLUDE_DIR`: The libpsl include directory. -# - `LIBPSL_LIBRARY`: Path to `libpsl` library. +# - `LIBPSL_INCLUDE_DIR`: Absolute path to libpsl include directory. +# - `LIBPSL_LIBRARY`: Absolute path to `libpsl` library. # -# Result variables: +# Defines: # -# - `LIBPSL_FOUND`: System has libpsl. -# - `LIBPSL_INCLUDE_DIRS`: The libpsl include directories. -# - `LIBPSL_LIBRARIES`: The libpsl library names. -# - `LIBPSL_LIBRARY_DIRS`: The libpsl library directories. -# - `LIBPSL_PC_REQUIRES`: The libpsl pkg-config packages. -# - `LIBPSL_CFLAGS`: Required compiler flags. -# - `LIBPSL_VERSION`: Version of libpsl. +# - `LIBPSL_FOUND`: System has libpsl. +# - `LIBPSL_VERSION`: Version of libpsl. +# - `CURL::libpsl`: libpsl library target. -set(LIBPSL_PC_REQUIRES "libpsl") +set(_libpsl_pc_requires "libpsl") if(CURL_USE_PKGCONFIG AND NOT DEFINED LIBPSL_INCLUDE_DIR AND NOT DEFINED LIBPSL_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(LIBPSL ${LIBPSL_PC_REQUIRES}) + pkg_check_modules(_libpsl ${_libpsl_pc_requires}) endif() -if(LIBPSL_FOUND AND LIBPSL_INCLUDE_DIRS) +if(_libpsl_FOUND AND _libpsl_INCLUDE_DIRS) set(Libpsl_FOUND TRUE) - string(REPLACE ";" " " LIBPSL_CFLAGS "${LIBPSL_CFLAGS}") - message(STATUS "Found Libpsl (via pkg-config): ${LIBPSL_INCLUDE_DIRS} (found version \"${LIBPSL_VERSION}\")") + set(LIBPSL_FOUND TRUE) + set(LIBPSL_VERSION ${_libpsl_VERSION}) + message(STATUS "Found Libpsl (via pkg-config): ${_libpsl_INCLUDE_DIRS} (found version \"${LIBPSL_VERSION}\")") else() find_path(LIBPSL_INCLUDE_DIR NAMES "libpsl.h") find_library(LIBPSL_LIBRARY NAMES "psl" "libpsl") @@ -75,9 +72,21 @@ else() ) if(LIBPSL_FOUND) - set(LIBPSL_INCLUDE_DIRS ${LIBPSL_INCLUDE_DIR}) - set(LIBPSL_LIBRARIES ${LIBPSL_LIBRARY}) + set(_libpsl_INCLUDE_DIRS ${LIBPSL_INCLUDE_DIR}) + set(_libpsl_LIBRARIES ${LIBPSL_LIBRARY}) endif() mark_as_advanced(LIBPSL_INCLUDE_DIR LIBPSL_LIBRARY) endif() + +if(LIBPSL_FOUND) + if(NOT TARGET CURL::libpsl) + add_library(CURL::libpsl INTERFACE IMPORTED) + set_target_properties(CURL::libpsl PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_libpsl_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_libpsl_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_libpsl_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_libpsl_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_libpsl_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindLibrtmp.cmake b/CMake/FindLibrtmp.cmake deleted file mode 100644 index 50fc9692bd..0000000000 --- a/CMake/FindLibrtmp.cmake +++ /dev/null @@ -1,103 +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 -# -########################################################################### -# Find the librtmp library -# -# Input variables: -# -# - `LIBRTMP_INCLUDE_DIR`: The librtmp include directory. -# - `LIBRTMP_LIBRARY`: Path to `librtmp` library. -# -# Result variables: -# -# - `LIBRTMP_FOUND`: System has librtmp. -# - `LIBRTMP_INCLUDE_DIRS`: The librtmp include directories. -# - `LIBRTMP_LIBRARIES`: The librtmp library names. -# - `LIBRTMP_LIBRARY_DIRS`: The librtmp library directories. -# - `LIBRTMP_PC_REQUIRES`: The librtmp pkg-config packages. -# - `LIBRTMP_CFLAGS`: Required compiler flags. -# - `LIBRTMP_VERSION`: Version of librtmp. - -set(LIBRTMP_PC_REQUIRES "librtmp") - -if(CURL_USE_PKGCONFIG AND - NOT DEFINED LIBRTMP_INCLUDE_DIR AND - NOT DEFINED LIBRTMP_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(LIBRTMP ${LIBRTMP_PC_REQUIRES}) -endif() - -if(LIBRTMP_FOUND AND LIBRTMP_INCLUDE_DIRS) - set(Librtmp_FOUND TRUE) - string(REPLACE ";" " " LIBRTMP_CFLAGS "${LIBRTMP_CFLAGS}") - message(STATUS "Found Librtmp (via pkg-config): ${LIBRTMP_INCLUDE_DIRS} (found version \"${LIBRTMP_VERSION}\")") -else() - find_path(LIBRTMP_INCLUDE_DIR NAMES "librtmp/rtmp.h") - find_library(LIBRTMP_LIBRARY NAMES "rtmp") - - unset(LIBRTMP_VERSION CACHE) - if(LIBRTMP_INCLUDE_DIR AND EXISTS "${LIBRTMP_INCLUDE_DIR}/librtmp/rtmp.h") - set(_version_regex "#[\t ]*define[\t ]+RTMP_LIB_VERSION[\t ]+0x([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F]).*") - file(STRINGS "${LIBRTMP_INCLUDE_DIR}/librtmp/rtmp.h" _version_str REGEX "${_version_regex}") - string(REGEX REPLACE "${_version_regex}" "\\1" _version_str1 "${_version_str}") - string(REGEX REPLACE "${_version_regex}" "\\2" _version_str2 "${_version_str}") - if(CMAKE_VERSION VERSION_LESS 3.13) - # No support for hex version numbers, just strip leading zeroes - string(REGEX REPLACE "^0" "" _version_str1 "${_version_str1}") - string(REGEX REPLACE "^0" "" _version_str2 "${_version_str2}") - else() - math(EXPR _version_str1 "0x${_version_str1}" OUTPUT_FORMAT DECIMAL) - math(EXPR _version_str2 "0x${_version_str2}" OUTPUT_FORMAT DECIMAL) - endif() - set(LIBRTMP_VERSION "${_version_str1}.${_version_str2}") - unset(_version_regex) - unset(_version_str1) - unset(_version_str2) - endif() - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(Librtmp - REQUIRED_VARS - LIBRTMP_INCLUDE_DIR - LIBRTMP_LIBRARY - VERSION_VAR - LIBRTMP_VERSION - ) - - if(LIBRTMP_FOUND) - set(LIBRTMP_INCLUDE_DIRS ${LIBRTMP_INCLUDE_DIR}) - set(LIBRTMP_LIBRARIES ${LIBRTMP_LIBRARY}) - endif() - - mark_as_advanced(LIBRTMP_INCLUDE_DIR LIBRTMP_LIBRARY) - - # Necessary when linking a static librtmp - find_package(OpenSSL) - if(OPENSSL_FOUND) - list(APPEND LIBRTMP_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) - endif() -endif() - -if(LIBRTMP_FOUND AND WIN32) - list(APPEND LIBRTMP_LIBRARIES "winmm") -endif() diff --git a/CMake/FindLibssh.cmake b/CMake/FindLibssh.cmake index e2b27d975c..3837d4de62 100644 --- a/CMake/FindLibssh.cmake +++ b/CMake/FindLibssh.cmake @@ -25,35 +25,44 @@ # # Input variables: # -# - `LIBSSH_INCLUDE_DIR`: The libssh include directory. -# - `LIBSSH_LIBRARY`: Path to libssh library. +# - `LIBSSH_INCLUDE_DIR`: Absolute path to libssh include directory. +# - `LIBSSH_LIBRARY`: Absolute path to `libssh` library. +# - `LIBSSH_USE_STATIC_LIBS`: Configure for static libssh libraries. # -# Result variables: +# Defines: # -# - `LIBSSH_FOUND`: System has libssh. -# - `LIBSSH_INCLUDE_DIRS`: The libssh include directories. -# - `LIBSSH_LIBRARIES`: The libssh library names. -# - `LIBSSH_LIBRARY_DIRS`: The libssh library directories. -# - `LIBSSH_PC_REQUIRES`: The libssh pkg-config packages. -# - `LIBSSH_CFLAGS`: Required compiler flags. -# - `LIBSSH_VERSION`: Version of libssh. +# - `LIBSSH_FOUND`: System has libssh. +# - `LIBSSH_VERSION`: Version of libssh. +# - `CURL::libssh`: libssh library target. -set(LIBSSH_PC_REQUIRES "libssh") +set(_libssh_pc_requires "libssh") if(CURL_USE_PKGCONFIG AND NOT DEFINED LIBSSH_INCLUDE_DIR AND NOT DEFINED LIBSSH_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(LIBSSH ${LIBSSH_PC_REQUIRES}) + pkg_check_modules(_libssh ${_libssh_pc_requires}) endif() -if(LIBSSH_FOUND) +if(_libssh_FOUND) set(Libssh_FOUND TRUE) - string(REPLACE ";" " " LIBSSH_CFLAGS "${LIBSSH_CFLAGS}") - message(STATUS "Found Libssh (via pkg-config): ${LIBSSH_INCLUDE_DIRS} (found version \"${LIBSSH_VERSION}\")") + set(LIBSSH_FOUND TRUE) + set(LIBSSH_VERSION ${_libssh_VERSION}) + if(LIBSSH_USE_STATIC_LIBS) + set(_libssh_CFLAGS "${_libssh_STATIC_CFLAGS}") + set(_libssh_INCLUDE_DIRS "${_libssh_STATIC_INCLUDE_DIRS}") + set(_libssh_LIBRARY_DIRS "${_libssh_STATIC_LIBRARY_DIRS}") + set(_libssh_LIBRARIES "${_libssh_STATIC_LIBRARIES}") + endif() + message(STATUS "Found Libssh (via pkg-config): ${_libssh_INCLUDE_DIRS} (found version \"${LIBSSH_VERSION}\")") else() find_path(LIBSSH_INCLUDE_DIR NAMES "libssh/libssh.h") - find_library(LIBSSH_LIBRARY NAMES "ssh" "libssh") + if(LIBSSH_USE_STATIC_LIBS) + set(_libssh_CFLAGS "-DLIBSSH_STATIC") + find_library(LIBSSH_LIBRARY NAMES "ssh_static" "libssh_static" "ssh" "libssh") + else() + find_library(LIBSSH_LIBRARY NAMES "ssh" "libssh") + endif() unset(LIBSSH_VERSION CACHE) if(LIBSSH_INCLUDE_DIR AND EXISTS "${LIBSSH_INCLUDE_DIR}/libssh/libssh_version.h") @@ -85,13 +94,25 @@ else() ) if(LIBSSH_FOUND) - set(LIBSSH_INCLUDE_DIRS ${LIBSSH_INCLUDE_DIR}) - set(LIBSSH_LIBRARIES ${LIBSSH_LIBRARY}) + set(_libssh_INCLUDE_DIRS ${LIBSSH_INCLUDE_DIR}) + set(_libssh_LIBRARIES ${LIBSSH_LIBRARY}) endif() mark_as_advanced(LIBSSH_INCLUDE_DIR LIBSSH_LIBRARY) endif() -if(LIBSSH_FOUND AND WIN32) - list(APPEND LIBSSH_LIBRARIES "iphlpapi") # for if_nametoindex +if(LIBSSH_FOUND) + if(WIN32) + list(APPEND _libssh_LIBRARIES "iphlpapi") # for if_nametoindex + endif() + + if(NOT TARGET CURL::libssh) + add_library(CURL::libssh INTERFACE IMPORTED) + set_target_properties(CURL::libssh PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_libssh_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_libssh_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_libssh_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_libssh_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_libssh_LIBRARIES}") + endif() endif() diff --git a/CMake/FindLibssh2.cmake b/CMake/FindLibssh2.cmake index 0b81ecb3cf..22481d67ae 100644 --- a/CMake/FindLibssh2.cmake +++ b/CMake/FindLibssh2.cmake @@ -25,35 +25,57 @@ # # Input variables: # -# - `LIBSSH2_INCLUDE_DIR`: The libssh2 include directory. -# - `LIBSSH2_LIBRARY`: Path to `libssh2` library. +# - `LIBSSH2_INCLUDE_DIR`: Absolute path to libssh2 include directory. +# - `LIBSSH2_LIBRARY`: Absolute path to `libssh2` library. +# - `LIBSSH2_USE_STATIC_LIBS`: Configure for static libssh2 libraries. # -# Result variables: +# Defines: # -# - `LIBSSH2_FOUND`: System has libssh2. -# - `LIBSSH2_INCLUDE_DIRS`: The libssh2 include directories. -# - `LIBSSH2_LIBRARIES`: The libssh2 library names. -# - `LIBSSH2_LIBRARY_DIRS`: The libssh2 library directories. -# - `LIBSSH2_PC_REQUIRES`: The libssh2 pkg-config packages. -# - `LIBSSH2_CFLAGS`: Required compiler flags. -# - `LIBSSH2_VERSION`: Version of libssh2. +# - `LIBSSH2_FOUND`: System has libssh2. +# - `LIBSSH2_VERSION`: Version of libssh2. +# - `CURL::libssh2`: libssh2 library target. -set(LIBSSH2_PC_REQUIRES "libssh2") +set(_libssh2_pc_requires "libssh2") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED LIBSSH2_INCLUDE_DIR AND +if(NOT DEFINED LIBSSH2_INCLUDE_DIR AND NOT DEFINED LIBSSH2_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(LIBSSH2 ${LIBSSH2_PC_REQUIRES}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_libssh2 ${_libssh2_pc_requires}) + endif() + if(NOT _libssh2_FOUND AND CURL_USE_CMAKECONFIG) + find_package(libssh2 CONFIG QUIET) + endif() endif() -if(LIBSSH2_FOUND AND LIBSSH2_INCLUDE_DIRS) +if(_libssh2_FOUND AND _libssh2_INCLUDE_DIRS) set(Libssh2_FOUND TRUE) - string(REPLACE ";" " " LIBSSH2_CFLAGS "${LIBSSH2_CFLAGS}") - message(STATUS "Found Libssh2 (via pkg-config): ${LIBSSH2_INCLUDE_DIRS} (found version \"${LIBSSH2_VERSION}\")") + set(LIBSSH2_FOUND TRUE) + set(LIBSSH2_VERSION ${_libssh2_VERSION}) + if(LIBSSH2_USE_STATIC_LIBS) + set(_libssh2_CFLAGS "${_libssh2_STATIC_CFLAGS}") + set(_libssh2_INCLUDE_DIRS "${_libssh2_STATIC_INCLUDE_DIRS}") + set(_libssh2_LIBRARY_DIRS "${_libssh2_STATIC_LIBRARY_DIRS}") + set(_libssh2_LIBRARIES "${_libssh2_STATIC_LIBRARIES}") + endif() + message(STATUS "Found Libssh2 (via pkg-config): ${_libssh2_INCLUDE_DIRS} (found version \"${LIBSSH2_VERSION}\")") +elseif(libssh2_CONFIG) + set(Libssh2_FOUND TRUE) + set(LIBSSH2_FOUND TRUE) + set(LIBSSH2_VERSION ${libssh2_VERSION}) + if(LIBSSH2_USE_STATIC_LIBS) + set(_libssh2_LIBRARIES libssh2::libssh2_static) + else() + set(_libssh2_LIBRARIES libssh2::libssh2) + endif() + message(STATUS "Found Libssh2 (via CMake Config): ${libssh2_CONFIG} (found version \"${LIBSSH2_VERSION}\")") else() find_path(LIBSSH2_INCLUDE_DIR NAMES "libssh2.h") - find_library(LIBSSH2_LIBRARY NAMES "ssh2" "libssh2") + if(LIBSSH2_USE_STATIC_LIBS) + find_library(LIBSSH2_LIBRARY NAMES "ssh2_static" "libssh2_static" "ssh2" "libssh2") + else() + find_library(LIBSSH2_LIBRARY NAMES "ssh2" "libssh2") + endif() unset(LIBSSH2_VERSION CACHE) if(LIBSSH2_INCLUDE_DIR AND EXISTS "${LIBSSH2_INCLUDE_DIR}/libssh2.h") @@ -75,9 +97,21 @@ else() ) if(LIBSSH2_FOUND) - set(LIBSSH2_INCLUDE_DIRS ${LIBSSH2_INCLUDE_DIR}) - set(LIBSSH2_LIBRARIES ${LIBSSH2_LIBRARY}) + set(_libssh2_INCLUDE_DIRS ${LIBSSH2_INCLUDE_DIR}) + set(_libssh2_LIBRARIES ${LIBSSH2_LIBRARY}) endif() mark_as_advanced(LIBSSH2_INCLUDE_DIR LIBSSH2_LIBRARY) endif() + +if(LIBSSH2_FOUND) + if(NOT TARGET CURL::libssh2) + add_library(CURL::libssh2 INTERFACE IMPORTED) + set_target_properties(CURL::libssh2 PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_libssh2_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_libssh2_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_libssh2_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_libssh2_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_libssh2_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindLibuv.cmake b/CMake/FindLibuv.cmake index b16b3554f6..47d043cff6 100644 --- a/CMake/FindLibuv.cmake +++ b/CMake/FindLibuv.cmake @@ -25,32 +25,29 @@ # # Input variables: # -# - `LIBUV_INCLUDE_DIR`: The libuv include directory. -# - `LIBUV_LIBRARY`: Path to `libuv` library. +# - `LIBUV_INCLUDE_DIR`: Absolute path to libuv include directory. +# - `LIBUV_LIBRARY`: Absolute path to `libuv` library. # -# Result variables: +# Defines: # -# - `LIBUV_FOUND`: System has libuv. -# - `LIBUV_INCLUDE_DIRS`: The libuv include directories. -# - `LIBUV_LIBRARIES`: The libuv library names. -# - `LIBUV_LIBRARY_DIRS`: The libuv library directories. -# - `LIBUV_PC_REQUIRES`: The libuv pkg-config packages. -# - `LIBUV_CFLAGS`: Required compiler flags. -# - `LIBUV_VERSION`: Version of libuv. +# - `LIBUV_FOUND`: System has libuv. +# - `LIBUV_VERSION`: Version of libuv. +# - `CURL::libuv`: libuv library target. -set(LIBUV_PC_REQUIRES "libuv") +set(_libuv_pc_requires "libuv") if(CURL_USE_PKGCONFIG AND NOT DEFINED LIBUV_INCLUDE_DIR AND NOT DEFINED LIBUV_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(LIBUV ${LIBUV_PC_REQUIRES}) + pkg_check_modules(_libuv ${_libuv_pc_requires}) endif() -if(LIBUV_FOUND) +if(_libuv_FOUND) set(Libuv_FOUND TRUE) - string(REPLACE ";" " " LIBUV_CFLAGS "${LIBUV_CFLAGS}") - message(STATUS "Found Libuv (via pkg-config): ${LIBUV_INCLUDE_DIRS} (found version \"${LIBUV_VERSION}\")") + set(LIBUV_FOUND TRUE) + set(LIBUV_VERSION ${_libuv_VERSION}) + message(STATUS "Found Libuv (via pkg-config): ${_libuv_INCLUDE_DIRS} (found version \"${LIBUV_VERSION}\")") else() find_path(LIBUV_INCLUDE_DIR NAMES "uv.h") find_library(LIBUV_LIBRARY NAMES "uv" "libuv") @@ -85,9 +82,21 @@ else() ) if(LIBUV_FOUND) - set(LIBUV_INCLUDE_DIRS ${LIBUV_INCLUDE_DIR}) - set(LIBUV_LIBRARIES ${LIBUV_LIBRARY}) + set(_libuv_INCLUDE_DIRS ${LIBUV_INCLUDE_DIR}) + set(_libuv_LIBRARIES ${LIBUV_LIBRARY}) endif() mark_as_advanced(LIBUV_INCLUDE_DIR LIBUV_LIBRARY) endif() + +if(LIBUV_FOUND) + if(NOT TARGET CURL::libuv) + add_library(CURL::libuv INTERFACE IMPORTED) + set_target_properties(CURL::libuv PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_libuv_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_libuv_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_libuv_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_libuv_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_libuv_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindMbedTLS.cmake b/CMake/FindMbedTLS.cmake index 4b5a6121f1..21a5f4aec7 100644 --- a/CMake/FindMbedTLS.cmake +++ b/CMake/FindMbedTLS.cmake @@ -25,20 +25,17 @@ # # Input variables: # -# - `MBEDTLS_INCLUDE_DIR`: The mbedTLS include directory. -# - `MBEDTLS_LIBRARY`: Path to `mbedtls` library. -# - `MBEDX509_LIBRARY`: Path to `mbedx509` library. -# - `MBEDCRYPTO_LIBRARY`: Path to `mbedcrypto` library. +# - `MBEDTLS_INCLUDE_DIR`: Absolute path to mbedTLS include directory. +# - `MBEDTLS_LIBRARY`: Absolute path to `mbedtls` library. +# - `MBEDX509_LIBRARY`: Absolute path to `mbedx509` library. +# - `MBEDCRYPTO_LIBRARY`: Absolute path to `mbedcrypto` library. +# - `MBEDTLS_USE_STATIC_LIBS`: Configure for static mbedTLS libraries. # -# Result variables: +# Defines: # -# - `MBEDTLS_FOUND`: System has mbedTLS. -# - `MBEDTLS_INCLUDE_DIRS`: The mbedTLS include directories. -# - `MBEDTLS_LIBRARIES`: The mbedTLS library names. -# - `MBEDTLS_LIBRARY_DIRS`: The mbedTLS library directories. -# - `MBEDTLS_PC_REQUIRES`: The mbedTLS pkg-config packages. -# - `MBEDTLS_CFLAGS`: Required compiler flags. -# - `MBEDTLS_VERSION`: Version of mbedTLS. +# - `MBEDTLS_FOUND`: System has mbedTLS. +# - `MBEDTLS_VERSION`: Version of mbedTLS. +# - `CURL::mbedtls`: mbedTLS library target. if(DEFINED MBEDTLS_INCLUDE_DIRS AND NOT DEFINED MBEDTLS_INCLUDE_DIR) message(WARNING "MBEDTLS_INCLUDE_DIRS is deprecated, use MBEDTLS_INCLUDE_DIR instead.") @@ -46,29 +43,57 @@ if(DEFINED MBEDTLS_INCLUDE_DIRS AND NOT DEFINED MBEDTLS_INCLUDE_DIR) unset(MBEDTLS_INCLUDE_DIRS) endif() -set(MBEDTLS_PC_REQUIRES "mbedtls" "mbedx509" "mbedcrypto") +set(_mbedtls_pc_requires "mbedtls" "mbedx509" "mbedcrypto") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED MBEDTLS_INCLUDE_DIR AND +if(NOT DEFINED MBEDTLS_INCLUDE_DIR AND NOT DEFINED MBEDTLS_LIBRARY AND NOT DEFINED MBEDX509_LIBRARY AND NOT DEFINED MBEDCRYPTO_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(MBEDTLS ${MBEDTLS_PC_REQUIRES}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_mbedtls ${_mbedtls_pc_requires}) + endif() + if(NOT _mbedtls_FOUND AND CURL_USE_CMAKECONFIG) + find_package(MbedTLS CONFIG QUIET) + endif() endif() -if(MBEDTLS_FOUND) +if(_mbedtls_FOUND) set(MbedTLS_FOUND TRUE) - set(MBEDTLS_VERSION "${MBEDTLS_mbedtls_VERSION}") - string(REPLACE ";" " " MBEDTLS_CFLAGS "${MBEDTLS_CFLAGS}") - message(STATUS "Found MbedTLS (via pkg-config): ${MBEDTLS_INCLUDE_DIRS} (found version \"${MBEDTLS_VERSION}\")") + set(MBEDTLS_FOUND TRUE) + set(MBEDTLS_VERSION ${_mbedtls_mbedtls_VERSION}) + if(MBEDTLS_USE_STATIC_LIBS) + set(_mbedtls_CFLAGS "${_mbedtls_STATIC_CFLAGS}") + set(_mbedtls_INCLUDE_DIRS "${_mbedtls_STATIC_INCLUDE_DIRS}") + set(_mbedtls_LIBRARY_DIRS "${_mbedtls_STATIC_LIBRARY_DIRS}") + set(_mbedtls_LIBRARIES "${_mbedtls_STATIC_LIBRARIES}") + endif() + message(STATUS "Found MbedTLS (via pkg-config): ${_mbedtls_INCLUDE_DIRS} (found version \"${MBEDTLS_VERSION}\")") +elseif(MbedTLS_CONFIG) + set(MbedTLS_FOUND TRUE) + set(MBEDTLS_FOUND TRUE) + set(MBEDTLS_VERSION ${MbedTLS_VERSION}) + if(MBEDTLS_VERSION GREATER_EQUAL 4.0.0) + set(_mbedtls_LIBRARIES MbedTLS::tfpsacrypto) + else() + set(_mbedtls_LIBRARIES MbedTLS::mbedcrypto) + endif() + list(APPEND _mbedtls_LIBRARIES MbedTLS::mbedx509 MbedTLS::mbedtls) + message(STATUS "Found MbedTLS (via CMake Config): ${MbedTLS_CONFIG} (found version \"${MBEDTLS_VERSION}\")") else() - set(MBEDTLS_PC_REQUIRES "") # Depend on pkg-config only when found via pkg-config + set(_mbedtls_pc_requires "") # Depend on pkg-config only when found via pkg-config find_path(MBEDTLS_INCLUDE_DIR NAMES "mbedtls/ssl.h") - find_library(MBEDTLS_LIBRARY NAMES "mbedtls" "libmbedtls") - find_library(MBEDX509_LIBRARY NAMES "mbedx509" "libmbedx509") - find_library(MBEDCRYPTO_LIBRARY NAMES "mbedcrypto" "libmbedcrypto") + if(MBEDTLS_USE_STATIC_LIBS) + find_library(MBEDTLS_LIBRARY NAMES "mbedtls_static" "libmbedtls_static" "mbedtls" "libmbedtls") + find_library(MBEDX509_LIBRARY NAMES "mbedx509_static" "libmbedx509_static" "mbedx509" "libmbedx509") + find_library(MBEDCRYPTO_LIBRARY NAMES "mbedcrypto_static" "libmbedcrypto_static" "mbedcrypto" "libmbedcrypto" + "tfpsacrypto_static" "libtfpsacrypto_static" "tfpsacrypto" "libtfpsacrypto") + else() + find_library(MBEDTLS_LIBRARY NAMES "mbedtls" "libmbedtls") + find_library(MBEDX509_LIBRARY NAMES "mbedx509" "libmbedx509") + find_library(MBEDCRYPTO_LIBRARY NAMES "mbedcrypto" "libmbedcrypto" "tfpsacrypto" "libtfpsacrypto") + endif() unset(MBEDTLS_VERSION CACHE) if(MBEDTLS_INCLUDE_DIR AND EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h") @@ -92,9 +117,21 @@ else() ) if(MBEDTLS_FOUND) - set(MBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) - set(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDX509_LIBRARY} ${MBEDCRYPTO_LIBRARY}) + set(_mbedtls_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) + set(_mbedtls_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDX509_LIBRARY} ${MBEDCRYPTO_LIBRARY}) endif() mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) endif() + +if(MBEDTLS_FOUND) + if(NOT TARGET CURL::mbedtls) + add_library(CURL::mbedtls INTERFACE IMPORTED) + set_target_properties(CURL::mbedtls PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_mbedtls_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_mbedtls_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_mbedtls_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_mbedtls_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_mbedtls_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindNGHTTP2.cmake b/CMake/FindNGHTTP2.cmake index b8f37fdaeb..f93113f404 100644 --- a/CMake/FindNGHTTP2.cmake +++ b/CMake/FindNGHTTP2.cmake @@ -25,34 +25,56 @@ # # Input variables: # -# - `NGHTTP2_INCLUDE_DIR`: The nghttp2 include directory. -# - `NGHTTP2_LIBRARY`: Path to `nghttp2` library. +# - `NGHTTP2_INCLUDE_DIR`: Absolute path to nghttp2 include directory. +# - `NGHTTP2_LIBRARY`: Absolute path to `nghttp2` library. +# - `NGHTTP2_USE_STATIC_LIBS`: Configure for static nghttp2 libraries. # -# Result variables: +# Defines: # -# - `NGHTTP2_FOUND`: System has nghttp2. -# - `NGHTTP2_INCLUDE_DIRS`: The nghttp2 include directories. -# - `NGHTTP2_LIBRARIES`: The nghttp2 library names. -# - `NGHTTP2_LIBRARY_DIRS`: The nghttp2 library directories. -# - `NGHTTP2_PC_REQUIRES`: The nghttp2 pkg-config packages. -# - `NGHTTP2_CFLAGS`: Required compiler flags. -# - `NGHTTP2_VERSION`: Version of nghttp2. +# - `NGHTTP2_FOUND`: System has nghttp2. +# - `NGHTTP2_VERSION`: Version of nghttp2. +# - `CURL::nghttp2`: nghttp2 library target. -set(NGHTTP2_PC_REQUIRES "libnghttp2") +set(_nghttp2_pc_requires "libnghttp2") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED NGHTTP2_INCLUDE_DIR AND +if(NOT DEFINED NGHTTP2_INCLUDE_DIR AND NOT DEFINED NGHTTP2_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(NGHTTP2 ${NGHTTP2_PC_REQUIRES}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_nghttp2 ${_nghttp2_pc_requires}) + endif() + if(NOT _nghttp2_FOUND AND CURL_USE_CMAKECONFIG) + find_package(nghttp2 CONFIG QUIET) + endif() endif() -if(NGHTTP2_FOUND) - string(REPLACE ";" " " NGHTTP2_CFLAGS "${NGHTTP2_CFLAGS}") - message(STATUS "Found NGHTTP2 (via pkg-config): ${NGHTTP2_INCLUDE_DIRS} (found version \"${NGHTTP2_VERSION}\")") +if(_nghttp2_FOUND) + set(NGHTTP2_FOUND TRUE) + set(NGHTTP2_VERSION ${_nghttp2_VERSION}) + if(NGHTTP2_USE_STATIC_LIBS) + set(_nghttp2_CFLAGS "${_nghttp2_STATIC_CFLAGS}") + set(_nghttp2_INCLUDE_DIRS "${_nghttp2_STATIC_INCLUDE_DIRS}") + set(_nghttp2_LIBRARY_DIRS "${_nghttp2_STATIC_LIBRARY_DIRS}") + set(_nghttp2_LIBRARIES "${_nghttp2_STATIC_LIBRARIES}") + endif() + message(STATUS "Found NGHTTP2 (via pkg-config): ${_nghttp2_INCLUDE_DIRS} (found version \"${NGHTTP2_VERSION}\")") +elseif(nghttp2_CONFIG) + set(NGHTTP2_FOUND TRUE) + set(NGHTTP2_VERSION ${nghttp2_VERSION}) + if(NGHTTP2_USE_STATIC_LIBS) + set(_nghttp2_LIBRARIES nghttp2::nghttp2_static) + else() + set(_nghttp2_LIBRARIES nghttp2::nghttp2) + endif() + message(STATUS "Found NGHTTP2 (via CMake Config): ${nghttp2_CONFIG} (found version \"${NGHTTP2_VERSION}\")") else() find_path(NGHTTP2_INCLUDE_DIR NAMES "nghttp2/nghttp2.h") - find_library(NGHTTP2_LIBRARY NAMES "nghttp2" "nghttp2_static") + if(NGHTTP2_USE_STATIC_LIBS) + set(_nghttp2_CFLAGS "-DNGHTTP2_STATICLIB") + find_library(NGHTTP2_LIBRARY NAMES "nghttp2_static" "nghttp2") + else() + find_library(NGHTTP2_LIBRARY NAMES "nghttp2" "nghttp2_static") + endif() unset(NGHTTP2_VERSION CACHE) if(NGHTTP2_INCLUDE_DIR AND EXISTS "${NGHTTP2_INCLUDE_DIR}/nghttp2/nghttp2ver.h") @@ -74,9 +96,21 @@ else() ) if(NGHTTP2_FOUND) - set(NGHTTP2_INCLUDE_DIRS ${NGHTTP2_INCLUDE_DIR}) - set(NGHTTP2_LIBRARIES ${NGHTTP2_LIBRARY}) + set(_nghttp2_INCLUDE_DIRS ${NGHTTP2_INCLUDE_DIR}) + set(_nghttp2_LIBRARIES ${NGHTTP2_LIBRARY}) endif() mark_as_advanced(NGHTTP2_INCLUDE_DIR NGHTTP2_LIBRARY) endif() + +if(NGHTTP2_FOUND) + if(NOT TARGET CURL::nghttp2) + add_library(CURL::nghttp2 INTERFACE IMPORTED) + set_target_properties(CURL::nghttp2 PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_nghttp2_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_nghttp2_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_nghttp2_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_nghttp2_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_nghttp2_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindNGHTTP3.cmake b/CMake/FindNGHTTP3.cmake index 99edd19955..427c139f21 100644 --- a/CMake/FindNGHTTP3.cmake +++ b/CMake/FindNGHTTP3.cmake @@ -25,34 +25,56 @@ # # Input variables: # -# - `NGHTTP3_INCLUDE_DIR`: The nghttp3 include directory. -# - `NGHTTP3_LIBRARY`: Path to `nghttp3` library. +# - `NGHTTP3_INCLUDE_DIR`: Absolute path to nghttp3 include directory. +# - `NGHTTP3_LIBRARY`: Absolute path to `nghttp3` library. +# - `NGHTTP3_USE_STATIC_LIBS`: Configure for static nghttp3 libraries. # -# Result variables: +# Defines: # -# - `NGHTTP3_FOUND`: System has nghttp3. -# - `NGHTTP3_INCLUDE_DIRS`: The nghttp3 include directories. -# - `NGHTTP3_LIBRARIES`: The nghttp3 library names. -# - `NGHTTP3_LIBRARY_DIRS`: The nghttp3 library directories. -# - `NGHTTP3_PC_REQUIRES`: The nghttp3 pkg-config packages. -# - `NGHTTP3_CFLAGS`: Required compiler flags. -# - `NGHTTP3_VERSION`: Version of nghttp3. +# - `NGHTTP3_FOUND`: System has nghttp3. +# - `NGHTTP3_VERSION`: Version of nghttp3. +# - `CURL::nghttp3`: nghttp3 library target. -set(NGHTTP3_PC_REQUIRES "libnghttp3") +set(_nghttp3_pc_requires "libnghttp3") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED NGHTTP3_INCLUDE_DIR AND +if(NOT DEFINED NGHTTP3_INCLUDE_DIR AND NOT DEFINED NGHTTP3_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(NGHTTP3 ${NGHTTP3_PC_REQUIRES}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_nghttp3 ${_nghttp3_pc_requires}) + endif() + if(NOT _nghttp3_FOUND AND CURL_USE_CMAKECONFIG) + find_package(nghttp3 CONFIG QUIET) + endif() endif() -if(NGHTTP3_FOUND) - string(REPLACE ";" " " NGHTTP3_CFLAGS "${NGHTTP3_CFLAGS}") - message(STATUS "Found NGHTTP3 (via pkg-config): ${NGHTTP3_INCLUDE_DIRS} (found version \"${NGHTTP3_VERSION}\")") +if(_nghttp3_FOUND) + set(NGHTTP3_FOUND TRUE) + set(NGHTTP3_VERSION ${_nghttp3_VERSION}) + if(NGHTTP3_USE_STATIC_LIBS) + set(_nghttp3_CFLAGS "${_nghttp3_STATIC_CFLAGS}") + set(_nghttp3_INCLUDE_DIRS "${_nghttp3_STATIC_INCLUDE_DIRS}") + set(_nghttp3_LIBRARY_DIRS "${_nghttp3_STATIC_LIBRARY_DIRS}") + set(_nghttp3_LIBRARIES "${_nghttp3_STATIC_LIBRARIES}") + endif() + message(STATUS "Found NGHTTP3 (via pkg-config): ${_nghttp3_INCLUDE_DIRS} (found version \"${NGHTTP3_VERSION}\")") +elseif(nghttp3_CONFIG) + set(NGHTTP3_FOUND TRUE) + set(NGHTTP3_VERSION ${nghttp3_VERSION}) + if(NGHTTP3_USE_STATIC_LIBS) + set(_nghttp3_LIBRARIES nghttp3::nghttp3_static) + else() + set(_nghttp3_LIBRARIES nghttp3::nghttp3) + endif() + message(STATUS "Found NGHTTP3 (via CMake Config): ${nghttp3_CONFIG} (found version \"${NGHTTP3_VERSION}\")") else() find_path(NGHTTP3_INCLUDE_DIR NAMES "nghttp3/nghttp3.h") - find_library(NGHTTP3_LIBRARY NAMES "nghttp3") + if(NGHTTP3_USE_STATIC_LIBS) + set(_nghttp3_CFLAGS "-DNGHTTP3_STATICLIB") + find_library(NGHTTP3_LIBRARY NAMES "nghttp3_static" "nghttp3") + else() + find_library(NGHTTP3_LIBRARY NAMES "nghttp3") + endif() unset(NGHTTP3_VERSION CACHE) if(NGHTTP3_INCLUDE_DIR AND EXISTS "${NGHTTP3_INCLUDE_DIR}/nghttp3/version.h") @@ -74,9 +96,21 @@ else() ) if(NGHTTP3_FOUND) - set(NGHTTP3_INCLUDE_DIRS ${NGHTTP3_INCLUDE_DIR}) - set(NGHTTP3_LIBRARIES ${NGHTTP3_LIBRARY}) + set(_nghttp3_INCLUDE_DIRS ${NGHTTP3_INCLUDE_DIR}) + set(_nghttp3_LIBRARIES ${NGHTTP3_LIBRARY}) endif() mark_as_advanced(NGHTTP3_INCLUDE_DIR NGHTTP3_LIBRARY) endif() + +if(NGHTTP3_FOUND) + if(NOT TARGET CURL::nghttp3) + add_library(CURL::nghttp3 INTERFACE IMPORTED) + set_target_properties(CURL::nghttp3 PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_nghttp3_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_nghttp3_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_nghttp3_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_nghttp3_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_nghttp3_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindNGTCP2.cmake b/CMake/FindNGTCP2.cmake index 700017f859..e492916377 100644 --- a/CMake/FindNGTCP2.cmake +++ b/CMake/FindNGTCP2.cmake @@ -35,24 +35,21 @@ # # Input variables: # -# - `NGTCP2_INCLUDE_DIR`: The ngtcp2 include directory. -# - `NGTCP2_LIBRARY`: Path to `ngtcp2` library. -# - `NGTCP2_CRYPTO_BORINGSSL_LIBRARY`: Path to `ngtcp2_crypto_boringssl` library. -# - `NGTCP2_CRYPTO_GNUTLS_LIBRARY`: Path to `ngtcp2_crypto_gnutls` library. -# - `NGTCP2_CRYPTO_LIBRESSL_LIBRARY`: Path to `ngtcp2_crypto_libressl` library. -# - `NGTCP2_CRYPTO_OSSL_LIBRARY`: Path to `ngtcp2_crypto_ossl` library. -# - `NGTCP2_CRYPTO_QUICTLS_LIBRARY`: Path to `ngtcp2_crypto_quictls` library. -# - `NGTCP2_CRYPTO_WOLFSSL_LIBRARY`: Path to `ngtcp2_crypto_wolfssl` library. +# - `NGTCP2_INCLUDE_DIR`: Absolute path to ngtcp2 include directory. +# - `NGTCP2_LIBRARY`: Absolute path to `ngtcp2` library. +# - `NGTCP2_CRYPTO_BORINGSSL_LIBRARY`: Absolute path to `ngtcp2_crypto_boringssl` library. +# - `NGTCP2_CRYPTO_GNUTLS_LIBRARY`: Absolute path to `ngtcp2_crypto_gnutls` library. +# - `NGTCP2_CRYPTO_LIBRESSL_LIBRARY`: Absolute path to `ngtcp2_crypto_libressl` library. +# - `NGTCP2_CRYPTO_OSSL_LIBRARY`: Absolute path to `ngtcp2_crypto_ossl` library. +# - `NGTCP2_CRYPTO_QUICTLS_LIBRARY`: Absolute path to `ngtcp2_crypto_quictls` library. +# - `NGTCP2_CRYPTO_WOLFSSL_LIBRARY`: Absolute path to `ngtcp2_crypto_wolfssl` library. +# - `NGTCP2_USE_STATIC_LIBS`: Configure for static ngtcp2 libraries. # -# Result variables: +# Defines: # # - `NGTCP2_FOUND`: System has ngtcp2. -# - `NGTCP2_INCLUDE_DIRS`: The ngtcp2 include directories. -# - `NGTCP2_LIBRARIES`: The ngtcp2 library names. -# - `NGTCP2_LIBRARY_DIRS`: The ngtcp2 library directories. -# - `NGTCP2_PC_REQUIRES`: The ngtcp2 pkg-config packages. -# - `NGTCP2_CFLAGS`: Required compiler flags. # - `NGTCP2_VERSION`: Version of ngtcp2. +# - `CURL::ngtcp2`: ngtcp2 library target. if(NGTCP2_FIND_COMPONENTS) set(_ngtcp2_crypto_backend "") @@ -71,27 +68,57 @@ if(NGTCP2_FIND_COMPONENTS) endif() endif() -set(NGTCP2_PC_REQUIRES "libngtcp2") +set(_ngtcp2_pc_requires "libngtcp2") if(_ngtcp2_crypto_backend) - list(APPEND NGTCP2_PC_REQUIRES "lib${_crypto_library_lower}") + list(APPEND _ngtcp2_pc_requires "lib${_crypto_library_lower}") endif() set(_tried_pkgconfig FALSE) -if(CURL_USE_PKGCONFIG AND - NOT DEFINED NGTCP2_INCLUDE_DIR AND +if(NOT DEFINED NGTCP2_INCLUDE_DIR AND NOT DEFINED NGTCP2_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(NGTCP2 ${NGTCP2_PC_REQUIRES}) - set(_tried_pkgconfig TRUE) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_ngtcp2 ${_ngtcp2_pc_requires}) + set(_tried_pkgconfig TRUE) + endif() + if(NOT _ngtcp2_FOUND AND CURL_USE_CMAKECONFIG) + find_package(ngtcp2 CONFIG QUIET) + # Skip using it if the crypto library target is not available + if(ngtcp2_CONFIG AND + NOT TARGET ngtcp2::${_crypto_library_lower}_static AND + NOT TARGET ngtcp2::${_crypto_library_lower}) + unset(ngtcp2_CONFIG) + endif() + endif() endif() -if(NGTCP2_FOUND) - set(NGTCP2_VERSION "${NGTCP2_libngtcp2_VERSION}") - string(REPLACE ";" " " NGTCP2_CFLAGS "${NGTCP2_CFLAGS}") - message(STATUS "Found NGTCP2 (via pkg-config): ${NGTCP2_INCLUDE_DIRS} (found version \"${NGTCP2_VERSION}\")") +if(_ngtcp2_FOUND) + set(NGTCP2_FOUND TRUE) + set(NGTCP2_VERSION ${_ngtcp2_libngtcp2_VERSION}) + if(NGTCP2_USE_STATIC_LIBS) + set(_ngtcp2_CFLAGS "${_ngtcp2_STATIC_CFLAGS}") + set(_ngtcp2_INCLUDE_DIRS "${_ngtcp2_STATIC_INCLUDE_DIRS}") + set(_ngtcp2_LIBRARY_DIRS "${_ngtcp2_STATIC_LIBRARY_DIRS}") + set(_ngtcp2_LIBRARIES "${_ngtcp2_STATIC_LIBRARIES}") + endif() + message(STATUS "Found NGTCP2 (via pkg-config): ${_ngtcp2_INCLUDE_DIRS} (found version \"${NGTCP2_VERSION}\")") +elseif(ngtcp2_CONFIG) + set(NGTCP2_FOUND TRUE) + set(NGTCP2_VERSION ${ngtcp2_VERSION}) + if(NGTCP2_USE_STATIC_LIBS) + set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2_static ngtcp2::${_crypto_library_lower}_static) + else() + set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2 ngtcp2::${_crypto_library_lower}) + endif() + message(STATUS "Found NGTCP2 (via CMake Config): ${ngtcp2_CONFIG} (found version \"${NGTCP2_VERSION}\")") else() find_path(NGTCP2_INCLUDE_DIR NAMES "ngtcp2/ngtcp2.h") - find_library(NGTCP2_LIBRARY NAMES "ngtcp2") + if(NGTCP2_USE_STATIC_LIBS) + set(_ngtcp2_CFLAGS "-DNGTCP2_STATICLIB") + find_library(NGTCP2_LIBRARY NAMES "ngtcp2_static" "ngtcp2") + else() + find_library(NGTCP2_LIBRARY NAMES "ngtcp2") + endif() unset(NGTCP2_VERSION CACHE) if(NGTCP2_INCLUDE_DIR AND EXISTS "${NGTCP2_INCLUDE_DIR}/ngtcp2/version.h") @@ -104,8 +131,18 @@ else() endif() if(_ngtcp2_crypto_backend) - get_filename_component(_ngtcp2_library_dir "${NGTCP2_LIBRARY}" DIRECTORY) - find_library(${_crypto_library_upper}_LIBRARY NAMES ${_crypto_library_lower} HINTS ${_ngtcp2_library_dir}) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) + cmake_path(GET NGTCP2_LIBRARY PARENT_PATH _ngtcp2_library_dir) + else() + get_filename_component(_ngtcp2_library_dir "${NGTCP2_LIBRARY}" DIRECTORY) + endif() + if(NGTCP2_USE_STATIC_LIBS) + find_library(${_crypto_library_upper}_LIBRARY NAMES ${_crypto_library_lower}_static ${_crypto_library_lower} + HINTS ${_ngtcp2_library_dir}) + else() + find_library(${_crypto_library_upper}_LIBRARY NAMES ${_crypto_library_lower} + HINTS ${_ngtcp2_library_dir}) + endif() if(${_crypto_library_upper}_LIBRARY) set(NGTCP2_${_ngtcp2_crypto_backend}_FOUND TRUE) @@ -124,8 +161,8 @@ else() ) if(NGTCP2_FOUND) - set(NGTCP2_INCLUDE_DIRS ${NGTCP2_INCLUDE_DIR}) - set(NGTCP2_LIBRARIES ${NGTCP2_LIBRARY} ${NGTCP2_CRYPTO_LIBRARY}) + set(_ngtcp2_INCLUDE_DIRS ${NGTCP2_INCLUDE_DIR}) + set(_ngtcp2_LIBRARIES ${NGTCP2_LIBRARY} ${NGTCP2_CRYPTO_LIBRARY}) endif() mark_as_advanced(NGTCP2_INCLUDE_DIR NGTCP2_LIBRARY NGTCP2_CRYPTO_LIBRARY) @@ -135,3 +172,15 @@ else() unset(NGTCP2_LIBRARY CACHE) endif() endif() + +if(NGTCP2_FOUND) + if(NOT TARGET CURL::ngtcp2) + add_library(CURL::ngtcp2 INTERFACE IMPORTED) + set_target_properties(CURL::ngtcp2 PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_ngtcp2_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_ngtcp2_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_ngtcp2_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_ngtcp2_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_ngtcp2_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindNettle.cmake b/CMake/FindNettle.cmake index c2decf6e7b..c963180cba 100644 --- a/CMake/FindNettle.cmake +++ b/CMake/FindNettle.cmake @@ -25,32 +25,29 @@ # # Input variables: # -# - `NETTLE_INCLUDE_DIR`: The nettle include directory. -# - `NETTLE_LIBRARY`: Path to `nettle` library. +# - `NETTLE_INCLUDE_DIR`: Absolute path to nettle include directory. +# - `NETTLE_LIBRARY`: Absolute path to `nettle` library. # -# Result variables: +# Defines: # -# - `NETTLE_FOUND`: System has nettle. -# - `NETTLE_INCLUDE_DIRS`: The nettle include directories. -# - `NETTLE_LIBRARIES`: The nettle library names. -# - `NETTLE_LIBRARY_DIRS`: The nettle library directories. -# - `NETTLE_PC_REQUIRES`: The nettle pkg-config packages. -# - `NETTLE_CFLAGS`: Required compiler flags. -# - `NETTLE_VERSION`: Version of nettle. +# - `NETTLE_FOUND`: System has nettle. +# - `NETTLE_VERSION`: Version of nettle. +# - `CURL::nettle`: nettle library target. -set(NETTLE_PC_REQUIRES "nettle") +set(_nettle_pc_requires "nettle") if(CURL_USE_PKGCONFIG AND NOT DEFINED NETTLE_INCLUDE_DIR AND NOT DEFINED NETTLE_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(NETTLE ${NETTLE_PC_REQUIRES}) + pkg_check_modules(_nettle ${_nettle_pc_requires}) endif() -if(NETTLE_FOUND) +if(_nettle_FOUND) set(Nettle_FOUND TRUE) - string(REPLACE ";" " " NETTLE_CFLAGS "${NETTLE_CFLAGS}") - message(STATUS "Found Nettle (via pkg-config): ${NETTLE_INCLUDE_DIRS} (found version \"${NETTLE_VERSION}\")") + set(NETTLE_FOUND TRUE) + set(NETTLE_VERSION ${_nettle_VERSION}) + message(STATUS "Found Nettle (via pkg-config): ${_nettle_INCLUDE_DIRS} (found version \"${NETTLE_VERSION}\")") else() find_path(NETTLE_INCLUDE_DIR NAMES "nettle/sha2.h") find_library(NETTLE_LIBRARY NAMES "nettle") @@ -80,9 +77,21 @@ else() ) if(NETTLE_FOUND) - set(NETTLE_INCLUDE_DIRS ${NETTLE_INCLUDE_DIR}) - set(NETTLE_LIBRARIES ${NETTLE_LIBRARY}) + set(_nettle_INCLUDE_DIRS ${NETTLE_INCLUDE_DIR}) + set(_nettle_LIBRARIES ${NETTLE_LIBRARY}) endif() mark_as_advanced(NETTLE_INCLUDE_DIR NETTLE_LIBRARY) endif() + +if(NETTLE_FOUND) + if(NOT TARGET CURL::nettle) + add_library(CURL::nettle INTERFACE IMPORTED) + set_target_properties(CURL::nettle PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_nettle_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_nettle_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_nettle_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_nettle_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_nettle_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindQuiche.cmake b/CMake/FindQuiche.cmake index 6939c64e0f..137679b5b3 100644 --- a/CMake/FindQuiche.cmake +++ b/CMake/FindQuiche.cmake @@ -25,32 +25,29 @@ # # Input variables: # -# - `QUICHE_INCLUDE_DIR`: The quiche include directory. -# - `QUICHE_LIBRARY`: Path to `quiche` library. +# - `QUICHE_INCLUDE_DIR`: Absolute path to quiche include directory. +# - `QUICHE_LIBRARY`: Absolute path to `quiche` library. # -# Result variables: +# Defines: # -# - `QUICHE_FOUND`: System has quiche. -# - `QUICHE_INCLUDE_DIRS`: The quiche include directories. -# - `QUICHE_LIBRARIES`: The quiche library names. -# - `QUICHE_LIBRARY_DIRS`: The quiche library directories. -# - `QUICHE_PC_REQUIRES`: The quiche pkg-config packages. -# - `QUICHE_CFLAGS`: Required compiler flags. -# - `QUICHE_VERSION`: Version of quiche. +# - `QUICHE_FOUND`: System has quiche. +# - `QUICHE_VERSION`: Version of quiche. +# - `CURL::quiche`: quiche library target. -set(QUICHE_PC_REQUIRES "quiche") +set(_quiche_pc_requires "quiche") if(CURL_USE_PKGCONFIG AND NOT DEFINED QUICHE_INCLUDE_DIR AND NOT DEFINED QUICHE_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(QUICHE ${QUICHE_PC_REQUIRES}) + pkg_check_modules(_quiche ${_quiche_pc_requires}) endif() -if(QUICHE_FOUND) +if(_quiche_FOUND) set(Quiche_FOUND TRUE) - string(REPLACE ";" " " QUICHE_CFLAGS "${QUICHE_CFLAGS}") - message(STATUS "Found Quiche (via pkg-config): ${QUICHE_INCLUDE_DIRS} (found version \"${QUICHE_VERSION}\")") + set(QUICHE_FOUND TRUE) + set(QUICHE_VERSION ${_quiche_VERSION}) + message(STATUS "Found Quiche (via pkg-config): ${_quiche_INCLUDE_DIRS} (found version \"${QUICHE_VERSION}\")") else() find_path(QUICHE_INCLUDE_DIR NAMES "quiche.h") find_library(QUICHE_LIBRARY NAMES "quiche") @@ -63,9 +60,21 @@ else() ) if(QUICHE_FOUND) - set(QUICHE_INCLUDE_DIRS ${QUICHE_INCLUDE_DIR}) - set(QUICHE_LIBRARIES ${QUICHE_LIBRARY}) + set(_quiche_INCLUDE_DIRS ${QUICHE_INCLUDE_DIR}) + set(_quiche_LIBRARIES ${QUICHE_LIBRARY}) endif() mark_as_advanced(QUICHE_INCLUDE_DIR QUICHE_LIBRARY) endif() + +if(QUICHE_FOUND) + if(NOT TARGET CURL::quiche) + add_library(CURL::quiche INTERFACE IMPORTED) + set_target_properties(CURL::quiche PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_quiche_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_quiche_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_quiche_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_quiche_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_quiche_LIBRARIES}") + endif() +endif() diff --git a/CMake/FindRustls.cmake b/CMake/FindRustls.cmake index 564b08ce15..1e07565189 100644 --- a/CMake/FindRustls.cmake +++ b/CMake/FindRustls.cmake @@ -25,34 +25,31 @@ # # Input variables: # -# - `RUSTLS_INCLUDE_DIR`: The Rustls include directory. -# - `RUSTLS_LIBRARY`: Path to `rustls` library. +# - `RUSTLS_INCLUDE_DIR`: Absolute path to Rustls include directory. +# - `RUSTLS_LIBRARY`: Absolute path to `rustls` library. # -# Result variables: +# Defines: # -# - `RUSTLS_FOUND`: System has Rustls. -# - `RUSTLS_INCLUDE_DIRS`: The Rustls include directories. -# - `RUSTLS_LIBRARIES`: The Rustls library names. -# - `RUSTLS_LIBRARY_DIRS`: The Rustls library directories. -# - `RUSTLS_PC_REQUIRES`: The Rustls pkg-config packages. -# - `RUSTLS_CFLAGS`: Required compiler flags. -# - `RUSTLS_VERSION`: Version of Rustls. +# - `RUSTLS_FOUND`: System has Rustls. +# - `RUSTLS_VERSION`: Version of Rustls. +# - `CURL::rustls`: Rustls library target. -set(RUSTLS_PC_REQUIRES "rustls") +set(_rustls_pc_requires "rustls") if(CURL_USE_PKGCONFIG AND NOT DEFINED RUSTLS_INCLUDE_DIR AND NOT DEFINED RUSTLS_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(RUSTLS ${RUSTLS_PC_REQUIRES}) + pkg_check_modules(_rustls ${_rustls_pc_requires}) endif() -if(RUSTLS_FOUND) +if(_rustls_FOUND) set(Rustls_FOUND TRUE) - string(REPLACE ";" " " RUSTLS_CFLAGS "${RUSTLS_CFLAGS}") - message(STATUS "Found Rustls (via pkg-config): ${RUSTLS_INCLUDE_DIRS} (found version \"${RUSTLS_VERSION}\")") + set(RUSTLS_FOUND TRUE) + set(RUSTLS_VERSION ${_rustls_VERSION}) + message(STATUS "Found Rustls (via pkg-config): ${_rustls_INCLUDE_DIRS} (found version \"${RUSTLS_VERSION}\")") else() - set(RUSTLS_PC_REQUIRES "") # Depend on pkg-config only when found via pkg-config + set(_rustls_pc_requires "") # Depend on pkg-config only when found via pkg-config find_path(RUSTLS_INCLUDE_DIR NAMES "rustls.h") find_library(RUSTLS_LIBRARY NAMES "rustls") @@ -65,8 +62,8 @@ else() ) if(RUSTLS_FOUND) - set(RUSTLS_INCLUDE_DIRS ${RUSTLS_INCLUDE_DIR}) - set(RUSTLS_LIBRARIES ${RUSTLS_LIBRARY}) + set(_rustls_INCLUDE_DIRS ${RUSTLS_INCLUDE_DIR}) + set(_rustls_LIBRARIES ${RUSTLS_LIBRARY}) endif() mark_as_advanced(RUSTLS_INCLUDE_DIR RUSTLS_LIBRARY) @@ -79,31 +76,41 @@ if(RUSTLS_FOUND) if(NOT SECURITY_FRAMEWORK) message(FATAL_ERROR "Security framework not found") endif() - list(APPEND RUSTLS_LIBRARIES "-framework Security") + list(APPEND _rustls_LIBRARIES "-framework Security") find_library(FOUNDATION_FRAMEWORK NAMES "Foundation") mark_as_advanced(FOUNDATION_FRAMEWORK) if(NOT FOUNDATION_FRAMEWORK) message(FATAL_ERROR "Foundation framework not found") endif() - list(APPEND RUSTLS_LIBRARIES "-framework Foundation") + list(APPEND _rustls_LIBRARIES "-framework Foundation") elseif(NOT WIN32) find_library(PTHREAD_LIBRARY NAMES "pthread") if(PTHREAD_LIBRARY) - list(APPEND RUSTLS_LIBRARIES ${PTHREAD_LIBRARY}) + list(APPEND _rustls_LIBRARIES ${PTHREAD_LIBRARY}) endif() mark_as_advanced(PTHREAD_LIBRARY) find_library(DL_LIBRARY NAMES "dl") if(DL_LIBRARY) - list(APPEND RUSTLS_LIBRARIES ${DL_LIBRARY}) + list(APPEND _rustls_LIBRARIES ${DL_LIBRARY}) endif() mark_as_advanced(DL_LIBRARY) find_library(MATH_LIBRARY NAMES "m") if(MATH_LIBRARY) - list(APPEND RUSTLS_LIBRARIES ${MATH_LIBRARY}) + list(APPEND _rustls_LIBRARIES ${MATH_LIBRARY}) endif() mark_as_advanced(MATH_LIBRARY) endif() + + if(NOT TARGET CURL::rustls) + add_library(CURL::rustls INTERFACE IMPORTED) + set_target_properties(CURL::rustls PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_rustls_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_rustls_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_rustls_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_rustls_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_rustls_LIBRARIES}") + endif() endif() diff --git a/CMake/FindWolfSSH.cmake b/CMake/FindWolfSSH.cmake deleted file mode 100644 index 98de656b92..0000000000 --- a/CMake/FindWolfSSH.cmake +++ /dev/null @@ -1,65 +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 -# -########################################################################### -# Find the wolfSSH library -# -# Input variables: -# -# - `WOLFSSH_INCLUDE_DIR`: The wolfSSH include directory. -# - `WOLFSSH_LIBRARY`: Path to `wolfssh` library. -# -# Result variables: -# -# - `WOLFSSH_FOUND`: System has wolfSSH. -# - `WOLFSSH_INCLUDE_DIRS`: The wolfSSH include directories. -# - `WOLFSSH_LIBRARIES`: The wolfSSH library names. -# - `WOLFSSH_VERSION`: Version of wolfSSH. - -find_path(WOLFSSH_INCLUDE_DIR NAMES "wolfssh/ssh.h") -find_library(WOLFSSH_LIBRARY NAMES "wolfssh" "libwolfssh") - -unset(WOLFSSH_VERSION CACHE) -if(WOLFSSH_INCLUDE_DIR AND EXISTS "${WOLFSSH_INCLUDE_DIR}/wolfssh/version.h") - set(_version_regex "#[\t ]*define[\t ]+LIBWOLFSSH_VERSION_STRING[\t ]+\"([^\"]*)\"") - file(STRINGS "${WOLFSSH_INCLUDE_DIR}/wolfssh/version.h" _version_str REGEX "${_version_regex}") - string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") - set(WOLFSSH_VERSION "${_version_str}") - unset(_version_regex) - unset(_version_str) -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(WolfSSH - REQUIRED_VARS - WOLFSSH_INCLUDE_DIR - WOLFSSH_LIBRARY - VERSION_VAR - WOLFSSH_VERSION -) - -if(WOLFSSH_FOUND) - set(WOLFSSH_INCLUDE_DIRS ${WOLFSSH_INCLUDE_DIR}) - set(WOLFSSH_LIBRARIES ${WOLFSSH_LIBRARY}) -endif() - -mark_as_advanced(WOLFSSH_INCLUDE_DIR WOLFSSH_LIBRARY) diff --git a/CMake/FindWolfSSL.cmake b/CMake/FindWolfSSL.cmake index 87b640a1f2..964320ad6b 100644 --- a/CMake/FindWolfSSL.cmake +++ b/CMake/FindWolfSSL.cmake @@ -25,18 +25,14 @@ # # Input variables: # -# - `WOLFSSL_INCLUDE_DIR`: The wolfSSL include directory. -# - `WOLFSSL_LIBRARY`: Path to `wolfssl` library. +# - `WOLFSSL_INCLUDE_DIR`: Absolute path to wolfSSL include directory. +# - `WOLFSSL_LIBRARY`: Absolute path to `wolfssl` library. # -# Result variables: +# Defines: # -# - `WOLFSSL_FOUND`: System has wolfSSL. -# - `WOLFSSL_INCLUDE_DIRS`: The wolfSSL include directories. -# - `WOLFSSL_LIBRARIES`: The wolfSSL library names. -# - `WOLFSSL_LIBRARY_DIRS`: The wolfSSL library directories. -# - `WOLFSSL_PC_REQUIRES`: The wolfSSL pkg-config packages. -# - `WOLFSSL_CFLAGS`: Required compiler flags. -# - `WOLFSSL_VERSION`: Version of wolfSSL. +# - `WOLFSSL_FOUND`: System has wolfSSL. +# - `WOLFSSL_VERSION`: Version of wolfSSL. +# - `CURL::wolfssl`: wolfSSL library target. if(DEFINED WolfSSL_INCLUDE_DIR AND NOT DEFINED WOLFSSL_INCLUDE_DIR) message(WARNING "WolfSSL_INCLUDE_DIR is deprecated, use WOLFSSL_INCLUDE_DIR instead.") @@ -47,19 +43,30 @@ if(DEFINED WolfSSL_LIBRARY AND NOT DEFINED WOLFSSL_LIBRARY) set(WOLFSSL_LIBRARY "${WolfSSL_LIBRARY}") endif() -set(WOLFSSL_PC_REQUIRES "wolfssl") +set(_wolfssl_pc_requires "wolfssl") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED WOLFSSL_INCLUDE_DIR AND +if(NOT DEFINED WOLFSSL_INCLUDE_DIR AND NOT DEFINED WOLFSSL_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(WOLFSSL ${WOLFSSL_PC_REQUIRES}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_wolfssl ${_wolfssl_pc_requires}) + endif() + if(NOT _wolfssl_FOUND AND CURL_USE_CMAKECONFIG) + find_package(wolfssl CONFIG QUIET) + endif() endif() -if(WOLFSSL_FOUND) +if(_wolfssl_FOUND) set(WolfSSL_FOUND TRUE) - string(REPLACE ";" " " WOLFSSL_CFLAGS "${WOLFSSL_CFLAGS}") - message(STATUS "Found WolfSSL (via pkg-config): ${WOLFSSL_INCLUDE_DIRS} (found version \"${WOLFSSL_VERSION}\")") + set(WOLFSSL_FOUND TRUE) + set(WOLFSSL_VERSION ${_wolfssl_VERSION}) + message(STATUS "Found WolfSSL (via pkg-config): ${_wolfssl_INCLUDE_DIRS} (found version \"${WOLFSSL_VERSION}\")") +elseif(wolfssl_CONFIG) + set(WolfSSL_FOUND TRUE) + set(WOLFSSL_FOUND TRUE) + set(WOLFSSL_VERSION ${wolfssl_VERSION}) + set(_wolfssl_LIBRARIES wolfssl::wolfssl) + message(STATUS "Found WolfSSL (via CMake Config): ${wolfssl_CONFIG} (found version \"${WOLFSSL_VERSION}\")") else() find_path(WOLFSSL_INCLUDE_DIR NAMES "wolfssl/ssl.h") find_library(WOLFSSL_LIBRARY NAMES "wolfssl") @@ -84,8 +91,8 @@ else() ) if(WOLFSSL_FOUND) - set(WOLFSSL_INCLUDE_DIRS ${WOLFSSL_INCLUDE_DIR}) - set(WOLFSSL_LIBRARIES ${WOLFSSL_LIBRARY}) + set(_wolfssl_INCLUDE_DIRS ${WOLFSSL_INCLUDE_DIR}) + set(_wolfssl_LIBRARIES ${WOLFSSL_LIBRARY}) endif() mark_as_advanced(WOLFSSL_INCLUDE_DIR WOLFSSL_LIBRARY) @@ -98,19 +105,31 @@ if(WOLFSSL_FOUND) if(NOT SECURITY_FRAMEWORK) message(FATAL_ERROR "Security framework not found") endif() - list(APPEND WOLFSSL_LIBRARIES "-framework Security") + list(APPEND _wolfssl_LIBRARIES "-framework Security") find_library(COREFOUNDATION_FRAMEWORK NAMES "CoreFoundation") mark_as_advanced(COREFOUNDATION_FRAMEWORK) if(NOT COREFOUNDATION_FRAMEWORK) message(FATAL_ERROR "CoreFoundation framework not found") endif() - list(APPEND WOLFSSL_LIBRARIES "-framework CoreFoundation") - elseif(NOT WIN32) + list(APPEND _wolfssl_LIBRARIES "-framework CoreFoundation") + elseif(WIN32) + list(APPEND _wolfssl_LIBRARIES "crypt32") + else() find_library(MATH_LIBRARY NAMES "m") if(MATH_LIBRARY) - list(APPEND WOLFSSL_LIBRARIES ${MATH_LIBRARY}) # for log and pow + list(APPEND _wolfssl_LIBRARIES ${MATH_LIBRARY}) # for log and pow endif() mark_as_advanced(MATH_LIBRARY) endif() + + if(NOT TARGET CURL::wolfssl) + add_library(CURL::wolfssl INTERFACE IMPORTED) + set_target_properties(CURL::wolfssl PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_wolfssl_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_wolfssl_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_wolfssl_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_wolfssl_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_wolfssl_LIBRARIES}") + endif() endif() diff --git a/CMake/FindZstd.cmake b/CMake/FindZstd.cmake index 0ea1fef467..8dc620a100 100644 --- a/CMake/FindZstd.cmake +++ b/CMake/FindZstd.cmake @@ -25,18 +25,15 @@ # # Input variables: # -# - `ZSTD_INCLUDE_DIR`: The zstd include directory. -# - `ZSTD_LIBRARY`: Path to `zstd` library. +# - `ZSTD_INCLUDE_DIR`: Absolute path to zstd include directory. +# - `ZSTD_LIBRARY`: Absolute path to `zstd` library. +# - `ZSTD_USE_STATIC_LIBS`: Configure for static zstd libraries. # -# Result variables: +# Defines: # -# - `ZSTD_FOUND`: System has zstd. -# - `ZSTD_INCLUDE_DIRS`: The zstd include directories. -# - `ZSTD_LIBRARIES`: The zstd library names. -# - `ZSTD_LIBRARY_DIRS`: The zstd library directories. -# - `ZSTD_PC_REQUIRES`: The zstd pkg-config packages. -# - `ZSTD_CFLAGS`: Required compiler flags. -# - `ZSTD_VERSION`: Version of zstd. +# - `ZSTD_FOUND`: System has zstd. +# - `ZSTD_VERSION`: Version of zstd. +# - `CURL::zstd`: zstd library target. if(DEFINED Zstd_INCLUDE_DIR AND NOT DEFINED ZSTD_INCLUDE_DIR) message(WARNING "Zstd_INCLUDE_DIR is deprecated, use ZSTD_INCLUDE_DIR instead.") @@ -47,22 +44,54 @@ if(DEFINED Zstd_LIBRARY AND NOT DEFINED ZSTD_LIBRARY) set(ZSTD_LIBRARY "${Zstd_LIBRARY}") endif() -set(ZSTD_PC_REQUIRES "libzstd") +set(_zstd_pc_requires "libzstd") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED ZSTD_INCLUDE_DIR AND +if(NOT DEFINED ZSTD_INCLUDE_DIR AND NOT DEFINED ZSTD_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(ZSTD ${ZSTD_PC_REQUIRES}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_zstd ${_zstd_pc_requires}) + endif() + if(NOT _zstd_FOUND AND CURL_USE_CMAKECONFIG) + find_package(Zstd CONFIG QUIET) + # Skip using if older than v1.4.5 + if(Zstd_CONFIG AND + NOT TARGET zstd::libzstd_static AND + NOT TARGET zstd::libzstd_shared) + unset(Zstd_CONFIG) + endif() + endif() endif() -if(ZSTD_FOUND) +if(_zstd_FOUND) set(Zstd_FOUND TRUE) - string(REPLACE ";" " " ZSTD_CFLAGS "${ZSTD_CFLAGS}") - message(STATUS "Found Zstd (via pkg-config): ${ZSTD_INCLUDE_DIRS} (found version \"${ZSTD_VERSION}\")") + set(ZSTD_FOUND TRUE) + set(ZSTD_VERSION ${_zstd_VERSION}) + if(ZSTD_USE_STATIC_LIBS) + set(_zstd_CFLAGS "${_zstd_STATIC_CFLAGS}") + set(_zstd_INCLUDE_DIRS "${_zstd_STATIC_INCLUDE_DIRS}") + set(_zstd_LIBRARY_DIRS "${_zstd_STATIC_LIBRARY_DIRS}") + set(_zstd_LIBRARIES "${_zstd_STATIC_LIBRARIES}") + endif() + message(STATUS "Found Zstd (via pkg-config): ${_zstd_INCLUDE_DIRS} (found version \"${ZSTD_VERSION}\")") +elseif(Zstd_CONFIG) + set(ZSTD_FOUND TRUE) + set(ZSTD_VERSION ${Zstd_VERSION}) + if(ZSTD_USE_STATIC_LIBS) + set(_zstd_LIBRARIES zstd::libzstd_static) + elseif(TARGET zstd::libzstd) + set(_zstd_LIBRARIES zstd::libzstd) # v1.5.6+ + else() + set(_zstd_LIBRARIES zstd::libzstd_shared) + endif() + message(STATUS "Found Zstd (via CMake Config): ${Zstd_CONFIG} (found version \"${ZSTD_VERSION}\")") else() find_path(ZSTD_INCLUDE_DIR NAMES "zstd.h") - find_library(ZSTD_LIBRARY NAMES "zstd") + if(ZSTD_USE_STATIC_LIBS) + find_library(ZSTD_LIBRARY NAMES "zstd_static" "zstd") + else() + find_library(ZSTD_LIBRARY NAMES "zstd") + endif() unset(ZSTD_VERSION CACHE) if(ZSTD_INCLUDE_DIR AND EXISTS "${ZSTD_INCLUDE_DIR}/zstd.h") @@ -94,9 +123,21 @@ else() ) if(ZSTD_FOUND) - set(ZSTD_INCLUDE_DIRS ${ZSTD_INCLUDE_DIR}) - set(ZSTD_LIBRARIES ${ZSTD_LIBRARY}) + set(_zstd_INCLUDE_DIRS ${ZSTD_INCLUDE_DIR}) + set(_zstd_LIBRARIES ${ZSTD_LIBRARY}) endif() mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY) endif() + +if(ZSTD_FOUND) + if(NOT TARGET CURL::zstd) + add_library(CURL::zstd INTERFACE IMPORTED) + set_target_properties(CURL::zstd PROPERTIES + INTERFACE_LIBCURL_PC_MODULES "${_zstd_pc_requires}" + INTERFACE_COMPILE_OPTIONS "${_zstd_CFLAGS}" + INTERFACE_INCLUDE_DIRECTORIES "${_zstd_INCLUDE_DIRS}" + INTERFACE_LINK_DIRECTORIES "${_zstd_LIBRARY_DIRS}" + INTERFACE_LINK_LIBRARIES "${_zstd_LIBRARIES}") + endif() +endif() diff --git a/CMake/Macros.cmake b/CMake/Macros.cmake index 710df70116..5e26c38469 100644 --- a/CMake/Macros.cmake +++ b/CMake/Macros.cmake @@ -40,26 +40,18 @@ set(CURL_TEST_DEFINES "") # Initialize global variable # Return result in variable: CURL_TEST_OUTPUT macro(curl_internal_test _curl_test) if(NOT DEFINED "${_curl_test}") - string(REPLACE ";" " " _cmake_required_definitions "${CMAKE_REQUIRED_DEFINITIONS}") - set(_curl_test_add_libraries "") - if(CMAKE_REQUIRED_LIBRARIES) - set(_curl_test_add_libraries - "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") - endif() - message(STATUS "Performing Test ${_curl_test}") try_compile(${_curl_test} ${PROJECT_BINARY_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/CMake/CurlTests.c" - CMAKE_FLAGS - "-DCOMPILE_DEFINITIONS:STRING=-D${_curl_test} ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS} ${_cmake_required_definitions}" - "${_curl_test_add_libraries}" + COMPILE_DEFINITIONS "-D${_curl_test}" ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS} ${CMAKE_REQUIRED_DEFINITIONS} + LINK_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}" OUTPUT_VARIABLE CURL_TEST_OUTPUT) if(${_curl_test}) - set(${_curl_test} 1 CACHE INTERNAL "Curl test") + set(${_curl_test} 1 CACHE INTERNAL "curl test") message(STATUS "Performing Test ${_curl_test} - Success") else() - set(${_curl_test} "" CACHE INTERNAL "Curl test") + set(${_curl_test} "" CACHE INTERNAL "curl test") message(STATUS "Performing Test ${_curl_test} - Failed") endif() endif() @@ -71,9 +63,21 @@ macro(curl_dependency_option _option_name _find_name _desc_name) set_property(CACHE ${_option_name} PROPERTY STRINGS "AUTO" "ON" "OFF") if(${_option_name} STREQUAL "AUTO") - find_package(${_find_name}) + if(_find_name STREQUAL "ZLIB") + find_package(${_find_name}) + else() + find_package(${_find_name} MODULE) + endif() elseif(${_option_name}) - find_package(${_find_name} REQUIRED) + if(_find_name STREQUAL "ZLIB") + find_package(${_find_name} REQUIRED) + else() + find_package(${_find_name} MODULE REQUIRED) + endif() + else() + string(TOUPPER "${_find_name}" _find_name_upper) + set(${_find_name}_FOUND OFF) # cmake-lint: disable=C0103 + set(${_find_name_upper}_FOUND OFF) # cmake-lint: disable=C0103 endif() endmacro() @@ -98,24 +102,24 @@ endmacro() # Internal: Recurse into target libraries and collect their include directories # and macro definitions. -macro(curl_collect_target_options _target) - get_target_property(_val ${_target} INTERFACE_INCLUDE_DIRECTORIES) - if(_val) - list(APPEND _includes ${_val}) - endif() - get_target_property(_val ${_target} INCLUDE_DIRECTORIES) - if(_val) - list(APPEND _includes ${_val}) - endif() - get_target_property(_val ${_target} COMPILE_DEFINITIONS) +macro(curl_collect_target_compile_options _target) + get_target_property(_val ${_target} INTERFACE_COMPILE_DEFINITIONS) if(_val) list(APPEND _definitions ${_val}) endif() + get_target_property(_val ${_target} INTERFACE_INCLUDE_DIRECTORIES) + if(_val) + list(APPEND _incsys ${_val}) + endif() + get_target_property(_val ${_target} INTERFACE_COMPILE_OPTIONS) + if(_val) + list(APPEND _options ${_val}) + endif() get_target_property(_val ${_target} LINK_LIBRARIES) if(_val) foreach(_lib IN LISTS _val) if(TARGET "${_lib}") - curl_collect_target_options(${_lib}) + curl_collect_target_compile_options(${_lib}) endif() endforeach() endif() @@ -123,35 +127,94 @@ macro(curl_collect_target_options _target) endmacro() # Create a clang-tidy target for test targets -macro(curl_add_clang_tidy_test_target _target_clang_tidy _target) +function(curl_add_clang_tidy_test_target _target_clang_tidy _target) if(CURL_CLANG_TIDY) - set(_includes "") set(_definitions "") + set(_includes "") + set(_incsys "") + set(_options "") - # Collect header directories and macro definitions applying to the directory - get_directory_property(_val INCLUDE_DIRECTORIES) - if(_val) - list(APPEND _includes ${_val}) - endif() + # Make a list of known system include directories + set(_sys_incdirs "${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES}") + foreach(_inc IN LISTS CMAKE_SYSTEM_PREFIX_PATH) + if(NOT _inc MATCHES "/$") + string(APPEND _inc "/") + endif() + string(APPEND _inc "include") + if(NOT _inc IN_LIST _sys_incdirs AND IS_DIRECTORY "${_inc}") + list(APPEND _sys_incdirs "${_inc}") + endif() + endforeach() + + # Collect options applying to the directory get_directory_property(_val COMPILE_DEFINITIONS) if(_val) list(APPEND _definitions ${_val}) endif() - unset(_val) + get_directory_property(_val INCLUDE_DIRECTORIES) + if(_val) + list(APPEND _includes ${_val}) + endif() + get_directory_property(_val COMPILE_OPTIONS) + if(_val) + list(APPEND _options ${_val}) + endif() + + # Collect options applying to the target + get_target_property(_val ${_target} COMPILE_DEFINITIONS) + if(_val) + list(APPEND _definitions ${_val}) + endif() + get_target_property(_val ${_target} INCLUDE_DIRECTORIES) + if(_val) + list(APPEND _includes ${_val}) + endif() + get_target_property(_val ${_target} COMPILE_OPTIONS) + if(_val) + list(APPEND _options ${_val}) + endif() # Collect header directories and macro definitions from lib dependencies - curl_collect_target_options(${_target}) - - list(REMOVE_ITEM _includes "") - string(REPLACE ";" ";-I" _includes ";${_includes}") - list(REMOVE_DUPLICATES _includes) + curl_collect_target_compile_options(${_target}) list(REMOVE_ITEM _definitions "") string(REPLACE ";" ";-D" _definitions ";${_definitions}") list(REMOVE_DUPLICATES _definitions) list(SORT _definitions) # Sort like CMake does + list(REMOVE_ITEM _includes "") + string(REPLACE ";" ";-I" _includes ";${_includes}") + list(REMOVE_DUPLICATES _includes) + + set(_incsys_tmp ${_incsys}) + list(REMOVE_DUPLICATES _incsys_tmp) + set(_incsys "") + set(_incsystop "") + foreach(_inc IN LISTS _incsys_tmp) + if(_inc IN_LIST _sys_incdirs) + list(APPEND _incsystop "${_inc}") # Save system prefixes to re-add them later to the end of list + continue() + endif() + # Avoid empty and '$' items. The latter + # evaluates to an empty path in this context. Also skip + # '$', as already present in '_includes'. + if(_inc AND + NOT _inc MATCHES "INSTALL_INTERFACE:" AND + NOT _inc MATCHES "BUILD_INTERFACE:") + list(APPEND _incsys "-isystem" "${_inc}") + endif() + endforeach() + foreach(_inc IN LISTS _incsystop) + list(APPEND _incsys "-isystem" "${_inc}") + endforeach() + + if(CMAKE_C_COMPILER_ID MATCHES "Clang") + list(REMOVE_DUPLICATES _options) # Keep the first of duplicates to imitate CMake + else() + set(_options) + endif() + # Assemble source list set(_sources "") foreach(_source IN ITEMS ${ARGN}) @@ -161,14 +224,55 @@ macro(curl_add_clang_tidy_test_target _target_clang_tidy _target) list(APPEND _sources "${_source}") endforeach() + set(_cc "${CMAKE_C_COMPILER}") + if(CMAKE_C_COMPILER_TARGET AND CMAKE_C_COMPILE_OPTIONS_TARGET) + list(APPEND _cc "${CMAKE_C_COMPILE_OPTIONS_TARGET}${CMAKE_C_COMPILER_TARGET}") + endif() + if(APPLE AND CMAKE_OSX_SYSROOT) + list(APPEND _cc "-isysroot" "${CMAKE_OSX_SYSROOT}") + elseif(CMAKE_SYSROOT AND CMAKE_C_COMPILE_OPTIONS_SYSROOT) + list(APPEND _cc "${CMAKE_C_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT}") + endif() + + # Pass -clang-diagnostic-unused-function to disable -Wunused-function implied by -Wunused add_custom_target(${_target_clang_tidy} USES_TERMINAL WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - COMMAND ${CMAKE_C_CLANG_TIDY} ${_sources} -- ${_includes} ${_definitions} + COMMAND ${CMAKE_C_CLANG_TIDY} + "--checks=-clang-diagnostic-unused-function" + ${_sources} -- ${_cc} ${_definitions} ${_includes} ${_incsys} ${_options} DEPENDS ${_sources}) add_dependencies(tests-clang-tidy ${_target_clang_tidy}) - - unset(_includes) - unset(_definitions) - unset(_sources) endif() +endfunction() + +# Internal: Recurse into interface targets and collect their libraries +# and library paths. +macro(curl_collect_target_link_options _target) + get_target_property(_val ${_target} INTERFACE_LINK_DIRECTORIES) + if(_val) + list(APPEND _libdirs ${_val}) + endif() + get_target_property(_val ${_target} IMPORTED) + if(_val) + # LOCATION is empty for interface library targets and safe to ignore. + # Explicitly skip this query to avoid CMake v3.18 and older erroring out. + get_target_property(_val ${_target} TYPE) + if(NOT "${_val}" STREQUAL "INTERFACE_LIBRARY") + get_target_property(_val ${_target} LOCATION) + if(_val) + list(APPEND _libs ${_val}) + endif() + endif() + endif() + get_target_property(_val ${_target} INTERFACE_LINK_LIBRARIES) + if(_val) + foreach(_lib IN LISTS _val) + if(TARGET "${_lib}") + curl_collect_target_link_options(${_lib}) + else() + list(APPEND _libs ${_lib}) + endif() + endforeach() + endif() + unset(_val) endmacro() diff --git a/CMake/OtherTests.cmake b/CMake/OtherTests.cmake index 9f59e0dc81..6619f3ab3e 100644 --- a/CMake/OtherTests.cmake +++ b/CMake/OtherTests.cmake @@ -50,19 +50,6 @@ if(NOT DEFINED HAVE_STRUCT_SOCKADDR_STORAGE) cmake_pop_check_state() endif() -if(NOT WIN32) - set(_source_epilogue "#undef inline") - curl_add_header_include(HAVE_SYS_TYPES_H "sys/types.h") - check_c_source_compiles("${_source_epilogue} - #include - int main(void) - { - int flag = MSG_NOSIGNAL; - (void)flag; - return 0; - }" HAVE_MSG_NOSIGNAL) -endif() - set(_source_epilogue "#undef inline") check_c_source_compiles("${_source_epilogue} #ifdef _MSC_VER @@ -91,7 +78,7 @@ if(WIN32) elseif(NOT HAVE_GETADDRINFO) set(HAVE_GETADDRINFO_THREADSAFE FALSE) elseif(APPLE OR - CMAKE_SYSTEM_NAME STREQUAL "AIX" OR + AIX OR CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "HP-UX" OR CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR diff --git a/CMake/PickyWarnings.cmake b/CMake/PickyWarnings.cmake index f67576d681..aca79ec947 100644 --- a/CMake/PickyWarnings.cmake +++ b/CMake/PickyWarnings.cmake @@ -29,12 +29,10 @@ set(_picky_nocheck "") # not to pass to feature checks if(CURL_WERROR) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) set(CMAKE_COMPILE_WARNING_AS_ERROR ON) - else() - if(MSVC) - list(APPEND _picky_nocheck "-WX") - else() # llvm/clang and gcc style options - list(APPEND _picky_nocheck "-Werror") - endif() + elseif(MSVC) + list(APPEND _picky_nocheck "-WX") + else() # llvm/clang and gcc-style options + list(APPEND _picky_nocheck "-Werror") endif() if((CMAKE_C_COMPILER_ID STREQUAL "GNU" AND @@ -47,8 +45,8 @@ endif() if(APPLE AND (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.6) OR - (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.3)) - list(APPEND _picky "-Werror=partial-availability") # clang 3.6 appleclang 6.3 + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.1)) + list(APPEND _picky "-Werror=partial-availability") # clang 3.6 appleclang 6.1 endif() if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") @@ -62,7 +60,9 @@ elseif(BORLAND) endif() if(PICKY_COMPILER) - if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") + # Leave disabled for GCC <4.6, because they lack #pragma features to silence locally. + if((CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.6) OR + CMAKE_C_COMPILER_ID MATCHES "Clang") # https://clang.llvm.org/docs/DiagnosticsReference.html # https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html @@ -77,9 +77,15 @@ if(PICKY_COMPILER) set(_picky_enable "-W") endif() - list(APPEND _picky_enable - -Wall -pedantic - ) + list(APPEND _picky_enable "-Wall") + + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.2) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.2) OR + CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8) + list(APPEND _picky_enable "-Wpedantic") # clang 3.2 gcc 4.8 appleclang 4.2 + else() + list(APPEND _picky_enable "-pedantic") + endif() # ---------------------------------- # Add new options here, if in doubt: @@ -87,6 +93,9 @@ if(PICKY_COMPILER) set(_picky_detect ) + # Notes: -Wno-* options should ideally be disabled at their precise cutoff versions, + # to suppress undesired warnings in case -Weverything is passed as a custom option. + # Assume these options always exist with both clang and gcc. # Require clang 3.0 / gcc 2.95 or later. list(APPEND _picky_enable @@ -110,7 +119,7 @@ if(PICKY_COMPILER) -Waddress # clang 2.7 gcc 4.3 -Wattributes # clang 2.7 gcc 4.1 -Wcast-align # clang 1.0 gcc 4.2 - -Wcast-qual # clang 3.0 gcc 3.4.6 + -Wcast-qual # clang 2.7 gcc 3.4.6 -Wdeclaration-after-statement # clang 1.0 gcc 3.4 -Wdiv-by-zero # clang 2.7 gcc 4.1 -Wempty-body # clang 2.7 gcc 4.3 @@ -120,14 +129,14 @@ if(PICKY_COMPILER) -Wignored-qualifiers # clang 2.8 gcc 4.3 -Wmissing-field-initializers # clang 2.7 gcc 4.1 -Wmissing-noreturn # clang 2.7 gcc 4.1 - -Wno-format-nonliteral # clang 1.0 gcc 2.96 (3.0) + -Wno-padded # clang 2.9 gcc 4.1 # Not used: We cannot change public structs -Wno-sign-conversion # clang 2.9 gcc 4.3 + -Wno-switch-default # clang 2.7 gcc 4.1 # Not used: Annoying to fix or silence + -Wno-switch-enum # clang 2.7 gcc 4.1 # Not used: It basically disallows default case -Wno-system-headers # clang 1.0 gcc 3.0 - # -Wpadded # clang 2.9 gcc 4.1 # Not used: We cannot change public structs -Wold-style-definition # clang 2.7 gcc 3.4 -Wredundant-decls # clang 2.7 gcc 4.1 -Wstrict-prototypes # clang 1.0 gcc 3.3 - # -Wswitch-enum # clang 2.7 gcc 4.1 # Not used: It basically disallows default case -Wtype-limits # clang 2.7 gcc 4.3 -Wunreachable-code # clang 2.7 gcc 4.1 # -Wunused-macros # clang 2.7 gcc 4.1 # Not practical @@ -139,9 +148,11 @@ if(PICKY_COMPILER) if(CMAKE_C_COMPILER_ID MATCHES "Clang") list(APPEND _picky_enable ${_picky_common_old} + -Wconditional-uninitialized # clang 3.0 + -Wno-used-but-marked-unused # clang 2.9 # for typecheck-gcc.h with clang 14+, dependency headers -Wshift-sign-overflow # clang 2.9 -Wshorten-64-to-32 # clang 1.0 - -Wformat=2 # clang 3.0 gcc 4.8 + -Wformat=2 # clang 2.7 gcc 4.8 ) if(NOT MSVC) list(APPEND _picky_enable @@ -149,39 +160,119 @@ if(PICKY_COMPILER) ) endif() # Enable based on compiler version - if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.6) OR - (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.3)) + if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.1) list(APPEND _picky_enable - -Wdouble-promotion # clang 3.6 gcc 4.6 appleclang 6.3 - -Wenum-conversion # clang 3.2 gcc 10.0 appleclang 4.6 g++ 11.0 + -Wno-covered-switch-default # clang 3.1 appleclang 3.1 # Annoying to fix or silence + -Wno-disabled-macro-expansion # clang 3.1 appleclang 3.1 # for std headers, and curl/curl.h (rare combos) + ) + if(MSVC) + list(APPEND _picky_enable + -Wno-format-non-iso # clang 3.1 appleclang 3.1 # 'q' length modifier is not supported by ISO C + ) + else() + list(APPEND _picky_enable + -Wformat-non-iso # clang 3.1 appleclang 3.1 + ) + endif() + endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.3) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.0)) + list(APPEND _picky_enable + -Wenum-conversion # clang 3.2 gcc 10.0 appleclang 4.2 g++ 11.0 + -Wmissing-variable-declarations # clang 3.2 appleclang 4.2 + -Wno-documentation-unknown-command # clang 3.3 appleclang 5.0 + -Wsometimes-uninitialized # clang 3.2 appleclang 4.2 + ) + endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.6) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.1)) + list(APPEND _picky_enable + -Wdouble-promotion # clang 3.6 gcc 4.6 appleclang 6.1 -Wheader-guard # clang 3.4 appleclang 5.1 -Wpragmas # clang 3.5 gcc 4.1 appleclang 6.0 - -Wsometimes-uninitialized # clang 3.2 appleclang 4.6 # -Wunreachable-code-break # clang 3.5 appleclang 6.0 # Not used: Silent in "unity" builds -Wunused-const-variable # clang 3.4 gcc 6.0 appleclang 5.1 ) endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.9) OR - (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.3)) + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.1)) list(APPEND _picky_enable - -Wcomma # clang 3.9 appleclang 8.3 - -Wmissing-variable-declarations # clang 3.2 appleclang 4.6 + -Wcomma # clang 3.9 appleclang 8.1 ) + if(MSVC) + list(APPEND _picky_enable + -Wno-nonportable-system-include-path # clang 3.9 appleclang 8.1 # No truly portable solution to this + ) + endif() endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 7.0) OR - (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10.3)) + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11)) list(APPEND _picky_enable - -Wassign-enum # clang 7.0 appleclang 10.3 - -Wextra-semi-stmt # clang 7.0 appleclang 10.3 + -Wassign-enum # clang 7.0 appleclang 11.0 + -Wextra-semi-stmt # clang 7.0 appleclang 11.0 ) endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0) OR - (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.4)) + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12)) list(APPEND _picky_enable - -Wimplicit-fallthrough # clang 4.0 gcc 7.0 appleclang 12.4 # We do silencing for clang 10.0 and above only - -Wxor-used-as-pow # clang 10.0 gcc 13.0 + -Wimplicit-fallthrough # clang 4.0 gcc 7.0 appleclang 9.0 # We do silencing for clang 10.0 and above only + -Wxor-used-as-pow # clang 10.0 gcc 13.0 appleclang 12.0 ) endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 13.1)) + list(APPEND _picky_enable + -Wcast-function-type # clang 13.0 appleclang 13.1 + -Wreserved-identifier # clang 13.0 appleclang 13.1 # Keep it before -Wno-reserved-macro-identifier + -Wno-reserved-macro-identifier # clang 13.0 appleclang 13.1 # External macros have to be set sometimes + ) + endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0.3)) + if(CMAKE_GENERATOR STREQUAL "FASTBuild") + list(APPEND _picky_enable + -Wno-gnu-line-marker # clang 15.0 appleclang 14.0.3 + ) + endif() + endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0)) + list(APPEND _picky_enable + -Wno-unsafe-buffer-usage # clang 16.0 appleclang 15.0 + ) + endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0)) + list(APPEND _picky_enable + -Wcast-function-type-strict # clang 16.0 appleclang 16.0 + ) + endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.1) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 17.0)) + list(APPEND _picky_enable + -Wno-format-signedness # clang 19.1 gcc 5.1 appleclang 17.0 # In clang-cl enums are signed ints by default + ) + endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 21.1) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 26.4)) + list(APPEND _picky_enable + -Warray-compare # clang 20.1 gcc 12.0 appleclang 26.4 + -Wc++-hidden-decl # clang 21.1 appleclang 26.4 + -Wimplicit-int-enum-cast # clang 21.1 + -Wjump-misses-init # clang 21.1 gcc 4.5 appleclang 26.4 + -Wno-implicit-void-ptr-cast # clang 21.1 appleclang 26.4 + -Wtentative-definition-compat # clang 21.1 appleclang 26.4 + ) + if(WIN32) + list(APPEND _picky_enable + -Wno-c++-keyword # clang 21.1 appleclang 26.4 # `wchar_t` triggers it on Windows + ) + else() + list(APPEND _picky_enable + -Wc++-keyword # clang 21.1 appleclang 26.4 + ) + endif() + endif() else() # gcc # Enable based on compiler version if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.3) @@ -197,7 +288,7 @@ if(PICKY_COMPILER) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.5) list(APPEND _picky_enable - -Wjump-misses-init # gcc 4.5 + -Wjump-misses-init # clang 21.1 gcc 4.5 appleclang 26.4 ) if(MINGW) list(APPEND _picky_enable @@ -207,23 +298,24 @@ if(PICKY_COMPILER) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8) list(APPEND _picky_enable - -Wdouble-promotion # clang 3.6 gcc 4.6 appleclang 6.3 - -Wformat=2 # clang 3.0 gcc 4.8 + -Wdouble-promotion # clang 3.6 gcc 4.6 appleclang 6.1 + -Wformat=2 # clang 2.7 gcc 4.8 -Wtrampolines # gcc 4.6 ) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.0) list(APPEND _picky_enable - -Warray-bounds=2 # clang 3.0 gcc 5.0 (clang default: -Warray-bounds) + -Warray-bounds=2 # clang 2.9 gcc 5.0 (clang default: -Warray-bounds) + -Wno-format-signedness # clang 19.1 gcc 5.1 appleclang 17.0 ) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0) list(APPEND _picky_enable -Wduplicated-cond # gcc 6.0 - -Wnull-dereference # clang 3.0 gcc 6.0 (clang default) + -Wnull-dereference # clang 2.9 gcc 6.0 (clang default) -fdelete-null-pointer-checks -Wshift-negative-value # clang 3.7 gcc 6.0 (clang default) - -Wshift-overflow=2 # clang 3.0 gcc 6.0 (clang default: -Wshift-overflow) + -Wshift-overflow=2 # clang 2.9 gcc 6.0 (clang default: -Wshift-overflow) -Wunused-const-variable # clang 3.4 gcc 6.0 appleclang 5.1 ) endif() @@ -232,21 +324,21 @@ if(PICKY_COMPILER) -Walloc-zero # gcc 7.0 -Wduplicated-branches # gcc 7.0 -Wformat-truncation=2 # gcc 7.0 - -Wimplicit-fallthrough # clang 4.0 gcc 7.0 + -Wimplicit-fallthrough # clang 4.0 gcc 7.0 appleclang 9.0 -Wrestrict # gcc 7.0 ) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0) list(APPEND _picky_enable -Warith-conversion # gcc 10.0 - -Wenum-conversion # clang 3.2 gcc 10.0 appleclang 4.6 g++ 11.0 + -Wenum-conversion # clang 3.2 gcc 10.0 appleclang 4.2 g++ 11.0 ) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0) list(APPEND _picky_enable - -Warray-compare # clang 20.0 gcc 12.0 + -Warray-compare # clang 20.1 gcc 12.0 appleclang 26.4 -Wenum-int-mismatch # gcc 13.0 - -Wxor-used-as-pow # clang 10.0 gcc 13.0 + -Wxor-used-as-pow # clang 10.0 gcc 13.0 appleclang 12.0 ) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0) @@ -258,12 +350,15 @@ if(PICKY_COMPILER) endif() endif() - # + # Assemble list of flags set(_picky_skipped "") foreach(_ccopt IN LISTS _picky_enable) - string(REGEX MATCH "-W([a-z0-9-]+)" _ccmatch "${_ccopt}") - if(_ccmatch AND CMAKE_C_FLAGS MATCHES "-Wno-${CMAKE_MATCH_1}" AND NOT _ccopt STREQUAL "-Wall" AND NOT _ccopt MATCHES "^-Wno-") + string(REGEX MATCH "-W([a-z0-9+-]+)" _ccmatch "${_ccopt}") + string(REPLACE "+" "\\+" _cmake_match_1 "${CMAKE_MATCH_1}") # escape '+' to make it a valid regex + if(_ccmatch AND "${CMAKE_C_FLAGS} " MATCHES "-Wno-${_cmake_match_1} " AND + NOT _ccopt STREQUAL "-Wall" AND + NOT _ccopt MATCHES "^-Wno-") string(APPEND _picky_skipped " ${_ccopt}") else() list(APPEND _picky "${_ccopt}") @@ -286,15 +381,6 @@ if(PICKY_COMPILER) endforeach() if(CMAKE_C_COMPILER_ID STREQUAL "GNU") - if(CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5) - # Avoid false positives - list(APPEND _picky "-Wno-shadow") - list(APPEND _picky "-Wno-unreachable-code") - endif() - if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.2 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.6) - # GCC <4.6 do not support #pragma to suppress warnings locally. Disable them globally instead. - list(APPEND _picky "-Wno-overlength-strings") - endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.0 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.7) list(APPEND _picky "-Wno-missing-field-initializers") # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36750 endif() @@ -305,7 +391,7 @@ if(PICKY_COMPILER) list(APPEND _picky "-Wno-conversion") # Avoid false positives endif() endif() - elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1944) # Skip for untested/unreleased newer versions + elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1950) # Skip for untested/unreleased newer versions list(APPEND _picky "-Wall") list(APPEND _picky "-wd4061") # enumerator 'A' in switch of enum 'B' is not explicitly handled by a case label list(APPEND _picky "-wd4191") # 'type cast': unsafe conversion from 'FARPROC' to 'void (__cdecl *)(void)' @@ -314,12 +400,11 @@ if(PICKY_COMPILER) list(APPEND _picky "-wd4548") # expression before comma has no effect; expected expression with side-effect (in FD_SET()) list(APPEND _picky "-wd4574") # 'M' is defined to be '0': did you mean to use '#if M'? (in ws2tcpip.h) list(APPEND _picky "-wd4668") # 'M' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' (in winbase.h) - list(APPEND _picky "-wd4710") # 'snprintf': function not inlined + list(APPEND _picky "-wd4710") # 'fprintf'/'printf'/'sscanf': function not inlined (in tests, with VS2022+ Release) list(APPEND _picky "-wd4711") # function 'A' selected for automatic inline expansion # volatile access of '' is subject to /volatile: setting; # consider using __iso_volatile_load/store intrinsic functions (ARM64) list(APPEND _picky "-wd4746") - list(APPEND _picky "-wd4774") # 'snprintf': format string expected in argument 3 is not a string literal list(APPEND _picky "-wd4820") # 'A': 'N' bytes padding added after data member 'B' if(MSVC_VERSION GREATER_EQUAL 1900) list(APPEND _picky "-wd5045") # Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified @@ -345,6 +430,21 @@ if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND MSVC) endforeach() endif() +if(CMAKE_C_STANDARD STREQUAL 90) + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.0) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.2)) + list(APPEND _picky "-Wno-c99-extensions") # Avoid: warning: '_Bool' is a C99 extension + endif() + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.9) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.1)) + list(APPEND _picky "-Wno-comma") # Just silly + endif() +endif() + +if(DOS AND CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0) + list(APPEND _picky "-Wno-arith-conversion") # Avoid warnings in DJGPP's built-in FD_SET() macro +endif() + if(_picky_nocheck OR _picky) set(_picky_tmp "${_picky_nocheck}" "${_picky}") string(REPLACE ";" " " _picky_tmp "${_picky_tmp}") diff --git a/CMake/Utilities.cmake b/CMake/Utilities.cmake index 335713c84c..f86a6aa1e8 100644 --- a/CMake/Utilities.cmake +++ b/CMake/Utilities.cmake @@ -59,6 +59,11 @@ function(curl_dumptargetprops _target) string(REPLACE "\n" ";" _cmake_property_list "${_cmake_property_list}") list(REMOVE_DUPLICATES _cmake_property_list) list(REMOVE_ITEM _cmake_property_list "") + list(APPEND _cmake_property_list "INTERFACE_LIBCURL_PC_MODULES") + get_target_property(_target_imported ${_target} IMPORTED) + if(NOT _target_imported) + list(REMOVE_ITEM _cmake_property_list "LOCATION" "LOCATION_" "MACOSX_PACKAGE_LOCATION" "VS_DEPLOYMENT_LOCATION") + endif() foreach(_prop IN LISTS _cmake_property_list) if(_prop MATCHES "") foreach(_config IN ITEMS "DEBUG" "RELEASE" "MINSIZEREL" "RELWITHDEBINFO") diff --git a/CMake/cmake_uninstall.cmake.in b/CMake/cmake_uninstall.in.cmake similarity index 100% rename from CMake/cmake_uninstall.cmake.in rename to CMake/cmake_uninstall.in.cmake diff --git a/CMake/curl-config.cmake.in b/CMake/curl-config.cmake.in deleted file mode 100644 index d1582b8d41..0000000000 --- a/CMake/curl-config.cmake.in +++ /dev/null @@ -1,77 +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 -# -########################################################################### -@PACKAGE_INIT@ - -include(CMakeFindDependencyMacro) -if("@USE_OPENSSL@") - if("@OPENSSL_VERSION_MAJOR@") - find_dependency(OpenSSL "@OPENSSL_VERSION_MAJOR@") - else() - find_dependency(OpenSSL) - endif() -endif() -if("@HAVE_LIBZ@") - find_dependency(ZLIB "@ZLIB_VERSION_MAJOR@") -endif() - -include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") - -# Alias for either shared or static library -if(NOT TARGET @PROJECT_NAME@::@LIB_NAME@) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.11 AND CMAKE_VERSION VERSION_LESS 3.18) - set_target_properties(@PROJECT_NAME@::@LIB_SELECTED@ PROPERTIES IMPORTED_GLOBAL TRUE) - endif() - add_library(@PROJECT_NAME@::@LIB_NAME@ ALIAS @PROJECT_NAME@::@LIB_SELECTED@) -endif() - -# For compatibility with CMake's FindCURL.cmake -set(CURL_VERSION_STRING "@CURLVERSION@") -set(CURL_LIBRARIES @PROJECT_NAME@::@LIB_NAME@) -set_and_check(CURL_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") - -set(CURL_SUPPORTED_PROTOCOLS "@CURL_SUPPORTED_PROTOCOLS_LIST@") -set(CURL_SUPPORTED_FEATURES "@CURL_SUPPORTED_FEATURES_LIST@") - -foreach(_item IN LISTS CURL_SUPPORTED_PROTOCOLS CURL_SUPPORTED_FEATURES) - set(CURL_SUPPORTS_${_item} TRUE) -endforeach() - -set(_missing_req "") -foreach(_item IN LISTS CURL_FIND_COMPONENTS) - if(CURL_SUPPORTS_${_item}) - set(CURL_${_item}_FOUND TRUE) - elseif(CURL_FIND_REQUIRED_${_item}) - list(APPEND _missing_req ${_item}) - endif() -endforeach() - -if(_missing_req) - string(REPLACE ";" " " _missing_req "${_missing_req}") - if(CURL_FIND_REQUIRED) - message(FATAL_ERROR "CURL: missing required components: ${_missing_req}") - endif() - unset(_missing_req) -endif() - -check_required_components("@PROJECT_NAME@") diff --git a/CMake/curl-config.in.cmake b/CMake/curl-config.in.cmake new file mode 100644 index 0000000000..317477197f --- /dev/null +++ b/CMake/curl-config.in.cmake @@ -0,0 +1,196 @@ +#*************************************************************************** +# _ _ ____ _ +# 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 +# +########################################################################### +@PACKAGE_INIT@ + +option(CURL_USE_CMAKECONFIG "Enable detecting @PROJECT_NAME@ dependencies via CMake Config. Default: @CURL_USE_CMAKECONFIG@" + "@CURL_USE_CMAKECONFIG@") +option(CURL_USE_PKGCONFIG "Enable pkg-config to detect @PROJECT_NAME@ dependencies. Default: @CURL_USE_PKGCONFIG@" + "@CURL_USE_PKGCONFIG@") + +if(CMAKE_VERSION VERSION_LESS @CMAKE_MINIMUM_REQUIRED_VERSION@) + message(STATUS "@PROJECT_NAME@: @PROJECT_NAME@-specific Find modules require " + "CMake @CMAKE_MINIMUM_REQUIRED_VERSION@ or upper, found: ${CMAKE_VERSION}.") +endif() + +include(CMakeFindDependencyMacro) + +if("@HAVE_THREADS_POSIX@" OR "@HAVE_THREADS_POSIX_BORINGSSL@") + find_dependency(Threads) # for Threads::Threads +endif() + +if("@USE_OPENSSL@") + if("@OPENSSL_VERSION_MAJOR@") + find_dependency(OpenSSL "@OPENSSL_VERSION_MAJOR@") + else() + find_dependency(OpenSSL) + endif() + # Define lib duplicate to fixup lib order for GCC binutils ld in static builds + if(TARGET OpenSSL::Crypto AND NOT TARGET CURL::OpenSSL_Crypto) + add_library(CURL::OpenSSL_Crypto INTERFACE IMPORTED) + set_target_properties(CURL::OpenSSL_Crypto PROPERTIES INTERFACE_LINK_LIBRARIES OpenSSL::Crypto) + endif() +endif() +if("@HAVE_LIBZ@") + find_dependency(ZLIB "@ZLIB_VERSION_MAJOR@") + # Define lib duplicate to fixup lib order for GCC binutils ld in static builds + if(TARGET ZLIB::ZLIB AND NOT TARGET CURL::ZLIB) + add_library(CURL::ZLIB INTERFACE IMPORTED) + set_target_properties(CURL::ZLIB PROPERTIES INTERFACE_LINK_LIBRARIES ZLIB::ZLIB) + endif() +endif() + +set(_curl_cmake_module_path_save ${CMAKE_MODULE_PATH}) +list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +set(_curl_libs "") + +if("@HAVE_BROTLI@") + find_dependency(Brotli MODULE) + list(APPEND _curl_libs CURL::brotli) +endif() +if("@USE_ARES@") + find_dependency(Cares MODULE) + list(APPEND _curl_libs CURL::cares) +endif() +if("@HAVE_GSSAPI@") + find_dependency(GSS MODULE) + list(APPEND _curl_libs CURL::gss) +endif() +if("@USE_BACKTRACE@") + find_dependency(Libbacktrace MODULE) + list(APPEND _curl_libs CURL::libbacktrace) +endif() +if("@USE_GSASL@") + find_dependency(Libgsasl MODULE) + list(APPEND _curl_libs CURL::libgsasl) +endif() +if(NOT "@USE_WIN32_LDAP@" AND NOT "@CURL_DISABLE_LDAP@") + find_dependency(LDAP MODULE) + list(APPEND _curl_libs CURL::ldap) +endif() +if("@HAVE_LIBIDN2@") + find_dependency(Libidn2 MODULE) + list(APPEND _curl_libs CURL::libidn2) +endif() +if("@USE_LIBPSL@") + find_dependency(Libpsl MODULE) + list(APPEND _curl_libs CURL::libpsl) +endif() +if("@USE_LIBSSH@") + find_dependency(Libssh MODULE) + list(APPEND _curl_libs CURL::libssh) +endif() +if("@USE_LIBSSH2@") + find_dependency(Libssh2 MODULE) + list(APPEND _curl_libs CURL::libssh2) +endif() +if("@USE_LIBUV@") + find_dependency(Libuv MODULE) + list(APPEND _curl_libs CURL::libuv) +endif() +if("@USE_MBEDTLS@") + find_dependency(MbedTLS MODULE) + list(APPEND _curl_libs CURL::mbedtls) +endif() +if("@USE_NGHTTP2@") + find_dependency(NGHTTP2 MODULE) + list(APPEND _curl_libs CURL::nghttp2) +endif() +if("@USE_NGHTTP3@") + find_dependency(NGHTTP3 MODULE) + list(APPEND _curl_libs CURL::nghttp3) +endif() +if("@USE_NGTCP2@") + find_dependency(NGTCP2 MODULE) + list(APPEND _curl_libs CURL::ngtcp2) +endif() +if("@USE_GNUTLS@") + find_dependency(GnuTLS MODULE) + list(APPEND _curl_libs CURL::gnutls) + find_dependency(Nettle MODULE) + list(APPEND _curl_libs CURL::nettle) +endif() +if("@USE_QUICHE@") + find_dependency(Quiche MODULE) + list(APPEND _curl_libs CURL::quiche) +endif() +if("@USE_RUSTLS@") + find_dependency(Rustls MODULE) + list(APPEND _curl_libs CURL::rustls) +endif() +if("@USE_WOLFSSL@") + find_dependency(WolfSSL MODULE) + list(APPEND _curl_libs CURL::wolfssl) +endif() +if("@HAVE_ZSTD@") + find_dependency(Zstd MODULE) + list(APPEND _curl_libs CURL::zstd) +endif() + +set(CMAKE_MODULE_PATH ${_curl_cmake_module_path_save}) + +# Define lib duplicate to fixup lib order for GCC binutils ld in static builds +if(WIN32 AND NOT TARGET CURL::win32_winsock) + add_library(CURL::win32_winsock INTERFACE IMPORTED) + set_target_properties(CURL::win32_winsock PROPERTIES INTERFACE_LINK_LIBRARIES "ws2_32") +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") + +# Alias for either shared or static library +if(NOT TARGET @PROJECT_NAME@::@LIB_NAME@) + add_library(@PROJECT_NAME@::@LIB_NAME@ ALIAS @PROJECT_NAME@::@LIB_SELECTED@) +endif() + +# For compatibility with CMake's FindCURL.cmake +set(CURL_VERSION_STRING "@CURLVERSION@") +set(CURL_LIBRARIES @PROJECT_NAME@::@LIB_NAME@) +set(CURL_LIBRARIES_PRIVATE "@LIBCURL_PC_LIBS_PRIVATE_LIST@") +set_and_check(CURL_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") + +set(CURL_SUPPORTED_PROTOCOLS "@CURL_SUPPORTED_PROTOCOLS_LIST@") +set(CURL_SUPPORTED_FEATURES "@CURL_SUPPORTED_FEATURES_LIST@") + +foreach(_curl_item IN LISTS CURL_SUPPORTED_PROTOCOLS CURL_SUPPORTED_FEATURES) + set(CURL_SUPPORTS_${_curl_item} TRUE) +endforeach() + +set(_curl_missing_req "") +foreach(_curl_item IN LISTS CURL_FIND_COMPONENTS) + if(CURL_SUPPORTS_${_curl_item}) + set(CURL_${_curl_item}_FOUND TRUE) + elseif(CURL_FIND_REQUIRED_${_curl_item}) + list(APPEND _curl_missing_req ${_curl_item}) + endif() +endforeach() + +if(_curl_missing_req) + string(REPLACE ";" " " _curl_missing_req "${_curl_missing_req}") + if(CURL_FIND_REQUIRED) + message(FATAL_ERROR "@PROJECT_NAME@: missing required components: ${_curl_missing_req}") + endif() + unset(_curl_missing_req) +endif() + +check_required_components("@PROJECT_NAME@") diff --git a/CMake/unix-cache.cmake b/CMake/unix-cache.cmake index 2c85626a49..8ecd206186 100644 --- a/CMake/unix-cache.cmake +++ b/CMake/unix-cache.cmake @@ -88,7 +88,6 @@ elseif(CYGWIN OR set(HAVE_FSETXATTR_5 1) set(HAVE_FSETXATTR_6 0) endif() -set(HAVE_FTRUNCATE 1) set(HAVE_GETADDRINFO 1) if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_GETADDRINFO_THREADSAFE 0) @@ -142,7 +141,7 @@ set(HAVE_GETRLIMIT 1) set(HAVE_GETSOCKNAME 1) set(HAVE_GETTIMEOFDAY 1) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(HAVE_GLIBC_STRERROR_R 1) + # Depends on C library. else() set(HAVE_GLIBC_STRERROR_R 0) endif() @@ -164,12 +163,12 @@ else() endif() set(HAVE_LIBGEN_H 1) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(HAVE_LINUX_TCP_H 1) + # Requires Linux kernel userspace headers. Expected with glibc. May be missing by default with MUSL. else() set(HAVE_LINUX_TCP_H 0) endif() set(HAVE_LOCALE_H 1) -set(HAVE_LONGLONG 1) +set(HAVE_LOCALTIME_R 1) if(APPLE) set(HAVE_MACH_ABSOLUTE_TIME 1) endif() @@ -179,7 +178,6 @@ if(APPLE OR else() set(HAVE_MEMRCHR 1) endif() -set(HAVE_MSG_NOSIGNAL 1) set(HAVE_NETDB_H 1) if(ANDROID) set(HAVE_NETINET_IN6_H 1) @@ -196,15 +194,18 @@ if(APPLE OR CYGWIN) set(HAVE_PIPE2 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + BSD OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") + CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "SunOS") set(HAVE_PIPE2 1) endif() set(HAVE_POLL 1) set(HAVE_POLL_H 1) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(HAVE_POSIX_STRERROR_R 0) + # Depends on C library. else() set(HAVE_POSIX_STRERROR_R 1) endif() @@ -223,30 +224,22 @@ else() endif() set(HAVE_SENDMSG 1) set(HAVE_SETLOCALE 1) -if(CYGWIN OR - CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(HAVE_SETMODE 0) -else() - set(HAVE_SETMODE 1) -endif() set(HAVE_SETRLIMIT 1) set(HAVE_SETSOCKOPT_SO_NONBLOCK 0) set(HAVE_SIGACTION 1) set(HAVE_SIGINTERRUPT 1) set(HAVE_SIGNAL 1) set(HAVE_SIGSETJMP 1) -set(HAVE_SNPRINTF 1) set(HAVE_SOCKADDR_IN6_SIN6_ADDR 1) set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1) set(HAVE_SOCKET 1) set(HAVE_SOCKETPAIR 1) set(HAVE_STDATOMIC_H 1) set(HAVE_STDBOOL_H 1) -set(HAVE_STDDEF_H 1) -set(HAVE_STDINT_H 1) +set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() +set(HAVE_STDINT_H 1) # detected by CMake internally in check_type_size() set(HAVE_STRCASECMP 1) set(HAVE_STRCMPI 0) -set(HAVE_STRDUP 1) set(HAVE_STRERROR_R 1) set(HAVE_STRICMP 0) set(HAVE_STRINGS_H 1) @@ -306,8 +299,5 @@ set(HAVE_UTIME 1) set(HAVE_UTIMES 1) set(HAVE_UTIME_H 1) set(HAVE_WRITABLE_ARGV 1) -if(CYGWIN) - set(HAVE__SETMODE 1) -endif() set(STDC_HEADERS 1) set(USE_UNIX_SOCKETS 1) diff --git a/CMake/win32-cache.cmake b/CMake/win32-cache.cmake index 058c35dd8d..bd9bb6e3fc 100644 --- a/CMake/win32-cache.cmake +++ b/CMake/win32-cache.cmake @@ -29,11 +29,9 @@ if(MINGW) set(HAVE_BASENAME 1) set(HAVE_BOOL_T 1) # = HAVE_STDBOOL_H set(HAVE_DIRENT_H 1) - set(HAVE_FTRUNCATE 1) set(HAVE_GETTIMEOFDAY 1) set(HAVE_LIBGEN_H 1) set(HAVE_OPENDIR 1) - set(HAVE_SNPRINTF 1) set(HAVE_STDBOOL_H 1) set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() set(HAVE_STDINT_H 1) # detected by CMake internally in check_type_size() @@ -43,7 +41,6 @@ if(MINGW) set(HAVE_UTIME_H 1) # wrapper to sys/utime.h else() set(HAVE_DIRENT_H 0) - set(HAVE_FTRUNCATE 0) set(HAVE_GETTIMEOFDAY 0) set(HAVE_LIBGEN_H 0) set(HAVE_OPENDIR 0) @@ -53,22 +50,13 @@ else() if(MSVC) set(HAVE_UNISTD_H 0) set(HAVE_STDDEF_H 1) # detected by CMake internally in check_type_size() - if(MSVC_VERSION GREATER_EQUAL 1600) - set(HAVE_STDINT_H 1) # detected by CMake internally in check_type_size() - else() - set(HAVE_STDINT_H 0) # detected by CMake internally in check_type_size() - endif() + set(HAVE_STDINT_H 1) # detected by CMake internally in check_type_size() if(MSVC_VERSION GREATER_EQUAL 1800) set(HAVE_STDBOOL_H 1) else() set(HAVE_STDBOOL_H 0) endif() set(HAVE_BOOL_T "${HAVE_STDBOOL_H}") - if(MSVC_VERSION GREATER_EQUAL 1900) - set(HAVE_SNPRINTF 1) - else() - set(HAVE_SNPRINTF 0) - endif() set(HAVE_BASENAME 0) endif() endif() @@ -127,8 +115,8 @@ set(HAVE_IOCTL_SIOCGIFADDR 0) set(HAVE_IO_H 1) set(HAVE_LINUX_TCP_H 0) set(HAVE_LOCALE_H 1) +set(HAVE_LOCALTIME_R 0) set(HAVE_MEMRCHR 0) -set(HAVE_MSG_NOSIGNAL 0) set(HAVE_NETDB_H 0) set(HAVE_NETINET_IN6_H 0) set(HAVE_NETINET_IN_H 0) @@ -147,7 +135,6 @@ set(HAVE_SEND 1) set(HAVE_SENDMMSG 0) set(HAVE_SENDMSG 0) set(HAVE_SETLOCALE 1) -set(HAVE_SETMODE 1) set(HAVE_SETRLIMIT 0) set(HAVE_SETSOCKOPT_SO_NONBLOCK 0) set(HAVE_SIGACTION 0) @@ -157,7 +144,6 @@ set(HAVE_SIGSETJMP 0) set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1) set(HAVE_SOCKET 1) set(HAVE_SOCKETPAIR 0) -set(HAVE_STRDUP 1) set(HAVE_STRERROR_R 0) set(HAVE_STROPTS_H 0) set(HAVE_STRUCT_SOCKADDR_STORAGE 1) @@ -177,7 +163,6 @@ set(HAVE_TERMIO_H 0) set(HAVE_TIME_T_UNSIGNED 0) set(HAVE_UTIME 1) set(HAVE_UTIMES 0) -set(HAVE__SETMODE 1) set(STDC_HEADERS 1) # Types and sizes @@ -188,11 +173,11 @@ set(HAVE_SIZEOF_SUSECONDS_T 0) if(MINGW OR MSVC) curl_prefill_type_size("INT" 4) curl_prefill_type_size("LONG" 4) - curl_prefill_type_size("LONG_LONG" 8) curl_prefill_type_size("__INT64" 8) curl_prefill_type_size("CURL_OFF_T" 8) - # CURL_SOCKET_T, SIZE_T: 8 for _WIN64, 4 otherwise - # TIME_T: 8 for _WIN64 or UCRT or MSVC and not Windows CE, 4 otherwise + curl_prefill_type_size("CURL_SOCKET_T" ${CMAKE_SIZEOF_VOID_P}) + curl_prefill_type_size("SIZE_T" ${CMAKE_SIZEOF_VOID_P}) + # TIME_T: 8 for _WIN64 or UCRT or MSVC, 4 otherwise # Also 4 for non-UCRT 32-bit when _USE_32BIT_TIME_T is set. # mingw-w64 sets _USE_32BIT_TIME_T unless __MINGW_USE_VC2005_COMPAT is explicit defined. if(MSVC) @@ -200,30 +185,8 @@ if(MINGW OR MSVC) set(HAVE_FILE_OFFSET_BITS 0) curl_prefill_type_size("OFF_T" 4) else() - # SSIZE_T: 8 for _WIN64, 4 otherwise + curl_prefill_type_size("SSIZE_T" ${CMAKE_SIZEOF_VOID_P}) set(HAVE_FILE_OFFSET_BITS 1) # mingw-w64 v3+ curl_prefill_type_size("OFF_T" 8) # mingw-w64 v3+ endif() endif() - -# Windows CE exceptions - -if(WINCE) - set(HAVE_FREEADDRINFO 0) - set(HAVE_GETADDRINFO 0) - set(HAVE_LOCALE_H 0) - set(HAVE_SETLOCALE 0) - set(HAVE_SETMODE 0) - set(HAVE_SIGNAL 0) - set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 0) - curl_prefill_type_size("CURL_SOCKET_T" 4) - curl_prefill_type_size("TIME_T" 4) - curl_prefill_type_size("SIZE_T" 4) - if(MINGW32CE) - set(HAVE_STRTOK_R 0) - set(HAVE__SETMODE 0) - set(HAVE_FILE_OFFSET_BITS 0) - curl_prefill_type_size("SSIZE_T" 4) - curl_prefill_type_size("OFF_T" 4) - endif() -endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 0265162e74..611388024f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,9 +21,8 @@ # SPDX-License-Identifier: curl # ########################################################################### -# by Tetetest and Sukender (Benoit Neil) -cmake_minimum_required(VERSION 3.7...3.16 FATAL_ERROR) +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) message(STATUS "Using CMake version ${CMAKE_VERSION}") # Collect command-line arguments for buildinfo.txt. @@ -37,7 +36,7 @@ if(NOT "$ENV{CURL_BUILDINFO}$ENV{CURL_CI}$ENV{CI}" STREQUAL "") get_property(_cache_var_type CACHE ${_cache_var} PROPERTY TYPE) get_property(_cache_var_value CACHE ${_cache_var} PROPERTY VALUE) if(_cache_var_type STREQUAL "UNINITIALIZED") - set(_cache_var_type) + set(_cache_var_type "") else() set(_cache_var_type ":${_cache_var_type}") endif() @@ -46,7 +45,7 @@ if(NOT "$ENV{CURL_BUILDINFO}$ENV{CURL_CI}$ENV{CI}" STREQUAL "") endforeach() endif() -set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH}) +list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake") include(Utilities) include(Macros) include(CMakeDependentOption) @@ -67,41 +66,10 @@ project(CURL LANGUAGES C) # CMake does not recognize some targets accurately. Touch up configuration manually as a workaround. -if(WINDOWS_STORE AND MINGW) # mingw UWP build +if(WINDOWS_STORE AND MINGW) # MinGW UWP build # CMake (as of v3.31.2) gets confused and applies the MSVC rc.exe command-line - # template to windres. Reset it to the windres template via 'Modules/Platform/Windows-windres.cmake': + # template to windres. Reset it to the windres template as in 'Modules/Platform/Windows-windres.cmake': set(CMAKE_RC_COMPILE_OBJECT " -O coff ") -elseif(WIN32 AND WINCE AND CMAKE_C_COMPILER_ID STREQUAL "GNU") # mingw32ce build - if(NOT MINGW32CE_LIBRARY_DIR) - message(FATAL_ERROR "Set MINGW32CE_LIBRARY_DIR variable to the mingw32ce platform library directory.") - endif() - - set(MINGW 1) - set(MINGW32CE 1) - - # Build implib with libcurl DLL. Copied from CMake's 'Modules/Platform/Windows-GNU.cmake'. - set(CMAKE_C_CREATE_SHARED_LIBRARY " ") - string(APPEND CMAKE_C_CREATE_SHARED_LIBRARY " -o -Wl,--out-implib,") - string(APPEND CMAKE_C_CREATE_SHARED_LIBRARY " ${CMAKE_GNULD_IMAGE_VERSION} ") - - # Build resources. Copied from CMake's 'Modules/Platform/Windows-windres.cmake'. - set(CMAKE_RC_COMPILE_OBJECT " -O coff ") - enable_language(RC) - - # To compile long long integer literals - set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-std=gnu99") - string(APPEND CMAKE_REQUIRED_FLAGS " -std=gnu99") - - set(CMAKE_C_COMPILE_OPTIONS_PIC "") # CMake sets it to '-fPIC', confusing the toolchain and breaking builds. Zap it. - - set(CMAKE_STATIC_LIBRARY_PREFIX "lib") - set(CMAKE_STATIC_LIBRARY_SUFFIX ".a") - set(CMAKE_SHARED_LIBRARY_PREFIX "lib") - set(CMAKE_SHARED_LIBRARY_SUFFIX ".dll") - set(CMAKE_IMPORT_LIBRARY_PREFIX "lib") - set(CMAKE_IMPORT_LIBRARY_SUFFIX ".dll.a") - set(CMAKE_FIND_LIBRARY_PREFIXES "lib" "") - set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll.a" ".a" ".lib") elseif(DOS AND CMAKE_C_COMPILER_ID STREQUAL "GNU") # DJGPP set(CMAKE_STATIC_LIBRARY_PREFIX "lib") set(CMAKE_STATIC_LIBRARY_SUFFIX ".a") @@ -130,9 +98,6 @@ endif() if(WIN32) string(APPEND _target_flags " WIN32") endif() -if(WINCE) - string(APPEND _target_flags " WINCE") -endif() if(WINDOWS_STORE) string(APPEND _target_flags " UWP") endif() @@ -148,6 +113,13 @@ endif() if(CMAKE_C_COMPILER_ID STREQUAL "GNU") string(APPEND _target_flags " GCC") endif() +if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang") + string(APPEND _target_flags " APPLE-CLANG") +elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND MSVC) + string(APPEND _target_flags " CLANG-CL") +elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") + string(APPEND _target_flags " LLVM-CLANG") +endif() if(MINGW) string(APPEND _target_flags " MINGW") endif() @@ -160,6 +132,9 @@ endif() if(CMAKE_CROSSCOMPILING) string(APPEND _target_flags " CROSS") endif() +if(CMAKE_C_STANDARD) + string(APPEND _target_flags " C${CMAKE_C_STANDARD}") +endif() message(STATUS "CMake platform flags:${_target_flags}") if(CMAKE_CROSSCOMPILING) @@ -174,6 +149,10 @@ else() set(CURL_OS "\"${CMAKE_SYSTEM_NAME}\"") endif() +if(CURL_PATCHSTAMP) + set(CURL_PATCHSTAMP "\"${CURL_PATCHSTAMP}\"") +endif() + set(LIB_NAME "libcurl") set(EXE_NAME "curl") @@ -197,34 +176,37 @@ endif() option(CURL_WERROR "Turn compiler warnings into errors" OFF) option(PICKY_COMPILER "Enable picky compiler options" ON) option(BUILD_CURL_EXE "Build curl executable" ON) -option(BUILD_SHARED_LIBS "Build shared libraries" ON) +get_property(_has_shared GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS) +option(BUILD_SHARED_LIBS "Build shared libraries" ${_has_shared}) option(BUILD_STATIC_LIBS "Build static libraries" OFF) option(BUILD_STATIC_CURL "Build curl executable with static libcurl" OFF) option(ENABLE_ARES "Enable c-ares support" OFF) option(CURL_DISABLE_INSTALL "Disable installation targets" OFF) +option(CURL_BUILD_EVERYTHING "Build optional build targets (examples, tests) by default" OFF) if(WIN32) option(ENABLE_UNICODE "Use the Unicode version of the Windows API functions" OFF) - if(WINDOWS_STORE OR WINCE) + if(WINDOWS_STORE) set(ENABLE_UNICODE ON) endif() if(ENABLE_UNICODE) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "UNICODE" "_UNICODE") - if(MINGW AND NOT MINGW32CE) + if(MINGW) set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-municode") endif() endif() # Apply to all feature checks list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DWIN32_LEAN_AND_MEAN") - if(MSVC) - list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_CRT_NONSTDC_NO_DEPRECATE") # for strdup() detection - endif() set(CURL_TARGET_WINDOWS_VERSION "" CACHE STRING "Minimum target Windows version as hex string") if(CURL_TARGET_WINDOWS_VERSION) - set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") - list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") # Apply to all feature checks + if(CURL_TARGET_WINDOWS_VERSION MATCHES "^0x[0-9a-fA-F]+$") + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") # Apply to all feature checks + else() + message(WARNING "CURL_TARGET_WINDOWS_VERSION value '${CURL_TARGET_WINDOWS_VERSION}' is not a valid hex string.") + endif() endif() # Detect actual value of _WIN32_WINNT and store as HAVE_WIN32_WINNT @@ -232,7 +214,7 @@ if(WIN32) if(HAVE_WIN32_WINNT) string(REGEX MATCH "_WIN32_WINNT=0x[0-9a-fA-F]+" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") string(REGEX REPLACE "_WIN32_WINNT=" "" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") - string(REGEX REPLACE "0x([0-9a-f][0-9a-f][0-9a-f])$" "0x0\\1" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") # pad to 4 digits + string(REGEX REPLACE "0x([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F])$" "0x0\\1" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}") # pad to 4 digits string(TOLOWER "${CURL_TEST_OUTPUT}" HAVE_WIN32_WINNT) message(STATUS "Found _WIN32_WINNT=${HAVE_WIN32_WINNT}") endif() @@ -253,7 +235,7 @@ if(WIN32) endif() unset(MINGW64_VERSION CACHE) # Avoid storing in CMake cache endif() -elseif(DOS OR AMIGA) +elseif(DOS) set(BUILD_SHARED_LIBS OFF) set(BUILD_STATIC_LIBS ON) endif() @@ -269,8 +251,6 @@ if(NOT DOS AND NOT AMIGA) option(ENABLE_THREADED_RESOLVER "Enable threaded DNS lookup" ${_enable_threaded_resolver_default}) endif() -include(PickyWarnings) - if(CYGWIN OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "GNU") set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "_GNU_SOURCE") # Required for accept4(), pipe2(), sendmmsg() list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE") # Apply to all feature checks @@ -280,34 +260,84 @@ option(ENABLE_DEBUG "Enable curl debug features (for developing curl itself)" OF if(ENABLE_DEBUG) message(WARNING "This curl build is Debug-enabled and insecure, do not use in production.") endif() -option(ENABLE_CURLDEBUG "Enable TrackMemory debug feature" ${ENABLE_DEBUG}) set(CURL_DEBUG_MACROS "") if(ENABLE_DEBUG) list(APPEND CURL_DEBUG_MACROS "DEBUGBUILD") endif() -if(ENABLE_CURLDEBUG) - list(APPEND CURL_DEBUG_MACROS "CURLDEBUG") -endif() option(CURL_CLANG_TIDY "Run the build through clang-tidy" OFF) if(CURL_CLANG_TIDY) - set(CMAKE_UNITY_BUILD OFF) # clang-tidy is not looking into #included sources, thus not compatible with unity builds. - set(_tidy_checks "") - list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.bzero") # for FD_ZERO() (seen on macOS) - list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.strcpy") - list(APPEND _tidy_checks "-clang-analyzer-optin.performance.Padding") - list(APPEND _tidy_checks "-clang-analyzer-security.ArrayBound") # false positives with clang-tidy v21.1.0 - list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling") - string(REPLACE ";" "," _tidy_checks "${_tidy_checks}") find_program(CLANG_TIDY NAMES "clang-tidy" REQUIRED) - set(CMAKE_C_CLANG_TIDY "${CLANG_TIDY}" "-checks=${_tidy_checks}" "-quiet") - unset(_tidy_checks) + if(NOT CMAKE_C_COMPILER_ID MATCHES "Clang") + set(PICKY_COMPILER OFF) # Do a best effort and skip passing non-clang warning options to clang-tidy. + # This lets through warning options enabled via CURL_WERROR=ON, affecting lib and src. + endif() + set(CURL_DISABLE_TYPECHECK ON) # to improve performance (1.4x), avoid potential interference and bugprone-macro-parentheses. + set(CMAKE_C_CLANG_TIDY "${CLANG_TIDY}") + list(APPEND CMAKE_C_CLANG_TIDY "--config-file=${PROJECT_SOURCE_DIR}/.clang-tidy.yml") if(CURL_WERROR) list(APPEND CMAKE_C_CLANG_TIDY "--warnings-as-errors=*") endif() if(CURL_CLANG_TIDYFLAGS) - list(APPEND CMAKE_C_CLANG_TIDY ${CURL_CLANG_TIDYFLAGS}) + string(REPLACE " " ";" _tidy_flags_list "${CURL_CLANG_TIDYFLAGS}") + list(APPEND CMAKE_C_CLANG_TIDY ${_tidy_flags_list}) + endif() +endif() + +option(CURL_GCC_ANALYZER "Enable GCC --analyzer option" OFF) +if(CURL_GCC_ANALYZER AND CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0) + set(CURL_DISABLE_TYPECHECK ON) # to improve performance (1.1x). + # https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html + set(CURL_ANALYZER_CFLAGS "-fanalyzer") + # disable checks causing false positives only + list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-fd-leak" "-Wno-analyzer-fd-use-without-check" "-Wno-analyzer-file-leak") + list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-infinite-loop") + list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-malloc-leak") + list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-out-of-bounds") +endif() + +option(CURL_CODE_COVERAGE "Enable code coverage build options" OFF) +if(CURL_CODE_COVERAGE) + if(CMAKE_C_COMPILER_ID STREQUAL "GNU") + set(CURL_COVERAGE_MACROS "NDEBUG") + set(CURL_COVERAGE_CFLAGS "-O0" "-g" "-fprofile-arcs") + if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.1) + list(APPEND CURL_COVERAGE_CFLAGS "--coverage") + else() + list(APPEND CURL_COVERAGE_CFLAGS "-ftest-coverage") + endif() + set(CURL_COVERAGE_LIBS "gcov") + elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") + set(CURL_COVERAGE_MACROS "NDEBUG") + set(CURL_COVERAGE_CFLAGS "-O0" "-g" "-fprofile-instr-generate" "-fcoverage-mapping") + set(CURL_COVERAGE_LDFLAGS "-fprofile-instr-generate" "-fcoverage-mapping") + else() + set(CURL_CODE_COVERAGE OFF) + endif() +endif() + +include(PickyWarnings) + +set(CURL_CFLAGS "") # C flags set for libcurl and curl tool (aka public binaries) only + +option(CURL_DROP_UNUSED "Drop unused code and data from built binaries" OFF) +if(CURL_DROP_UNUSED) + if(APPLE) + set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-Wl,-dead_strip") + elseif(MSVC) # Options below are toolchain defaults in Release configurations. + # This option does not seem to have an effect with VS2010: + set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-OPT:REF") + # Optional, but reduces binary size further, with the cost of larger objects/static libraries: + list(APPEND CURL_CFLAGS "-Gy") + elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") + if(WIN32) + # To make -Wl,--gc-sections work on Windows: https://sourceware.org/bugzilla/show_bug.cgi?id=11539 + set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-fno-asynchronous-unwind-tables") + endif() + set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-Wl,--gc-sections") + # Optional, but reduces binary size further, with the cost of larger objects/static libraries: + list(APPEND CURL_CFLAGS "-ffunction-sections" "-fdata-sections") endif() endif() @@ -355,6 +385,14 @@ if(WIN32) endif() endif() +# Override to force-disable or force-enable the use of CMake Configs. +if(MSVC AND NOT VCPKG_TOOLCHAIN AND NOT CMAKE_CROSSCOMPILING) + set(_curl_use_cmakeconfig_default ON) +else() + set(_curl_use_cmakeconfig_default OFF) +endif() +option(CURL_USE_CMAKECONFIG "Enable detecting dependencies via CMake Config" ${_curl_use_cmakeconfig_default}) + # Override to force-disable or force-enable the use of pkg-config. if((UNIX AND NOT ANDROID AND (NOT APPLE OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")) OR VCPKG_TOOLCHAIN OR @@ -365,23 +403,14 @@ else() endif() option(CURL_USE_PKGCONFIG "Enable pkg-config to detect dependencies" ${_curl_use_pkgconfig_default}) -# Initialize variables collecting dependency libs, paths, pkg-config names. +# Initialize variables collecting system and dependency libs. set(CURL_NETWORK_AND_TIME_LIBS "") set(CURL_LIBS "") -set(CURL_LIBDIRS "") -set(LIBCURL_PC_REQUIRES_PRIVATE "") if(ENABLE_ARES) set(USE_ARES 1) - find_package(Cares REQUIRED) - list(APPEND CURL_LIBS ${CARES_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${CARES_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${CARES_PC_REQUIRES}) - include_directories(SYSTEM ${CARES_INCLUDE_DIRS}) - link_directories(${CARES_LIBRARY_DIRS}) - if(CARES_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${CARES_CFLAGS}") - endif() + find_package(Cares MODULE REQUIRED) + list(APPEND CURL_LIBS CURL::cares) endif() include(CurlSymbolHiding) @@ -447,8 +476,8 @@ option(CURL_DISABLE_BINDLOCAL "Disable local binding support" OFF) mark_as_advanced(CURL_DISABLE_BINDLOCAL) option(CURL_DISABLE_NETRC "Disable netrc parser" OFF) mark_as_advanced(CURL_DISABLE_NETRC) -option(CURL_DISABLE_NTLM "Disable NTLM support" OFF) -mark_as_advanced(CURL_DISABLE_NTLM) +option(CURL_ENABLE_NTLM "Enable NTLM support" OFF) +mark_as_advanced(CURL_ENABLE_NTLM) option(CURL_DISABLE_PARSEDATE "Disable date parsing" OFF) mark_as_advanced(CURL_DISABLE_PARSEDATE) option(CURL_DISABLE_POP3 "Disable POP3" OFF) @@ -465,11 +494,11 @@ option(CURL_DISABLE_SHA512_256 "Disable SHA-512/256 hash algorithm" OFF) mark_as_advanced(CURL_DISABLE_SHA512_256) option(CURL_DISABLE_SHUFFLE_DNS "Disable shuffle DNS feature" OFF) mark_as_advanced(CURL_DISABLE_SHUFFLE_DNS) -option(CURL_DISABLE_SMB "Disable SMB" OFF) -mark_as_advanced(CURL_DISABLE_SMB) +option(CURL_ENABLE_SMB "Enable SMB" OFF) +mark_as_advanced(CURL_ENABLE_SMB) option(CURL_DISABLE_SMTP "Disable SMTP" OFF) mark_as_advanced(CURL_DISABLE_SMTP) -option(CURL_DISABLE_SOCKETPAIR "Disable use of socketpair for curl_multi_poll" OFF) +option(CURL_DISABLE_SOCKETPAIR "Disable use of socketpair for curl_multi_poll()" OFF) mark_as_advanced(CURL_DISABLE_SOCKETPAIR) option(CURL_DISABLE_WEBSOCKETS "Disable WebSocket" OFF) mark_as_advanced(CURL_DISABLE_WEBSOCKETS) @@ -477,8 +506,21 @@ option(CURL_DISABLE_TELNET "Disable Telnet" OFF) mark_as_advanced(CURL_DISABLE_TELNET) option(CURL_DISABLE_TFTP "Disable TFTP" OFF) mark_as_advanced(CURL_DISABLE_TFTP) +option(CURL_DISABLE_TYPECHECK "Disable curl_easy_setopt()/curl_easy_getinfo() type checking" OFF) +mark_as_advanced(CURL_DISABLE_TYPECHECK) option(CURL_DISABLE_VERBOSE_STRINGS "Disable verbose strings" OFF) mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS) +option(CURL_DEBUG_GLOBAL_MEM "Debug curl_global_init_mem" OFF) +mark_as_advanced(CURL_DEBUG_GLOBAL_MEM) + +if(CURL_DEBUG_GLOBAL_MEM) + # Set it via the command-line to make it apply to the entire directory. + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURL_DEBUG_GLOBAL_MEM") +endif() +if(CURL_DISABLE_TYPECHECK) + # Set it via the command-line to make it apply to examples also. + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURL_DISABLE_TYPECHECK") +endif() if(CURL_DISABLE_HTTP) set(CURL_DISABLE_ALTSVC ON) @@ -504,16 +546,18 @@ if(HTTP_ONLY) set(CURL_DISABLE_MQTT ON) set(CURL_DISABLE_POP3 ON) set(CURL_DISABLE_RTSP ON) - set(CURL_DISABLE_SMB ON) set(CURL_DISABLE_SMTP ON) set(CURL_DISABLE_TELNET ON) set(CURL_DISABLE_TFTP ON) + set(CURL_DISABLE_WEBSOCKETS ON) endif() -if(WINDOWS_STORE OR WINCE) +if(WINDOWS_STORE) set(CURL_DISABLE_TELNET ON) # telnet code needs fixing to compile for UWP. endif() +option(CURL_LINT "Run lint checks while building" OFF) + find_package(Perl) if(PERL_EXECUTABLE) @@ -527,18 +571,32 @@ if(PERL_EXECUTABLE) COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/firefox-db2pem.sh" "lib/ca-bundle.crt" DEPENDS "${PROJECT_SOURCE_DIR}/scripts/firefox-db2pem.sh" ) + add_custom_target(curl-lint + COMMENT "Running lint checks" VERBATIM USES_TERMINAL + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/badwords-all" + COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/checksrc-all.pl" + COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/spacecheck.pl" + DEPENDS + "${PROJECT_SOURCE_DIR}/scripts/badwords-all" "${PROJECT_SOURCE_DIR}/scripts/badwords" + "${PROJECT_SOURCE_DIR}/scripts/checksrc-all.pl" "${PROJECT_SOURCE_DIR}/scripts/checksrc.pl" + "${PROJECT_SOURCE_DIR}/scripts/spacecheck.pl" + ) + if(CURL_LINT) + set_target_properties(curl-lint PROPERTIES EXCLUDE_FROM_ALL FALSE) + endif() endif() option(BUILD_LIBCURL_DOCS "Build libcurl man pages" ON) option(BUILD_MISC_DOCS "Build misc man pages (e.g. curl-config and mk-ca-bundle)" ON) option(ENABLE_CURL_MANUAL "Build the man page for curl and enable its -M/--manual option" ON) -if((ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS) AND NOT PERL_FOUND) +if((ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS) AND NOT Perl_FOUND) message(WARNING "Perl not found. Will not build manuals.") endif() # If we are on AIX, do the _ALL_SOURCE magic -if(CMAKE_SYSTEM_NAME STREQUAL "AIX") +if(AIX OR CMAKE_SYSTEM_NAME STREQUAL "AIX") set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "_ALL_SOURCE") endif() @@ -561,6 +619,7 @@ include(CheckTypeSize) include(CheckCSourceCompiles) option(_CURL_PREFILL "Fast-track known feature detection results (Windows, some Apple)" "${WIN32}") +mark_as_advanced(_CURL_PREFILL) if(_CURL_PREFILL) if(WIN32) include("${CMAKE_CURRENT_SOURCE_DIR}/CMake/win32-cache.cmake") @@ -584,33 +643,25 @@ if(DOS OR AMIGA) set(HAVE_TIME_T_UNSIGNED 1) endif() -if(ENABLE_THREADED_RESOLVER) - if(WIN32) - set(USE_THREADS_WIN32 ON) - else() - find_package(Threads REQUIRED) - set(USE_THREADS_POSIX ${CMAKE_USE_PTHREADS_INIT}) - set(HAVE_PTHREAD_H ${CMAKE_USE_PTHREADS_INIT}) - list(APPEND CURL_NETWORK_AND_TIME_LIBS ${CMAKE_THREAD_LIBS_INIT}) +if(NOT WIN32) + find_package(Threads) + if(CMAKE_USE_PTHREADS_INIT) + set(HAVE_THREADS_POSIX 1) + list(APPEND CURL_NETWORK_AND_TIME_LIBS Threads::Threads) endif() endif() -# Check for all needed libraries -if(WIN32) - if(WINCE) - set(_win32_winsock "ws2") - else() - set(_win32_winsock "ws2_32") +if(ENABLE_THREADED_RESOLVER) + if(NOT WIN32 AND NOT HAVE_THREADS_POSIX) + message(FATAL_ERROR "Threaded resolver requires POSIX Threads.") endif() - set(_win32_crypt32 "crypt32") - set(_win32_secur32 "secur32") + set(USE_RESOLV_THREADED ON) +elseif(USE_ARES) + set(USE_RESOLV_ARES ON) +endif() - if(MINGW32CE) # FIXME upstream: must specify the full path to avoid CMake converting "ws2" to "ws2.lib" - set(_win32_winsock "${MINGW32CE_LIBRARY_DIR}/lib${_win32_winsock}.a") - set(_win32_crypt32 "${MINGW32CE_LIBRARY_DIR}/lib${_win32_crypt32}.a") - set(_win32_secur32 "${MINGW32CE_LIBRARY_DIR}/lib${_win32_secur32}.a") - endif() -elseif(DOS) +# Check for all needed libraries +if(DOS) if(WATT_ROOT) set(USE_WATT32 ON) # FIXME upstream: must specify the full path to avoid CMake converting "watt" to "watt.lib" @@ -618,7 +669,7 @@ elseif(DOS) include_directories(SYSTEM "${WATT_ROOT}/inc") list(APPEND CMAKE_REQUIRED_INCLUDES "${WATT_ROOT}/inc") else() - message(FATAL_ERROR "Set WATT_ROOT variable to the root installation of Watt-32.") + message(FATAL_ERROR "Set WATT_ROOT variable to the absolute path to the root installation of Watt-32.") endif() elseif(AMIGA) if(AMISSL_INCLUDE_DIR AND AMISSL_STUBS_LIBRARY AND AMISSL_AUTO_LIBRARY) @@ -631,10 +682,10 @@ elseif(AMIGA) set(CURL_USE_OPENSSL ON) set(CURL_CA_FALLBACK ON CACHE BOOL "") endif() -elseif(NOT APPLE) +elseif(NOT WIN32 AND NOT APPLE) check_library_exists("socket" "connect" "" HAVE_LIBSOCKET) if(HAVE_LIBSOCKET) - set(CURL_NETWORK_AND_TIME_LIBS "socket" ${CURL_NETWORK_AND_TIME_LIBS}) + list(PREPEND CURL_NETWORK_AND_TIME_LIBS "socket") endif() endif() @@ -666,7 +717,7 @@ if(ENABLE_IPV6) endif() endif() endif() -if(ENABLE_IPV6 AND NOT WINCE) +if(ENABLE_IPV6) set(USE_IPV6 ON) endif() @@ -700,11 +751,6 @@ else() set(_openssl_default ON) endif() cmake_dependent_option(CURL_USE_OPENSSL "Enable OpenSSL for SSL/TLS" ${_openssl_default} CURL_ENABLE_SSL OFF) -option(USE_OPENSSL_QUIC "Use OpenSSL and nghttp3 libraries for HTTP/3 support" OFF) -if(USE_OPENSSL_QUIC AND NOT CURL_USE_OPENSSL) - message(WARNING "OpenSSL QUIC has been requested, but without enabling OpenSSL. Will not enable QUIC.") - set(USE_OPENSSL_QUIC OFF) -endif() option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenSSL configuration" OFF) curl_count_true(_enabled_ssl_options_count @@ -737,6 +783,26 @@ if(CURL_WINDOWS_SSPI AND NOT WINDOWS_STORE) set(USE_WINDOWS_SSPI ON) endif() +if(APPLE) + option(USE_APPLE_SECTRUST "Use Apple OS-native certificate verification" OFF) + if(USE_APPLE_SECTRUST) + if(NOT CURL_USE_OPENSSL AND NOT CURL_USE_GNUTLS) + message(FATAL_ERROR "Apple SecTrust is only supported with Openssl/GnuTLS") + endif() + find_library(SECURITY_FRAMEWORK NAMES "Security") + mark_as_advanced(SECURITY_FRAMEWORK) + if(NOT SECURITY_FRAMEWORK) + message(FATAL_ERROR "Security framework not found") + endif() + list(APPEND CURL_LIBS "-framework Security") + + set(_use_core_foundation_and_core_services ON) + message(STATUS "Apple OS-native certificate verification enabled") + endif() +else() + set(USE_APPLE_SECTRUST OFF) +endif() + if(_use_core_foundation_and_core_services) find_library(COREFOUNDATION_FRAMEWORK NAMES "CoreFoundation") mark_as_advanced(COREFOUNDATION_FRAMEWORK) @@ -761,7 +827,6 @@ if(CURL_USE_OPENSSL) # Depend on OpenSSL via imported targets. This allows our dependents to # get our dependencies transitively. list(APPEND CURL_LIBS OpenSSL::SSL OpenSSL::Crypto) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "openssl") if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "openssl") set(_valid_default_ssl_backend TRUE) @@ -782,6 +847,16 @@ if(CURL_USE_OPENSSL) cmake_pop_check_state() if(HAVE_BORINGSSL OR HAVE_AWSLC) + if(NOT MSVC AND NOT ANDROID) # BoringSSL/AWS-LC MSVC builds use native Windows threads + find_package(Threads) + if(CMAKE_USE_PTHREADS_INIT) + set(HAVE_THREADS_POSIX_BORINGSSL 1) + list(APPEND CURL_NETWORK_AND_TIME_LIBS Threads::Threads) + list(APPEND CMAKE_REQUIRED_LIBRARIES Threads::Threads) + elseif(OPENSSL_USE_STATIC_LIBS) + message(WARNING "BoringSSL/AWS-LC requires POSIX Threads.") + endif() + endif() if(OPENSSL_USE_STATIC_LIBS AND CMAKE_C_COMPILER_ID MATCHES "Clang") list(APPEND CURL_LIBS "stdc++") list(APPEND CMAKE_REQUIRED_LIBRARIES "stdc++") @@ -789,6 +864,9 @@ if(CURL_USE_OPENSSL) endif() if(HAVE_BORINGSSL) + if(BORINGSSL_VERSION) + set(CURL_BORINGSSL_VERSION "\"${BORINGSSL_VERSION}\"") + endif() set(_openssl "BoringSSL") elseif(HAVE_AWSLC) set(_openssl "AWS-LC") @@ -802,72 +880,55 @@ if(CURL_USE_OPENSSL) endif() if(CURL_USE_MBEDTLS) - find_package(MbedTLS REQUIRED) + find_package(MbedTLS MODULE REQUIRED) if(MBEDTLS_VERSION VERSION_LESS 3.2.0) message(FATAL_ERROR "mbedTLS v3.2.0 or newer is required.") endif() set(_ssl_enabled ON) set(USE_MBEDTLS ON) - list(APPEND CURL_LIBS ${MBEDTLS_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${MBEDTLS_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${MBEDTLS_PC_REQUIRES}) - include_directories(SYSTEM ${MBEDTLS_INCLUDE_DIRS}) - link_directories(${MBEDTLS_LIBRARY_DIRS}) - if(MBEDTLS_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${MBEDTLS_CFLAGS}") - endif() + list(APPEND CURL_LIBS CURL::mbedtls) if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "mbedtls") set(_valid_default_ssl_backend TRUE) endif() set(_curl_ca_bundle_supported TRUE) + + if(MBEDTLS_VERSION VERSION_GREATER_EQUAL 4.0.0) + set(HAVE_MBEDTLS_DES_CRYPT_ECB 0) # pre-fill detection result + endif() + if(NOT DEFINED HAVE_MBEDTLS_DES_CRYPT_ECB) + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_LIBRARIES CURL::mbedtls) + check_function_exists("mbedtls_des_crypt_ecb" HAVE_MBEDTLS_DES_CRYPT_ECB) # in mbedTLS <4 + cmake_pop_check_state() + endif() endif() if(CURL_USE_WOLFSSL) - find_package(WolfSSL REQUIRED) + find_package(WolfSSL MODULE REQUIRED) set(_ssl_enabled ON) set(USE_WOLFSSL ON) - list(APPEND CURL_LIBS ${WOLFSSL_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${WOLFSSL_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${WOLFSSL_PC_REQUIRES}) - include_directories(SYSTEM ${WOLFSSL_INCLUDE_DIRS}) - link_directories(${WOLFSSL_LIBRARY_DIRS}) - if(WOLFSSL_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${WOLFSSL_CFLAGS}") - endif() + list(APPEND CURL_LIBS CURL::wolfssl) if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "wolfssl") set(_valid_default_ssl_backend TRUE) endif() set(_curl_ca_bundle_supported TRUE) + + if(USE_OPENSSL AND WOLFSSL_VERSION VERSION_LESS 5.7.6) + message(FATAL_ERROR "wolfSSL 5.7.6 or newer is required to coexist with OpenSSL.") + endif() + + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "WOLFSSL_OPTIONS_IGNORE_SYS") endif() if(CURL_USE_GNUTLS) - if(CURL_USE_PKGCONFIG) - find_package(PkgConfig QUIET) - pkg_check_modules(GNUTLS "gnutls") - if(GNUTLS_FOUND) - set(GNUTLS_LIBRARIES ${GNUTLS_LINK_LIBRARIES}) - string(REPLACE ";" " " GNUTLS_CFLAGS "${GNUTLS_CFLAGS}") - if(GNUTLS_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${GNUTLS_CFLAGS}") - endif() - endif() - endif() - if(NOT GNUTLS_FOUND) - find_package(GnuTLS REQUIRED) - endif() - find_package(Nettle REQUIRED) + find_package(GnuTLS MODULE REQUIRED) + list(APPEND CURL_LIBS CURL::gnutls) + find_package(Nettle MODULE REQUIRED) + list(APPEND CURL_LIBS CURL::nettle) set(_ssl_enabled ON) set(USE_GNUTLS ON) - list(APPEND CURL_LIBS ${GNUTLS_LIBRARIES} ${NETTLE_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${GNUTLS_LIBRARY_DIRS} ${NETTLE_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "gnutls" ${NETTLE_PC_REQUIRES}) - include_directories(SYSTEM ${GNUTLS_INCLUDE_DIRS} ${NETTLE_INCLUDE_DIRS}) - link_directories(${NETTLE_LIBRARY_DIRS}) - if(NETTLE_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${NETTLE_CFLAGS}") - endif() if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "gnutls") set(_valid_default_ssl_backend TRUE) @@ -876,34 +937,26 @@ if(CURL_USE_GNUTLS) if(NOT DEFINED HAVE_GNUTLS_SRP AND NOT CURL_DISABLE_SRP) cmake_push_check_state() - list(APPEND CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIRS}") - list(APPEND CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}") + list(APPEND CMAKE_REQUIRED_LIBRARIES CURL::gnutls) + # In GnuTLS 3.8.0 (2023-02-10) and upper, this check always succeeds. + # Detecting actual TLS-SRP support needs poking the API at runtime. check_symbol_exists("gnutls_srp_verifier" "gnutls/gnutls.h" HAVE_GNUTLS_SRP) cmake_pop_check_state() endif() endif() if(CURL_USE_RUSTLS) - find_package(Rustls REQUIRED) + find_package(Rustls MODULE REQUIRED) set(_ssl_enabled ON) set(USE_RUSTLS ON) - list(APPEND CURL_LIBS ${RUSTLS_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${RUSTLS_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${RUSTLS_PC_REQUIRES}) - include_directories(SYSTEM ${RUSTLS_INCLUDE_DIRS}) - link_directories(${RUSTLS_LIBRARY_DIRS}) - if(RUSTLS_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${RUSTLS_CFLAGS}") - endif() + list(APPEND CURL_LIBS CURL::rustls) if(NOT DEFINED HAVE_RUSTLS_SUPPORTED_HPKE) if(RUSTLS_VERSION AND RUSTLS_VERSION VERSION_GREATER_EQUAL 0.15) set(HAVE_RUSTLS_SUPPORTED_HPKE TRUE) elseif(NOT RUSTLS_VERSION) cmake_push_check_state() - list(APPEND CMAKE_REQUIRED_INCLUDES "${RUSTLS_INCLUDE_DIRS}") - list(APPEND CMAKE_REQUIRED_LIBRARIES "${RUSTLS_LIBRARIES}") - curl_required_libpaths("${RUSTLS_LIBRARY_DIRS}") + list(APPEND CMAKE_REQUIRED_LIBRARIES CURL::rustls) check_symbol_exists("rustls_supported_hpke" "rustls.h" HAVE_RUSTLS_SUPPORTED_HPKE) cmake_pop_check_state() endif() @@ -932,21 +985,13 @@ if(ZLIB_FOUND) # Depend on ZLIB via imported targets. This allows our dependents to # get our dependencies transitively. list(APPEND CURL_LIBS ZLIB::ZLIB) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "zlib") endif() set(HAVE_BROTLI OFF) curl_dependency_option(CURL_BROTLI Brotli "brotli") if(BROTLI_FOUND) set(HAVE_BROTLI ON) - list(APPEND CURL_LIBS ${BROTLI_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${BROTLI_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${BROTLI_PC_REQUIRES}) - include_directories(SYSTEM ${BROTLI_INCLUDE_DIRS}) - link_directories(${BROTLI_LIBRARY_DIRS}) - if(BROTLI_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${BROTLI_CFLAGS}") - endif() + list(APPEND CURL_LIBS CURL::brotli) endif() set(HAVE_ZSTD OFF) @@ -954,14 +999,7 @@ curl_dependency_option(CURL_ZSTD Zstd "zstd") if(ZSTD_FOUND) if(ZSTD_VERSION VERSION_GREATER_EQUAL 1.0.0) set(HAVE_ZSTD ON) - list(APPEND CURL_LIBS ${ZSTD_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${ZSTD_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${ZSTD_PC_REQUIRES}) - include_directories(SYSTEM ${ZSTD_INCLUDE_DIRS}) - link_directories(${ZSTD_LIBRARY_DIRS}) - if(ZSTD_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${ZSTD_CFLAGS}") - endif() + list(APPEND CURL_LIBS CURL::zstd) else() message(WARNING "zstd v1.0.0 or newer is required, disabling zstd support.") endif() @@ -976,21 +1014,20 @@ macro(curl_openssl_check_exists) if(HAVE_LIBZ) list(APPEND CMAKE_REQUIRED_LIBRARIES ZLIB::ZLIB) endif() - if(WIN32 AND NOT WINCE) - list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt") # for OpenSSL/LibreSSL + if(WIN32) + list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt") # for OpenSSL/LibreSSL BCryptGenRandom() endif() endif() if(USE_WOLFSSL) - list(APPEND CMAKE_REQUIRED_INCLUDES "${WOLFSSL_INCLUDE_DIRS}") - list(APPEND CMAKE_REQUIRED_LIBRARIES "${WOLFSSL_LIBRARIES}") - curl_required_libpaths("${WOLFSSL_LIBRARY_DIRS}") + list(APPEND CMAKE_REQUIRED_LIBRARIES CURL::wolfssl) + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DWOLFSSL_OPTIONS_IGNORE_SYS") if(HAVE_LIBZ) list(APPEND CMAKE_REQUIRED_LIBRARIES ZLIB::ZLIB) # Public wolfSSL headers also require zlib headers endif() list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DHAVE_UINTPTR_T") # to pull in stdint.h (as of wolfSSL v5.5.4) endif() if(WIN32) - list(APPEND CMAKE_REQUIRED_LIBRARIES "${_win32_winsock}" "${_win32_crypt32}") # for OpenSSL/wolfSSL + list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32" "crypt32") # for OpenSSL/wolfSSL endif() if(${ARGC} EQUAL 2) check_function_exists(${ARGN}) @@ -1002,7 +1039,7 @@ endmacro() # Ensure that OpenSSL (or fork) or wolfSSL actually supports QUICTLS API. macro(curl_openssl_check_quic) - if(USE_OPENSSL AND NOT USE_OPENSSL_QUIC) + if(USE_OPENSSL) if(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.5.0) if(NOT DEFINED HAVE_SSL_SET_QUIC_TLS_CBS) curl_openssl_check_exists("SSL_set_quic_tls_cbs" HAVE_SSL_SET_QUIC_TLS_CBS) @@ -1026,12 +1063,15 @@ endmacro() if(USE_WOLFSSL) curl_openssl_check_exists("wolfSSL_get_peer_certificate" HAVE_WOLFSSL_GET_PEER_CERTIFICATE) curl_openssl_check_exists("wolfSSL_UseALPN" HAVE_WOLFSSL_USEALPN) - curl_openssl_check_exists("wolfSSL_DES_ecb_encrypt" HAVE_WOLFSSL_DES_ECB_ENCRYPT) curl_openssl_check_exists("wolfSSL_BIO_new" HAVE_WOLFSSL_BIO_NEW) curl_openssl_check_exists("wolfSSL_BIO_set_shutdown" HAVE_WOLFSSL_BIO_SET_SHUTDOWN) + curl_openssl_check_exists("wc_Des_EcbEncrypt" HAVE_WC_DES_ECBENCRYPT) endif() if(USE_OPENSSL) + if(NOT DEFINED HAVE_DES_ECB_ENCRYPT) + curl_openssl_check_exists("DES_ecb_encrypt" "openssl/des.h" HAVE_DES_ECB_ENCRYPT) + endif() if(NOT DEFINED HAVE_SSL_SET0_WBIO) curl_openssl_check_exists("SSL_set0_wbio" HAVE_SSL_SET0_WBIO) endif() @@ -1080,16 +1120,9 @@ endif() option(USE_NGHTTP2 "Use nghttp2 library" ON) if(USE_NGHTTP2) - find_package(NGHTTP2) + find_package(NGHTTP2 MODULE) if(NGHTTP2_FOUND) - list(APPEND CURL_LIBS ${NGHTTP2_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${NGHTTP2_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${NGHTTP2_PC_REQUIRES}) - include_directories(SYSTEM ${NGHTTP2_INCLUDE_DIRS}) - link_directories(${NGHTTP2_LIBRARY_DIRS}) - if(NGHTTP2_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${NGHTTP2_CFLAGS}") - endif() + list(APPEND CURL_LIBS CURL::nghttp2) else() set(USE_NGHTTP2 OFF) endif() @@ -1101,49 +1134,35 @@ if(USE_NGTCP2) message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.") elseif(USE_OPENSSL OR USE_WOLFSSL) if(USE_WOLFSSL) - find_package(NGTCP2 REQUIRED COMPONENTS "wolfSSL") + find_package(NGTCP2 MODULE REQUIRED COMPONENTS "wolfSSL") elseif(HAVE_BORINGSSL OR HAVE_AWSLC) - find_package(NGTCP2 REQUIRED COMPONENTS "BoringSSL") - elseif(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.5.0 AND NOT USE_OPENSSL_QUIC) - find_package(NGTCP2 REQUIRED COMPONENTS "ossl") + find_package(NGTCP2 MODULE REQUIRED COMPONENTS "BoringSSL") + elseif(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.5.0) + find_package(NGTCP2 MODULE REQUIRED COMPONENTS "ossl") if(NGTCP2_VERSION VERSION_LESS 1.12.0) message(FATAL_ERROR "ngtcp2 1.12.0 or upper required for OpenSSL") endif() set(OPENSSL_QUIC_API2 1) elseif(HAVE_LIBRESSL) - find_package(NGTCP2 COMPONENTS "LibreSSL") + find_package(NGTCP2 MODULE COMPONENTS "LibreSSL") if(NOT NGTCP2_FOUND) - find_package(NGTCP2 REQUIRED COMPONENTS "quictls") # for ngtcp2 <1.15.0 + find_package(NGTCP2 MODULE REQUIRED COMPONENTS "quictls") # for ngtcp2 <1.15.0 endif() else() - find_package(NGTCP2 REQUIRED COMPONENTS "quictls") + find_package(NGTCP2 MODULE REQUIRED COMPONENTS "quictls") set(_openssl "quictls") endif() curl_openssl_check_quic() elseif(USE_GNUTLS) - find_package(NGTCP2 REQUIRED "GnuTLS") + find_package(NGTCP2 MODULE REQUIRED "GnuTLS") else() message(FATAL_ERROR "ngtcp2 requires a supported TLS-backend") endif() - list(APPEND CURL_LIBS ${NGTCP2_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${NGTCP2_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${NGTCP2_PC_REQUIRES}) - include_directories(SYSTEM ${NGTCP2_INCLUDE_DIRS}) - link_directories(${NGTCP2_LIBRARY_DIRS}) - if(NGTCP2_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${NGTCP2_CFLAGS}") - endif() + list(APPEND CURL_LIBS CURL::ngtcp2) - find_package(NGHTTP3 REQUIRED) + find_package(NGHTTP3 MODULE REQUIRED) set(USE_NGHTTP3 ON) - list(APPEND CURL_LIBS ${NGHTTP3_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${NGHTTP3_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${NGHTTP3_PC_REQUIRES}) - include_directories(SYSTEM ${NGHTTP3_INCLUDE_DIRS}) - link_directories(${NGHTTP3_LIBRARY_DIRS}) - if(NGHTTP3_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${NGHTTP3_CFLAGS}") - endif() + list(APPEND CURL_LIBS CURL::nghttp3) endif() option(USE_QUICHE "Use quiche library for HTTP/3 support" OFF) @@ -1153,54 +1172,26 @@ if(USE_QUICHE) elseif(CURL_WITH_MULTI_SSL) message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.") endif() - find_package(Quiche REQUIRED) + find_package(Quiche MODULE REQUIRED) if(NOT HAVE_BORINGSSL) message(FATAL_ERROR "quiche requires BoringSSL") endif() curl_openssl_check_quic() - list(APPEND CURL_LIBS ${QUICHE_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${QUICHE_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${QUICHE_PC_REQUIRES}) - include_directories(SYSTEM ${QUICHE_INCLUDE_DIRS}) - link_directories(${QUICHE_LIBRARY_DIRS}) - if(QUICHE_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${QUICHE_CFLAGS}") - endif() + list(APPEND CURL_LIBS CURL::quiche) if(NOT DEFINED HAVE_QUICHE_CONN_SET_QLOG_FD) cmake_push_check_state() - list(APPEND CMAKE_REQUIRED_INCLUDES "${QUICHE_INCLUDE_DIRS}") - list(APPEND CMAKE_REQUIRED_LIBRARIES "${QUICHE_LIBRARIES}") + list(APPEND CMAKE_REQUIRED_LIBRARIES CURL::quiche) check_symbol_exists("quiche_conn_set_qlog_fd" "quiche.h" HAVE_QUICHE_CONN_SET_QLOG_FD) cmake_pop_check_state() endif() endif() -if(USE_OPENSSL_QUIC) - if(USE_NGTCP2 OR USE_QUICHE) - message(FATAL_ERROR "Only one HTTP/3 backend can be selected") - elseif(CURL_WITH_MULTI_SSL) - message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.") - endif() - find_package(OpenSSL 3.3.0 REQUIRED) - - find_package(NGHTTP3 REQUIRED) - set(USE_NGHTTP3 ON) - list(APPEND CURL_LIBS ${NGHTTP3_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${NGHTTP3_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${NGHTTP3_PC_REQUIRES}) - include_directories(SYSTEM ${NGHTTP3_INCLUDE_DIRS}) - link_directories(${NGHTTP3_LIBRARY_DIRS}) - if(NGHTTP3_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${NGHTTP3_CFLAGS}") - endif() -endif() - if(NOT CURL_DISABLE_SRP AND (HAVE_GNUTLS_SRP OR HAVE_OPENSSL_SRP)) set(USE_TLS_SRP 1) endif() if(NOT CURL_DISABLE_LDAP) - if(WIN32 AND NOT WINDOWS_STORE AND NOT WINCE) + if(WIN32 AND NOT WINDOWS_STORE) option(USE_WIN32_LDAP "Use Windows LDAP implementation" ON) if(USE_WIN32_LDAP) list(APPEND CURL_LIBS "wldap32") @@ -1217,25 +1208,15 @@ if(NOT CURL_DISABLE_LDAP) if(USE_OPENSSL) list(APPEND CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) endif() - find_package(LDAP) + find_package(LDAP MODULE) if(LDAP_FOUND) set(HAVE_LBER_H 1) - set(CURL_LIBS ${LDAP_LIBRARIES} ${CURL_LIBS}) - list(APPEND CURL_LIBDIRS ${LDAP_LIBRARY_DIRS}) - if(LDAP_PC_REQUIRES) - set(LIBCURL_PC_REQUIRES_PRIVATE ${LDAP_PC_REQUIRES} ${LIBCURL_PC_REQUIRES_PRIVATE}) - endif() - include_directories(SYSTEM ${LDAP_INCLUDE_DIRS}) - link_directories(${LDAP_LIBRARY_DIRS}) - if(LDAP_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${LDAP_CFLAGS}") - endif() + list(PREPEND CURL_LIBS CURL::ldap) # LDAP feature checks list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DLDAP_DEPRECATED=1") - list(APPEND CMAKE_REQUIRED_LIBRARIES "${LDAP_LIBRARIES}") - curl_required_libpaths("${LDAP_LIBRARY_DIRS}") + list(APPEND CMAKE_REQUIRED_LIBRARIES CURL::ldap) check_function_exists("ldap_url_parse" HAVE_LDAP_URL_PARSE) check_function_exists("ldap_init_fd" HAVE_LDAP_INIT_FD) @@ -1267,7 +1248,7 @@ endif() if(WIN32) option(USE_WIN32_IDN "Use WinIDN for IDN support" OFF) if(USE_WIN32_IDN) - list(APPEND CURL_LIBS "normaliz") + list(APPEND CURL_LIBS "normaliz") # for IdnToAscii(), IdnToUnicode() endif() else() set(USE_WIN32_IDN OFF) @@ -1295,16 +1276,9 @@ option(USE_LIBIDN2 "Use libidn2 for IDN support" ON) set(HAVE_IDN2_H OFF) set(HAVE_LIBIDN2 OFF) if(USE_LIBIDN2 AND NOT USE_APPLE_IDN AND NOT USE_WIN32_IDN) - find_package(Libidn2) + find_package(Libidn2 MODULE) if(LIBIDN2_FOUND) - set(CURL_LIBS ${LIBIDN2_LIBRARIES} ${CURL_LIBS}) - list(APPEND CURL_LIBDIRS ${LIBIDN2_LIBRARY_DIRS}) - set(LIBCURL_PC_REQUIRES_PRIVATE ${LIBIDN2_PC_REQUIRES} ${LIBCURL_PC_REQUIRES_PRIVATE}) - include_directories(SYSTEM ${LIBIDN2_INCLUDE_DIRS}) - link_directories(${LIBIDN2_LIBRARY_DIRS}) - if(LIBIDN2_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${LIBIDN2_CFLAGS}") - endif() + list(PREPEND CURL_LIBS CURL::libidn2) set(HAVE_IDN2_H 1) set(HAVE_LIBIDN2 1) endif() @@ -1314,17 +1288,9 @@ endif() option(CURL_USE_LIBPSL "Use libpsl" ON) mark_as_advanced(CURL_USE_LIBPSL) set(USE_LIBPSL OFF) - if(CURL_USE_LIBPSL) - find_package(Libpsl REQUIRED) - list(APPEND CURL_LIBS ${LIBPSL_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${LIBPSL_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${LIBPSL_PC_REQUIRES}) - include_directories(SYSTEM ${LIBPSL_INCLUDE_DIRS}) - link_directories(${LIBPSL_LIBRARY_DIRS}) - if(LIBPSL_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${LIBPSL_CFLAGS}") - endif() + find_package(Libpsl MODULE REQUIRED) + list(APPEND CURL_LIBS CURL::libpsl) set(USE_LIBPSL ON) endif() @@ -1332,18 +1298,10 @@ endif() option(CURL_USE_LIBSSH2 "Use libssh2" ON) mark_as_advanced(CURL_USE_LIBSSH2) set(USE_LIBSSH2 OFF) - if(CURL_USE_LIBSSH2) - find_package(Libssh2) + find_package(Libssh2 MODULE) if(LIBSSH2_FOUND) - set(CURL_LIBS ${LIBSSH2_LIBRARIES} ${CURL_LIBS}) # keep it before TLS-crypto, compression - list(APPEND CURL_LIBDIRS ${LIBSSH2_LIBRARY_DIRS}) - set(LIBCURL_PC_REQUIRES_PRIVATE ${LIBSSH2_PC_REQUIRES} ${LIBCURL_PC_REQUIRES_PRIVATE}) - include_directories(SYSTEM ${LIBSSH2_INCLUDE_DIRS}) - link_directories(${LIBSSH2_LIBRARY_DIRS}) - if(LIBSSH2_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${LIBSSH2_CFLAGS}") - endif() + list(PREPEND CURL_LIBS CURL::libssh2) # keep it before TLS-crypto, compression set(USE_LIBSSH2 ON) endif() endif() @@ -1352,47 +1310,16 @@ endif() option(CURL_USE_LIBSSH "Use libssh" OFF) mark_as_advanced(CURL_USE_LIBSSH) if(NOT USE_LIBSSH2 AND CURL_USE_LIBSSH) - find_package(Libssh REQUIRED) - set(CURL_LIBS ${LIBSSH_LIBRARIES} ${CURL_LIBS}) # keep it before TLS-crypto, compression - list(APPEND CURL_LIBDIRS ${LIBSSH_LIBRARY_DIRS}) - set(LIBCURL_PC_REQUIRES_PRIVATE ${LIBSSH_PC_REQUIRES} ${LIBCURL_PC_REQUIRES_PRIVATE}) - include_directories(SYSTEM ${LIBSSH_INCLUDE_DIRS}) - link_directories(${LIBSSH_LIBRARY_DIRS}) - if(LIBSSH_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${LIBSSH_CFLAGS}") - endif() + find_package(Libssh MODULE REQUIRED) + list(PREPEND CURL_LIBS CURL::libssh) # keep it before TLS-crypto, compression set(USE_LIBSSH ON) endif() -# wolfSSH -option(CURL_USE_WOLFSSH "Use wolfSSH" OFF) -mark_as_advanced(CURL_USE_WOLFSSH) -set(USE_WOLFSSH OFF) -if(NOT USE_LIBSSH2 AND NOT USE_LIBSSH AND CURL_USE_WOLFSSH) - if(USE_WOLFSSL) - find_package(WolfSSH) - if(WOLFSSH_FOUND) - set(CURL_LIBS ${WOLFSSH_LIBRARIES} ${CURL_LIBS}) # keep it before TLS-crypto, compression - include_directories(SYSTEM ${WOLFSSH_INCLUDE_DIRS}) - set(USE_WOLFSSH ON) - endif() - else() - message(WARNING "wolfSSH requires wolfSSL. Skipping.") - endif() -endif() - option(CURL_USE_GSASL "Use libgsasl" OFF) mark_as_advanced(CURL_USE_GSASL) if(CURL_USE_GSASL) - find_package(Libgsasl REQUIRED) - list(APPEND CURL_LIBS ${LIBGSASL_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${LIBGSASL_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${LIBGSASL_PC_REQUIRES}) - include_directories(SYSTEM ${LIBGSASL_INCLUDE_DIRS}) - link_directories(${LIBGSASL_LIBRARY_DIRS}) - if(LIBGSASL_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${LIBGSASL_CFLAGS}") - endif() + find_package(Libgsasl MODULE REQUIRED) + list(APPEND CURL_LIBS CURL::libgsasl) set(USE_GSASL ON) endif() @@ -1400,93 +1327,51 @@ option(CURL_USE_GSSAPI "Use GSSAPI implementation" OFF) mark_as_advanced(CURL_USE_GSSAPI) if(CURL_USE_GSSAPI) - find_package(GSS) + find_package(GSS MODULE) set(HAVE_GSSAPI ${GSS_FOUND}) if(GSS_FOUND) - list(APPEND CURL_LIBS ${GSS_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${GSS_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${GSS_PC_REQUIRES}) - include_directories(SYSTEM ${GSS_INCLUDE_DIRS}) - link_directories(${GSS_LIBRARY_DIRS}) - if(GSS_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${GSS_CFLAGS}") - endif() + list(APPEND CURL_LIBS CURL::gss) - if(GSS_FLAVOUR STREQUAL "GNU") + get_target_property(_gss_flavour CURL::gss INTERFACE_CURL_GSS_FLAVOUR) + if(_gss_flavour STREQUAL "GNU") set(HAVE_GSSGNU 1) - else() - cmake_push_check_state() - list(APPEND CMAKE_REQUIRED_INCLUDES "${GSS_INCLUDE_DIRS}") - - set(_include_list "") - check_include_file("gssapi/gssapi.h" HAVE_GSSAPI_GSSAPI_H) - if(HAVE_GSSAPI_GSSAPI_H) - list(APPEND _include_list "gssapi/gssapi.h") - endif() - check_include_files("${_include_list};gssapi/gssapi_generic.h" HAVE_GSSAPI_GSSAPI_GENERIC_H) - - if(GSS_FLAVOUR STREQUAL "MIT") - check_include_files("${_include_list};gssapi/gssapi_krb5.h" _have_gssapi_gssapi_krb5_h) - if(HAVE_GSSAPI_GSSAPI_GENERIC_H) - list(APPEND _include_list "gssapi/gssapi_generic.h") - endif() - if(_have_gssapi_gssapi_krb5_h) - list(APPEND _include_list "gssapi/gssapi_krb5.h") - endif() - - if(NOT DEFINED HAVE_GSS_C_NT_HOSTBASED_SERVICE) - string(APPEND CMAKE_REQUIRED_FLAGS " ${GSS_CFLAGS}") - list(APPEND CMAKE_REQUIRED_LIBRARIES "${GSS_LIBRARIES}") - curl_required_libpaths("${GSS_LIBRARY_DIRS}") - check_symbol_exists("GSS_C_NT_HOSTBASED_SERVICE" "${_include_list}" HAVE_GSS_C_NT_HOSTBASED_SERVICE) - endif() - if(NOT HAVE_GSS_C_NT_HOSTBASED_SERVICE) - set(HAVE_OLD_GSSMIT ON) - endif() - endif() - unset(_include_list) - cmake_pop_check_state() + elseif(GSS_VERSION) # MIT + set(CURL_KRB5_VERSION "\"${GSS_VERSION}\"") endif() else() message(WARNING "GSSAPI has been requested, but no supporting libraries found. Skipping.") endif() endif() +# libbacktrace +option(CURL_USE_LIBBACKTRACE "Use libbacktrace. Requires debug-enabled build and DWARF debug information." OFF) +if(CURL_USE_LIBBACKTRACE) + if(NOT ENABLE_DEBUG) + message(FATAL_ERROR "libbacktrace requires debug-enabled build for TrackMemory") + endif() + if(NOT CMAKE_BUILD_TYPE MATCHES "(Debug|RelWithDebInfo)") + message(FATAL_ERROR "libbacktrace requires debug information") + endif() + find_package(Libbacktrace MODULE REQUIRED) + list(APPEND CURL_LIBS CURL::libbacktrace) + set(USE_BACKTRACE ON) +endif() + # libuv option(CURL_USE_LIBUV "Use libuv for event-based tests" OFF) if(CURL_USE_LIBUV) if(NOT ENABLE_DEBUG) message(FATAL_ERROR "Using libuv without debug support enabled is useless") endif() - find_package(Libuv REQUIRED) - list(APPEND CURL_LIBS ${LIBUV_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${LIBUV_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${LIBUV_PC_REQUIRES}) - include_directories(SYSTEM ${LIBUV_INCLUDE_DIRS}) - link_directories(${LIBUV_LIBRARY_DIRS}) - if(LIBUV_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${LIBUV_CFLAGS}") - endif() + find_package(Libuv MODULE REQUIRED) + list(APPEND CURL_LIBS CURL::libuv) set(USE_LIBUV ON) set(HAVE_UV_H ON) endif() -option(USE_LIBRTMP "Enable librtmp from rtmpdump" OFF) -if(USE_LIBRTMP) - find_package(Librtmp REQUIRED) - list(APPEND CURL_LIBS ${LIBRTMP_LIBRARIES}) - list(APPEND CURL_LIBDIRS ${LIBRTMP_LIBRARY_DIRS}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${LIBRTMP_PC_REQUIRES}) - include_directories(SYSTEM ${LIBRTMP_INCLUDE_DIRS}) - link_directories(${LIBRTMP_LIBRARY_DIRS}) - if(LIBRTMP_CFLAGS) - string(APPEND CMAKE_C_FLAGS " ${LIBRTMP_CFLAGS}") - endif() -endif() - option(ENABLE_UNIX_SOCKETS "Enable Unix domain sockets support" ON) -if(ENABLE_UNIX_SOCKETS AND NOT WINCE) +if(ENABLE_UNIX_SOCKETS) if(WIN32 OR DOS) set(USE_UNIX_SOCKETS 1) else() @@ -1501,15 +1386,24 @@ endif() # # CA handling # +option(CURL_CA_NATIVE "Use native CA store" OFF) +if(CURL_CA_NATIVE) + set(_curl_disable_ca_search_default ON) +else() + set(_curl_disable_ca_search_default OFF) +endif() + if(_curl_ca_bundle_supported) + set(_ca_opt_desc "Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") + set(CURL_CA_BUNDLE "auto" CACHE - STRING "Path to the CA bundle. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") + STRING "Absolute path to the CA bundle. ${_ca_opt_desc}") set(CURL_CA_FALLBACK OFF CACHE BOOL "Use built-in CA store of OpenSSL. Defaults to OFF") set(CURL_CA_PATH "auto" CACHE - STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") + STRING "Absolute path to a directory containing CA certificates stored individually. ${_ca_opt_desc}") set(CURL_CA_EMBED "" CACHE - STRING "Path to the CA bundle to embed in the curl tool.") + STRING "Absolute path to the CA bundle to embed in the curl tool.") if(CURL_CA_FALLBACK AND NOT CURL_USE_OPENSSL) message(FATAL_ERROR "CURL_CA_FALLBACK only works with OpenSSL.") @@ -1521,7 +1415,7 @@ if(_curl_ca_bundle_supported) unset(CURL_CA_BUNDLE CACHE) elseif(CURL_CA_BUNDLE STREQUAL "auto") unset(CURL_CA_BUNDLE CACHE) - if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32) + if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32 AND NOT USE_APPLE_SECTRUST AND NOT CURL_CA_NATIVE) set(_curl_ca_bundle_autodetect TRUE) endif() else() @@ -1535,7 +1429,7 @@ if(_curl_ca_bundle_supported) unset(CURL_CA_PATH CACHE) elseif(CURL_CA_PATH STREQUAL "auto") unset(CURL_CA_PATH CACHE) - if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32) + if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32 AND NOT USE_APPLE_SECTRUST AND NOT CURL_CA_NATIVE) set(_curl_ca_path_autodetect TRUE) endif() else() @@ -1560,8 +1454,8 @@ if(_curl_ca_bundle_supported) if(EXISTS "${_search_ca_bundle_path}") message(STATUS "Found CA bundle: ${_search_ca_bundle_path}") set(CURL_CA_BUNDLE "${_search_ca_bundle_path}" CACHE - STRING "Path to the CA bundle. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") - set(CURL_CA_BUNDLE_SET TRUE CACHE BOOL "Path to the CA bundle has been set") + STRING "Absolute path to the CA bundle. ${_ca_opt_desc}") + set(CURL_CA_BUNDLE_SET TRUE CACHE BOOL "Absolute path to the CA bundle has been set") break() endif() endforeach() @@ -1574,8 +1468,8 @@ if(_curl_ca_bundle_supported) unset(_curl_ca_files_found) message(STATUS "Found CA path: ${_search_ca_path}") set(CURL_CA_PATH "${_search_ca_path}" CACHE - STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") - set(CURL_CA_PATH_SET TRUE CACHE BOOL "Path to the CA bundle has been set") + STRING "Absolute path to a directory containing CA certificates stored individually. ${_ca_opt_desc}") + set(CURL_CA_PATH_SET TRUE CACHE BOOL "Absolute path to the CA bundle has been set") endif() endif() endif() @@ -1592,30 +1486,20 @@ if(_curl_ca_bundle_supported) endif() if(WIN32) - option(CURL_DISABLE_CA_SEARCH "Disable unsafe CA bundle search in PATH on Windows" OFF) + option(CURL_DISABLE_CA_SEARCH "Disable unsafe CA bundle search in PATH on Windows" ${_curl_disable_ca_search_default}) option(CURL_CA_SEARCH_SAFE "Enable safe CA bundle search (within the curl tool directory) on Windows" OFF) endif() +set(CURL_INCLUDES "") + # Check for header files if(WIN32) list(APPEND CURL_INCLUDES "winsock2.h") list(APPEND CURL_INCLUDES "ws2tcpip.h") - if(HAVE_WIN32_WINNT AND HAVE_WIN32_WINNT LESS 0x0501 AND NOT WINCE) - # Windows XP is required for freeaddrinfo, getaddrinfo - message(FATAL_ERROR "Building for Windows XP or newer is required.") - endif() - - # Pre-fill detection results based on target OS version - if(_CURL_PREFILL) - if(NOT HAVE_WIN32_WINNT OR HAVE_WIN32_WINNT LESS 0x0600 OR # older than Windows Vista - WINCE OR WINDOWS_STORE) - set(HAVE_IF_NAMETOINDEX 0) - unset(HAVE_IF_NAMETOINDEX CACHE) - elseif(MSVC OR MINGW) - set(HAVE_IF_NAMETOINDEX 1) - unset(HAVE_IF_NAMETOINDEX CACHE) - endif() + if(HAVE_WIN32_WINNT AND HAVE_WIN32_WINNT LESS 0x0600) + # Windows Vista is required for freeaddrinfo, getaddrinfo, if_nametoindex + message(FATAL_ERROR "Building for Windows Vista or newer is required.") endif() endif() @@ -1661,7 +1545,6 @@ check_include_file("poll.h" HAVE_POLL_H) check_include_file("pwd.h" HAVE_PWD_H) check_include_file("stdatomic.h" HAVE_STDATOMIC_H) check_include_file("stdbool.h" HAVE_STDBOOL_H) -check_include_file("stdint.h" HAVE_STDINT_H) check_include_file("strings.h" HAVE_STRINGS_H) check_include_file("stropts.h" HAVE_STROPTS_H) check_include_file("termio.h" HAVE_TERMIO_H) @@ -1684,13 +1567,12 @@ foreach(_variable IN ITEMS HAVE_UNISTD_H ) if(${_variable}) - string(APPEND CURL_TEST_DEFINES " -D${_variable}") + list(APPEND CURL_TEST_DEFINES "-D${_variable}") endif() endforeach() check_type_size("size_t" SIZEOF_SIZE_T) check_type_size("ssize_t" SIZEOF_SSIZE_T) -check_type_size("long long" SIZEOF_LONG_LONG) check_type_size("long" SIZEOF_LONG) check_type_size("int" SIZEOF_INT) check_type_size("__int64" SIZEOF___INT64) @@ -1706,9 +1588,6 @@ if(NOT HAVE_SIZEOF_SSIZE_T) endif() # off_t is sized later, after the HAVE_FILE_OFFSET_BITS test -if(SIZEOF_LONG_LONG) - set(HAVE_LONGLONG 1) -endif() if(SIZEOF_SUSECONDS_T) set(HAVE_SUSECONDS_T 1) endif() @@ -1717,8 +1596,8 @@ endif() # Apply to all feature checks if(WIN32) - list(APPEND CMAKE_REQUIRED_LIBRARIES "${_win32_winsock}") - if(NOT WINCE AND NOT WINDOWS_STORE) + list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32") + if(NOT WINDOWS_STORE) list(APPEND CMAKE_REQUIRED_LIBRARIES "iphlpapi") endif() elseif(HAVE_LIBSOCKET) @@ -1739,7 +1618,6 @@ check_symbol_exists("send" "${CURL_INCLUDES}" HAVE_SEND) # proto/bsd check_function_exists("sendmsg" HAVE_SENDMSG) check_function_exists("sendmmsg" HAVE_SENDMMSG) check_symbol_exists("select" "${CURL_INCLUDES}" HAVE_SELECT) # proto/bsdsocket.h sys/select.h sys/socket.h -check_symbol_exists("strdup" "string.h" HAVE_STRDUP) check_symbol_exists("memrchr" "string.h" HAVE_MEMRCHR) check_symbol_exists("alarm" "unistd.h" HAVE_ALARM) check_symbol_exists("fcntl" "fcntl.h" HAVE_FCNTL) @@ -1755,6 +1633,7 @@ check_function_exists("getpwuid_r" HAVE_GETPWUID_R) check_function_exists("geteuid" HAVE_GETEUID) check_function_exists("utime" HAVE_UTIME) check_symbol_exists("gmtime_r" "stdlib.h;time.h" HAVE_GMTIME_R) +check_symbol_exists("localtime_r" "stdlib.h;time.h" HAVE_LOCALTIME_R) check_symbol_exists("gethostbyname_r" "netdb.h" HAVE_GETHOSTBYNAME_R) check_symbol_exists("gethostname" "${CURL_INCLUDES}" HAVE_GETHOSTNAME) # winsock2.h unistd.h proto/bsdsocket.h @@ -1767,32 +1646,27 @@ check_symbol_exists("getaddrinfo" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_ check_symbol_exists("getifaddrs" "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS) # ifaddrs.h check_symbol_exists("freeaddrinfo" "${CURL_INCLUDES}" HAVE_FREEADDRINFO) # ws2tcpip.h sys/socket.h netdb.h check_function_exists("pipe" HAVE_PIPE) -check_function_exists("pipe2" HAVE_PIPE2) check_function_exists("eventfd" HAVE_EVENTFD) -check_symbol_exists("ftruncate" "unistd.h" HAVE_FTRUNCATE) check_symbol_exists("getpeername" "${CURL_INCLUDES}" HAVE_GETPEERNAME) # winsock2.h unistd.h proto/bsdsocket.h check_symbol_exists("getsockname" "${CURL_INCLUDES}" HAVE_GETSOCKNAME) # winsock2.h unistd.h proto/bsdsocket.h -check_function_exists("getrlimit" HAVE_GETRLIMIT) -check_function_exists("setlocale" HAVE_SETLOCALE) -check_function_exists("setrlimit" HAVE_SETRLIMIT) +check_function_exists("getrlimit" HAVE_GETRLIMIT) +check_function_exists("setlocale" HAVE_SETLOCALE) +check_function_exists("setrlimit" HAVE_SETRLIMIT) -if(WIN32) - # include wincrypt.h as a workaround for mingw-w64 __MINGW64_VERSION_MAJOR <= 5 header bug */ - check_symbol_exists("if_nametoindex" "winsock2.h;wincrypt.h;iphlpapi.h" HAVE_IF_NAMETOINDEX) # Windows Vista+ non-UWP */ -else() +if(NOT APPLE) + # Apple platforms do not offer pipe2(), but the iPhone Simulator-specific + # /usr/lib/system/libsystem_sim_kernel.dylib exports it. To avoid false + # detection, omit this feature check for Apple targets. + check_function_exists("pipe2" HAVE_PIPE2) +endif() + +if(NOT WIN32) check_function_exists("if_nametoindex" HAVE_IF_NAMETOINDEX) # net/if.h check_function_exists("realpath" HAVE_REALPATH) check_function_exists("sched_yield" HAVE_SCHED_YIELD) - check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP) - check_symbol_exists("stricmp" "string.h" HAVE_STRICMP) - check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI) -endif() - -if(NOT MINGW32CE) # Avoid false detections - check_function_exists("setmode" HAVE_SETMODE) - if(WIN32 OR CYGWIN) - check_function_exists("_setmode" HAVE__SETMODE) - endif() + check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP) + check_symbol_exists("stricmp" "string.h" HAVE_STRICMP) + check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI) endif() if(AMIGA) @@ -1803,11 +1677,6 @@ if(NOT _ssl_enabled) check_symbol_exists("arc4random" "${CURL_INCLUDES};stdlib.h" HAVE_ARC4RANDOM) endif() -if(NOT MSVC) - check_function_exists("snprintf" HAVE_SNPRINTF) # to match detection method in ./configure -elseif(MSVC_VERSION GREATER_EQUAL 1900) # Earlier MSVC compilers had faulty snprintf implementations - check_symbol_exists("snprintf" "stdio.h" HAVE_SNPRINTF) # snprintf may be a compatibility macro, not an exported function -endif() if(APPLE) check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME) endif() @@ -1890,7 +1759,7 @@ check_type_size("off_t" SIZEOF_OFF_T) if(NOT WIN32) # fseeko may not exist with _FILE_OFFSET_BITS=64 but can exist with - # _FILE_OFFSET_BITS unset or 32 (e.g. Android ARMv7 with NDK 26b and API level < 24) + # _FILE_OFFSET_BITS unset or 32 (as in Android ARMv7 with NDK 26b and API level < 24) # so we need to test fseeko after testing for _FILE_OFFSET_BITS check_symbol_exists("fseeko" "${CURL_INCLUDES};stdio.h" HAVE_FSEEKO) @@ -1917,7 +1786,7 @@ if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) { (void)argc; argv[0][0] = ' '; - return (argv[0][0] == ' ')?0:1; + return (argv[0][0] == ' ') ? 0 : 1; }" HAVE_WRITABLE_ARGV) endif() @@ -1954,17 +1823,11 @@ include(CMake/OtherTests.cmake) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "HAVE_CONFIG_H") if(WIN32) - list(APPEND CURL_NETWORK_AND_TIME_LIBS "${_win32_winsock}") - if(NOT WINCE AND NOT WINDOWS_STORE) - list(APPEND CURL_NETWORK_AND_TIME_LIBS "iphlpapi") - endif() - if(NOT WINCE) - list(APPEND CURL_LIBS "bcrypt") - endif() - - if(NOT WINCE) - set(USE_WIN32_LARGE_FILES ON) + list(APPEND CURL_NETWORK_AND_TIME_LIBS "ws2_32") + if(NOT WINDOWS_STORE) + list(APPEND CURL_NETWORK_AND_TIME_LIBS "iphlpapi") # for if_nametoindex() endif() + list(APPEND CURL_LIBS "bcrypt") # for BCryptGenRandom() # We use crypto functions that are not available for UWP apps if(NOT WINDOWS_STORE) @@ -1973,29 +1836,55 @@ if(WIN32) # Link required libraries for USE_WIN32_CRYPTO or USE_SCHANNEL if(USE_WIN32_CRYPTO OR USE_SCHANNEL) - if(NOT WINCE) - list(APPEND CURL_LIBS "advapi32") - endif() - list(APPEND CURL_LIBS "${_win32_crypt32}") + # for CryptAcquireContext(), CryptCreateHash(), CryptDestroyHash(), CryptGetHashParam(), CryptHashData(), + # CryptReleaseContext() in NTLM, md4, md5, sha256, Schannel + # for CryptDestroyKey(), CryptEncrypt(), CryptImportKey() in NTLM + list(APPEND CURL_LIBS "advapi32") + # for Cert*() in openssl.c Native CA, Schannel + # for CryptDecodeObjectEx(), CryptQueryObject(), CryptStringToBinary(), PFXImportCertStore() in Schannel + list(APPEND CURL_LIBS "crypt32") endif() if(USE_WINDOWS_SSPI) - list(APPEND CURL_LIBS "${_win32_secur32}") + list(APPEND CURL_LIBS "secur32") # for InitSecurityInterface() endif() endif() list(APPEND CURL_LIBS ${CURL_NETWORK_AND_TIME_LIBS}) +if(CURL_CODE_COVERAGE) + list(APPEND CURL_LIBS ${CURL_COVERAGE_LIBS}) +endif() + +# Hack to add some libraries to the end of the library list to make binutils ld +# for GCC find symbols when linking statically. Necessary for libs detected via +# CMake's built-in find modules, which CMake adds to the beginning of the lib +# list on the linker command-line for some reason. This makes them appear +# before dependencies detected via curl's custom Find modules, and breaks +# linkers sensitive to lib order. There must be a better solution to this. +# Enable the workaround for all compilers, to make it available when using GCC +# to consume libcurl, regardless of the compiler used to build libcurl itself. +if(CMAKE_C_COMPILER_ID STREQUAL "GNU") + if(USE_OPENSSL AND TARGET OpenSSL::Crypto) + add_library(CURL::OpenSSL_Crypto INTERFACE IMPORTED) + set_target_properties(CURL::OpenSSL_Crypto PROPERTIES INTERFACE_LINK_LIBRARIES OpenSSL::Crypto) + list(APPEND CURL_LIBS CURL::OpenSSL_Crypto) + endif() + if(HAVE_LIBZ AND TARGET ZLIB::ZLIB) + add_library(CURL::ZLIB INTERFACE IMPORTED) + set_target_properties(CURL::ZLIB PROPERTIES INTERFACE_LINK_LIBRARIES ZLIB::ZLIB) + list(APPEND CURL_LIBS CURL::ZLIB) + endif() + if(WIN32) + add_library(CURL::win32_winsock INTERFACE IMPORTED) + set_target_properties(CURL::win32_winsock PROPERTIES INTERFACE_LINK_LIBRARIES "ws2_32") + list(APPEND CURL_LIBS CURL::win32_winsock) + endif() +endif() if(CMAKE_C_COMPILER_ID STREQUAL "MSVC") # MSVC but exclude clang-cl set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-MP") # Parallel compilation endif() if(CURL_LTO) - if(CMAKE_VERSION VERSION_LESS 3.9) - message(FATAL_ERROR "LTO has been requested, but your cmake version ${CMAKE_VERSION} is to old. You need at least 3.9") - endif() - - cmake_policy(SET CMP0069 NEW) - include(CheckIPOSupported) check_ipo_supported(RESULT CURL_HAS_LTO OUTPUT _lto_error LANGUAGES C) if(CURL_HAS_LTO) @@ -2005,14 +1894,12 @@ if(CURL_LTO) endif() endif() - # Ugly (but functional) way to include "Makefile.inc" by transforming it # (= regenerate it). function(curl_transform_makefile_inc _input_file _output_file) file(READ ${_input_file} _makefile_inc_text) - # cmake-lint: disable=W0106 - string(REPLACE "$(top_srcdir)" "\${PROJECT_SOURCE_DIR}" _makefile_inc_text ${_makefile_inc_text}) - string(REPLACE "$(top_builddir)" "\${PROJECT_BINARY_DIR}" _makefile_inc_text ${_makefile_inc_text}) + string(REPLACE "$(top_srcdir)" "\${PROJECT_SOURCE_DIR}" _makefile_inc_text ${_makefile_inc_text}) # cmake-lint: disable=W0106 + string(REPLACE "$(top_builddir)" "\${PROJECT_BINARY_DIR}" _makefile_inc_text ${_makefile_inc_text}) # cmake-lint: disable=W0106 string(REGEX REPLACE "\\\\\n" "!^!^!" _makefile_inc_text ${_makefile_inc_text}) string(REGEX REPLACE "([a-zA-Z_][a-zA-Z0-9_]*)[\t ]*=[\t ]*([^\n]*)" "set(\\1 \\2)" _makefile_inc_text ${_makefile_inc_text}) @@ -2036,13 +1923,13 @@ set(_project_config "${_generated_dir}/${PROJECT_NAME}Config.cmake") set(_version_config "${_generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") option(BUILD_TESTING "Build tests" ON) -if(BUILD_TESTING AND PERL_FOUND) +if(BUILD_TESTING AND Perl_FOUND) set(CURL_BUILD_TESTING ON) else() set(CURL_BUILD_TESTING OFF) endif() -if(PERL_FOUND) +if(Perl_FOUND) set(CURL_MANPAGE "${PROJECT_BINARY_DIR}/docs/cmdline-opts/curl.1") set(CURL_ASCIIPAGE "${PROJECT_BINARY_DIR}/docs/cmdline-opts/curl.txt") add_subdirectory(docs) @@ -2050,8 +1937,6 @@ endif() add_subdirectory(scripts) # for shell completions -list(REMOVE_DUPLICATES CURL_LIBDIRS) - add_subdirectory(lib) if(BUILD_CURL_EXE) @@ -2072,18 +1957,18 @@ endif() macro(curl_add_if _label) # Needs to be a macro to allow this indirection if(${ARGN}) - set(_items ${_items} "${_label}") + list(APPEND _items "${_label}") endif() endmacro() # NTLM support requires crypto functions from various SSL libs. # These conditions must match those in lib/curl_setup.h. -if(NOT CURL_DISABLE_NTLM AND - (USE_OPENSSL OR - USE_MBEDTLS OR +if(CURL_ENABLE_NTLM AND + ((USE_OPENSSL AND HAVE_DES_ECB_ENCRYPT) OR + (USE_MBEDTLS AND HAVE_MBEDTLS_DES_CRYPT_ECB) OR USE_GNUTLS OR USE_WIN32_CRYPTO OR - (USE_WOLFSSL AND HAVE_WOLFSSL_DES_ECB_ENCRYPT))) + (USE_WOLFSSL AND HAVE_WC_DES_ECBENCRYPT))) set(_use_curl_ntlm_core ON) endif() @@ -2108,19 +1993,19 @@ curl_add_if("POP3" NOT CURL_DISABLE_POP3) curl_add_if("POP3S" NOT CURL_DISABLE_POP3 AND _ssl_enabled) curl_add_if("IMAP" NOT CURL_DISABLE_IMAP) curl_add_if("IMAPS" NOT CURL_DISABLE_IMAP AND _ssl_enabled) -curl_add_if("SMB" NOT CURL_DISABLE_SMB AND +curl_add_if("SMB" CURL_ENABLE_SMB AND _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) -curl_add_if("SMBS" NOT CURL_DISABLE_SMB AND _ssl_enabled AND +curl_add_if("SMBS" CURL_ENABLE_SMB AND _ssl_enabled AND _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) curl_add_if("SMTP" NOT CURL_DISABLE_SMTP) curl_add_if("SMTPS" NOT CURL_DISABLE_SMTP AND _ssl_enabled) -curl_add_if("SCP" USE_LIBSSH2 OR USE_LIBSSH OR USE_WOLFSSH) -curl_add_if("SFTP" USE_LIBSSH2 OR USE_LIBSSH OR USE_WOLFSSH) +curl_add_if("SCP" USE_LIBSSH2 OR USE_LIBSSH) +curl_add_if("SFTP" USE_LIBSSH2 OR USE_LIBSSH) curl_add_if("IPFS" NOT CURL_DISABLE_IPFS) curl_add_if("IPNS" NOT CURL_DISABLE_IPFS) curl_add_if("RTSP" NOT CURL_DISABLE_RTSP) -curl_add_if("RTMP" USE_LIBRTMP) curl_add_if("MQTT" NOT CURL_DISABLE_MQTT) +curl_add_if("MQTTS" NOT CURL_DISABLE_MQTT AND _ssl_enabled) curl_add_if("WS" NOT CURL_DISABLE_WEBSOCKETS) curl_add_if("WSS" NOT CURL_DISABLE_WEBSOCKETS AND _ssl_enabled) if(_items) @@ -2140,13 +2025,12 @@ curl_add_if("libz" HAVE_LIBZ) curl_add_if("brotli" HAVE_BROTLI) curl_add_if("gsasl" USE_GSASL) curl_add_if("zstd" HAVE_ZSTD) -curl_add_if("AsynchDNS" USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32) -curl_add_if("asyn-rr" USE_ARES AND ENABLE_THREADED_RESOLVER AND USE_HTTPSRR) +curl_add_if("AsynchDNS" USE_RESOLV_ARES OR USE_RESOLV_THREADED) +curl_add_if("asyn-rr" USE_ARES AND USE_RESOLV_THREADED AND USE_HTTPSRR) curl_add_if("IDN" (HAVE_LIBIDN2 AND HAVE_IDN2_H) OR USE_WIN32_IDN OR USE_APPLE_IDN) -curl_add_if("Largefile" (SIZEOF_CURL_OFF_T GREATER 4) AND - ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES)) +curl_add_if("Largefile" (SIZEOF_CURL_OFF_T GREATER 4) AND ((SIZEOF_OFF_T GREATER 4) OR WIN32)) curl_add_if("SSPI" USE_WINDOWS_SSPI) curl_add_if("GSS-API" HAVE_GSSAPI) curl_add_if("alt-svc" NOT CURL_DISABLE_ALTSVC) @@ -2155,32 +2039,27 @@ curl_add_if("SPNEGO" NOT CURL_DISABLE_NEGOTIATE_AUTH AND (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) curl_add_if("Kerberos" NOT CURL_DISABLE_KERBEROS_AUTH AND (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) -curl_add_if("NTLM" NOT CURL_DISABLE_NTLM AND +curl_add_if("NTLM" CURL_ENABLE_NTLM AND (_use_curl_ntlm_core OR USE_WINDOWS_SSPI)) curl_add_if("TLS-SRP" USE_TLS_SRP) curl_add_if("HTTP2" USE_NGHTTP2) -curl_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE OR USE_OPENSSL_QUIC) +curl_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE) curl_add_if("MultiSSL" CURL_WITH_MULTI_SSL) curl_add_if("HTTPS-proxy" NOT CURL_DISABLE_PROXY AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS OR USE_SCHANNEL OR USE_RUSTLS OR USE_MBEDTLS OR (USE_WOLFSSL AND HAVE_WOLFSSL_BIO_NEW))) curl_add_if("Unicode" ENABLE_UNICODE) -curl_add_if("threadsafe" HAVE_ATOMIC OR - (USE_THREADS_POSIX AND HAVE_PTHREAD_H) OR - (WIN32 AND HAVE_WIN32_WINNT GREATER_EQUAL 0x0600)) +curl_add_if("threadsafe" HAVE_ATOMIC OR HAVE_THREADS_POSIX OR WIN32) curl_add_if("Debug" ENABLE_DEBUG) -curl_add_if("TrackMemory" ENABLE_CURLDEBUG) curl_add_if("ECH" _ssl_enabled AND HAVE_ECH) curl_add_if("HTTPSRR" _ssl_enabled AND USE_HTTPSRR) curl_add_if("PSL" USE_LIBPSL) curl_add_if("CAcert" CURL_CA_EMBED_SET) curl_add_if("SSLS-EXPORT" _ssl_enabled AND USE_SSLS_EXPORT) +curl_add_if("AppleSecTrust" USE_APPLE_SECTRUST AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS)) +curl_add_if("NativeCA" NOT USE_APPLE_SECTRUST AND _ssl_enabled AND CURL_CA_NATIVE) if(_items) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - list(SORT _items CASE INSENSITIVE) - else() - list(SORT _items) - endif() + list(SORT _items CASE INSENSITIVE) endif() set(CURL_SUPPORTED_FEATURES_LIST "${_items}") string(REPLACE ";" " " SUPPORT_FEATURES "${_items}") @@ -2189,19 +2068,14 @@ message(STATUS "Features: ${SUPPORT_FEATURES}") # Clear list and collect SSL backends set(_items "") curl_add_if("Schannel" _ssl_enabled AND USE_SCHANNEL) -curl_add_if("${_openssl}" _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_LESS 3.0.0) -curl_add_if("${_openssl} v3+" _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_GREATER_EQUAL 3.0.0) +curl_add_if("${_openssl}" _ssl_enabled AND USE_OPENSSL) curl_add_if("mbedTLS" _ssl_enabled AND USE_MBEDTLS) curl_add_if("wolfSSL" _ssl_enabled AND USE_WOLFSSL) curl_add_if("GnuTLS" _ssl_enabled AND USE_GNUTLS) curl_add_if("Rustls" _ssl_enabled AND USE_RUSTLS) if(_items) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - list(SORT _items CASE INSENSITIVE) - else() - list(SORT _items) - endif() + list(SORT _items CASE INSENSITIVE) endif() string(REPLACE ";" " " SSL_BACKENDS "${_items}") message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}") @@ -2274,62 +2148,94 @@ if(NOT CURL_DISABLE_INSTALL) endif() endforeach() - foreach(_libdir IN LISTS _custom_libdirs CURL_LIBDIRS) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) - cmake_path(SET _libdir NORMALIZE "${_libdir}") - endif() - list(FIND _sys_libdirs "${_libdir}" _libdir_index) - if(_libdir_index LESS 0) - list(APPEND _ldflags "-L${_libdir}") - endif() - endforeach() - set(_implicit_libs "") if(NOT MINGW AND NOT UNIX) set(_implicit_libs "${CMAKE_C_IMPLICIT_LINK_LIBRARIES}") endif() - foreach(_lib IN LISTS _implicit_libs _custom_libs CURL_LIBS) + set(_explicit_libdirs "") + set(LIBCURL_PC_REQUIRES_PRIVATE "") + set(LIBCURL_PC_LIBS_PRIVATE_LIST "") + foreach(_lib IN LISTS CURL_LIBS _custom_libs _implicit_libs) if(TARGET "${_lib}") - set(_libname "${_lib}") - get_target_property(_imported "${_libname}" IMPORTED) + set(_explicit_libs "") + get_target_property(_imported "${_lib}" IMPORTED) if(NOT _imported) # Reading the LOCATION property on non-imported target will error out. # Assume the user will not need this information in the .pc file. continue() endif() - get_target_property(_lib "${_libname}" LOCATION) - if(NOT _lib) - message(WARNING "Bad lib in library list: ${_libname}") - continue() + set(_libdirs "") + set(_libs "") + curl_collect_target_link_options("${_lib}") # look into the target recursively + list(APPEND _explicit_libdirs ${_libdirs}) + list(APPEND _explicit_libs ${_libs}) + if(NOT _libs AND NOT _libdirs AND NOT _lib STREQUAL Threads::Threads) + message(WARNING "Bad lib in library list: ${_lib}") endif() - endif() - if(_lib MATCHES "^-") # '-framework ' - list(APPEND _ldflags "${_lib}") - elseif(_lib MATCHES "/") - # This gets a bit more complex, because we want to specify the - # directory separately, and only once per directory - get_filename_component(_libdir ${_lib} DIRECTORY) - get_filename_component(_libname ${_lib} NAME_WE) - if(_libname MATCHES "^lib") - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) - cmake_path(SET _libdir NORMALIZE "${_libdir}") - endif() - list(FIND _sys_libdirs "${_libdir}" _libdir_index) - if(_libdir_index LESS 0) - list(APPEND _ldflags "-L${_libdir}") - endif() - string(REGEX REPLACE "^lib" "" _libname "${_libname}") - list(APPEND LIBCURL_PC_LIBS_PRIVATE "-l${_libname}") + if(_lib STREQUAL OpenSSL::SSL AND NOT HAVE_BORINGSSL) # BoringSSL does not provide openssl.pc + set(_modules "openssl") + elseif(_lib STREQUAL ZLIB::ZLIB) + set(_modules "zlib") else() - list(APPEND LIBCURL_PC_LIBS_PRIVATE "${_lib}") + get_target_property(_modules "${_lib}" INTERFACE_LIBCURL_PC_MODULES) endif() + if(_modules) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "${_modules}") + endif() + + foreach(_lib IN LISTS _explicit_libs) + if(_lib MATCHES "/") + # This gets a bit more complex, because we want to specify the + # directory separately, and only once per directory + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) + cmake_path(GET _lib PARENT_PATH _libdir) + cmake_path(GET _lib STEM _libname) + else() + get_filename_component(_libdir "${_lib}" DIRECTORY) + get_filename_component(_libname "${_lib}" NAME_WE) + endif() + if(_libname MATCHES "^lib") + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) + cmake_path(SET _libdir NORMALIZE "${_libdir}") + endif() + if(NOT _libdir IN_LIST _sys_libdirs) + list(APPEND _ldflags "-L${_libdir}") + endif() + string(REGEX REPLACE "^lib" "" _libname "${_libname}") + list(APPEND LIBCURL_PC_LIBS_PRIVATE "-l${_libname}") + list(APPEND LIBCURL_PC_LIBS_PRIVATE_LIST "${_lib}") + else() + list(APPEND LIBCURL_PC_LIBS_PRIVATE "${_lib}") + list(APPEND LIBCURL_PC_LIBS_PRIVATE_LIST "${_lib}") + endif() + else() + list(APPEND LIBCURL_PC_LIBS_PRIVATE "-l${_lib}") + list(APPEND LIBCURL_PC_LIBS_PRIVATE_LIST "${_lib}") + endif() + endforeach() + elseif(_lib MATCHES "^-") # '-framework ' + list(APPEND _ldflags "${_lib}") + list(APPEND LIBCURL_PC_LIBS_PRIVATE_LIST "${_lib}") else() list(APPEND LIBCURL_PC_LIBS_PRIVATE "-l${_lib}") + list(APPEND LIBCURL_PC_LIBS_PRIVATE_LIST "${_lib}") endif() endforeach() + foreach(_libdir IN LISTS _custom_libdirs _explicit_libdirs) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) + cmake_path(SET _libdir NORMALIZE "${_libdir}") + endif() + if(NOT _libdir IN_LIST _sys_libdirs) + list(APPEND _ldflags "-L${_libdir}") + endif() + endforeach() + + list(REMOVE_DUPLICATES _ldflags) + if(LIBCURL_PC_REQUIRES_PRIVATE) + list(REMOVE_DUPLICATES LIBCURL_PC_REQUIRES_PRIVATE) string(REPLACE ";" "," LIBCURL_PC_REQUIRES_PRIVATE "${LIBCURL_PC_REQUIRES_PRIVATE}") endif() if(LIBCURL_PC_LIBS_PRIVATE) @@ -2426,8 +2332,7 @@ if(NOT CURL_DISABLE_INSTALL) FILES_MATCHING PATTERN "*.h") include(CMakePackageConfigHelpers) - write_basic_package_version_file( - "${_version_config}" + write_basic_package_version_file("${_version_config}" VERSION ${_curl_version} COMPATIBILITY SameMajorVersion) file(READ "${_version_config}" _generated_version_config) @@ -2440,15 +2345,42 @@ if(NOT CURL_DISABLE_INSTALL) ${_generated_version_config}") # Consumed custom variables: + # CMAKE_MINIMUM_REQUIRED_VERSION # CURLVERSION + # LIBCURL_PC_LIBS_PRIVATE_LIST # LIB_NAME # LIB_SELECTED + # LIB_STATIC # TARGETS_EXPORT_NAME - # USE_OPENSSL OPENSSL_VERSION_MAJOR - # HAVE_LIBZ ZLIB_VERSION_MAJOR # CURL_SUPPORTED_FEATURES_LIST # CURL_SUPPORTED_PROTOCOLS_LIST - configure_package_config_file("CMake/curl-config.cmake.in" + # CURL_USE_CMAKECONFIG + # CURL_USE_PKGCONFIG + # HAVE_BROTLI + # HAVE_GSSAPI + # HAVE_LIBIDN2 + # HAVE_LIBZ ZLIB_VERSION_MAJOR + # HAVE_THREADS_POSIX + # HAVE_THREADS_POSIX_BORINGSSL + # HAVE_ZSTD + # USE_ARES + # USE_BACKTRACE + # USE_GNUTLS + # USE_GSASL + # USE_LIBPSL + # USE_LIBSSH + # USE_LIBSSH2 + # USE_LIBUV + # USE_MBEDTLS + # USE_NGHTTP2 + # USE_NGHTTP3 + # USE_NGTCP2 + # USE_OPENSSL OPENSSL_VERSION_MAJOR + # USE_QUICHE + # USE_RUSTLS + # USE_WIN32_LDAP CURL_DISABLE_LDAP + # USE_WOLFSSL + configure_package_config_file("CMake/curl-config.in.cmake" "${_project_config}" INSTALL_DESTINATION ${_install_cmake_dir} PATH_VARS CMAKE_INSTALL_INCLUDEDIR) @@ -2459,12 +2391,36 @@ if(NOT CURL_DISABLE_INSTALL) DESTINATION ${_install_cmake_dir}) endif() - install(FILES ${_version_config} ${_project_config} + install( + FILES + ${_version_config} + ${_project_config} + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindBrotli.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindCares.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindGSS.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindGnuTLS.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLDAP.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibbacktrace.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibgsasl.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibidn2.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibpsl.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibssh.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibssh2.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibuv.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindMbedTLS.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindNGHTTP2.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindNGHTTP3.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindNGTCP2.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindNettle.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindQuiche.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindRustls.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindWolfSSL.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindZstd.cmake" DESTINATION ${_install_cmake_dir}) if(NOT TARGET curl_uninstall) configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.in.cmake" "${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake" @ONLY) diff --git a/COPYING b/COPYING index 3fa85ebb64..2f71d999a9 100644 --- a/COPYING +++ b/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/Dockerfile b/Dockerfile index 454df8dea9..54380237c0 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:df52e55e3361a81ac1bead266f3373ee55d29aa50cf0975d440c2be3483d8ed3 +FROM debian:bookworm-slim@sha256:4724b8cc51e33e398f0e2e15e18d5ec2851ff0c2280647e1310bc1642182655d 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/GIT-INFO.md b/GIT-INFO.md index 1ff22010cb..ee912560fd 100644 --- a/GIT-INFO.md +++ b/GIT-INFO.md @@ -1,3 +1,8 @@ + _ _ ____ _ ___| | | | _ \| | / __| | | | |_) | | diff --git a/LICENSES/BSD-3-Clause.txt b/LICENSES/BSD-3-Clause.txt deleted file mode 100644 index 086d3992cb..0000000000 --- a/LICENSES/BSD-3-Clause.txt +++ /dev/null @@ -1,11 +0,0 @@ -Copyright (c) . - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LICENSES/BSD-4-Clause-UC.txt b/LICENSES/BSD-4-Clause-UC.txt index 69edbe3242..0fffd039d8 100644 --- a/LICENSES/BSD-4-Clause-UC.txt +++ b/LICENSES/BSD-4-Clause-UC.txt @@ -1,15 +1,33 @@ BSD-4-Clause (University of California-Specific) -Copyright [various years] The Regents of the University of California. All rights reserved. +Copyright [various years] The Regents of the University of California. +All rights reserved. -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. -3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors. +3. All advertising materials mentioning features or use of this software must + display the following acknowledgement: This product includes software + developed by the University of California, Berkeley and its contributors. -4. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +4. Neither the name of the University nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile.am b/Makefile.am index b5ab0442a8..83fdadf035 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,75 +26,59 @@ AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 -CMAKE_DIST = \ - CMake/cmake_uninstall.cmake.in \ - CMake/CMakeConfigurableFile.in \ - CMake/curl-config.cmake.in \ - CMake/CurlSymbolHiding.cmake \ - CMake/CurlTests.c \ - CMake/FindBrotli.cmake \ - CMake/FindCares.cmake \ - CMake/FindGSS.cmake \ - CMake/FindLDAP.cmake \ - CMake/FindLibgsasl.cmake \ - CMake/FindLibidn2.cmake \ - CMake/FindLibpsl.cmake \ - CMake/FindLibrtmp.cmake \ - CMake/FindLibssh.cmake \ - CMake/FindLibssh2.cmake \ - CMake/FindLibuv.cmake \ - CMake/FindMbedTLS.cmake \ - CMake/FindNGHTTP2.cmake \ - CMake/FindNGHTTP3.cmake \ - CMake/FindNGTCP2.cmake \ - CMake/FindNettle.cmake \ - CMake/FindQuiche.cmake \ - CMake/FindRustls.cmake \ - CMake/FindWolfSSH.cmake \ - CMake/FindWolfSSL.cmake \ - CMake/FindZstd.cmake \ - CMake/Macros.cmake \ - CMake/OtherTests.cmake \ - CMake/PickyWarnings.cmake \ - CMake/Utilities.cmake \ - CMake/unix-cache.cmake \ - CMake/win32-cache.cmake \ - CMakeLists.txt \ - tests/cmake/CMakeLists.txt \ - tests/cmake/test.c \ - tests/cmake/test.sh +CMAKE_DIST = \ + CMake/cmake_uninstall.in.cmake \ + CMake/curl-config.in.cmake \ + CMake/CurlSymbolHiding.cmake \ + CMake/CurlTests.c \ + CMake/FindBrotli.cmake \ + CMake/FindCares.cmake \ + CMake/FindGnuTLS.cmake \ + CMake/FindGSS.cmake \ + CMake/FindLDAP.cmake \ + CMake/FindLibbacktrace.cmake \ + CMake/FindLibgsasl.cmake \ + CMake/FindLibidn2.cmake \ + CMake/FindLibpsl.cmake \ + CMake/FindLibssh.cmake \ + CMake/FindLibssh2.cmake \ + CMake/FindLibuv.cmake \ + CMake/FindMbedTLS.cmake \ + CMake/FindNGHTTP2.cmake \ + CMake/FindNGHTTP3.cmake \ + CMake/FindNGTCP2.cmake \ + CMake/FindNettle.cmake \ + CMake/FindQuiche.cmake \ + CMake/FindRustls.cmake \ + CMake/FindWolfSSL.cmake \ + CMake/FindZstd.cmake \ + CMake/Macros.cmake \ + CMake/OtherTests.cmake \ + CMake/PickyWarnings.cmake \ + CMake/Utilities.cmake \ + CMake/unix-cache.cmake \ + CMake/win32-cache.cmake \ + CMakeLists.txt \ + tests/cmake/CMakeLists.txt \ + tests/cmake/test.c \ + tests/cmake/test.cpp \ + tests/cmake/test.sh -VC_DIST = projects/README.md projects/generate.bat - -WINBUILD_DIST = winbuild/README.md \ - winbuild/MakefileBuild.vc winbuild/Makefile.vc winbuild/makedebug.bat - -PLAN9_DIST = plan9/include/mkfile \ - plan9/include/mkfile \ - plan9/mkfile.proto \ - plan9/mkfile \ - plan9/README \ - plan9/lib/mkfile.inc \ - plan9/lib/mkfile \ - plan9/src/mkfile.inc \ - plan9/src/mkfile - -EXTRA_DIST = CHANGES.md COPYING RELEASE-NOTES Dockerfile .editorconfig \ - $(CMAKE_DIST) $(VC_DIST) $(WINBUILD_DIST) $(PLAN9_DIST) +EXTRA_DIST = CHANGES.md COPYING RELEASE-NOTES Dockerfile .clang-tidy.yml .editorconfig $(CMAKE_DIST) DISTCLEANFILES = buildinfo.txt bin_SCRIPTS = curl-config SUBDIRS = lib docs src scripts -DIST_SUBDIRS = $(SUBDIRS) tests packages include docs +DIST_SUBDIRS = $(SUBDIRS) tests projects include docs pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libcurl.pc dist-hook: rm -rf $(top_builddir)/tests/log - find $(distdir) -name "*.dist" -exec rm {} \; + find $(distdir) -name "*.dist" -exec rm -- {} \; (distit=`find $(srcdir) -name "*.dist" | grep -v Makefile`; \ for file in $$distit; do \ strip=`echo $$file | sed -e s/^$(srcdir)// -e s/\.dist//`; \ @@ -114,7 +98,7 @@ pytest: test pytest-ci: test test: - @echo "NOTICE: we can't run the tests when cross-compiling!" + @echo "NOTICE: we cannot run the tests when cross-compiling!" else @@ -153,32 +137,6 @@ examples: check-docs: @(cd docs/libcurl; $(MAKE) check) -# Build source and binary rpms. For rpm-3.0 and above, the ~/.rpmmacros -# must contain the following line: -# %_topdir /home/loic/local/rpm -# and that /home/loic/local/rpm contains the directory SOURCES, BUILD etc. -# -# cd /home/loic/local/rpm ; mkdir -p SOURCES BUILD RPMS/i386 SPECS SRPMS -# -# If additional configure flags are needed to build the package, add the -# following in ~/.rpmmacros -# %configure CFLAGS="%{optflags}" ./configure %{_target_platform} --prefix=%{_prefix} ${AM_CONFIGFLAGS} -# and run make rpm in the following way: -# AM_CONFIGFLAGS='--with-uri=/home/users/loic/local/RedHat-6.2' make rpm -# - -rpms: - $(MAKE) RPMDIST=curl rpm - $(MAKE) RPMDIST=curl-ssl rpm - -rpm: - RPM_TOPDIR=`rpm --showrc | @PERL@ -n -e 'print if(s/.*_topdir\s+(.*)/$$1/)'` ; \ - cp $(srcdir)/packages/Linux/RPM/$(RPMDIST).spec $$RPM_TOPDIR/SPECS ; \ - cp $(PACKAGE)-$(VERSION).tar.gz $$RPM_TOPDIR/SOURCES ; \ - rpm -ba --clean --rmsource $$RPM_TOPDIR/SPECS/$(RPMDIST).spec ; \ - mv $$RPM_TOPDIR/RPMS/i386/$(RPMDIST)-*.rpm . ; \ - mv $$RPM_TOPDIR/SRPMS/$(RPMDIST)-*.src.rpm . - # We extend the standard install with a custom hook: if BUILD_DOCS install-data-hook: @@ -211,7 +169,13 @@ checksrc: (cd tests && $(MAKE) checksrc) (cd include/curl && $(MAKE) checksrc) (cd docs/examples && $(MAKE) checksrc) - (cd packages && $(MAKE) checksrc) + (cd projects && $(MAKE) checksrc) + +badwords: + @PERL@ $(top_srcdir)/scripts/badwords-all + +lint: badwords checksrc + @PERL@ $(top_srcdir)/scripts/spacecheck.pl tidy: (cd src && $(MAKE) tidy) diff --git a/README b/README index f5efbd70a6..4ee7e43a2c 100644 --- a/README +++ b/README @@ -15,7 +15,8 @@ README available to be used by your software. Read the libcurl.3 man page to learn how. - You find answers to the most frequent questions we get in the FAQ document. + You find answers to the most frequent questions we get in the FAQ.md + document. Study the COPYING file for distribution terms. @@ -32,24 +33,18 @@ WEBSITE Visit the curl website for the latest news and downloads: - https://curl.se/ + https://curl.se/ GIT To download the latest source code off the GIT server, do this: - git clone https://github.com/curl/curl.git + git clone https://github.com/curl/curl (you will get a directory named curl created, filled with the source code) SECURITY PROBLEMS - Report suspected security problems via our HackerOne page and not in public. + Report suspected security problems privately and not in public. - https://hackerone.com/curl - -NOTICE - - Curl contains pieces of source code that is Copyright (c) 1998, 1999 - Kungliga Tekniska Högskolan. This notice is included here to comply with the - distribution terms. + https://curl.se/dev/vuln-disclosure.html diff --git a/README.md b/README.md index 3359818fd5..9f77de607b 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,13 @@ SPDX-License-Identifier: curl # [![curl logo](https://curl.se/logo/curl-logo.svg)](https://curl.se/) -curl is a command-line tool for transferring data specified with URL syntax. +curl is a command-line tool for transferring data from or to a server using +URLs. It supports these protocols: DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, +HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, +SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. + Learn how to use curl by reading [the -manpage](https://curl.se/docs/manpage.html) or [everything +man page](https://curl.se/docs/manpage.html) or [everything curl](https://everything.curl.dev/). Find out how to install curl by reading [the INSTALL @@ -16,7 +20,7 @@ document](https://curl.se/docs/install.html). libcurl is the library curl is using to do its job. It is readily available to be used by your software. Read [the libcurl -manpage](https://curl.se/libcurl/c/libcurl.html) to learn how. +man page](https://curl.se/libcurl/c/libcurl.html) to learn how. ## Open Source @@ -46,18 +50,12 @@ Visit the [curl website](https://curl.se/) for the latest news and downloads. Download the latest source from the Git server: - git clone https://github.com/curl/curl.git + git clone https://github.com/curl/curl ## Security problems -Report suspected security problems via [our HackerOne -page](https://hackerone.com/curl) and not in public. - -## Notice - -curl contains pieces of source code that is Copyright (c) 1998, 1999 Kungliga -Tekniska Högskolan. This notice is included here to comply with the -distribution terms. +Report suspected security problems +[privately](https://curl.se/dev/vuln-disclosure.html) and not in public. ## Backers diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 5b9f4324af..8c081f3ac9 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,293 +1,242 @@ -curl and libcurl 8.16.0 +curl and libcurl 8.20.0 - Public curl releases: 270 - Command line options: 272 + Public curl releases: 274 + Command line options: 273 curl_easy_setopt() options: 308 - Public functions in libcurl: 98 - Contributors: 3499 + Public functions in libcurl: 100 + Authors: 1461 + Contributors: 3649 This release includes the following changes: - o build: bump minimum required mingw-w64 to v3.0 (from v1.0) [33] - o curl: add --follow [129] - o curl: add --out-null [101] - o curl: add --parallel-max-host to limit concurrent connections per host [81] - 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] - o smtp: allow suffix behind a mail address for RFC 3461 [127] - 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] + o async-thrdd: use thread queue for resolving [144] + o build: make NTLM disabled by default [90] + o cmake: drop support for CMake 3.17 and older [108] + o lib: add thread pool and queue [74] + o lib: drop support for < c-ares 1.16.0 [64] + o lib: make SMB support opt-in [18] + o multi.h: add CURLMNWC_CLEAR_ALL [127] + o rtmp: drop support [91] This release includes the following bugfixes: - o _PROTOCOLS.md: mention file:// is only for absolute paths [102] - o acinclude: --with-ca-fallback only works with OpenSSL [217] - o alpn: query filter [104] - o ares: destroy channel on shutdown [178] - o ares: use `ares_strerror()` to retrieve error messages [236] - o asyn-thrdd: fix --disable-socketpair builds [235] - o asyn-thrdd: fix Curl_async_pollset without socketpair [205] - o asyn-thrdd: fix no `HAVE_GETADDRINFO` builds [214] - o asyn-thrdd: manage DEFERRED and locks better [228] - o autotools: make curl-config executable [253] - o aws-lc: do not use large buffer [250] - o BINDINGS.md: add LibQurl [156] - o bufq: add integer overflow checks before chunk allocations [108] - o bufq: removed "Useless Assignment" [188] - o bufq: simplify condition [207] - o build: allow libtests/clients to use libcurl dependencies directly [87] - o build: disable `TCP_NODELAY` for emscripten [176] - o build: enable _GNU_SOURCE on GNU/Hurd [27] - o build: extend GNU C guards to clang where applicable, fix fallouts [61] - o build: fix build errors/warnings in rare configurations [7] - o build: fix disable-verbose [48] - o build: fix mingw-w64 version guard for mingw32ce [124] - o build: if no perl, fix to use the pre-built hugehelp, if present [144] - o build: link to Apple frameworks required by static wolfSSL [40] - o build: support LibreSSL native crypto lib with ngtcp2 1.15.0+ [209] - o build: tidy up compiler definition for tests [37] - o cf-https-connect: delete unused declaration [15] - o clang-tidy: disable `clang-analyzer-security.ArrayBound` [265] - o cmake: `CURL_CA_FALLBACK` only works with OpenSSL [215] - o cmake: capitalize 'Rustls' in the config summary - o cmake: defer building `unitprotos.h` till a test target needs it [75] - o cmake: define `WIN32_LEAN_AND_MEAN` for examples [159] - o cmake: drop redundant unity mode for `curlinfo` [155] - o cmake: enable `-Wall` for MSVC 1944 [128] - o cmake: fix `ENABLE_UNIX_SOCKETS=OFF` with pre-fill enabled on unix - o cmake: fix setting LTO properties on the wrong targets [258] - o cmake: fix to disable Schannel and SSPI for non-Windows targets - o cmake: fix to restrict `SystemConfiguration` to macOS [139] - o cmake: honor `CMAKE_C_FLAGS` in test 1119 and 1167 [206] - 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] - o cmake: omit linking duplicate/unnecessary libs to tests & examples [45] - o cmake: re-add simple test target, and name it `tests` [142] - o cmake: set `CURL_DIRSUFFIX` automatically in multi-config builds [154] - o CODE_STYLE: sync with recent `checksrc.pl` updates [49] - o config-win32.h: do not use winsock2 `inet_ntop()`/`inet_pton()` [58] - o configure: if no perl, disable unity and shell completion, related tidy ups [137] - o configure: tidy up internal names in ngtcp2 ossl detection logic [212] - 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 cookie: remove expired cookies before listing [158] - 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] - o curl: tool_read_cb fix of segfault [18] - o curl_addrinfo: drop workaround for old-mingw [14] - o curl_easy_ssls_export: make the example more clear [78] - o curl_fnmatch, servers: drop local macros in favour of `sizeof()` [21] - o curl_mime_data_cb.md: mention what datasize is for [107] - o curl_ossl: extend callback table for nghttp3 1.11.0 [46] - o curl_setup.h: include `stdint.h` earlier [260] - o curl_setup.h: move UWP detection after `config-win32.h` (revert) [51] - o curl_setup.h: move UWP detection after `config-win32.h` [23] - o CURLINFO_FILETIME*.md: correct the examples [242] - o CURLOPT: bump `CURL_REDIR_*` macros to `long` [110] - o CURLOPT: bump `CURL_SSLVERSION_*` macros to `long` [149] - o CURLOPT: bump `CURLALTSVC_*` macros to `long` [96] - o CURLOPT: bump `CURLFTP*` enums to `long`, drop casts [54] - o CURLOPT: bump `CURLHEADER_*` macros to `long`, drop casts [94] - o CURLOPT: bump `CURLPROTO_*` macros to `long` [148] - o CURLOPT: bump `CURLPROXY_*` enums to `long`, drop casts [95] - o CURLOPT: bump `CURLWS_NOAUTOPONG`, `CURLWS_RAW_MODE` macros to `long` [150] - 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 CURLOPT_SSL_CTX_*: replace the base64 with XXXX [171] - o delta: fix warnings, fix for non-GNU `date` tool [99] - o DEPRECATE.md: drop old OpenSSL versions [266] - o DEPRECATE.md: drop support for c-ares versions before 1.16.0 [191] - o DEPRECATE.md: drop support for Windows XP/2003 [31] - o DEPRECATE.md: remove leftover "nothing" [57] - o DISTROS.md: add Haiku [39] - o docs/cmdline-opts: the auth types are not mutually exclusive [103] - o docs: add CURLOPT type change history, drop casts where present [143] - o docs: add major incident section to vuln disclosure policy [271] - o docs: fix link CONTRIBUTE.md link [192] - o docs: fix name in curl_easy_ssls_export man page [12] - o docs: fix typo (staring -> starting) [211] - o docs: point two broken links to archive.org [134] - o docs: put `<>` within backticks in titles [261] - 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: make `CURLPIPE_MULTIPLEX` fallback `long` [233] - o examples: remove base64 encoded chunks from examples [189] - o examples: remove href_extractor.c [186] - o ftp: store dir components as start+len instead of memdup'ing [198] - o ftp: use 'conn' instead of 'data->conn' [208] - o gnutls: fix building with older supported GnuTLS versions [241] - o gnutls: some small cleanups [41] - o hmac: return error if init fails [2] - o hostip: do DNS cache pruning in milliseconds [132] - o HTTP3.md: avoid `configure` issue for ngtcp2 1.14.0+ compatibility [182] - o http: const up readonly H2_NON_FIELD [10] - o http: do the cookie list access under lock [270] - o http: silence `-Warray-bounds` with gcc 13+ [44] - o idn: reject conversions that end up as a zero length hostname [273] - o inet_pton, inet_ntop: drop declarations when unused [59] - o lib1560: fix memory leak when run without UTF-8 support [17] - o lib1560: replace an `int` with `bool` [97] - o lib2700: use `testnum` [151] - o lib517: use `LL` 64-bit literals & re-enable a test case (`time_t`) [100] - o lib: drop `UNUSED_PARAM` macro [259] - o libcurl: reset rewind flag in curl_easy_reset() [184] - o libssh: Use sftp_aio instead of sftp_async for sftp_recv [92] - 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 mdlinkcheck: handle links with a leading slash properly [195] - o memanalyze: fix warnings [22] - o memory: make function overrides work reliably in unity builds [93] - o multi event: remove only announced [25] - o multi: don't insert a node into the splay tree twice [68] - o multi: fix assert in multi_getsock() [53] - o multi: fix bad splay management [133] - o multi: process pending, one by one [90] - o multi: replace remaining EXPIRE_RUN_NOW [67] - o multissl: initialize when requesting a random number [30] - o ngtcp2: extend callback tables for nghttp3 1.11.0 and ngtcp2 1.14.0 [47] - o ngtcp2: handshake timeout should be equal to --connect-timeout [262] - o ngtcp2: use custom mem funcs [204] - o openldap: fix `-Wtentative-definition-compat` [268] - o openssl: add and use `HAVE_BORINGSSL_LIKE` internal macro [222] - o openssl: add and use `HAVE_OPENSSL3` internal macro [223] - o openssl: assume `OPENSSL_VERSION_NUMBER` [181] - o openssl: auto-pause on verify callback retry [167] - o openssl: check SSL_write() length on retries [152] - o openssl: clear errors after a failed `d2i_X509()` [161] - o openssl: drop more legacy cruft [224] - o openssl: drop redundant `HAVE_OPENSSL_VERSION` macro [221] - o openssl: drop redundant version check [246] - o openssl: drop single-use interim macro `USE_OPENSSL_SRP` [201] - o openssl: enable `HAVE_KEYLOG_CALLBACK` for AWS-LC [220] - o openssl: merge two `#if` blocks [218] - o openssl: output unescaped utf8 x509 issuer/subject DNs [169] - o openssl: remove legacy cruft, document macro guards [231] - 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 openssl: sync an AWS-LC guard with BoringSSL [199] - o openssl: use `RSA_flags()` again with BoringSSL [219] - o parallel-max: bump the max value to 65535 [86] - o parsedate: make Curl_getdate_capped able to return epoch [229] - o processhelp.pm: fix to use the correct null device on Windows [164] - o processhelp.pm: use `Win32::Process*` perl modules if available [200] - o projects: drop unused logic from `generate.bat` [157] - o projects: fix Windows project 'clean' function [203] - o pytest: add SOCKS tests and scoring [9] - o pytest: fix test_17_09_ssl_min_max for BoringSSL [197] - o pytest: increase server KeepAliveTimeout [26] - o pytest: relax error check on test_07_22 [16] - o resolving: dns error tracing [196] - o runtests: assume `Time::HiRes`, drop Perl Win32 dependency [163] - o runtests: remove warning message [230] - o runtests: replace `--ci` with `--buidinfo`, show OS/Perl version again [247] - o runtests: show still running tests when nothing has happened for a while [227] - 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] - o schannel: drop fallbacks for unused `BCRYPT_*` macros [122] - o schannel: drop old-mingw special case [77] - o schannel: fix recent update for mingw32ce [123] - o schannel: fix renegotiation [202] - o schannel: improve handshake procedure [239] - o schannel: not supported with UWP, drop redundant code [105] - o schannel: use if(result) like the code style says [125] - o scripts: enable strict warnings in Perl where missing, fix fallouts [63] - o scripts: fix two Perl uninitialized value warnings [60] - o sendf: getting less data than "max allowed" is okay [170] - o servers: convert two macros to scoped static const strings [89] - o setopt: refactor out the booleans from setopt_long to setopt_bool [83] - o setopt: split out cookielist() and cookiefile() [130] - o socks: do_SOCKS5: Fix invalid buffer content on short send [43] - o socks_sspi: simplify, clean up Curl_SOCKS5_gssapi_negotiate [237] - o spacecheck.pl: when detecting unicode, mention line number [85] - o spacecheck: warn for 3+ empty lines in a row, fix fallouts [240] - o spelling: file system [232] - o test1148: drop redundant `LC_NUMBER=` env setting [13] - o test1557: pass `long` type to `multi_setopt()` [234] - o test1560: set locale/codeset with `LC_ALL` (was: `LANG`), test in CI [19] - o test1560: skip some URLs if UTF-8 is not supported [34] - o test1: raise alloc limits [11] - o test428: re-enable for Windows [5] - o test436: fix running on Windows with `_curlrc` present [153] - o test: add `cygwin` feature and use it (test 1056, 1517) [249] - o tests/ech_tests.sh: indent, if/for style, inline ifs [131] - o tests: constify command-line arguments [82] - o tests: delete unused commands [177] - o tests: drop unused `BLANK` envs, unset `CURL_NOT_SET` [248] - o tests: drop unused `CURL_FORCEHOST` envs [36] - o tests: fix perl warnings in http2-server, http3-server [119] - o tests: fix prechecks to call the bundle libtest tool [120] - o tests: fix UTF-8 detection, per-test `LC_*` settings, CI coverage [6] - o tests: merge clients into libtests, drop duplicate code [76] - o tests: remove the QUIT filters [210] - o tests: set `CURL_ENTROPY` per test, not globally [35] - o tests: unset some envs instead of blanking them [4] - o threaded-resolver: fix shutdown [252] - o tidy-up: `Curl_thread_create()` callback return type [20] - 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 TODO: remove the expand ~ idea [216] - o tool_cb_wrt: stop alloc/free for every chunk windows console output [140] - o tool_filetime: accept setting negative filetime [256] - o tool_getparam: let --trace-config override -v [238] - o tool_getparam: warn on more unicode prefixes [275] - o tool_operate: avoid superfluous strdup'ing output [1] - o tool_operate: use stricter curl_multi_setopt() arguments [225] - o tool_operate: use the correct config pointer [115] - o tool_paramhlp: fix secs2ms() [116] - o tool_parsecfg: use dynbuf for quoted arguments [162] - o tool_urlglob: add integer overflow protection [244] - o tool_urlglob: polish, cleanups, improvements [141] - o typecheck-gcc: add type checks for curl_multi_setopt() [226] - 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 urlglob: only accept 255 globs - o vquic-tls: fix SSL backend type for QUIC connections using gnutls [29] - o vquic: replace assert [254] - o vquic: use curl_getenv [168] - o vtls: set seen http version on successful ALPN [160] - o websocket example: cast print values to unsigned int [251] - o websocket: handling of PONG frames [213] - o websocket: improve handling of 0-len frames [269] - o websocket: reset upload_done when sending data [245] - o windows: assume `ADDRESS_FAMILY`, drop feature checks [88] - o windows: document toolchain support for `CERT_NAME_SEARCH_ALL_NAMES_FLAG` - o windows: document toolchain support for some macros (cont.) [111] - o windows: document toolchain support for some macros [113] - o windows: drop `CRYPT_E_*` macro fallbacks, limit one to mingw32ce [118] - o windows: drop two interim, single-use macros [106] - o windows: drop unused `curlx/version_win32.h` includes [52] - o windows: fix `if_nametoindex()` detection with autotools, improve with cmake [24] - o windows: include `wincrypt.h` before `iphlpapi.h` for mingw-w64 <6 [50] - o windows: target version macro tidy-ups [3] - o wolfssl: rename ML-KEM hybrids to match IETF draft [173] - o write-out.md: header_json is not included the json object [243] - o ws: avoid NULL pointer deref in curl_ws_recv [91] - o ws: get a new mask for each new outgoing frame [255] + o altsvc: cap the list at 5,000 entries [183] + o altsvc: drop the prio field from the struct [185] + o altsvc: skip expired entries read from file [187] + o asyn-ares: connect async [220] + o asyn-ares: drop orphaned variable references [86] + o asyn-ares: fix HTTPS-lookup when not on port 443 [100] + o asyn-thrdd: fix clang-tidy unused value warning [125] + o autotools: limit checksrc target to ignore non-repo test sources [12] + o badwords-all: exit with correct code on errors [50] + o badwords: combine the whitelisting into a single regex [1] + o badwords: detect the the and with with [51] + o badwords: only check comments and strings in source code [61] + o badwords: rework exceptions, fix many of them [15] + o boringssl: fix more coexist cases with Schannel/WinCrypt [170] + o build: assume `snprintf()` in `mprintf`, drop feature check [107] + o build: compiler warning silencing tidy-ups [4] + o build: drop `openssl` module dependency for BoringSSL from `libcurl.pc` [33] + o build: drop duplicate `pthread.h` includes [158] + o build: drop redundant `USE_QUICHE` guards [159] + o build: enable `-Wimplicit-int-enum-cast` compiler warning, fix issues [84] + o build: skip detecting `pipe2()` for Apple targets [227] + o cf-https-connect: silence `-Wimplicit-int-enum-cast` with HTTPS-RR [132] + o cf-https-connect: silence `-Wimplicit-int-enum-cast` with HTTPS-RR [63] + o cf-ip-happy: limit concurrent attempts [191] + o cf-socket: avoid low risk integer overflow on ancient Solaris [56] + o cfilters: fix Curl_pollset_poll() return code mixup [206] + o clang-tidy: avoid assignments in `if` expressions [175] + o cmake: add CMake Config-based dependency detection [87] + o cmake: add CMake Config-based dependency detection for c-ares, wolfSSL [134] + o cmake: document functions used from Windows system DLLs [103] + o cmake: enable pthreads for BoringSSL/AWS-LC [196] + o cmake: resolve targets recursively when generating `libcurl.pc` [45] + o cmake: rework binutils ld hack to not read `LOCATION` property [41] + o cmake: silence bad library `Threads::Threads` warning [131] + o cmake: use `AIX` built-in variable (with CMake 4.0+) [163] + o config2setopts: make --capath work in proxy disabled builds [113] + o configure: fix `--with-ngtcp2=` option for crypto libs [26] + o configure: fix LibreSSL ngtcp2 1.15.0+ crypto lib selection logic [3] + o configure: prefer dependency-specific variables over `$withval` [35] + o configure: remove superfluous experimental warning for HTTP/3 [169] + o configure: silence useless clang warnings in C89 builds [156] + o configure: tidy up comments [202] + o cookie: fix rejection when tabs in value [189] + o curl-wolfssl.m4: fix to use the correct value for pkg-config directory [36] + o curl.h: replace macros with C++-friendly method to enforce 3 args [110] + o curl_ctype.h: fix spelling in a couple of locally used macros [28] + o curl_get_line: error out on read errors [9] + o curl_get_line: fix potential infinite loop when filename is a directory [46] + o curl_ngtcp2: extend and update callbacks for 1.22.0+ [165] + o curl_ntlm_core: drop redundant PP condition [140] + o curl_ntlm_core: use wolfCrypt DES API with wolfSSL [200] + o curl_setup.h: drop stray/unused `USE_OPENSSL_QUIC` guard [210] + o curl_sha512_256: support delegating to wolfSSL API [149] + o curl_version_info.md: clarify age details [69] + o CURLOPT_HAPROXY_CLIENT_IP.md: mention assumption on data format [96] + o CURLOPT_SOCKS5_AUTH.md: an access property [212] + o CURLOPT_SSL_CTX_FUNCTION.md: expand on effects connection reuse [105] + o CURLOPT_UPLOAD_FLAGS.md: expand [223] + o curlx_now(), prevent zero timestamp [93] + o DEPRECATE: fix minor release number typo + o digest: pass in the user name quoted (as well) [34] + o dns: https-eyeballing async [229] + o dnscache: own source file, improvements [116] + o docs/cmdline-opts: tidy up retry-connrefused [190] + o docs/lib: fix typos [53] + o docs: CURLOPT_LOGIN_OPTIONS is a login property [228] + o docs: enable more compiler warnings for C snippets, fix 3 finds [71] + o docs: list more dependencies for running Python HTTP tests [123] + o docs: mention more zip bomb precautions [166] + o docs: minor wording tweaks + o docs: noproxy wants the punycoded hostname version [214] + o docs: SSH host verification is done at connect time [197] + o docs: use the correct CURLOPT_WRITEFUNCTION signature [142] + o doh: fix memory-leak when doing a second DoH resolve [55] + o doh: remove superfluous doh_req check [222] + o examples/websocket: fix to sleep more on Windows [92] + o examples: drop warning silencers no longer hit [14] + o examples: fix typo in comment [75] + o file: init fd to -1 to prevent close fd 0 on early failure [40] + o fopen: for temp files, inherit permissions only for owner [146] + o ftp: do not strdup DATA hostname [29] + o ftp: make the MDTM date parser stricter (again) [115] + o ftp: reject PWD responses containing control characters [95] + o gcc: guard `#pragma diagnostic` in core code for <4.6 [94] + o generate.bat: remove extra % from VC11 and VC12 runs + o genserv.pl: make external calls safe [119] + o getinfo: initialize `PureInfo` field `used_proxy` [43] + o getinfo: repair CURLINFO_TLS_SESSION [193] + o gnutls: fix clang-tidy warning with !verbose [126] + o gtls: fail for large files in `load_file()` [174] + o h3: HTTPS-RR use in HTTP/3 [221] + o hostip: clear the sockaddr_in6 structure before use [20] + o HSTS: cap the list [177] + o hsts: make the HSTS read callback handle name dupes [141] + o hsts: skip expired HSTS entries read from file [188] + o hsts: when a dupe host adds subdomains, use that [130] + o http2: clear the h2 session at delete [99] + o http2: prevent secure schemes pushed over insecure connections [181] + o http2: return error on OOM in push headers [65] + o HTTP3.md: drop outdated mentions of OpenSSL-QUIC [2] + o http: fix auth_used and auth_avail [154] + o http: fix Curl_compareheader for multi value headers [11] + o http: make Curl_compareheader handle multiple commas in header + o http: on 303, switch to GET [208] + o imap: reset the UIDVALIDITY state between transfers [7] + o include: drop 'will' from public headers [73] + o INSTALL.md: update Cygwin instructions [198] + o keylog.h: replace literal number with macro in declaration [171] + o keylog: drop unused/redundant includes and guards [172] + o ldap: drop duplicate `ldap_set_option()` on Windows [42] + o ldap: fix to initialize cleartext connection on Windows [49] + o lib: accept larger input to md5/hmac/sha256/sha512 functions [194] + o lib: always use Curl_1st_fatal instead of Curl_1st_err [89] + o lib: make resolving HTTPS DNS records reliable: [176] + o lib: replace `PRI*32` printf masks with C89 ones [201] + o libssh2: fix error handling on quote errors [21] + o libssh: fix 64-bit printf mask for mingw-w64 <=6.0.0 [215] + o libssh: fix `-Wsign-compare` in 32-bit builds [217] + o libssh: path length precaution [164] + o libssh: propagate error back in SFTP function [178] + o libtest: drop duplicate include [111] + o location/follow: mention netrc [138] + o man: fix argument type for `CURLSHOPT_[UN]SHARE` options [211] + o mbedtls: fix ECJPAKE matching [135] + o md4, md5: switch to wolfCrypt API in wolfSSL builds [139] + o mk-ca-bundle.pl: make `ca-bundle.crt` timestamp match `certdata.txt`'s [44] + o multi: fix connection retry for non-http [180] + o multi: improve wakeup and wait code [118] + o netrc: find login-less password when user is given in URL [6] + o netrc: remove unused parsenetrc() macro for netrc-disabled [121] + o netrc: skip malformed macdef lines [67] + o openssl channel_binding: lookup digest algorithm without NID [117] + o openssl: drop obsolete SSLv2 logic [27] + o openssl: fix build with 4.0.0-beta1 no-deprecated [184] + o openssl: fix memory leaks in ECH code (OpenSSL 3) [78] + o openssl: trace count of found / imported Windows native CA roots [8] + o OS400: add new definitions to the ILE/RPG binding. [153] + o os400sys: fix typo in comment (symetry -> symmetry) [58] + o parsedate: bsearch the time zones [232] + o parsedate: fix wrong treatment of "military time zones" [182] + o perl: harden external command invocations [133] + o progress: count amount of data "delivered" to application [66] + o protocol.h: fix the CURLPROTO_MASK [31] + o protocol: disable connection reuse for SMB(S) [199] + o protocol: use scheme names lowercase [38] + o proxy: chunked response, error code [143] + o pytest: add additional quiche check for flaky test_05_01 [22] + o rand: use `BCryptGenRandom()` in UWP builds [88] + o ratelimit: reset on start [150] + o request: reset resp_trailer in new requests [186] + o rustls: handle EOF during initial handshake [203] + o scripts: drop redundant double-quotes: `"$var"` -> `$var` (Perl) [109] + o scripts: harden / tidy up more Perl `system()` calls [70] + o sendf: fix CR detection if no LF is in the chunk [219] + o sha256, sha512_256: switch to wolfCrypt API [147] + o sha256: support delegating to wolfSSL API [148] + o share: concurrency handling, easy updates [104] + o share: do bitshifts after the type is checked to be valid [216] + o socks: reject zero-length GSSAPI/SSPI tokens from proxy [157] + o spelling: fix typos [173] + o src: use ftruncate() unconditionally [128] + o sshserver.pl: harden more `system()` calls [81] + o sshserver.pl: pass command-line to `system()` safely [82] + o strerr: correct the strerror_s() return code condition [25] + o sws: fix potential OOB write [80] + o synctime: fix off-by-one read and write to a read-only buffer (Windows) [85] + o test 766: flag as timing-dependent [136] + o test459: switch to mode="warn" for stderr check [5] + o testcurl.pl: replace shell commands with Perl `rmtree()` [76] + o tests/unit/README: describe how to unit test static functions [60] + o tool: check for curlinfo->age when determining if ssh backend [77] + o tool: fix memory mixups [106] + o tool: fix retries in parallel mode [137] + o tool: fix two more allocator mismatches [155] + o tool_cb_hdr: only truncate etags output when regular file [129] + o tool_cb_rea: make waitfd() return void [168] + o tool_cb_wrt: fix no-clobber error handling [39] + o tool_cfgable: free the SSL signature algorithms [62] + o tool_formparse: propagate my_get_line errors when reading headers [102] + o tool_getparam: use correct free function for libcurl memory [68] + o tool_ipfs: accept IPFS gateway URL without set port number [13] + o tool_msgs: avoid null pointer deref for early errors [98] + o tool_operate: actually apply the --parallel-max-host limit [167] + o tool_operate: drop the scheme-guessing in the -G handling [54] + o tool_operate: fix condition for loading `curl-ca-bundle.crt` (Windows) [79] + o tool_operate: fix memory-leak on failed uploads [124] + o tool_operate: fix minor memory-leak on early error [23] + o tool_operhlp: fix `add_file_name_to_url()` result on OOM [32] + o tool_operhlp: iterate through all slashes to find name [114] + o tool_operhlp: propagate low-level OOM in `add_file_name_to_url()` [112] + o tool_setopt: return error on OOM correctly [152] + o tool_urlglob: fix memory-leak on glob range overflow [19] + o top-complexity: prevent filename-based shell injection risk [101] + o transfer: clear the URL pointer in OOM to avoid UAF [179] + o transfer: enable custom methods again on next transfer [30] + o transfer: enhance secure check [10] + o url: do not reuse a non-tls starttls connection if new requires TLS [145] + o url: improve connection reuse on negotiate [160] + o url: init req.no_body in DO so that it works for h2 push [161] + o url: set default upload flags to CURLULFLAG_SEEN [224] + o url: use the socks type for socks proxy [47] + o url: use URL for url even in comments [52] + o urlapi: fix handling of "file:///" [122] + o urlapi: make dedotdotify handle leading dots correctly [97] + o urlapi: verify the last letter of a scheme when set explicitly [16] + o urldata: connection bit ipv6_ip is wrong [59] + o urldata: import port types and conn destination format [57] + o urldata: make hstslist only present in HSTS builds [120] + o urldata: make speeder_c uint32 [37] + o urldata: remove trailers_state [17] + o vquic: fix variable name in fallback code [207] + o vtls_scache: include cert_blob independently of verifypeer [231] + o wolfssl: document v5.0.0 (2021-11-01) as minimum required [151] + o wolfssl: fix handling of abrupt connection close [24] + o x509asn1: fix to return error in an error case from `encodeOID()` [83] + o x509asn1: fixed and adapted for ASN1tostr unit testing [48] + o x509asn1: improve encodeOID [72] This release includes the following known bugs: @@ -299,307 +248,252 @@ For all changes ever done in curl: Planned upcoming removals include: - o Builds using VS2008 - o OpenSSL 1.x support - o Support for c-ares versions before 1.16.0 - o Support for Windows XP/2003 - o The winbuild build system - o Windows CE support + o local crypto implementations + o NTLM + o SMB + o TLS-SRP support See https://curl.se/dev/deprecate.html This release would not have looked like this without help, code, reports and advice from friends like these: - adamse on github, Ahmad Gani, Alice Lee Poetics, Ammar Faizi, - Andrew Kirillov, Andriy Druk, Anthony Hu, Berthin Torres Callañaupa, - BobodevMm on github, Calvin Ruocco, Caolán McNamara, Cole Leavitt, - correctmost on github, d1r3ct0r, Dan Fandrich, Daniel Böhmer, Daniel Engberg, - Daniel McCarney, Daniel Stenberg, David Zhuang, devgs on github, - Dominik Tomecki, Dustin L. Howett, Eshan Kelkar, Florian Friedrich, - Gabriel Marin, Gisle Vanem, Google Big Sleep, Harry Sintonen, - IoannisGS on github, James Fuller, Jelle Raaijmakers, Jeroen Ooms, - Kai Pastor, Karthik Das, kkmuffme on github, kupavcevdenis on github, - letshack9707 on hackerone, lf- on github, LoRd_MuldeR, Marcel Raad, - Michael Osipov, Michał Petryka, Natris on github, nevakrien on github, - Oxan van Leeuwen, Paul Gilmartin, Pavel Kropachev, Petar Popovic, - Philippe Antoine, Pino Toscano, Qriist on github, Ray Satiro, - renovate[bot], RepoRascal on hackerone, rm-rmonaghan on github, - Roberto Hidalgo, Samuel Henrique, Schrijvers Luc, Sebastian Carlos, - Sergio Durigan Junior, Simon Dalvai, Stanislav Osipov, Stefan Eissing, - stephannn on github, sunriseL, Tal Regev, Terence Eden, Todd Gamblin, - Viktor Szakats, Waldemar Kornewald, XCas13, xfangfang, yaoy6 on github, - Yedaya Katsman, ウさん - (76 contributors) + Alex Hamilton, am-perip on hackerone, Arkadi Vainbrand, bird on github, + BlackFuffey on github, Carlos Henrique Lima Melara, crawfordxx, + Cutiapreta on hackerone, Dan Arnfield, Dan Fandrich, Daniel McCarney, + Daniel Stenberg, dependabot[bot], Dexter Gerig, Ercan Ermis, + fds242 on github, Flavio Amieiro, Geeknik Labs, Greg Kroah-Hartman, + Harry Sintonen, Henrique Pereira, Ian Spence, Izan on hackerone, + James Fuller, Jason Stangroome, John Haugabook, Kai Pastor, Kaixuan Li, + kpcyrd, lg_oled77c5pua on hackerone, M42kL33 on hackerone, + m777m0 on hackerone, Marcel Raad, Martin Dürrmeier, Mehtab Zafar, + Michael Hendricks, Michael Kaufmann, Orgad Shaneh, Osama Hamad, Otis Cui Lei, + Patrick Monnerat, Ray Satiro, renovate[bot], Richard Tollerton, + Rob Crittenden, Samuel Henrique, Scott Boudreaux, Sergey Fedorov, + sergio-nsk on github, Stefan Eissing, Ted Lyngmo, Viktor Szakats, + Vladimír Marek, xkilua on hackerone, Yoshiro Yoneya + (55 contributors) References to bug reports and discussions on issues: - [1] = https://curl.se/bug/?i=17946 - [2] = https://curl.se/bug/?i=18008 - [3] = https://curl.se/bug/?i=17981 - [4] = https://curl.se/bug/?i=17994 - [5] = https://curl.se/bug/?i=17991 - [6] = https://curl.se/bug/?i=17988 - [7] = https://curl.se/bug/?i=17962 - [8] = https://curl.se/bug/?i=17729 - [9] = https://curl.se/bug/?i=17986 - [10] = https://curl.se/bug/?i=17996 - [11] = https://curl.se/bug/?i=18004 - [12] = https://curl.se/bug/?i=17995 - [13] = https://curl.se/bug/?i=17993 - [14] = https://curl.se/bug/?i=18038 - [15] = https://curl.se/bug/?i=18036 - [16] = https://curl.se/bug/?i=18050 - [17] = https://curl.se/bug/?i=17998 - [18] = https://curl.se/bug/?i=17978 - [19] = https://curl.se/bug/?i=17938 - [20] = https://curl.se/bug/?i=17889 - [21] = https://curl.se/bug/?i=17898 - [22] = https://curl.se/bug/?i=18049 - [23] = https://curl.se/bug/?i=17980 - [24] = https://curl.se/bug/?i=17982 - [25] = https://curl.se/bug/?i=17949 - [26] = https://curl.se/bug/?i=17968 - [27] = https://curl.se/bug/?i=17975 - [28] = https://curl.se/bug/?i=17958 - [29] = https://curl.se/bug/?i=17976 - [30] = https://curl.se/bug/?i=17963 - [31] = https://curl.se/bug/?i=18016 - [32] = https://curl.se/bug/?i=17967 - [33] = https://curl.se/bug/?i=17984 - [34] = https://curl.se/bug/?i=17933 - [35] = https://curl.se/bug/?i=17971 - [36] = https://curl.se/bug/?i=17972 - [37] = https://curl.se/bug/?i=17768 - [38] = https://curl.se/bug/?i=17951 - [39] = https://curl.se/bug/?i=17953 - [40] = https://github.com/microsoft/vcpkg/pull/46444#pullrequestreview-3026575393 - [41] = https://curl.se/bug/?i=17941 - [42] = https://curl.se/bug/?i=17940 - [43] = https://curl.se/bug/?i=17942 - [44] = https://curl.se/bug/?i=18030 - [45] = https://curl.se/bug/?i=17696 - [46] = https://curl.se/bug/?i=18026 - [47] = https://curl.se/bug/?i=18019 - [48] = https://curl.se/bug/?i=18053 - [49] = https://curl.se/bug/?i=18015 - [50] = https://curl.se/bug/?i=18012 - [51] = https://curl.se/bug/?i=18014 - [52] = https://curl.se/bug/?i=18011 - [53] = https://curl.se/bug/?i=18046 - [54] = https://curl.se/bug/?i=17797 - [55] = https://curl.se/bug/?i=17791 - [56] = https://curl.se/bug/?i=17992 - [57] = https://curl.se/bug/?i=18044 - [58] = https://curl.se/bug/?i=18045 - [59] = https://curl.se/bug/?i=18043 - [60] = https://curl.se/bug/?i=18047 - [61] = https://curl.se/bug/?i=17955 - [62] = https://curl.se/bug/?i=17884 - [63] = https://curl.se/bug/?i=17877 - [64] = https://curl.se/bug/?i=18018 - [65] = https://curl.se/bug/?i=17876 - [66] = https://curl.se/bug/?i=18041 - [67] = https://curl.se/bug/?i=17883 - [68] = https://curl.se/bug/?i=18005 - [69] = https://curl.se/bug/?i=17789 - [70] = https://curl.se/bug/?i=17712 - [71] = https://curl.se/bug/?i=17894 - [72] = https://curl.se/bug/?i=18081 - [73] = https://curl.se/bug/?i=18088 - [74] = https://curl.se/bug/?i=18119 - [75] = https://curl.se/bug/?i=18086 - [76] = https://curl.se/bug/?i=18079 - [77] = https://curl.se/bug/?i=18084 - [78] = https://curl.se/bug/?i=18117 - [79] = https://curl.se/bug/?i=18066 - [80] = https://curl.se/bug/?i=18105 - [81] = https://curl.se/bug/?i=18052 - [82] = https://curl.se/bug/?i=18076 - [83] = https://curl.se/bug/?i=17887 - [84] = https://curl.se/bug/?i=17225 - [85] = https://curl.se/bug/?i=18120 - [86] = https://curl.se/bug/?i=18068 - [87] = https://curl.se/bug/?i=18069 - [88] = https://curl.se/bug/?i=18057 - [89] = https://curl.se/bug/?i=18067 - [90] = https://curl.se/bug/?i=18017 - [91] = https://curl.se/bug/?i=18065 - [92] = https://curl.se/bug/?i=17440 - [93] = https://curl.se/bug/?i=17827 - [94] = https://curl.se/bug/?i=18055 - [95] = https://curl.se/bug/?i=18054 - [96] = https://curl.se/bug/?i=18063 - [97] = https://curl.se/bug/?i=18064 - [98] = https://curl.se/bug/?i=18062 - [99] = https://curl.se/bug/?i=18061 - [100] = https://curl.se/bug/?i=18032 - [101] = https://curl.se/bug/?i=17800 - [102] = https://curl.se/bug/?i=18060 - [103] = https://curl.se/bug/?i=18059 - [104] = https://curl.se/bug/?i=17947 - [105] = https://curl.se/bug/?i=18116 - [106] = https://curl.se/bug/?i=18114 - [107] = https://curl.se/bug/?i=18115 - [108] = https://curl.se/bug/?i=18112 - [109] = https://curl.se/bug/?i=18106 - [110] = https://curl.se/bug/?i=18110 - [111] = https://curl.se/bug/?i=18113 - [112] = https://curl.se/bug/?i=18109 - [113] = https://curl.se/bug/?i=18085 - [114] = https://curl.se/bug/?i=18108 - [115] = https://curl.se/bug/?i=18200 - [116] = https://curl.se/bug/?i=18167 - [117] = https://curl.se/bug/?i=18096 - [118] = https://curl.se/bug/?i=18092 - [119] = https://curl.se/bug/?i=18100 - [120] = https://curl.se/bug/?i=18099 - [121] = https://curl.se/bug/?i=18093 - [122] = https://curl.se/bug/?i=18091 - [123] = https://curl.se/bug/?i=18097 - [124] = https://curl.se/bug/?i=18095 - [125] = https://curl.se/bug/?i=18094 - [126] = https://curl.se/bug/?i=17960 - [127] = https://curl.se/bug/?i=16643 - [128] = https://curl.se/bug/?i=18165 - [129] = https://curl.se/bug/?i=16543 - [130] = https://curl.se/bug/?i=18162 - [131] = https://curl.se/bug/?i=18187 - [132] = https://curl.se/bug/?i=18160 - [133] = https://curl.se/bug/?i=18201 - [134] = https://curl.se/bug/?i=18393 - [135] = https://curl.se/bug/?i=18143 - [136] = https://curl.se/bug/?i=18142 - [137] = https://curl.se/bug/?i=18141 - [138] = https://curl.se/bug/?i=18158 - [139] = https://curl.se/bug/?i=18149 - [140] = https://curl.se/bug/?i=18233 - [141] = https://curl.se/bug/?i=18198 - [142] = https://curl.se/bug/?i=18145 - [143] = https://curl.se/bug/?i=18130 - [144] = https://curl.se/bug/?i=18118 - [145] = https://curl.se/bug/?i=18139 - [146] = https://curl.se/bug/?i=17977 - [147] = https://curl.se/bug/?i=18134 - [148] = https://curl.se/bug/?i=18136 - [149] = https://curl.se/bug/?i=18135 - [150] = https://curl.se/bug/?i=18137 - [151] = https://curl.se/bug/?i=18138 - [152] = https://curl.se/bug/?i=18121 - [153] = https://curl.se/bug/?i=18242 - [154] = https://curl.se/bug/?i=18241 - [155] = https://curl.se/bug/?i=18238 - [156] = https://curl.se/bug/?i=18195 - [157] = https://curl.se/bug/?i=18397 - [158] = https://curl.se/bug/?i=18299 - [159] = https://curl.se/bug/?i=18232 - [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 - [167] = https://curl.se/mail/lib-2025-08/0012.html - [168] = https://curl.se/bug/?i=18170 - [169] = https://curl.se/bug/?i=18171 - [170] = https://curl.se/bug/?i=18283 - [171] = https://curl.se/bug/?i=18261 - [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 - [177] = https://curl.se/bug/?i=18319 - [178] = https://curl.se/bug/?i=18216 - [179] = https://curl.se/bug/?i=18272 - [180] = https://curl.se/bug/?i=18254 - [181] = https://curl.se/bug/?i=18388 - [182] = https://curl.se/bug/?i=18188 - [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 - [188] = https://curl.se/bug/?i=18322 - [189] = https://curl.se/bug/?i=18260 - [190] = https://curl.se/bug/?i=18259 - [191] = https://curl.se/bug/?i=18408 - [192] = https://curl.se/bug/?i=18372 - [193] = https://curl.se/bug/?i=17683 - [194] = https://curl.se/bug/?i=18243 - [195] = https://curl.se/bug/?i=18382 - [196] = https://curl.se/bug/?i=18247 - [197] = https://curl.se/bug/?i=18385 - [198] = https://curl.se/bug/?i=18312 - [199] = https://curl.se/bug/?i=18384 - [200] = https://curl.se/bug/?i=18308 - [201] = https://curl.se/bug/?i=18383 - [202] = https://curl.se/bug/?i=18029 - [203] = https://curl.se/bug/?i=18412 - [204] = https://curl.se/bug/?i=18196 - [205] = https://curl.se/bug/?i=18306 - [206] = https://curl.se/bug/?i=18307 - [207] = https://curl.se/bug/?i=18305 - [208] = https://curl.se/bug/?i=18304 - [209] = https://curl.se/bug/?i=18377 - [210] = https://curl.se/bug/?i=18405 - [211] = https://curl.se/bug/?i=18450 - [212] = https://curl.se/bug/?i=18378 - [213] = https://curl.se/bug/?i=16706 - [214] = https://curl.se/bug/?i=18371 - [215] = https://curl.se/bug/?i=18365 - [216] = https://curl.se/bug/?i=18363 - [217] = https://curl.se/bug/?i=18362 - [218] = https://curl.se/bug/?i=18370 - [219] = https://curl.se/bug/?i=18369 - [220] = https://curl.se/bug/?i=18368 - [221] = https://curl.se/bug/?i=18367 - [222] = https://curl.se/bug/?i=18358 - [223] = https://curl.se/bug/?i=18360 - [224] = https://curl.se/bug/?i=18359 - [225] = https://curl.se/bug/?i=18357 - [226] = https://curl.se/bug/?i=18357 - [227] = https://curl.se/bug/?i=18349 - [228] = https://curl.se/bug/?i=18350 - [229] = https://curl.se/bug/?i=18445 - [230] = https://curl.se/bug/?i=18404 - [231] = https://curl.se/bug/?i=18351 - [232] = https://curl.se/bug/?i=18348 - [233] = https://curl.se/bug/?i=18356 - [234] = https://curl.se/bug/?i=18355 - [235] = https://curl.se/bug/?i=18347 - [236] = https://curl.se/bug/?i=18251 - [237] = https://curl.se/bug/?i=18315 - [238] = https://curl.se/bug/?i=18346 - [239] = https://curl.se/bug/?i=18323 - [240] = https://curl.se/bug/?i=18478 - [241] = https://curl.se/bug/?i=18335 - [242] = https://curl.se/bug/?i=18447 - [243] = https://curl.se/bug/?i=18390 - [244] = https://curl.se/bug/?i=18398 - [245] = https://curl.se/bug/?i=18476 - [246] = https://curl.se/bug/?i=18333 - [247] = https://curl.se/bug/?i=18329 - [248] = https://curl.se/bug/?i=18328 - [249] = https://curl.se/bug/?i=18327 - [250] = https://curl.se/bug/?i=18434 - [251] = https://curl.se/bug/?i=18326 - [252] = https://curl.se/bug/?i=18263 - [253] = https://curl.se/bug/?i=18433 - [254] = https://curl.se/bug/?i=18495 - [255] = https://curl.se/bug/?i=18496 - [256] = https://curl.se/bug/?i=18424 - [258] = https://curl.se/bug/?i=18469 - [259] = https://curl.se/bug/?i=18455 - [260] = https://curl.se/bug/?i=18430 - [261] = https://curl.se/bug/?i=18498 - [262] = https://curl.se/bug/?i=18431 - [265] = https://curl.se/bug/?i=18422 - [266] = https://curl.se/bug/?i=18413 - [268] = https://curl.se/bug/?i=18470 - [269] = https://curl.se/bug/?i=18286 - [270] = https://curl.se/bug/?i=18457 - [271] = https://curl.se/bug/?i=18483 - [273] = https://curl.se/bug/?i=18462 - [275] = https://curl.se/bug/?i=18459 + [1] = https://curl.se/bug/?i=20880 + [2] = https://curl.se/bug/?i=20914 + [3] = https://curl.se/bug/?i=20889 + [4] = https://curl.se/bug/?i=20908 + [5] = https://curl.se/bug/?i=20910 + [6] = https://curl.se/bug/?i=20950 + [7] = https://curl.se/bug/?i=20962 + [8] = https://curl.se/bug/?i=20899 + [9] = https://curl.se/bug/?i=20958 + [10] = https://curl.se/bug/?i=20951 + [11] = https://curl.se/bug/?i=20894 + [12] = https://curl.se/bug/?i=20898 + [13] = https://curl.se/bug/?i=20957 + [14] = https://curl.se/bug/?i=20896 + [15] = https://curl.se/bug/?i=20886 + [16] = https://curl.se/bug/?i=20893 + [17] = https://curl.se/bug/?i=20960 + [18] = https://curl.se/bug/?i=20846 + [19] = https://curl.se/bug/?i=20956 + [20] = https://curl.se/bug/?i=20885 + [21] = https://curl.se/bug/?i=20883 + [22] = https://curl.se/bug/?i=20952 + [23] = https://curl.se/bug/?i=20954 + [24] = https://curl.se/bug/?i=21002 + [25] = https://curl.se/bug/?i=20955 + [26] = https://curl.se/bug/?i=18022 + [27] = https://curl.se/bug/?i=20945 + [28] = https://curl.se/bug/?i=20810 + [29] = https://curl.se/bug/?i=20953 + [30] = https://curl.se/bug/?i=21037 + [31] = https://curl.se/bug/?i=21031 + [32] = https://curl.se/bug/?i=21011 + [33] = https://curl.se/bug/?i=20926 + [34] = https://curl.se/bug/?i=20940 + [35] = https://curl.se/bug/?i=20944 + [36] = https://curl.se/bug/?i=20943 + [37] = https://curl.se/bug/?i=21036 + [38] = https://curl.se/bug/?i=21033 + [39] = https://curl.se/bug/?i=20939 + [40] = https://curl.se/bug/?i=21029 + [41] = https://curl.se/bug/?i=20839 + [42] = https://curl.se/bug/?i=20930 + [43] = https://curl.se/bug/?i=21020 + [44] = https://curl.se/bug/?i=20528 + [45] = https://curl.se/bug/?i=20840 + [46] = https://curl.se/bug/?i=20823 + [47] = https://curl.se/bug/?i=21025 + [48] = https://curl.se/bug/?i=21013 + [49] = https://curl.se/bug/?i=20927 + [50] = https://curl.se/bug/?i=20934 + [51] = https://curl.se/bug/?i=20934 + [52] = https://curl.se/bug/?i=20935 + [53] = https://curl.se/bug/?i=20933 + [54] = https://curl.se/bug/?i=20992 + [55] = https://curl.se/bug/?i=20929 + [56] = https://curl.se/bug/?i=21111 + [57] = https://curl.se/bug/?i=20918 + [58] = https://curl.se/bug/?i=20923 + [59] = https://curl.se/bug/?i=20919 + [60] = https://curl.se/bug/?i=21018 + [61] = https://curl.se/bug/?i=20909 + [62] = https://curl.se/bug/?i=20915 + [63] = https://curl.se/bug/?i=21057 + [64] = https://curl.se/bug/?i=20911 + [65] = https://hackerone.com/reports/3636044 + [66] = https://curl.se/bug/?i=20787 + [67] = https://curl.se/bug/?i=21049 + [68] = https://curl.se/bug/?i=21075 + [69] = https://curl.se/bug/?i=21052 + [70] = https://curl.se/bug/?i=21007 + [71] = https://curl.se/bug/?i=21006 + [72] = https://curl.se/bug/?i=21003 + [73] = https://curl.se/bug/?i=21005 + [74] = https://curl.se/bug/?i=20916 + [75] = https://curl.se/bug/?i=21001 + [76] = https://curl.se/bug/?i=21053 + [77] = https://curl.se/bug/?i=21050 + [78] = https://curl.se/bug/?i=20993 + [79] = https://curl.se/bug/?i=20989 + [80] = https://curl.se/bug/?i=20988 + [81] = https://curl.se/bug/?i=20997 + [82] = https://curl.se/bug/?i=20996 + [83] = https://curl.se/bug/?i=20991 + [84] = https://curl.se/bug/?i=20990 + [85] = https://curl.se/bug/?i=20987 + [86] = https://curl.se/bug/?i=20999 + [87] = https://curl.se/bug/?i=20814 + [88] = https://curl.se/bug/?i=20983 + [89] = https://curl.se/bug/?i=20980 + [90] = https://curl.se/bug/?i=20698 + [91] = https://curl.se/bug/?i=20673 + [92] = https://curl.se/bug/?i=20978 + [93] = https://curl.se/bug/?i=21034 + [94] = https://curl.se/bug/?i=20892 + [95] = https://curl.se/bug/?i=20949 + [96] = https://curl.se/bug/?i=21042 + [97] = https://curl.se/bug/?i=20974 + [98] = https://curl.se/bug/?i=20967 + [99] = https://curl.se/bug/?i=20975 + [100] = https://curl.se/bug/?i=20966 + [101] = https://curl.se/bug/?i=20969 + [102] = https://curl.se/bug/?i=20963 + [103] = https://curl.se/bug/?i=20965 + [104] = https://curl.se/bug/?i=20870 + [105] = https://curl.se/bug/?i=21164 + [106] = https://curl.se/bug/?i=21099 + [107] = https://curl.se/bug/?i=20763 + [108] = https://curl.se/bug/?i=20407 + [109] = https://curl.se/bug/?i=21009 + [110] = https://curl.se/bug/?i=20709 + [111] = https://curl.se/bug/?i=21046 + [112] = https://curl.se/bug/?i=21011 + [113] = https://curl.se/bug/?i=21063 + [114] = https://curl.se/bug/?i=21165 + [115] = https://curl.se/bug/?i=21041 + [116] = https://curl.se/bug/?i=20864 + [117] = https://curl.se/bug/?i=20590 + [118] = https://curl.se/bug/?i=20832 + [119] = https://curl.se/bug/?i=20971 + [120] = https://curl.se/bug/?i=21068 + [121] = https://curl.se/bug/?i=21067 + [122] = https://curl.se/bug/?i=21070 + [123] = https://curl.se/bug/?i=21110 + [124] = https://curl.se/bug/?i=21062 + [125] = https://curl.se/bug/?i=21061 + [126] = https://curl.se/bug/?i=21060 + [127] = https://curl.se/bug/?i=20968 + [128] = https://curl.se/bug/?i=21109 + [129] = https://curl.se/bug/?i=21103 + [130] = https://curl.se/bug/?i=21108 + [131] = https://curl.se/bug/?i=21170 + [132] = https://curl.se/bug/?i=21167 + [133] = https://curl.se/bug/?i=21097 + [134] = https://curl.se/bug/?i=21098 + [135] = https://curl.se/bug/?i=21264 + [136] = https://curl.se/bug/?i=21155 + [137] = https://curl.se/bug/?i=20669 + [138] = https://curl.se/bug/?i=21091 + [139] = https://curl.se/bug/?i=21093 + [140] = https://curl.se/bug/?i=21096 + [141] = https://curl.se/bug/?i=21201 + [142] = https://curl.se/bug/?i=21265 + [143] = https://curl.se/bug/?i=21084 + [144] = https://curl.se/bug/?i=20936 + [145] = https://curl.se/bug/?i=21082 + [146] = https://curl.se/bug/?i=21092 + [147] = https://curl.se/bug/?i=21090 + [148] = https://curl.se/bug/?i=21078 + [149] = https://curl.se/bug/?i=21077 + [150] = https://curl.se/bug/?i=21086 + [151] = https://curl.se/bug/?i=21080 + [152] = https://curl.se/bug/?i=21083 + [153] = https://curl.se/bug/?i=20672 + [154] = https://curl.se/bug/?i=21274 + [155] = https://curl.se/bug/?i=21150 + [156] = https://curl.se/bug/?i=21263 + [157] = https://curl.se/bug/?i=21159 + [158] = https://curl.se/bug/?i=21144 + [159] = https://curl.se/bug/?i=21135 + [160] = https://curl.se/bug/?i=21203 + [161] = https://curl.se/bug/?i=21194 + [163] = https://curl.se/bug/?i=21134 + [164] = https://curl.se/bug/?i=21193 + [165] = https://curl.se/bug/?i=21152 + [166] = https://curl.se/bug/?i=21143 + [167] = https://curl.se/bug/?i=21147 + [168] = https://curl.se/bug/?i=21127 + [169] = https://curl.se/bug/?i=21139 + [170] = https://curl.se/bug/?i=21136 + [171] = https://curl.se/bug/?i=21141 + [172] = https://curl.se/bug/?i=21137 + [173] = https://curl.se/bug/?i=21198 + [174] = https://curl.se/bug/?i=21256 + [175] = https://curl.se/bug/?i=21256 + [176] = https://curl.se/bug/?i=21175 + [177] = https://curl.se/bug/?i=21190 + [178] = https://curl.se/bug/?i=21122 + [179] = https://curl.se/bug/?i=21123 + [180] = https://curl.se/bug/?i=21121 + [181] = https://curl.se/bug/?i=21113 + [182] = https://curl.se/bug/?i=21251 + [183] = https://curl.se/bug/?i=21183 + [184] = https://curl.se/bug/?i=21119 + [185] = https://curl.se/bug/?i=21188 + [186] = https://curl.se/bug/?i=21112 + [187] = https://curl.se/bug/?i=21187 + [188] = https://curl.se/bug/?i=21186 + [189] = https://curl.se/bug/?i=21185 + [190] = https://curl.se/bug/?i=21182 + [191] = https://curl.se/bug/?i=21252 + [193] = https://curl.se/bug/?i=21290 + [194] = https://curl.se/bug/?i=21174 + [196] = https://curl.se/bug/?i=21168 + [197] = https://curl.se/bug/?i=21173 + [198] = https://curl.se/bug/?i=20995 + [199] = https://curl.se/bug/?i=21238 + [200] = https://curl.se/bug/?i=21247 + [201] = https://curl.se/bug/?i=21234 + [202] = https://curl.se/bug/?i=21246 + [203] = https://curl.se/bug/?i=21242 + [206] = https://curl.se/bug/?i=21231 + [207] = https://curl.se/bug/?i=21281 + [208] = https://curl.se/bug/?i=20715 + [210] = https://curl.se/bug/?i=21235 + [211] = https://curl.se/bug/?i=21232 + [212] = https://curl.se/bug/?i=21230 + [214] = https://curl.se/bug/?i=21228 + [215] = https://curl.se/bug/?i=21229 + [216] = https://curl.se/bug/?i=21224 + [217] = https://curl.se/bug/?i=21225 + [219] = https://curl.se/bug/?i=21221 + [220] = https://curl.se/bug/?i=21205 + [221] = https://curl.se/bug/?i=21253 + [222] = https://curl.se/bug/?i=21216 + [223] = https://curl.se/bug/?i=21218 + [224] = https://curl.se/bug/?i=21217 + [227] = https://curl.se/bug/?i=21236 + [228] = https://curl.se/bug/?i=21215 + [229] = https://curl.se/bug/?i=21267 + [231] = https://curl.se/bug/?i=21222 + [232] = https://curl.se/bug/?i=21266 diff --git a/REUSE.toml b/REUSE.toml index 81c214863f..c2e8b79288 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -2,7 +2,7 @@ # SPDX-FileCopyrightText: Daniel Stenberg, , et al. # This file describes the licensing and copyright situation for files that -# cannot be annotated directly, for example because of being simply +# cannot be annotated directly, for example because of being # uncommentable. Unless this is the case, a file should be annotated directly. # # This follows the REUSE specification: https://reuse.software/spec-3.2/#reusetoml @@ -13,41 +13,26 @@ SPDX-PackageDownloadLocation = "https://curl.se/" [[annotations]] path = [ - ".mailmap", - "docs/FAQ", "docs/INSTALL", - "docs/KNOWN_BUGS", "docs/libcurl/symbols-in-versions", - "docs/MAIL-ETIQUETTE", "docs/options-in-versions", "docs/THANKS", - "docs/TODO", - "GIT-INFO.md", "lib/libcurl.vers.in", "lib/libcurl.def", - "packages/OS400/README.OS400", - "packages/vms/build_vms.com", - "packages/vms/curl_release_note_start.txt", - "packages/vms/curlmsg.sdl", - "packages/vms/macro32_exactcase.patch", - "packages/vms/readme", - "plan9/README", + "projects/OS400/README.OS400", + "projects/vms/build_vms.com", + "projects/vms/curl_release_note_start.txt", + "projects/vms/curlmsg.sdl", + "projects/vms/macro32_exactcase.patch", + "projects/vms/readme", "projects/Windows/**", "README", "RELEASE-NOTES", "renovate.json", "tests/certs/**", + "tests/data/data**", "tests/data/test**", "tests/valgrind.supp", - # checksrc control files - "lib/.checksrc", - "lib/curlx/.checksrc", - "lib/vauth/.checksrc", - "lib/vquic/.checksrc", - "lib/vssh/.checksrc", - "lib/vtls/.checksrc", - "src/.checksrc", - "tests/server/.checksrc", ] SPDX-FileCopyrightText = "Daniel Stenberg, , et al." SPDX-License-Identifier = "curl" diff --git a/SECURITY.md b/SECURITY.md index 64e0d2feab..e579ebb6e6 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -10,19 +10,20 @@ Read our [Vulnerability Disclosure Policy](docs/VULN-DISCLOSURE-POLICY.md). ## Reporting a Vulnerability -If you have found or just suspect a security problem somewhere in curl or -libcurl, report it on [HackerOne](https://hackerone.com/curl). +If you have found or suspect a security problem somewhere in curl or libcurl, +[report it](https://curl.se/dev/vuln-disclosure.html)! -We treat security issues with confidentiality until controlled and disclosed responsibly. +We treat security issues with confidentiality until controlled and disclosed +responsibly. ## OpenSSF Best Practices curl has achieved Gold status on the Open Source Security Foundation (OpenSSF) [Best Practices](https://bestpractices.dev/) (formerly Core Infrastructure -Initiative Best Practices), reflecting its adherence to rigorous -security and best practice standards. This achievement highlights curl's -comprehensive documentation, secure development processes, effective change -control mechanisms, and strong maintenance routines. Meeting these criteria +Initiative Best Practices), reflecting its adherence to rigorous security and +best practice standards. This achievement highlights curl's comprehensive +documentation, secure development processes, effective change control +mechanisms, and strong maintenance routines. Meeting these criteria demonstrates curl's commitment to security and reliability, ensuring the project's sustainability and trustworthiness. This underscores curl's role as a leader in open-source software practices. More information can be found on diff --git a/acinclude.m4 b/acinclude.m4 index fe10c2ec97..d98d4b1bef 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -32,12 +32,12 @@ dnl result in a set of double-quoted strings the returned expansion will dnl actually be a single double-quoted string concatenating all them. AC_DEFUN([CURL_CHECK_DEF], [ - AC_REQUIRE([CURL_CPP_P])dnl + AC_REQUIRE([CURL_CPP_P]) OLDCPPFLAGS=$CPPFLAGS - # CPPPFLAG comes from CURL_CPP_P + dnl CPPPFLAG comes from CURL_CPP_P CPPFLAGS="$CPPFLAGS $CPPPFLAG" - AS_VAR_PUSHDEF([ac_HaveDef], [curl_cv_have_def_$1])dnl - AS_VAR_PUSHDEF([ac_Def], [curl_cv_def_$1])dnl + AS_VAR_PUSHDEF([ac_HaveDef], [curl_cv_have_def_$1]) + AS_VAR_PUSHDEF([ac_Def], [curl_cv_def_$1]) if test -z "$SED"; then AC_MSG_ERROR([SED not set. Cannot continue without SED being set.]) fi @@ -70,8 +70,8 @@ AC_DEFUN([CURL_CHECK_DEF], [ AS_VAR_SET(ac_Def, $tmp_exp) ifelse($3,,[AC_MSG_RESULT([$tmp_exp])]) fi - AS_VAR_POPDEF([ac_Def])dnl - AS_VAR_POPDEF([ac_HaveDef])dnl + AS_VAR_POPDEF([ac_Def]) + AS_VAR_POPDEF([ac_HaveDef]) CPPFLAGS=$OLDCPPFLAGS ]) @@ -84,7 +84,7 @@ dnl default includes even if no INCLUDES argument is given. This macro dnl will run silently when invoked with three arguments. AC_DEFUN([CURL_CHECK_DEF_CC], [ - AS_VAR_PUSHDEF([ac_HaveDef], [curl_cv_have_def_$1])dnl + AS_VAR_PUSHDEF([ac_HaveDef], [curl_cv_have_def_$1]) ifelse($3,,[AC_MSG_CHECKING([for compiler definition of $1])]) AC_COMPILE_IFELSE([ AC_LANG_SOURCE( @@ -109,7 +109,7 @@ AC_DEFUN([CURL_CHECK_DEF_CC], [ AS_VAR_SET(ac_HaveDef, no) ifelse($3,,[AC_MSG_RESULT([no])]) fi - AS_VAR_POPDEF([ac_HaveDef])dnl + AS_VAR_POPDEF([ac_HaveDef]) ]) @@ -152,8 +152,8 @@ AC_DEFUN([CURL_CHECK_AIX_ALL_SOURCE], [ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif]) - AC_BEFORE([$0], [AC_SYS_LARGEFILE])dnl - AC_BEFORE([$0], [CURL_CONFIGURE_REENTRANT])dnl + AC_BEFORE([$0], [AC_SYS_LARGEFILE]) + AC_BEFORE([$0], [CURL_CONFIGURE_REENTRANT]) AC_MSG_CHECKING([if OS is AIX (to define _ALL_SOURCE)]) AC_EGREP_CPP([yes_this_is_aix],[ #ifdef _AIX @@ -190,7 +190,7 @@ AC_DEFUN([CURL_CHECK_NATIVE_WINDOWS], [ curl_cv_native_windows="no" ]) ]) - AM_CONDITIONAL(DOING_NATIVE_WINDOWS, test "x$curl_cv_native_windows" = xyes) + AM_CONDITIONAL(DOING_NATIVE_WINDOWS, test "$curl_cv_native_windows" = "yes") ]) @@ -200,7 +200,7 @@ dnl Check for compilable and valid lber.h header, dnl and check if it is needed even with ldap.h AC_DEFUN([CURL_CHECK_HEADER_LBER], [ - AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS])dnl + AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS]) AC_CACHE_CHECK([for lber.h], [curl_cv_header_lber_h], [ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ @@ -233,7 +233,7 @@ AC_DEFUN([CURL_CHECK_HEADER_LBER], [ if test "$curl_cv_header_lber_h" = "yes"; then AC_DEFINE_UNQUOTED(HAVE_LBER_H, 1, [Define to 1 if you have the lber.h header file.]) - # + AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #undef inline @@ -264,7 +264,7 @@ AC_DEFUN([CURL_CHECK_HEADER_LBER], [ ],[ curl_cv_need_header_lber_h="yes" ]) - # + case "$curl_cv_need_header_lber_h" in yes) AC_DEFINE_UNQUOTED(NEED_LBER_H, 1, @@ -280,7 +280,7 @@ dnl ------------------------------------------------- dnl Check for compilable and valid ldap.h header AC_DEFUN([CURL_CHECK_HEADER_LDAP], [ - AC_REQUIRE([CURL_CHECK_HEADER_LBER])dnl + AC_REQUIRE([CURL_CHECK_HEADER_LBER]) AC_CACHE_CHECK([for ldap.h], [curl_cv_header_ldap_h], [ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ @@ -327,7 +327,7 @@ dnl ------------------------------------------------- dnl Check for compilable and valid ldap_ssl.h header AC_DEFUN([CURL_CHECK_HEADER_LDAP_SSL], [ - AC_REQUIRE([CURL_CHECK_HEADER_LDAP])dnl + AC_REQUIRE([CURL_CHECK_HEADER_LDAP]) AC_CACHE_CHECK([for ldap_ssl.h], [curl_cv_header_ldap_ssl_h], [ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ @@ -380,12 +380,12 @@ dnl whitespace separated list of libraries to check dnl before the WINLDAP default ones. AC_DEFUN([CURL_CHECK_LIBS_WINLDAP], [ - AC_REQUIRE([CURL_CHECK_HEADER_WINBER])dnl - # + AC_REQUIRE([CURL_CHECK_HEADER_WINBER]) + AC_MSG_CHECKING([for WINLDAP libraries]) - # + u_libs="" - # + ifelse($1,,,[ for x_lib in $1; do case "$x_lib" in @@ -403,10 +403,10 @@ AC_DEFUN([CURL_CHECK_LIBS_WINLDAP], [ fi done ]) - # + curl_cv_save_LIBS="$LIBS" curl_cv_ldap_LIBS="unknown" - # + for x_nlibs in '' "$u_libs" \ '-lwldap32' ; do if test "$curl_cv_ldap_LIBS" = "unknown"; then @@ -441,9 +441,9 @@ AC_DEFUN([CURL_CHECK_LIBS_WINLDAP], [ ]) fi done - # + LIBS="$curl_cv_save_LIBS" - # + case X-"$curl_cv_ldap_LIBS" in X-unknown) AC_MSG_RESULT([cannot find WINLDAP libraries]) @@ -460,7 +460,6 @@ AC_DEFUN([CURL_CHECK_LIBS_WINLDAP], [ AC_MSG_RESULT([$curl_cv_ldap_LIBS]) ;; esac - # ]) @@ -473,12 +472,12 @@ dnl whitespace separated list of libraries to check dnl before the default ones. AC_DEFUN([CURL_CHECK_LIBS_LDAP], [ - AC_REQUIRE([CURL_CHECK_HEADER_LDAP])dnl - # + AC_REQUIRE([CURL_CHECK_HEADER_LDAP]) + AC_MSG_CHECKING([for LDAP libraries]) - # + u_libs="" - # + ifelse($1,,,[ for x_lib in $1; do case "$x_lib" in @@ -496,10 +495,10 @@ AC_DEFUN([CURL_CHECK_LIBS_LDAP], [ fi done ]) - # + curl_cv_save_LIBS="$LIBS" curl_cv_ldap_LIBS="unknown" - # + for x_nlibs in '' "$u_libs" \ '-lldap' \ '-lldap -llber' \ @@ -552,9 +551,9 @@ AC_DEFUN([CURL_CHECK_LIBS_LDAP], [ ]) fi done - # + LIBS="$curl_cv_save_LIBS" - # + case X-"$curl_cv_ldap_LIBS" in X-unknown) AC_MSG_RESULT([cannot find LDAP libraries]) @@ -568,14 +567,13 @@ AC_DEFUN([CURL_CHECK_LIBS_LDAP], [ else LIBS="$curl_cv_ldap_LIBS $curl_cv_save_LIBS" fi - # FIXME: Enable when ldap was detected via pkg-config + dnl FIXME: Enable when ldap was detected via pkg-config if false; then LIBCURL_PC_REQUIRES_PRIVATE="ldap $LIBCURL_PC_REQUIRES_PRIVATE" fi AC_MSG_RESULT([$curl_cv_ldap_LIBS]) ;; esac - # ]) @@ -616,10 +614,10 @@ dnl ------------------------------------------------- dnl Test if the socket recv() function is available, AC_DEFUN([CURL_CHECK_FUNC_RECV], [ - AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS])dnl - AC_REQUIRE([CURL_INCLUDES_BSDSOCKET])dnl + AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS]) + AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) AC_CHECK_HEADERS(sys/types.h) - # + AC_MSG_CHECKING([for recv]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ @@ -646,7 +644,7 @@ AC_DEFUN([CURL_CHECK_FUNC_RECV], [ AC_MSG_RESULT([no]) curl_cv_recv="no" ]) - # + if test "$curl_cv_recv" = "yes"; then AC_DEFINE_UNQUOTED(HAVE_RECV, 1, [Define to 1 if you have the recv function.]) @@ -662,10 +660,10 @@ dnl ------------------------------------------------- dnl Test if the socket send() function is available, AC_DEFUN([CURL_CHECK_FUNC_SEND], [ - AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS])dnl - AC_REQUIRE([CURL_INCLUDES_BSDSOCKET])dnl + AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS]) + AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) AC_CHECK_HEADERS(sys/types.h) - # + AC_MSG_CHECKING([for send]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ @@ -693,7 +691,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SEND], [ AC_MSG_RESULT([no]) curl_cv_send="no" ]) - # + if test "$curl_cv_send" = "yes"; then AC_DEFINE_UNQUOTED(HAVE_SEND, 1, [Define to 1 if you have the send function.]) @@ -703,52 +701,13 @@ AC_DEFUN([CURL_CHECK_FUNC_SEND], [ fi ]) -dnl CURL_CHECK_MSG_NOSIGNAL -dnl ------------------------------------------------- -dnl Check for MSG_NOSIGNAL - -AC_DEFUN([CURL_CHECK_MSG_NOSIGNAL], [ - AC_CHECK_HEADERS(sys/types.h) - AC_CACHE_CHECK([for MSG_NOSIGNAL], [curl_cv_msg_nosignal], [ - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - #undef inline - #ifdef _WIN32 - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #include - #else - #ifdef HAVE_SYS_TYPES_H - #include - #endif - #include - #endif - ]],[[ - int flag = MSG_NOSIGNAL; - (void)flag; - ]]) - ],[ - curl_cv_msg_nosignal="yes" - ],[ - curl_cv_msg_nosignal="no" - ]) - ]) - case "$curl_cv_msg_nosignal" in - yes) - AC_DEFINE_UNQUOTED(HAVE_MSG_NOSIGNAL, 1, - [Define to 1 if you have the MSG_NOSIGNAL flag.]) - ;; - esac -]) - dnl CURL_CHECK_STRUCT_TIMEVAL dnl ------------------------------------------------- dnl Check for timeval struct AC_DEFUN([CURL_CHECK_STRUCT_TIMEVAL], [ - AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS])dnl + AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS]) AC_CHECK_HEADERS(sys/types.h) AC_CACHE_CHECK([for struct timeval], [curl_cv_struct_timeval], [ AC_COMPILE_IFELSE([ @@ -769,7 +728,7 @@ AC_DEFUN([CURL_CHECK_STRUCT_TIMEVAL], [ #include ]],[[ struct timeval ts; - ts.tv_sec = 0; + ts.tv_sec = 0; ts.tv_usec = 0; (void)ts; ]]) @@ -795,7 +754,6 @@ dnl Check if monotonic clock_gettime is available. AC_DEFUN([CURL_CHECK_FUNC_CLOCK_GETTIME_MONOTONIC], [ AC_CHECK_HEADERS(sys/types.h) AC_MSG_CHECKING([for monotonic clock_gettime]) - # AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ @@ -820,7 +778,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOCK_GETTIME_MONOTONIC], [ ]) dnl Definition of HAVE_CLOCK_GETTIME_MONOTONIC is intentionally postponed - dnl until library linking and run-time checks for clock_gettime succeed. + dnl until library linking and runtime checks for clock_gettime succeed. ]) dnl CURL_CHECK_FUNC_CLOCK_GETTIME_MONOTONIC_RAW @@ -830,7 +788,7 @@ dnl Check if monotonic clock_gettime is available. AC_DEFUN([CURL_CHECK_FUNC_CLOCK_GETTIME_MONOTONIC_RAW], [ AC_CHECK_HEADERS(sys/types.h) AC_MSG_CHECKING([for raw monotonic clock_gettime]) - # + AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #ifdef HAVE_SYS_TYPES_H @@ -861,15 +819,15 @@ dnl If monotonic clock_gettime is available then, dnl check and prepended to LIBS any needed libraries. AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [ - AC_REQUIRE([CURL_CHECK_FUNC_CLOCK_GETTIME_MONOTONIC])dnl - # + AC_REQUIRE([CURL_CHECK_FUNC_CLOCK_GETTIME_MONOTONIC]) + if test "$curl_func_clock_gettime" = "yes"; then - # + AC_MSG_CHECKING([for clock_gettime in libraries]) - # + curl_cv_save_LIBS="$LIBS" curl_cv_gclk_LIBS="unknown" - # + for x_xlibs in '' '-lrt' '-lposix4' ; do if test "$curl_cv_gclk_LIBS" = "unknown"; then if test -z "$x_xlibs"; then @@ -896,9 +854,9 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [ ]) fi done - # + LIBS="$curl_cv_save_LIBS" - # + case X-"$curl_cv_gclk_LIBS" in X-unknown) AC_MSG_RESULT([cannot find clock_gettime]) @@ -910,7 +868,7 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [ curl_func_clock_gettime="yes" ;; *) - if test "x$dontwant_rt" = "xyes" ; then + if test "$dontwant_rt" = "yes"; then AC_MSG_WARN([needs -lrt but asked not to use it, HAVE_CLOCK_GETTIME_MONOTONIC will not be defined]) curl_func_clock_gettime="no" else @@ -924,9 +882,9 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [ fi ;; esac - # + dnl only do runtime verification when not cross-compiling - if test "x$cross_compiling" != "xyes" && + if test "$cross_compiling" != "yes" && test "$curl_func_clock_gettime" = "yes"; then AC_MSG_CHECKING([if monotonic clock_gettime works]) CURL_RUN_IFELSE([ @@ -955,16 +913,14 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [ LIBS="$curl_cv_save_LIBS" ]) fi - # + case "$curl_func_clock_gettime" in yes) AC_DEFINE_UNQUOTED(HAVE_CLOCK_GETTIME_MONOTONIC, 1, [Define to 1 if you have the clock_gettime function and monotonic timer.]) ;; esac - # fi - # ]) @@ -974,8 +930,8 @@ dnl Verify if network connect function is already available dnl using current libraries or if another one is required. AC_DEFUN([CURL_CHECK_LIBS_CONNECT], [ - AC_REQUIRE([CURL_INCLUDES_WINSOCK2])dnl - AC_REQUIRE([CURL_INCLUDES_BSDSOCKET])dnl + AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) + AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) AC_MSG_CHECKING([for connect in libraries]) tst_connect_save_LIBS="$LIBS" tst_connect_need_LIBS="unknown" @@ -999,7 +955,7 @@ AC_DEFUN([CURL_CHECK_LIBS_CONNECT], [ fi done LIBS="$tst_connect_save_LIBS" - # + case X-"$tst_connect_need_LIBS" in X-unknown) AC_MSG_RESULT([cannot find connect]) @@ -1021,10 +977,10 @@ dnl ------------------------------------------------- dnl Test if the socket select() function is available. AC_DEFUN([CURL_CHECK_FUNC_SELECT], [ - AC_REQUIRE([CURL_CHECK_STRUCT_TIMEVAL])dnl - AC_REQUIRE([CURL_INCLUDES_BSDSOCKET])dnl + AC_REQUIRE([CURL_CHECK_STRUCT_TIMEVAL]) + AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) AC_CHECK_HEADERS(sys/select.h) - # + AC_MSG_CHECKING([for select]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ @@ -1060,7 +1016,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SELECT], [ AC_MSG_RESULT([no]) curl_cv_select="no" ]) - # + if test "$curl_cv_select" = "yes"; then AC_DEFINE_UNQUOTED(HAVE_SELECT, 1, [Define to 1 if you have the select function.]) @@ -1082,11 +1038,11 @@ dnl macro. It must also run AFTER all lib-checking macros are complete. AC_DEFUN([CURL_VERIFY_RUNTIMELIBS], [ dnl this test is of course not sensible if we are cross-compiling! - if test "x$cross_compiling" != xyes; then + if test "$cross_compiling" != "yes"; then dnl just run a program to verify that the libs checked for previous to this - dnl point also is available run-time! - AC_MSG_CHECKING([run-time libs availability]) + dnl point also is available runtime! + AC_MSG_CHECKING([runtime libs availability]) CURL_RUN_IFELSE([ int main(void) { @@ -1095,7 +1051,7 @@ AC_DEFUN([CURL_VERIFY_RUNTIMELIBS], [ ], AC_MSG_RESULT([fine]), AC_MSG_RESULT([failed]) - AC_MSG_ERROR([one or more libs available at link-time are not available run-time. Libs used at link-time: $LIBS]) + AC_MSG_ERROR([one or more libs available at link-time are not available runtime. Libs used at link-time: $LIBS]) ) dnl if this test fails, configure has already stopped @@ -1122,7 +1078,7 @@ AC_DEFUN([CURL_CHECK_CA_BUNDLE], [ AC_ARG_WITH(ca-bundle, AS_HELP_STRING([--with-ca-bundle=FILE], [Absolute path to a file containing CA certificates (example: /etc/ca-bundle.crt)]) -AS_HELP_STRING([--without-ca-bundle], [Don't use a default CA bundle]), +AS_HELP_STRING([--without-ca-bundle], [Do not use a default CA bundle]), [ want_ca="$withval" if test "x$want_ca" = "xyes"; then @@ -1136,7 +1092,7 @@ AS_HELP_STRING([--with-ca-path=DIRECTORY], their filenames in a hash format. This option can be used with the OpenSSL, \ GnuTLS, mbedTLS and wolfSSL backends. Refer to OpenSSL c_rehash for details. \ (example: /etc/certificates)]) -AS_HELP_STRING([--without-ca-path], [Don't use a default CA path]), +AS_HELP_STRING([--without-ca-path], [Do not use a default CA path]), [ want_capath="$withval" if test "x$want_capath" = "xyes"; then @@ -1149,33 +1105,45 @@ AS_HELP_STRING([--without-ca-path], [Don't use a default CA path]), capath_warning=" (warning: certs not found)" check_capath="" - if test "x$want_ca" != "xno" -a "x$want_ca" != "xunset" -a \ - "x$want_capath" != "xno" -a "x$want_capath" != "xunset"; then + if test "$APPLE_SECTRUST_ENABLED" = "1"; then + ca_native="Apple SecTrust" + elif test "$ca_native_opt" = "1"; then + ca_native="yes" + else + ca_native="no" + fi + + if test "x$want_ca" != "xno" && test "x$want_ca" != "xunset" && + test "x$want_capath" != "xno" && test "x$want_capath" != "xunset"; then dnl both given ca="$want_ca" capath="$want_capath" - elif test "x$want_ca" != "xno" -a "x$want_ca" != "xunset"; then + elif test "x$want_ca" != "xno" && test "x$want_ca" != "xunset"; then dnl --with-ca-bundle given ca="$want_ca" capath="no" - elif test "x$want_capath" != "xno" -a "x$want_capath" != "xunset"; then + elif test "x$want_capath" != "xno" && test "x$want_capath" != "xunset"; then dnl --with-ca-path given capath="$want_capath" ca="no" + elif test "$ca_native" != "no"; then + dnl native ca configured, do not look further + ca="no" + capath="no" else dnl First try auto-detecting a CA bundle, then a CA path. dnl Both auto-detections can be skipped by --without-ca-* ca="no" capath="no" - if test "x$cross_compiling" != "xyes" -a \ - "x$curl_cv_native_windows" != "xyes"; then + if test "$cross_compiling" != "yes" && + test "$curl_cv_native_windows" != "yes"; then dnl NOT cross-compiling and... dnl neither of the --with-ca-* options are provided if test "x$want_ca" = "xunset"; then dnl the path we previously would have installed the curl CA bundle dnl to, and thus we now check for an already existing cert in that dnl place in case we find no other - if test "x$prefix" != xNONE; then + if test "x$prefix" != "xNONE"; then cac="${prefix}/share/curl/curl-ca-bundle.crt" else cac="$ac_default_prefix/share/curl/curl-ca-bundle.crt" @@ -1211,7 +1179,7 @@ AS_HELP_STRING([--without-ca-path], [Don't use a default CA path]), check_capath="$capath" fi - if test ! -z "$check_capath"; then + if test -n "$check_capath"; then for a in "$check_capath"; do if test -d "$a" && ls "$a"/[[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]].0 >/dev/null 2>/dev/null; then if test "x$capath" = "xno"; then @@ -1245,16 +1213,16 @@ AS_HELP_STRING([--without-ca-path], [Don't use a default CA path]), AC_MSG_CHECKING([whether to use OpenSSL's built-in CA store]) AC_ARG_WITH(ca-fallback, AS_HELP_STRING([--with-ca-fallback], [Use OpenSSL's built-in CA store]) -AS_HELP_STRING([--without-ca-fallback], [Don't use OpenSSL's built-in CA store]), +AS_HELP_STRING([--without-ca-fallback], [Do not use OpenSSL's built-in CA store]), [ - if test "x$with_ca_fallback" != "xyes" -a "x$with_ca_fallback" != "xno"; then + if test "x$with_ca_fallback" != "xyes" && test "x$with_ca_fallback" != "xno"; then AC_MSG_ERROR([--with-ca-fallback only allows yes or no as parameter]) fi ], [ with_ca_fallback="no"]) AC_MSG_RESULT([$with_ca_fallback]) if test "x$with_ca_fallback" = "xyes"; then - if test "x$OPENSSL_ENABLED" != "x1"; then + if test "$OPENSSL_ENABLED" != "1"; then AC_MSG_ERROR([--with-ca-fallback only works with OpenSSL]) fi AC_DEFINE_UNQUOTED(CURL_CA_FALLBACK, 1, [define "1" to use OpenSSL's built-in CA store]) @@ -1273,7 +1241,7 @@ AC_DEFUN([CURL_CHECK_CA_EMBED], [ AC_ARG_WITH(ca-embed, AS_HELP_STRING([--with-ca-embed=FILE], [Absolute path to a file containing CA certificates to embed in the curl tool (example: /etc/ca-bundle.crt)]) -AS_HELP_STRING([--without-ca-embed], [Don't embed a default CA bundle in the curl tool]), +AS_HELP_STRING([--without-ca-embed], [Do not embed a default CA bundle in the curl tool]), [ want_ca_embed="$withval" if test "x$want_ca_embed" = "xyes"; then @@ -1283,7 +1251,7 @@ AS_HELP_STRING([--without-ca-embed], [Don't embed a default CA bundle in the cur [ want_ca_embed="unset" ]) CURL_CA_EMBED='' - if test "x$want_ca_embed" != "xno" -a "x$want_ca_embed" != "xunset" -a -f "$want_ca_embed"; then + if test "x$want_ca_embed" != "xno" && test "x$want_ca_embed" != "xunset" && test -f "$want_ca_embed"; then if test -n "$PERL"; then CURL_CA_EMBED="$want_ca_embed" AC_SUBST(CURL_CA_EMBED) @@ -1297,47 +1265,15 @@ AS_HELP_STRING([--without-ca-embed], [Don't embed a default CA bundle in the cur fi ]) -dnl CURL_CHECK_WIN32_LARGEFILE -dnl ------------------------------------------------- -dnl Check if curl's Win32 large file will be used - -AC_DEFUN([CURL_CHECK_WIN32_LARGEFILE], [ - AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS])dnl - if test "$curl_cv_native_windows" = 'yes'; then - AC_MSG_CHECKING([whether build target supports Win32 large files]) - if test "$curl_cv_wince" = 'yes'; then - dnl Windows CE does not support large files - curl_win32_has_largefile='no' - else - dnl All mingw-w64 versions support large files - curl_win32_has_largefile='yes' - fi - case "$curl_win32_has_largefile" in - yes) - if test x"$enable_largefile" = 'xno'; then - AC_MSG_RESULT([yes (large file disabled)]) - else - AC_MSG_RESULT([yes (large file enabled)]) - AC_DEFINE_UNQUOTED(USE_WIN32_LARGE_FILES, 1, - [Define to 1 if you are building a Windows target with large file support.]) - fi - ;; - *) - AC_MSG_RESULT([no]) - ;; - esac - fi -]) - dnl CURL_CHECK_WIN32_CRYPTO dnl ------------------------------------------------- dnl Check if curl's Win32 crypto lib can be used AC_DEFUN([CURL_CHECK_WIN32_CRYPTO], [ - AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS])dnl + AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS]) AC_MSG_CHECKING([whether build target supports Win32 crypto API]) curl_win32_crypto_api="no" - if test "$curl_cv_native_windows" = "yes" -a "$curl_cv_winuwp" != "yes"; then + if test "$curl_cv_native_windows" = "yes" && test "$curl_cv_winuwp" != "yes"; then AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #undef inline @@ -1370,22 +1306,65 @@ AC_DEFUN([CURL_CHECK_WIN32_CRYPTO], [ esac ]) -dnl CURL_EXPORT_PCDIR ($pcdir) +dnl CURL_EXPORT_PCDIR ($pcdir, [$additive]) dnl ------------------------ -dnl if $pcdir is not empty, set PKG_CONFIG_LIBDIR to $pcdir and export +dnl if $pcdir is not empty, set PKG_CONFIG_LIBDIR to $pcdir and export. +dnl if $additive is set, extend PKG_CONFIG_PATH instead, by prepending $pcdir +dnl to it, to ensure that system locations are still checked. This is +dnl necessary for modules that depend on modules residing there +dnl (e.g. gnutls.pc). dnl -dnl we need this macro since pkg-config distinguishes among empty and unset -dnl variable while checking PKG_CONFIG_LIBDIR +dnl we need this macro to limit/expand search locations to/with a custom +dnl configured one. dnl AC_DEFUN([CURL_EXPORT_PCDIR], [ if test -n "$1"; then - PKG_CONFIG_LIBDIR="$1" - export PKG_CONFIG_LIBDIR + if test -n "$2"; then + dnl honor system locations + PKG_CONFIG_PATH="$1${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" + export PKG_CONFIG_PATH + else + dnl ignore and override system locations + PKG_CONFIG_LIBDIR="$1" + export PKG_CONFIG_LIBDIR + fi fi ]) -dnl CURL_CHECK_PKGCONFIG ($module, [$pcdir]) +dnl CURL_TRACE_PCDIR ($module, [$pcdir], [$additive]) +dnl ------------------------ +dnl show pkg-config module lookup details, along with a detailed errors +dnl message in case of failure. Supports both pkg-config and pkgconf. +dnl + +AC_DEFUN([CURL_TRACE_PCDIR], [ + dnl Example pkgconf line: + dnl libpkgconf/pkg.c:746 [pkgconf_pkg_t *pkgconf_pkg_try_specific_path(pkgconf_client_t *, [...]*)]: + dnl trying path: /usr/local/lib/pkgconfig for libngtcp2_crypto_gnutls + dnl Rest of strings are for catching classic pkg-config lines. + trc=`CURL_EXPORT_PCDIR([$2], [$3]) + if test -n "$PKG_CONFIG_LIBDIR"; then + echo "PKG_CONFIG_LIBDIR: '$PKG_CONFIG_LIBDIR'" + fi + if test -n "$PKG_CONFIG_PATH"; then + echo "PKG_CONFIG_PATH: '$PKG_CONFIG_PATH'" + fi + $PKGCONFIG --exists --debug $1 2>&1 | \ + $EGREP '(trying path:|Adding directory|Looking for|Scanning directory|Cannot open directory)' | \ + $SED 's/^.*trying path:/trying path:/'` + msg=`CURL_EXPORT_PCDIR([$2], [$3]) + $PKGCONFIG --exists --print-errors $1 2>&1` + if test -n "$msg"; then + trc=`echo "$trc"; echo '==== error:'; echo "$msg"` + fi + AC_MSG_NOTICE([pkg-config --exists $1 trace: +---- begin +${trc} +---- end]) +]) + +dnl CURL_CHECK_PKGCONFIG ($module, [$pcdir], [$additive]) dnl ------------------------ dnl search for the pkg-config tool. Set the PKGCONFIG variable to hold the dnl path to it, or 'no' if not found/present. @@ -1404,19 +1383,25 @@ AC_DEFUN([CURL_CHECK_PKGCONFIG], [ [$PATH:/usr/bin:/usr/local/bin]) fi - if test "x$PKGCONFIG" != "xno"; then + if test "$PKGCONFIG" != "no"; then AC_MSG_CHECKING([for $1 options with pkg-config]) dnl ask pkg-config about $1 - itexists=`CURL_EXPORT_PCDIR([$2]) dnl + itexists=`CURL_EXPORT_PCDIR([$2], [$3]) $PKGCONFIG --exists $1 >/dev/null 2>&1 && echo 1` if test -z "$itexists"; then dnl pkg-config does not have info about the given module! set the dnl variable to 'no' - PKGCONFIG="no" AC_MSG_RESULT([no]) + if test -n "$CURL_TRACE_PKG_CONFIG$CURL_CI"; then + CURL_TRACE_PCDIR([$1], [$2], [$3]) + fi + PKGCONFIG="no" else AC_MSG_RESULT([found]) + if test -n "$CURL_TRACE_PKG_CONFIG"; then + CURL_TRACE_PCDIR([$1], [$2], [$3]) + fi fi fi ]) @@ -1430,7 +1415,7 @@ dnl when the configure script runs. For portability reasons, test dnl harness needs information on how to run the C preprocessor. AC_DEFUN([CURL_PREPARE_CONFIGUREHELP_PM], [ - AC_REQUIRE([AC_PROG_CPP])dnl + AC_REQUIRE([AC_PROG_CPP]) tmp_cpp=`eval echo "$ac_cpp" 2>/dev/null` if test -z "$tmp_cpp"; then tmp_cpp='cpp' @@ -1445,7 +1430,7 @@ dnl Save build info for test runner to pick up and log AC_DEFUN([CURL_PREPARE_BUILDINFO], [ curl_pflags="" - if test "$curl_cv_apple" = 'yes'; then + if test "$curl_cv_apple" = "yes"; then curl_pflags="${curl_pflags} APPLE" fi case $host in @@ -1465,37 +1450,31 @@ AC_DEFUN([CURL_PREPARE_BUILDINFO], [ fi ;; esac - if test "$curl_cv_native_windows" = 'yes'; then + if test "$curl_cv_native_windows" = "yes"; then curl_pflags="${curl_pflags} WIN32" fi - if test "$curl_cv_wince" = 'yes'; then - curl_pflags="${curl_pflags} WINCE" - fi - if test "$curl_cv_winuwp" = 'yes'; then + if test "$curl_cv_winuwp" = "yes"; then curl_pflags="${curl_pflags} UWP" fi - case $host in - *-*-*bsd*|*-*-aix*|*-*-hpux*|*-*-interix*|*-*-irix*|*-*-linux*|*-*-solaris*|*-*-sunos*|*-apple-*|*-*-cygwin*|*-*-msys*) - curl_pflags="${curl_pflags} UNIX";; + case $host_os in + cygwin*|msys*) curl_pflags="${curl_pflags} CYGWIN";; esac - case $host in - *-*-*bsd*) - curl_pflags="${curl_pflags} BSD";; - esac - if test "$curl_cv_cygwin" = 'yes'; then - curl_pflags="${curl_pflags} CYGWIN" - fi case $host_os in msdos*) curl_pflags="${curl_pflags} DOS";; amiga*) curl_pflags="${curl_pflags} AMIGA";; esac - if test "x$compiler_id" = 'xGNU_C'; then + if test "$compiler_id" = "GNU_C"; then curl_pflags="${curl_pflags} GCC" fi + if test "$compiler_id" = "APPLECLANG"; then + curl_pflags="${curl_pflags} APPLE-CLANG" + elif test "$compiler_id" = "CLANG"; then + curl_pflags="${curl_pflags} LLVM-CLANG" + fi case $host_os in mingw*) curl_pflags="${curl_pflags} MINGW";; esac - if test "x$cross_compiling" = 'xyes'; then + if test "$cross_compiling" = "yes"; then curl_pflags="${curl_pflags} CROSS" fi squeeze curl_pflags @@ -1537,7 +1516,7 @@ TEST EINVAL TEST AC_MSG_RESULT([$cpp]) dnl we need cpp -P so check if it works then - if test "x$cpp" = "xyes"; then + if test "$cpp" = "yes"; then AC_MSG_CHECKING([if cpp -P works]) OLDCPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS -P" @@ -1547,18 +1526,18 @@ TEST EINVAL TEST ], [cpp_p=yes], [cpp_p=no]) AC_MSG_RESULT([$cpp_p]) - if test "x$cpp_p" = "xno"; then + if test "$cpp_p" = "no"; then AC_MSG_WARN([failed to figure out cpp -P alternative]) - # without -P + dnl without -P CPPPFLAG="" else - # with -P + dnl with -P CPPPFLAG="-P" fi dnl restore CPPFLAGS CPPFLAGS=$OLDCPPFLAGS else - # without -P + dnl without -P CPPPFLAG="" fi ]) @@ -1567,7 +1546,7 @@ TEST EINVAL TEST dnl CURL_DARWIN_CFLAGS dnl dnl Set -Werror=partial-availability to detect possible breaking code -dnl with very low deployment targets. +dnl with low deployment targets. dnl AC_DEFUN([CURL_DARWIN_CFLAGS], [ @@ -1584,7 +1563,7 @@ AC_DEFUN([CURL_DARWIN_CFLAGS], [ dnl CURL_SUPPORTS_BUILTIN_AVAILABLE dnl dnl Check to see if the compiler supports __builtin_available. This built-in -dnl compiler function first appeared in Apple LLVM 9.0.0. It's so new that, at +dnl compiler function first appeared in Apple LLVM 9.0.0. It is so new that, at dnl the time this macro was written, the function was not yet documented. Its dnl purpose is to return true if the code is running under a certain OS version dnl or later. diff --git a/appveyor.sh b/appveyor.sh index 1e4c16362f..a69fae7742 100644 --- a/appveyor.sh +++ b/appveyor.sh @@ -28,98 +28,83 @@ set -eux; [ -n "${BASH:-}${ZSH_NAME:-}" ] && set -o pipefail # build -case "${TARGET:-}" in - *Win32) openssl_suffix='-Win32';; - *) openssl_suffix='-Win64';; -esac +if [ -n "${CMAKE_GENERATOR:-}" ]; then -if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = 'Visual Studio 2022' ]; then - openssl_root_win="C:/OpenSSL-v34${openssl_suffix}" -else - openssl_root_win="C:/OpenSSL-v111${openssl_suffix}" -fi -openssl_root="$(cygpath "${openssl_root_win}")" + PRJ_CFG='Debug' + [[ "${APPVEYOR_JOB_NAME}" = *'Release'* ]] && PRJ_CFG='Release' + + # Configure OpenSSL + case "${CMAKE_GENERATE:-}" in + *Win32*) openssl_suffix='-Win32';; + *) openssl_suffix='-Win64';; + esac + + if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = 'Visual Studio 2022' ]; then + openssl_root_win="C:/OpenSSL-v35${openssl_suffix}" + openssl_root="$(cygpath "${openssl_root_win}")" + elif [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = 'Visual Studio 2019' ]; then + openssl_root_win="C:/OpenSSL-v30${openssl_suffix}" + openssl_root="$(cygpath "${openssl_root_win}")" + fi + + # Install custom cmake version + if [ -n "${CMAKE_VERSION:-}" ]; then + cmake_ver="$(printf '%02d%02d' \ + "$(echo "${CMAKE_VERSION}" | cut -f1 -d.)" \ + "$(echo "${CMAKE_VERSION}" | cut -f2 -d.)")" + if [ "${cmake_ver}" -ge '0320' ]; then + fn="cmake-${CMAKE_VERSION}-windows-x86_64" + else + fn="cmake-${CMAKE_VERSION}-win64-x64" + fi + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ + --location "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${fn}.zip" --output pkg.bin + sha256sum pkg.bin && sha256sum pkg.bin | grep -qwF -- "${CMAKE_SHA256}" && 7z x -y pkg.bin >/dev/null && rm -f pkg.bin + PATH="$PWD/${fn}/bin:$PATH" + fi -if [ "${BUILD_SYSTEM}" = 'CMake' ]; then # Set env CHKPREFILL to the value '_chkprefill' to compare feature detection # results with and without the pre-fill feature. They have to match. for _chkprefill in '' ${CHKPREFILL:-}; do options='' [ "${_chkprefill}" = '_chkprefill' ] && options+=' -D_CURL_PREFILL=OFF' - [[ "${TARGET}" = *'ARM64'* ]] && SKIP_RUN='ARM64 architecture' - [ -n "${TOOLSET:-}" ] && options+=" -T ${TOOLSET}" - [ -n "${WINTARGET:-}" ] && options+=" -DCURL_TARGET_WINDOWS_VERSION=${WINTARGET}" - [ "${OPENSSL}" = 'ON' ] && options+=" -DOPENSSL_ROOT_DIR=${openssl_root_win}" - [ -n "${CURLDEBUG:-}" ] && options+=" -DENABLE_CURLDEBUG=${CURLDEBUG}" - if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = 'Visual Studio 2013' ]; then - mkdir "_bld${_chkprefill}" - cd "_bld${_chkprefill}" - options+=' ..' - root='..' - else - options+=" -B _bld${_chkprefill}" - options+=' -DCMAKE_VS_GLOBALS=TrackFileAccess=false' - options+=" -DCMAKE_UNITY_BUILD=${UNITY}" - root='.' - fi + [[ "${CMAKE_GENERATE:-}" = *'-A ARM64'* ]] && SKIP_RUN='ARM64 architecture' + [[ "${CMAKE_GENERATE:-}" = *'-DCURL_USE_OPENSSL=ON'* ]] && options+=" -DOPENSSL_ROOT_DIR=${openssl_root_win}" # shellcheck disable=SC2086 - time cmake -G "${PRJ_GEN}" ${TARGET} \ - -DCURL_WERROR=ON \ - -DBUILD_SHARED_LIBS="${SHARED}" \ + time cmake -B "_bld${_chkprefill}" \ + -DENABLE_DEBUG=ON \ + -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON \ + -DCMAKE_VS_GLOBALS=TrackFileAccess=false \ -DCURL_STATIC_CRT=ON \ - -DENABLE_DEBUG="${DEBUG}" \ - -DENABLE_UNICODE="${ENABLE_UNICODE}" \ - -DHTTP_ONLY="${HTTP_ONLY}" \ - -DCURL_USE_SCHANNEL="${SCHANNEL}" \ - -DCURL_USE_OPENSSL="${OPENSSL}" \ - -DCURL_USE_LIBPSL=OFF \ + -DCURL_DROP_UNUSED=ON \ + -DCURL_USE_SCHANNEL=ON -DCURL_USE_LIBPSL=OFF \ + ${CMAKE_GENERATE:-} \ ${options} \ - || { cat ${root}/_bld/CMakeFiles/CMake* 2>/dev/null; false; } - [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = 'Visual Studio 2013' ] && cd .. + || { cat _bld/CMakeFiles/CMake* 2>/dev/null; false; } done if [ -d _bld_chkprefill ] && ! diff -u _bld/lib/curl_config.h _bld_chkprefill/lib/curl_config.h; then cat _bld_chkprefill/CMakeFiles/CMake* 2>/dev/null || true false fi echo 'curl_config.h'; grep -F '#define' _bld/lib/curl_config.h | sort || true - # shellcheck disable=SC2086 - time cmake --build _bld --config "${PRJ_CFG}" --parallel 2 -- ${BUILD_OPT:-} - [ "${SHARED}" = 'ON' ] && PATH="$PWD/_bld/lib/${PRJ_CFG}:$PATH" - [ "${OPENSSL}" = 'ON' ] && { PATH="${openssl_root}:$PATH"; cp "${openssl_root}"/*.dll "_bld/src/${PRJ_CFG}"; } + time cmake --build _bld --config "${PRJ_CFG}" --parallel 2 + [[ "${CMAKE_GENERATE:-}" != *'-DBUILD_SHARED_LIBS=OFF'* ]] && PATH="$PWD/_bld/lib/${PRJ_CFG}:$PATH" + [[ "${CMAKE_GENERATE:-}" = *'-DCURL_USE_OPENSSL=ON'* ]] && { PATH="${openssl_root}:$PATH"; cp "${openssl_root}"/*.dll "_bld/src/${PRJ_CFG}"; } curl="_bld/src/${PRJ_CFG}/curl.exe" -elif [ "${BUILD_SYSTEM}" = 'VisualStudioSolution' ]; then +else ( - cd projects + cd projects/Windows ./generate.bat "${VC_VERSION}" - msbuild.exe -maxcpucount "-property:Configuration=${PRJ_CFG}" "Windows/${VC_VERSION}/curl-all.sln" + msbuild.exe -maxcpucount "-property:Configuration=${PRJ_CFG}" "-property:Platform=${PLAT}" "${VC_VERSION}/curl-all.sln" ) - curl="build/Win32/${VC_VERSION}/${PRJ_CFG}/curld.exe" -elif [ "${BUILD_SYSTEM}" = 'winbuild_vs2015' ]; then - ( - cd winbuild - cat << EOF > _make.bat - call "C:/Program Files/Microsoft SDKs/Windows/v7.1/Bin/SetEnv.cmd" /x64 - call "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/vcvarsall.bat" x86_amd64 - nmake -f Makefile.vc mode=dll VC=14 "SSL_PATH=${openssl_root_win}" WITH_SSL=dll MACHINE=x64 DEBUG=${DEBUG} ENABLE_UNICODE=${ENABLE_UNICODE} WINBUILD_ACKNOWLEDGE_DEPRECATED=yes -EOF - ./_make.bat - rm _make.bat - ) - curl="builds/libcurl-vc14-x64-${PATHPART}-dll-ssl-dll-ipv6-sspi/bin/curl.exe" -elif [ "${BUILD_SYSTEM}" = 'winbuild_vs2017' ]; then - ( - cd winbuild - cat << EOF > _make.bat - call "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Auxiliary/Build/vcvars64.bat" - nmake -f Makefile.vc mode=dll VC=14.10 "SSL_PATH=${openssl_root_win}" WITH_SSL=dll MACHINE=x64 DEBUG=${DEBUG} ENABLE_UNICODE=${ENABLE_UNICODE} WINBUILD_ACKNOWLEDGE_DEPRECATED=yes -EOF - ./_make.bat - rm _make.bat - ) - curl="builds/libcurl-vc14.10-x64-${PATHPART}-dll-ssl-dll-ipv6-sspi/bin/curl.exe" + [ "${PLAT}" = 'x64' ] && platdir='Win64' || platdir='Win32' + [[ "${PRJ_CFG}" = *'Debug'* ]] && binsuffix='d' || binsuffix='' + curl="build/${platdir}/${VC_VERSION}/${PRJ_CFG}/curl${binsuffix}.exe" fi -find . \( -name '*.exe' -o -name '*.dll' -o -name '*.lib' -o -name '*.pdb' \) -exec file '{}' \; +find . \( -name '*.exe' -o -name '*.dll' -o -name '*.lib' -o -name '*.pdb' \) -print0 | grep -z curl | xargs -0 file -- +find . \( -name '*.exe' -o -name '*.dll' -o -name '*.lib' -o -name '*.pdb' \) -print0 | grep -z curl | xargs -0 stat -c '%10s bytes: %n' -- + if [ -z "${SKIP_RUN:-}" ]; then "${curl}" --disable --version else @@ -128,42 +113,19 @@ fi # build tests -if [ "${TFLAGS}" != 'skipall' ] && \ - [ "${BUILD_SYSTEM}" = 'CMake' ]; then +if [ -n "${CMAKE_GENERATOR:-}" ] && [[ "${APPVEYOR_JOB_NAME}" = *'Build-tests'* ]]; then time cmake --build _bld --config "${PRJ_CFG}" --parallel 2 --target testdeps fi -# run tests - -if [ "${TFLAGS}" != 'skipall' ] && \ - [ "${TFLAGS}" != 'skiprun' ]; then - if [ -x "$(cygpath "${SYSTEMROOT}/System32/curl.exe")" ]; then - TFLAGS+=" -ac $(cygpath "${SYSTEMROOT}/System32/curl.exe")" - elif [ -x "$(cygpath 'C:/msys64/usr/bin/curl.exe')" ]; then - TFLAGS+=" -ac $(cygpath 'C:/msys64/usr/bin/curl.exe')" - fi - TFLAGS+=' -j0' - if [ "${BUILD_SYSTEM}" = 'CMake' ]; then - time cmake --build _bld --config "${PRJ_CFG}" --target test-ci - else - ( - TFLAGS="-a -p !flaky -r ${TFLAGS}" - cd _bld/tests - time ./runtests.pl - ) - fi -fi - # build examples -if [ "${EXAMPLES}" = 'ON' ] && \ - [ "${BUILD_SYSTEM}" = 'CMake' ]; then +if [ -n "${CMAKE_GENERATOR:-}" ] && [[ "${APPVEYOR_JOB_NAME}" = *'examples'* ]]; then time cmake --build _bld --config "${PRJ_CFG}" --parallel 2 --target curl-examples-build fi # disk space used du -sh .; echo; du -sh -t 250KB ./* -if [ "${BUILD_SYSTEM}" = 'CMake' ]; then +if [ -n "${CMAKE_GENERATOR:-}" ]; then echo; du -h -t 250KB _bld fi diff --git a/appveyor.yml b/appveyor.yml index effd8589d1..c33717c4e9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -31,179 +31,92 @@ version: 7.50.0.{build} environment: - BUILD_SYSTEM: CMake - UNITY: 'ON' - OPENSSL: 'OFF' - SCHANNEL: 'OFF' - ENABLE_UNICODE: 'OFF' - DEBUG: 'ON' - SHARED: 'OFF' - HTTP_ONLY: 'OFF' - TFLAGS: 'skiprun' - EXAMPLES: 'OFF' - matrix: + # CMake Visual Studio builds - # generated CMake-based Visual Studio builds + - job_name: 'CM VS2022, Release, x64, OpenSSL 3.5, Shared, Build-tests' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' + CMAKE_GENERATOR: 'Visual Studio 17 2022' + CMAKE_GENERATE: '-A x64 -DCURL_USE_SCHANNEL=OFF -DCURL_USE_OPENSSL=ON' - - job_name: 'CMake, VS2022, Release, x64, OpenSSL 3.4, Shared, Build-tests' + - job_name: 'CM VS2022, Release, arm64, Schannel, Static, !DEBUGBUILD, Build-tests' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' - PRJ_GEN: 'Visual Studio 17 2022' - TARGET: '-A x64' - PRJ_CFG: Release - OPENSSL: 'ON' - SHARED: 'ON' - - job_name: 'CMake, VS2022, Release, arm64, Schannel, Static, Build-tests' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' - PRJ_GEN: 'Visual Studio 17 2022' - TARGET: '-A ARM64' - PRJ_CFG: Release - SCHANNEL: 'ON' - DEBUG: 'OFF' - CURLDEBUG: 'ON' - - job_name: 'CMake, VS2010, Debug, x64, Schannel, Shared, Build-tests & examples, XP' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2013' - PRJ_GEN: 'Visual Studio 10 2010' - TARGET: '-A x64' - WINTARGET: 0x0501 # Windows XP - PRJ_CFG: Debug - SCHANNEL: 'ON' - SHARED: 'ON' - EXAMPLES: 'ON' - - job_name: 'CMake, VS2012, Release, x86, OpenSSL 1.1.1 + Schannel, Shared, Build-tests' + CMAKE_GENERATOR: 'Visual Studio 17 2022' + CMAKE_GENERATE: '-A ARM64 -DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=OFF' + + - job_name: 'CM VS2010, Debug, x64, Schannel, Shared, Build-tests & examples' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' - PRJ_GEN: 'Visual Studio 11 2012' - TARGET: '-A Win32' - PRJ_CFG: Release - OPENSSL: 'ON' - SCHANNEL: 'ON' - SHARED: 'ON' - - job_name: 'CMake, VS2013, Debug, x64, OpenSSL 1.1.1, Shared, Build-only' + CMAKE_VERSION: 3.18.4 + CMAKE_SHA256: a932bc0c8ee79f1003204466c525b38a840424d4ae29f9e5fb88959116f2407d + CMAKE_GENERATOR: 'Visual Studio 10 2010' + CMAKE_GENERATE: '-A x64' + + - job_name: 'CM VS2012, Release, x86, Schannel, Shared, Build-tests' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' - PRJ_GEN: 'Visual Studio 12 2013' - TARGET: '-A x64' - PRJ_CFG: Debug - OPENSSL: 'ON' - SHARED: 'ON' - TFLAGS: 'skipall' - - job_name: 'CMake, VS2015, Debug, x64, OpenSSL 1.1.1, Static, Build-only' + CMAKE_VERSION: 3.21.7 + CMAKE_SHA256: 4c4840e2dc2bf82e8a16081ff506bba54f3a228b91ce36317129fed4035ef2e3 + CMAKE_GENERATOR: 'Visual Studio 11 2012' + CMAKE_GENERATE: '-A Win32' + + - job_name: 'CM VS2013, Debug, x64, Schannel, Shared' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' - PRJ_GEN: 'Visual Studio 14 2015' - TARGET: '-A x64' - PRJ_CFG: Debug - OPENSSL: 'ON' - TFLAGS: 'skipall' - - job_name: 'CMake, VS2017, Debug, x64, OpenSSL 1.1.1, Shared, Build-only' + CMAKE_VERSION: 3.18.4 + CMAKE_SHA256: a932bc0c8ee79f1003204466c525b38a840424d4ae29f9e5fb88959116f2407d + CMAKE_GENERATOR: 'Visual Studio 12 2013' + CMAKE_GENERATE: '-A x64' + + - job_name: 'CM VS2015, Debug, x64, Schannel, Static' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' + CMAKE_VERSION: 3.19.8 + CMAKE_SHA256: 2a30877a3d6b50da305b289f4d1c03befdfaeb2edba02a563c681e883d810380 + CMAKE_GENERATOR: 'Visual Studio 14 2015' + CMAKE_GENERATE: '-A x64 -DBUILD_SHARED_LIBS=OFF' + + - job_name: 'CM VS2017, Debug, x64, Schannel, Shared' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2017' - PRJ_GEN: 'Visual Studio 15 2017' - TARGET: '-A x64' - PRJ_CFG: Debug - OPENSSL: 'ON' - SHARED: 'ON' - TFLAGS: 'skipall' - - job_name: 'CMake, VS2019, Debug, x64, OpenSSL 1.1.1 + Schannel, Shared, Build-tests' + CMAKE_VERSION: 3.20.6 + CMAKE_SHA256: f240a38c964712aac474644b3ba21bdc2b4e8d5e31179f67bd2e6f45fa349419 + CMAKE_GENERATOR: 'Visual Studio 15 2017' + CMAKE_GENERATE: '-A x64' + + - job_name: 'CM VS2019, Debug, x64, OpenSSL 3.0 + Schannel, Shared, !verbose, Build-tests' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2019' - PRJ_GEN: 'Visual Studio 16 2019' - TARGET: '-A x64' - PRJ_CFG: Debug - OPENSSL: 'ON' - SCHANNEL: 'ON' - SHARED: 'ON' - - job_name: 'CMake, VS2022, Debug, x64, OpenSSL 3.4 + Schannel, Static, Unicode, Build-tests & examples, clang-cl' + CMAKE_GENERATOR: 'Visual Studio 16 2019' + CMAKE_GENERATE: '-A x64 -DCURL_USE_OPENSSL=ON -DCURL_DISABLE_VERBOSE_STRINGS=ON' + + - job_name: 'CM VS2022, Debug, x64, OpenSSL 3.5 + Schannel, Static, Unicode, Build-tests & examples, clang-cl' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' - PRJ_GEN: 'Visual Studio 17 2022' - TARGET: '-A x64' - PRJ_CFG: Debug - OPENSSL: 'ON' - SCHANNEL: 'ON' + CMAKE_GENERATOR: 'Visual Studio 17 2022' + CMAKE_GENERATE: '-A x64 -T ClangCl -DBUILD_SHARED_LIBS=OFF -DCURL_USE_OPENSSL=ON -DENABLE_UNICODE=ON' + + - job_name: 'CM VS2022, Release, x64, Schannel, Shared, Unicode, !DEBUGBUILD, Build-tests' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' + CMAKE_GENERATOR: 'Visual Studio 17 2022' ENABLE_UNICODE: 'ON' - EXAMPLES: 'ON' - TOOLSET: 'ClangCl' - - job_name: 'CMake, VS2022, Debug, x64, Schannel, Static, Unicode, Build-tests' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' - PRJ_GEN: 'Visual Studio 17 2022' - TARGET: '-A x64' - PRJ_CFG: Debug - SCHANNEL: 'ON' - ENABLE_UNICODE: 'ON' - - job_name: 'CMake, VS2022, Release, x64, Schannel, Shared, Unicode, DEBUGBUILD, no-CURLDEBUG, Build-tests' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' - PRJ_GEN: 'Visual Studio 17 2022' - TARGET: '-A x64' - PRJ_CFG: Release - SCHANNEL: 'ON' - ENABLE_UNICODE: 'ON' - SHARED: 'ON' - CURLDEBUG: 'OFF' - - job_name: 'CMake, VS2022, Debug, x64, no SSL, Static, Build-tests' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' - PRJ_GEN: 'Visual Studio 17 2022' - TARGET: '-A x64' - PRJ_CFG: Debug - - job_name: 'CMake, VS2022, Debug, x64, no SSL, Static, HTTP only, Build-tests' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' - PRJ_GEN: 'Visual Studio 17 2022' - TARGET: '-A x64' - PRJ_CFG: Debug - HTTP_ONLY: 'ON' + CMAKE_GENERATE: '-A x64 -DENABLE_UNICODE=ON -DENABLE_DEBUG=OFF' - # winbuild-based builds + - job_name: 'CM VS2022, Debug, x64, !ssl, Static, Build-tests' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' + CMAKE_GENERATOR: 'Visual Studio 17 2022' + CMAKE_GENERATE: '-A x64 -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=OFF' - - job_name: 'winbuild, VS2015, Debug, x64, OpenSSL 1.1.1, Build-only' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' - BUILD_SYSTEM: winbuild_vs2015 - DEBUG: 'yes' - PATHPART: debug - ENABLE_UNICODE: 'no' - - job_name: 'winbuild, VS2015, Release, x64, OpenSSL 1.1.1, Build-only' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' - BUILD_SYSTEM: winbuild_vs2015 - DEBUG: 'no' - PATHPART: release - ENABLE_UNICODE: 'no' - - job_name: 'winbuild, VS2017, Debug, x64, OpenSSL 1.1.1, Build-only' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2017' - BUILD_SYSTEM: winbuild_vs2017 - DEBUG: 'yes' - PATHPART: debug - ENABLE_UNICODE: 'no' - - job_name: 'winbuild, VS2017, Release, x64, OpenSSL 1.1.1, Build-only' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2017' - BUILD_SYSTEM: winbuild_vs2017 - DEBUG: 'no' - PATHPART: release - ENABLE_UNICODE: 'no' - - job_name: 'winbuild, VS2015, Debug, x64, OpenSSL 1.1.1, Unicode, Build-only' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' - BUILD_SYSTEM: winbuild_vs2015 - DEBUG: 'yes' - PATHPART: debug - ENABLE_UNICODE: 'yes' - - job_name: 'winbuild, VS2015, Release, x64, OpenSSL 1.1.1, Unicode, Build-only' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' - BUILD_SYSTEM: winbuild_vs2015 - DEBUG: 'no' - PATHPART: release - ENABLE_UNICODE: 'yes' - - job_name: 'winbuild, VS2017, Debug, x64, OpenSSL 1.1.1, Unicode, Build-only' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2017' - BUILD_SYSTEM: winbuild_vs2017 - DEBUG: 'yes' - PATHPART: debug - ENABLE_UNICODE: 'yes' - - job_name: 'winbuild, VS2017, Release, x64, OpenSSL 1.1.1, Unicode, Build-only' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2017' - BUILD_SYSTEM: winbuild_vs2017 - DEBUG: 'no' - PATHPART: release - ENABLE_UNICODE: 'yes' + - job_name: 'CM VS2022, Debug, x64, !ssl, Static, HTTP-only, Build-tests' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' + CMAKE_GENERATOR: 'Visual Studio 17 2022' + CMAKE_GENERATE: '-A x64 -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=OFF -DHTTP_ONLY=ON' - # generated VisualStudioSolution-based builds + # VisualStudioSolution builds - - job_name: 'VisualStudioSolution, VS2013, Debug, x86, Schannel, Build-only' + - job_name: 'VisualStudioSolution VS2010, Release, x86, Schannel' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2013' + PRJ_CFG: 'DLL Release - DLL Windows SSPI - DLL WinIDN' + PLAT: 'Win32' + VC_VERSION: VC10 + + - job_name: 'VisualStudioSolution VS2013, Debug, x64, Schannel' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' - BUILD_SYSTEM: VisualStudioSolution PRJ_CFG: 'DLL Debug - DLL Windows SSPI - DLL WinIDN' + PLAT: 'x64' VC_VERSION: VC12 install: @@ -225,11 +138,13 @@ skip_commits: - '.circleci/*' - '.github/**/*' - 'Dockerfile' - - 'packages/**/*' - - 'plan9/**/*' + - 'projects/OS400/*' + - 'projects/vms/*' #artifacts: # - path: '**/curl.exe' # name: curl # - path: '**/*.dll' # name: libcurl dll +# - path: '**/*.lib' +# name: libcurl lib diff --git a/buildconf b/buildconf deleted file mode 100755 index ee6a2800be..0000000000 --- a/buildconf +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -# -# Copyright (C) Daniel Stenberg, , et al. -# -# SPDX-License-Identifier: curl - -echo "*** Do not use buildconf. Instead, just use: autoreconf -fi" >&2 -exec ${AUTORECONF:-autoreconf} -fi "${@}" diff --git a/configure.ac b/configure.ac index af005beffd..d06024bc10 100644 --- a/configure.ac +++ b/configure.ac @@ -25,11 +25,10 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.59) -dnl We don't know the version number "statically" so we use a dash here +dnl We do not know the version number "statically" so we use a dash here AC_INIT([curl], [-], [a suitable curl mailing list: https://curl.se/mail/]) XC_OVR_ZZ50 -XC_OVR_ZZ60 CURL_OVERRIDE_AUTOCONF dnl configure script copyright @@ -45,12 +44,10 @@ AM_MAINTAINER_MODE m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) CURL_CHECK_OPTION_DEBUG -AM_CONDITIONAL(DEBUGBUILD, test x$want_debug = xyes) +AM_CONDITIONAL(DEBUGBUILD, test "$want_debug" = "yes") CURL_CHECK_OPTION_OPTIMIZE CURL_CHECK_OPTION_WARNINGS CURL_CHECK_OPTION_WERROR -CURL_CHECK_OPTION_CURLDEBUG -AM_CONDITIONAL(CURLDEBUG, test x$want_curldebug = xyes) CURL_CHECK_OPTION_SYMBOL_HIDING CURL_CHECK_OPTION_ARES CURL_CHECK_OPTION_RT @@ -60,9 +57,9 @@ CURL_CHECK_OPTION_SSLS_EXPORT XC_CHECK_PATH_SEPARATOR -# -# save the configure arguments -# +dnl +dnl save the configure arguments +dnl CONFIGURE_OPTIONS="\"$ac_configure_args\"" AC_SUBST(CONFIGURE_OPTIONS) @@ -123,14 +120,16 @@ AC_SUBST([AR]) AC_SUBST(libext) +if test -z "$CLANG_TIDY"; then + CLANG_TIDY=clang-tidy +fi +AC_SUBST(CLANG_TIDY) + dnl figure out the libcurl version CURLVERSION=`$SED -ne 's/^#define LIBCURL_VERSION "\(.*\)".*/\1/p' ${srcdir}/include/curl/curlver.h` XC_CHECK_PROG_CC CURL_ATOMIC -dnl for --enable-code-coverage -CURL_COVERAGE - XC_AUTOMAKE AC_MSG_CHECKING([curl version]) AC_MSG_RESULT($CURLVERSION) @@ -144,36 +143,36 @@ AC_SUBST(VERSIONNUM) dnl dnl initialize all the info variables - curl_ssl_msg="no (--with-{openssl,gnutls,mbedtls,wolfssl,schannel,amissl,rustls} )" - curl_ssh_msg="no (--with-{libssh,libssh2})" - curl_zlib_msg="no (--with-zlib)" - curl_brotli_msg="no (--with-brotli)" - curl_zstd_msg="no (--with-zstd)" - curl_gss_msg="no (--with-gssapi)" - curl_gsasl_msg="no (--with-gsasl)" -curl_tls_srp_msg="no (--enable-tls-srp)" - curl_res_msg="blocking (--enable-ares / --enable-threaded-resolver)" - curl_ipv6_msg="no (--enable-ipv6)" -curl_unix_sockets_msg="no (--enable-unix-sockets)" - curl_idn_msg="no (--with-{libidn2,winidn})" - curl_docs_msg="enabled (--disable-docs)" - curl_manual_msg="no (--enable-manual)" -curl_libcurl_msg="enabled (--disable-libcurl-option)" -curl_verbose_msg="enabled (--disable-verbose)" - curl_sspi_msg="no (--enable-sspi)" - curl_ldap_msg="no (--enable-ldap / --with-ldap-lib / --with-lber-lib)" - curl_ldaps_msg="no (--enable-ldaps)" - curl_ipfs_msg="no (--enable-ipfs)" - curl_rtsp_msg="no (--enable-rtsp)" - curl_rtmp_msg="no (--with-librtmp)" - curl_psl_msg="no (--with-libpsl)" - curl_altsvc_msg="enabled (--disable-alt-svc)" -curl_headers_msg="enabled (--disable-headers-api)" - curl_hsts_msg="enabled (--disable-hsts)" - ssl_backends= - curl_h1_msg="enabled (internal)" - curl_h2_msg="no (--with-nghttp2)" - curl_h3_msg="no (--with-ngtcp2 --with-nghttp3, --with-quiche, --with-openssl-quic)" + curl_ssl_msg="no (--with-{openssl,gnutls,mbedtls,wolfssl,schannel,amissl,rustls} )" + curl_ssh_msg="no (--with-{libssh,libssh2})" + curl_zlib_msg="no (--with-zlib)" + curl_brotli_msg="no (--with-brotli)" + curl_zstd_msg="no (--with-zstd)" + curl_gss_msg="no (--with-gssapi)" + curl_gsasl_msg="no (--with-gsasl)" + curl_tls_srp_msg="no (--enable-tls-srp)" + curl_res_msg="blocking (--enable-ares / --enable-threaded-resolver)" + curl_ipv6_msg="no (--enable-ipv6)" +curl_unix_sockets_msg="no (--enable-unix-sockets)" + curl_idn_msg="no (--with-{libidn2,winidn})" + curl_docs_msg="enabled (--disable-docs)" + curl_manual_msg="no (--enable-manual)" + curl_libcurl_msg="enabled (--disable-libcurl-option)" + curl_typecheck_msg="enabled (--disable-typecheck)" + curl_verbose_msg="enabled (--disable-verbose)" + curl_sspi_msg="no (--enable-sspi)" + curl_ldap_msg="no (--enable-ldap / --with-ldap-lib / --with-lber-lib)" + curl_ldaps_msg="no (--enable-ldaps)" + curl_ipfs_msg="no (--enable-ipfs)" + curl_rtsp_msg="no (--enable-rtsp)" + curl_psl_msg="no (--with-libpsl)" + curl_altsvc_msg="enabled (--disable-alt-svc)" + curl_headers_msg="enabled (--disable-headers-api)" + curl_hsts_msg="enabled (--disable-hsts)" + ssl_backends= + curl_h1_msg="enabled (internal)" + curl_h2_msg="no (--with-nghttp2)" + curl_h3_msg="no (--with-ngtcp2 --with-nghttp3, --with-quiche)" enable_altsvc="yes" hsts="yes" @@ -208,13 +207,13 @@ dnl See which TLS backend(s) that are requested. Just do all the dnl TLS AC_ARG_WITH() invokes here and do the checks later dnl ********************************************************************** OPT_SCHANNEL=no -AC_ARG_WITH(schannel,dnl +AC_ARG_WITH(schannel, AS_HELP_STRING([--with-schannel],[enable Windows native SSL/TLS]), OPT_SCHANNEL=$withval - TLSCHOICE="schannel") + TLSCHOICE="Schannel") OPT_AMISSL=no -AC_ARG_WITH(amissl,dnl +AC_ARG_WITH(amissl, AS_HELP_STRING([--with-amissl],[enable Amiga native SSL/TLS (AmiSSL)]),[ OPT_AMISSL=$withval TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }AmiSSL" @@ -223,72 +222,81 @@ AS_HELP_STRING([--with-amissl],[enable Amiga native SSL/TLS (AmiSSL)]),[ OPT_OPENSSL=no dnl Default to no CA bundle ca="no" -AC_ARG_WITH(ssl,dnl +AC_ARG_WITH(ssl, AS_HELP_STRING([--with-ssl=PATH],[old version of --with-openssl]) AS_HELP_STRING([--without-ssl], [build without any TLS library]),[ OPT_SSL=$withval OPT_OPENSSL=$withval - if test X"$withval" != Xno; then + if test "x$withval" != "xno"; then TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }OpenSSL" else SSL_DISABLED="D" fi ]) -AC_ARG_WITH(openssl,dnl -AS_HELP_STRING([--with-openssl=PATH],[Where to look for OpenSSL, PATH points to the SSL installation (default: /usr/local/ssl); when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]),[ +AC_ARG_WITH(openssl, +AS_HELP_STRING([--with-openssl=PATH],[Where to look for OpenSSL, PATH points + to the SSL installation (default: /usr/local/ssl); when possible, set + the PKG_CONFIG_PATH environment variable instead of using this option]), +[ OPT_OPENSSL=$withval - if test X"$withval" != Xno; then + if test "x$withval" != "xno"; then TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }OpenSSL" fi ]) OPT_GNUTLS=no -AC_ARG_WITH(gnutls,dnl +AC_ARG_WITH(gnutls, AS_HELP_STRING([--with-gnutls=PATH],[where to look for GnuTLS, PATH points to the installation root]),[ OPT_GNUTLS=$withval - if test X"$withval" != Xno; then + if test "x$withval" != "xno"; then TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }GnuTLS" fi ]) OPT_MBEDTLS=no -AC_ARG_WITH(mbedtls,dnl +AC_ARG_WITH(mbedtls, AS_HELP_STRING([--with-mbedtls=PATH],[where to look for mbedTLS, PATH points to the installation root]),[ OPT_MBEDTLS=$withval - if test X"$withval" != Xno; then + if test "x$withval" != "xno"; then TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }mbedTLS" fi ]) OPT_WOLFSSL=no -AC_ARG_WITH(wolfssl,dnl +AC_ARG_WITH(wolfssl, AS_HELP_STRING([--with-wolfssl=PATH],[where to look for wolfSSL, PATH points to the installation root (default: system lib default)]),[ OPT_WOLFSSL=$withval - if test X"$withval" != Xno; then + if test "x$withval" != "xno"; then TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }wolfSSL" fi ]) OPT_RUSTLS=no -AC_ARG_WITH(rustls,dnl +AC_ARG_WITH(rustls, AS_HELP_STRING([--with-rustls=PATH],[where to look for Rustls, PATH points to the installation root]),[ OPT_RUSTLS=$withval - if test X"$withval" != Xno; then - TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }rustls" - experimental="$experimental rustls" + if test "x$withval" != "xno"; then + TLSCHOICE="${TLSCHOICE:+$TLSCHOICE, }Rustls" + experimental="$experimental Rustls" fi ]) +OPT_APPLE_SECTRUST=$curl_cv_apple +AC_ARG_WITH(apple-sectrust, +AS_HELP_STRING([--with-apple-sectrust],[enable Apple OS native certificate verification]),[ + OPT_APPLE_SECTRUST=$withval +]) + AC_PATH_PROG(PERL, perl,, $PATH:/usr/local/bin/perl:/usr/bin/:/usr/local/bin) AC_SUBST(PERL) AM_CONDITIONAL(PERL, test -n "$PERL") TEST_NGHTTPX=nghttpx -AC_ARG_WITH(test-nghttpx,dnl +AC_ARG_WITH(test-nghttpx, AS_HELP_STRING([--with-test-nghttpx=PATH],[where to find nghttpx for testing]), TEST_NGHTTPX=$withval - if test X"$TEST_NGHTTPX" = "Xno"; then + if test "x$TEST_NGHTTPX" = "xno"; then TEST_NGHTTPX="" fi ) @@ -301,10 +309,10 @@ elif test -x /usr/local/bin/caddy; then elif test -x "`brew --prefix 2>/dev/null`/bin/caddy"; then CADDY=`brew --prefix`/bin/caddy fi -AC_ARG_WITH(test-caddy,dnl +AC_ARG_WITH(test-caddy, AS_HELP_STRING([--with-test-caddy=PATH],[where to find caddy for testing]), CADDY=$withval - if test X"$CADDY" = "Xno"; then + if test "x$CADDY" = "xno"; then CADDY="" fi ) @@ -317,46 +325,46 @@ elif test -x /usr/local/sbin/vsftpd; then elif test -x "`brew --prefix 2>/dev/null`/sbin/vsftpd"; then VSFTPD=`brew --prefix`/sbin/vsftpd fi -AC_ARG_WITH(test-vsftpd,dnl +AC_ARG_WITH(test-vsftpd, AS_HELP_STRING([--with-test-vsftpd=PATH],[where to find vsftpd for testing]), VSFTPD=$withval - if test X"$VSFTPD" = "Xno"; then + if test "x$VSFTPD" = "xno"; then VSFTPD="" fi ) AC_SUBST(VSFTPD) -dnl we'd like a httpd as test server +dnl we would like an httpd as test server dnl HTTPD_ENABLED="maybe" AC_ARG_WITH(test-httpd, [AS_HELP_STRING([--with-test-httpd=PATH], - [where to find httpd/apache2 for testing])], + [where to find httpd/apache2 for testing])], [request_httpd=$withval], [request_httpd=check]) -if test x"$request_httpd" = "xcheck" -o x"$request_httpd" = "xyes"; then +if test "x$request_httpd" = "xcheck" || test "x$request_httpd" = "xyes"; then if test -x "/usr/sbin/apache2"; then - # common location on distros (debian/ubuntu) + dnl common location on distros (debian/ubuntu) HTTPD="/usr/sbin/apache2" AC_PATH_PROG([APXS], [apxs]) - if test "x$APXS" = "x"; then + if test -z "$APXS"; then AC_MSG_NOTICE([apache2-dev not installed, httpd tests disabled]) HTTPD_ENABLED="no" fi else AC_PATH_PROG([HTTPD], [httpd]) - if test "x$HTTPD" = "x"; then + if test -z "$HTTPD"; then AC_PATH_PROG([HTTPD], [apache2]) fi AC_PATH_PROG([APXS], [apxs]) - if test "x$HTTPD" = "x"; then + if test -z "$HTTPD"; then AC_MSG_NOTICE([httpd/apache2 not in PATH, http tests disabled]) HTTPD_ENABLED="no" fi - if test "x$APXS" = "x"; then + if test -z "$APXS"; then AC_MSG_NOTICE([apxs not in PATH, http tests disabled]) HTTPD_ENABLED="no" fi fi -elif test x"$request_httpd" != "xno"; then +elif test "x$request_httpd" != "xno"; then HTTPD="${request_httpd}/bin/httpd" APXS="${request_httpd}/bin/apxs" if test ! -x "${HTTPD}"; then @@ -369,30 +377,30 @@ elif test x"$request_httpd" != "xno"; then AC_MSG_NOTICE([using HTTPD=$HTTPD for tests]) fi fi -if test x"$HTTPD_ENABLED" = "xno"; then +if test "$HTTPD_ENABLED" = "no"; then HTTPD="" APXS="" fi AC_SUBST(HTTPD) AC_SUBST(APXS) -dnl we'd like a dante as test socks server +dnl we would like a dante as test socks server dnl DANTED_ENABLED="maybe" AC_ARG_WITH(test-danted, [AS_HELP_STRING([--with-test-danted=PATH], [where to find danted socks daemon for testing])], [request_danted=$withval], [request_danted=check]) -if test x"$request_danted" = "xcheck" -o x"$request_danted" = "xyes"; then +if test "x$request_danted" = "xcheck" || test "x$request_danted" = "xyes"; then if test -x "/usr/sbin/danted"; then - # common location on distros (debian/ubuntu) + dnl common location on distros (debian/ubuntu) DANTED="/usr/sbin/danted" else AC_PATH_PROG([DANTED], [danted]) - if test "x$DANTED" = "x"; then + if test -z "$DANTED"; then AC_PATH_PROG([DANTED], [danted]) fi fi -elif test x"$request_danted" != "xno"; then +elif test "x$request_danted" != "xno"; then DANTED="${request_danted}" if test ! -x "${DANTED}"; then AC_MSG_NOTICE([danted not found as ${DANTED}, danted tests disabled]) @@ -401,13 +409,58 @@ elif test x"$request_danted" != "xno"; then AC_MSG_NOTICE([using DANTED=$DANTED for tests]) fi fi -if test x"$DANTED_ENABLED" = "xno"; then +if test "$DANTED_ENABLED" = "no"; then DANTED="" fi AC_SUBST(DANTED) +dnl we would like a sshd as test server +dnl +SSHD_ENABLED="maybe" +AC_ARG_WITH(test-sshd, [AS_HELP_STRING([--with-test-sshd=PATH], + [where to find sshd for testing])], + [request_sshd=$withval], [request_sshd=check]) +if test "x$request_sshd" = "xcheck" || test "x$request_sshd" = "xyes"; then + if test -x "/usr/sbin/sshd"; then + dnl common location on distros (debian/ubuntu) + SSHD="/usr/sbin/sshd" + else + AC_PATH_PROG([SSHD], [sshd]) + if test -z "$SSHD"; then + AC_PATH_PROG([SSHD], [sshd]) + fi + fi +elif test "x$request_sshd" != "xno"; then + SSHD="${request_sshd}" + if test ! -x "${SSHD}"; then + AC_MSG_NOTICE([sshd not found as ${SSHD}, sshd tests disabled]) + SSHD_ENABLED="no" + else + AC_MSG_NOTICE([using SSHD=$SSHD for tests]) + fi +fi +if test "$SSHD_ENABLED" = "no"; then + SSHD="" + SFTPD="" +else + if test -x "/usr/libexec/sftp-server"; then + dnl common location on macOS) + SFTPD="/usr/libexec/sftp-server" + elif test -x "/usr/lib/openssh/sftp-server"; then + dnl common location on debian + SFTPD="/usr/lib/openssh/sftp-server" + else + AC_PATH_PROG([SFTPD], [sftp-server]) + if test -z "$SFTPD"; then + AC_PATH_PROG([SFTPD], [sftp-server]) + fi + fi +fi +AC_SUBST(SSHD) +AC_SUBST(SFTPD) + dnl the nghttpx we might use in httpd testing -if test "x$TEST_NGHTTPX" != "x" -a "x$TEST_NGHTTPX" != "xnghttpx"; then +if test -n "$TEST_NGHTTPX" && test "x$TEST_NGHTTPX" != "xnghttpx"; then HTTPD_NGHTTPX="$TEST_NGHTTPX" else AC_PATH_PROG([HTTPD_NGHTTPX], [nghttpx], [], @@ -416,7 +469,7 @@ fi AC_SUBST(HTTPD_NGHTTPX) dnl the Caddy server we might use in testing -if test "x$TEST_CADDY" != "x"; then +if test -n "$TEST_CADDY"; then CADDY="$TEST_CADDY" else AC_PATH_PROG([CADDY], [caddy]) @@ -434,7 +487,7 @@ Select from these: --with-amissl --with-gnutls --with-mbedtls - --with-openssl (also works for BoringSSL and LibreSSL) + --with-openssl (also works for AWS-LC, BoringSSL and LibreSSL) --with-rustls --with-schannel --with-wolfssl @@ -453,7 +506,7 @@ AC_CANONICAL_HOST dnl Get system canonical name AC_DEFINE_UNQUOTED(CURL_OS, "${host}", [cpu-machine-OS]) -# Silence warning: ar: 'u' modifier ignored since 'D' is the default +dnl Silence warning: ar: 'u' modifier ignored since 'D' is the default AC_SUBST(AR_FLAGS, [cr]) dnl This defines _ALL_SOURCE for AIX @@ -472,68 +525,81 @@ LT_LANG([Windows Resource]) AM_CONDITIONAL(NOT_CURL_CI, test -z "$CURL_CI") -# -# Automake conditionals based on libtool related checks -# +dnl +dnl Automake conditionals based on libtool related checks +dnl AM_CONDITIONAL([CURL_LT_SHLIB_USE_VERSION_INFO], - [test "x$xc_lt_shlib_use_version_info" = 'xyes']) + [test "$xc_lt_shlib_use_version_info" = "yes"]) AM_CONDITIONAL([CURL_LT_SHLIB_USE_NO_UNDEFINED], - [test "x$xc_lt_shlib_use_no_undefined" = 'xyes']) + [test "$xc_lt_shlib_use_no_undefined" = "yes"]) AM_CONDITIONAL([CURL_LT_SHLIB_USE_MIMPURE_TEXT], - [test "x$xc_lt_shlib_use_mimpure_text" = 'xyes']) + [test "$xc_lt_shlib_use_mimpure_text" = "yes"]) -# -# Due to libtool and automake machinery limitations of not allowing -# specifying separate CPPFLAGS or CFLAGS when compiling objects for -# inclusion of these in shared or static libraries, we are forced to -# build using separate configure runs for shared and static libraries -# on systems where different CPPFLAGS or CFLAGS are mandatory in order -# to compile objects for each kind of library. Notice that relying on -# the '-DPIC' CFLAG that libtool provides is not valid given that the -# user might for example choose to build static libraries with PIC. -# +dnl +dnl Due to libtool and automake machinery limitations of not allowing +dnl specifying separate CPPFLAGS or CFLAGS when compiling objects for +dnl inclusion of these in shared or static libraries, we are forced to +dnl build using separate configure runs for shared and static libraries +dnl on systems where different CPPFLAGS or CFLAGS are mandatory in order +dnl to compile objects for each kind of library. Notice that relying on +dnl the '-DPIC' CFLAG that libtool provides is not valid given that the +dnl user might for example choose to build static libraries with PIC. +dnl -# -# Make our Makefile.am files use the staticlib CPPFLAG only when strictly -# targeting a static library and not building its shared counterpart. -# +dnl +dnl Make our Makefile.am files use the staticlib CPPFLAG only when strictly +dnl targeting a static library and not building its shared counterpart. +dnl AM_CONDITIONAL([USE_CPPFLAG_CURL_STATICLIB], - [test "x$xc_lt_build_static_only" = 'xyes']) + [test "$xc_lt_build_static_only" = "yes"]) -# -# Make staticlib CPPFLAG variable and its definition visible in output -# files unconditionally, providing an empty definition unless strictly -# targeting a static library and not building its shared counterpart. -# +dnl +dnl Make staticlib CPPFLAG variable and its definition visible in output +dnl files unconditionally, providing an empty definition unless strictly +dnl targeting a static library and not building its shared counterpart. +dnl LIBCURL_PC_CFLAGS_PRIVATE='-DCURL_STATICLIB' AC_SUBST(LIBCURL_PC_CFLAGS_PRIVATE) LIBCURL_PC_CFLAGS= -if test "x$xc_lt_build_static_only" = 'xyes'; then +if test "$xc_lt_build_static_only" = "yes"; then LIBCURL_PC_CFLAGS="${LIBCURL_PC_CFLAGS_PRIVATE}" fi AC_SUBST([LIBCURL_PC_CFLAGS]) - dnl ********************************************************************** dnl platform/compiler/architecture specific checks/flags dnl ********************************************************************** CURL_CHECK_COMPILER +dnl for --enable-code-coverage +CURL_COVERAGE CURL_CHECK_NATIVE_WINDOWS -curl_cv_wince='no' curl_cv_winuwp='no' if test "$curl_cv_native_windows" = "yes"; then - case $host_os in - mingw32ce*) curl_cv_wince='yes';; - esac case "$CPPFLAGS" in *-DWINSTORECOMPAT*) curl_cv_winuwp='yes';; esac + + AC_MSG_CHECKING([if building for Windows Vista or newer]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ + #include + ]],[[ + #if (_WIN32_WINNT < 0x600) + #error + #endif + ]]) + ],[ + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) + AC_MSG_ERROR([Building for Windows Vista or newer is required.]) + ]) fi CURL_SET_COMPILER_BASIC_OPTS @@ -542,7 +608,6 @@ CURL_SET_COMPILER_OPTIMIZE_OPTS CURL_SET_COMPILER_WARNING_OPTS if test "$compiler_id" = "INTEL_UNIX_C"; then - # if test "$compiler_num" -ge "1000"; then dnl icc 10.X or later CFLAGS="$CFLAGS -shared-intel" @@ -550,11 +615,19 @@ if test "$compiler_id" = "INTEL_UNIX_C"; then dnl icc 9.X specific CFLAGS="$CFLAGS -i-dynamic" fi - # fi +case $host in + *msdosdjgpp) + if test "$compiler_num" -ge "1000"; then + dnl Avoid warnings in DJGPP's built-in FD_SET() macro + CFLAGS="$CFLAGS -Wno-arith-conversion" + fi + ;; +esac + CURL_CFLAG_EXTRAS="" -if test X"$want_werror" = Xyes; then +if test "$want_werror" = "yes"; then CURL_CFLAG_EXTRAS="-Werror" if test "$compiler_id" = "GNU_C"; then dnl enable -pedantic-errors for GCC 5 and later, @@ -562,12 +635,12 @@ if test X"$want_werror" = Xyes; then if test "$compiler_num" -ge "500"; then CURL_CFLAG_EXTRAS="$CURL_CFLAG_EXTRAS -pedantic-errors" fi - elif test "$compiler_id" = "CLANG" -o "$compiler_id" = "APPLECLANG"; then + elif test "$compiler_id" = "CLANG" || test "$compiler_id" = "APPLECLANG"; then CURL_CFLAG_EXTRAS="$CURL_CFLAG_EXTRAS -pedantic-errors" fi fi AC_SUBST(CURL_CFLAG_EXTRAS) -AM_CONDITIONAL(CURL_WERROR, test X"$want_werror" = Xyes) +AM_CONDITIONAL(CURL_WERROR, test "$want_werror" = "yes") CURL_CHECK_COMPILER_HALT_ON_ERROR CURL_CHECK_COMPILER_ARRAY_SIZE_NEGATIVE @@ -575,17 +648,17 @@ CURL_CHECK_COMPILER_PROTOTYPE_MISMATCH CURL_CHECK_COMPILER_SYMBOL_HIDING supports_unittests=yes -# cross-compilation of unit tests static library/programs fails when -# libcurl shared library is built. This might be due to a libtool or -# automake issue. In this case we disable unit tests. -if test "x$cross_compiling" != "xno" && - test "x$enable_shared" != "xno"; then +dnl cross-compilation of unit tests static library/programs fails when +dnl libcurl shared library is built. This might be due to a libtool or +dnl automake issue. In this case we disable unit tests. +if test "$cross_compiling" != "no" && + test "$enable_shared" != "no"; then supports_unittests=no fi -# IRIX 6.5.24 gcc 3.3 autobuilds fail unittests library compilation due to -# a problem related with OpenSSL headers and library versions not matching. -# Disable unit tests while time to further investigate this is found. +dnl IRIX 6.5.24 gcc 3.3 autobuilds fail unittests library compilation due to +dnl a problem related with OpenSSL headers and library versions not matching. +dnl Disable unit tests while time to further investigate this is found. case $host in mips-sgi-irix6.5) if test "$compiler_id" = "GNU_C"; then @@ -594,30 +667,31 @@ case $host in ;; esac -# All AIX autobuilds fails unit tests linking against unittests library -# due to unittests library being built with no symbols or members. Libtool ? -# Disable unit tests while time to further investigate this is found. +dnl All AIX autobuilds fails unit tests linking against unittests library +dnl due to unittests library being built with no symbols or members. Libtool ? +dnl Disable unit tests while time to further investigate this is found. case $host_os in aix*) supports_unittests=no ;; esac -AM_CONDITIONAL(BUILD_UNITTESTS, test x$supports_unittests = xyes) +AM_CONDITIONAL(BUILD_UNITTESTS, test "$supports_unittests" = "yes") -# In order to detect support of sendmmsg() and accept4(), we need to escape the POSIX -# jail by defining _GNU_SOURCE or will not expose it. +dnl In order to detect support of sendmmsg() and accept4(), we need to escape the POSIX +dnl jail by defining _GNU_SOURCE or will not expose it. case $host_os in *linux*|cygwin*|msys*|gnu*) CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE" ;; esac +AM_CONDITIONAL(CLANG, test "$compiler_id" = "APPLECLANG" || test "$compiler_id" = "CLANG") + dnl ********************************************************************** dnl Compilation based checks should not be done before this point. dnl ********************************************************************** -CURL_CHECK_WIN32_LARGEFILE CURL_CHECK_WIN32_CRYPTO curl_cv_apple='no' @@ -625,16 +699,11 @@ case $host in *-apple-*) curl_cv_apple='yes';; esac -if test "$curl_cv_apple" = 'yes'; then +if test "$curl_cv_apple" = "yes"; then CURL_DARWIN_CFLAGS CURL_SUPPORTS_BUILTIN_AVAILABLE fi -curl_cv_cygwin='no' -case $host_os in - cygwin*|msys*) curl_cv_cygwin='yes';; -esac - AM_CONDITIONAL([HAVE_WINDRES], [test "$curl_cv_native_windows" = "yes" && test -n "${RC}"]) @@ -663,11 +732,11 @@ AS_HELP_STRING([--disable-unity],[Disable unity (default)]), esac ], AC_MSG_RESULT([no]) ) -if test -z "$PERL" -a "$want_unity" = 'yes'; then +if test -z "$PERL" && test "$want_unity" = "yes"; then AC_MSG_WARN([perl was not found. Will not enable unity.]) want_unity='no' fi -AM_CONDITIONAL([USE_UNITY], [test "$want_unity" = 'yes']) +AM_CONDITIONAL([USE_UNITY], [test "$want_unity" = "yes"]) dnl ************************************************************ dnl switch off particular protocols @@ -745,7 +814,7 @@ AS_HELP_STRING([--disable-ipfs],[Disable IPFS support]), CURL_DISABLE_IPFS=1 ;; *) - if test x$CURL_DISABLE_HTTP = x1; then + if test "$CURL_DISABLE_HTTP" = "1"; then AC_MSG_ERROR(HTTP support needs to be enabled in order to enable IPFS support!) else AC_MSG_RESULT(yes) @@ -753,7 +822,7 @@ AS_HELP_STRING([--disable-ipfs],[Disable IPFS support]), fi ;; esac ], - if test "x$CURL_DISABLE_HTTP" != "x1"; then + if test "$CURL_DISABLE_HTTP" != "1"; then AC_MSG_RESULT(yes) curl_ipfs_msg="enabled" else @@ -791,7 +860,7 @@ AS_HELP_STRING([--disable-ldaps],[Disable LDAPS support]), CURL_DISABLE_LDAPS=1 ;; *) - if test "x$CURL_DISABLE_LDAP" = "x1"; then + if test "$CURL_DISABLE_LDAP" = "1"; then AC_MSG_RESULT(LDAP needs to be enabled to support LDAPS) AC_DEFINE(CURL_DISABLE_LDAPS, 1, [to disable LDAPS]) CURL_DISABLE_LDAPS=1 @@ -802,7 +871,7 @@ AS_HELP_STRING([--disable-ldaps],[Disable LDAPS support]), fi ;; esac ],[ - if test "x$CURL_DISABLE_LDAP" = "x1"; then + if test "$CURL_DISABLE_LDAP" = "1"; then AC_MSG_RESULT(no) AC_DEFINE(CURL_DISABLE_LDAPS, 1, [to disable LDAPS]) CURL_DISABLE_LDAPS=1 @@ -817,27 +886,27 @@ AC_MSG_CHECKING([whether to support rtsp]) AC_ARG_ENABLE(rtsp, AS_HELP_STRING([--enable-rtsp],[Enable RTSP support]) AS_HELP_STRING([--disable-rtsp],[Disable RTSP support]), - [ case "$enableval" in - no) +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + AC_DEFINE(CURL_DISABLE_RTSP, 1, [to disable RTSP]) + CURL_DISABLE_RTSP=1 + ;; + *) + if test "$CURL_DISABLE_HTTP" = "1"; then + AC_MSG_ERROR(HTTP support needs to be enabled in order to enable RTSP support!) + else + AC_MSG_RESULT(yes) + curl_rtsp_msg="enabled" + fi + ;; + esac ], + if test "$CURL_DISABLE_HTTP" != "1"; then + AC_MSG_RESULT(yes) + curl_rtsp_msg="enabled" + else AC_MSG_RESULT(no) - AC_DEFINE(CURL_DISABLE_RTSP, 1, [to disable RTSP]) - CURL_DISABLE_RTSP=1 - ;; - *) - if test x$CURL_DISABLE_HTTP = x1; then - AC_MSG_ERROR(HTTP support needs to be enabled in order to enable RTSP support!) - else - AC_MSG_RESULT(yes) - curl_rtsp_msg="enabled" - fi - ;; - esac ], - if test "x$CURL_DISABLE_HTTP" != "x1"; then - AC_MSG_RESULT(yes) - curl_rtsp_msg="enabled" - else - AC_MSG_RESULT(no) - fi + fi ) AC_MSG_CHECKING([whether to support proxies]) @@ -892,7 +961,7 @@ AS_HELP_STRING([--disable-telnet],[Disable TELNET support]), AC_MSG_RESULT(yes) ) -if test "$curl_cv_winuwp" = 'yes' -o "$curl_cv_wince" = 'yes'; then +if test "$curl_cv_winuwp" = "yes"; then AC_DEFINE(CURL_DISABLE_TELNET, 1, [to disable TELNET]) CURL_DISABLE_TELNET=1 fi @@ -950,19 +1019,19 @@ AS_HELP_STRING([--disable-imap],[Disable IMAP support]), AC_MSG_CHECKING([whether to support smb]) AC_ARG_ENABLE(smb, -AS_HELP_STRING([--enable-smb],[Enable SMB/CIFS support]) -AS_HELP_STRING([--disable-smb],[Disable SMB/CIFS support]), +AS_HELP_STRING([--enable-smb],[Enable SMB support]) +AS_HELP_STRING([--disable-smb],[Disable SMB support]), [ case "$enableval" in - no) - AC_MSG_RESULT(no) - AC_DEFINE(CURL_DISABLE_SMB, 1, [to disable SMB/CIFS]) - CURL_DISABLE_SMB=1 + yes) + AC_MSG_RESULT(yes) + AC_DEFINE(CURL_ENABLE_SMB, 1, [to enable SMB]) + CURL_ENABLE_SMB=1 ;; *) - AC_MSG_RESULT(yes) + AC_MSG_RESULT(no) ;; esac ], - AC_MSG_RESULT(yes) + AC_MSG_RESULT(no) ) AC_MSG_CHECKING([whether to support smtp]) @@ -1016,6 +1085,23 @@ AS_HELP_STRING([--disable-mqtt],[Disable MQTT support]), AC_MSG_RESULT(no) ) +AC_MSG_CHECKING([enable curl_global_init_mem debug build]) +AC_ARG_ENABLE(init-mem-debug, +AS_HELP_STRING([--enable-init-mem-debug],[curl_global_init_mem debug]) +AS_HELP_STRING([--disable-init-mem-debug],[]), +[ case "$enableval" in + yes) + AC_MSG_RESULT(yes) + AC_DEFINE(CURL_DEBUG_GLOBAL_MEM, 1, [curl_debug_global_mem debug build]) + SUPPORT_FEATURES="$SUPPORT_FEATURES global-mem-debug" + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) + dnl ********************************************************************** dnl Check for built-in manual dnl ********************************************************************** @@ -1063,7 +1149,7 @@ AS_HELP_STRING([--disable-docs],[Disable documentation]), AC_MSG_RESULT(yes) BUILD_DOCS=1 ) -if test -z "$PERL" -a "x$BUILD_DOCS" != "x0"; then +if test -z "$PERL" && test "$BUILD_DOCS" != "0"; then AC_MSG_WARN([perl was not found. Will not build documentation.]) BUILD_DOCS=0 fi @@ -1107,6 +1193,22 @@ AS_HELP_STRING([--enable-libgcc],[use libgcc when linking]), AC_MSG_RESULT(no) ) +AC_MSG_CHECKING([whether to use libbacktrace]) +AC_ARG_WITH(backtrace, +AS_HELP_STRING([--enable-backtrace],[use libbacktrace when linking]), +[ case "$enableval" in + yes) + LIBS="-lbacktrace $LIBS" + AC_DEFINE(USE_BACKTRACE, 1, [if libbacktrace is in use]) + AC_MSG_RESULT(yes) + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) + CURL_CHECK_LIB_XNET dnl gethostbyname without lib or in the nsl lib? @@ -1173,70 +1275,37 @@ if test "$HAVE_GETHOSTBYNAME" != "1"; then ]) fi -if test "$HAVE_GETHOSTBYNAME" != "1"; then - if test "$curl_cv_wince" = 'yes'; then - dnl This is for Windows CE systems - winsock_LIB="-lws2" - if test ! -z "$winsock_LIB"; then - my_ac_save_LIBS=$LIBS - LIBS="$winsock_LIB $LIBS" - AC_MSG_CHECKING([for gethostbyname in $winsock_LIB]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ - #ifdef _WIN32 - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #include - #endif - ]],[[ - gethostbyname("localhost"); - ]]) - ],[ - AC_MSG_RESULT([yes]) - HAVE_GETHOSTBYNAME="1" - ],[ - AC_MSG_RESULT([no]) - winsock_LIB="" - LIBS=$my_ac_save_LIBS - ]) - fi - fi -fi - -# In UWP mode gethostbyname gets detected via the core libs, but some -# code (in6addr_any) still need ws2_32, so let us detect and add it. -if test "$HAVE_GETHOSTBYNAME" != "1" -o "$curl_cv_winuwp" = "yes"; then +dnl In UWP mode gethostbyname gets detected via the core libs, but some +dnl code (in6addr_any) still need ws2_32, so let us detect and add it. +if test "$HAVE_GETHOSTBYNAME" != "1" || test "$curl_cv_winuwp" = "yes"; then if test "$curl_cv_native_windows" = "yes"; then dnl This is for Winsock systems winsock_LIB="-lws2_32" if test "$curl_cv_winuwp" != "yes"; then winsock_LIB="$winsock_LIB -liphlpapi" fi - if test ! -z "$winsock_LIB"; then - my_ac_save_LIBS=$LIBS - LIBS="$winsock_LIB $LIBS" - AC_MSG_CHECKING([for gethostbyname in $winsock_LIB]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ - #ifdef _WIN32 - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #include - #endif - ]],[[ - gethostbyname("localhost"); - ]]) - ],[ - AC_MSG_RESULT([yes]) - HAVE_GETHOSTBYNAME="1" - ],[ - AC_MSG_RESULT([no]) - winsock_LIB="" - LIBS=$my_ac_save_LIBS - ]) - fi + my_ac_save_LIBS=$LIBS + LIBS="$winsock_LIB $LIBS" + AC_MSG_CHECKING([for gethostbyname in $winsock_LIB]) + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[ + #ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + #endif + ]],[[ + gethostbyname("localhost"); + ]]) + ],[ + AC_MSG_RESULT([yes]) + HAVE_GETHOSTBYNAME="1" + ],[ + AC_MSG_RESULT([no]) + winsock_LIB="" + LIBS=$my_ac_save_LIBS + ]) fi fi @@ -1276,7 +1345,7 @@ if test "$HAVE_GETHOSTBYNAME" != "1"; then ]) fi -if test "$HAVE_GETHOSTBYNAME" != "1" -o "${with_amissl+set}" = set; then +if test "$HAVE_GETHOSTBYNAME" != "1" || test "${with_amissl+set}" = "set"; then dnl This is for AmigaOS with bsdsocket.library - needs testing before -lnet AC_MSG_CHECKING([for gethostbyname for AmigaOS bsdsocket.library]) AC_LINK_IFELSE([ @@ -1346,10 +1415,10 @@ AS_HELP_STRING([--with-zlib=PATH],[search for zlib in PATH]) AS_HELP_STRING([--without-zlib],[disable use of zlib]), [OPT_ZLIB="$withval"]) -if test "$OPT_ZLIB" = "no"; then +if test "x$OPT_ZLIB" = "xno"; then AC_MSG_WARN([zlib disabled]) else - if test "$OPT_ZLIB" = "yes"; then + if test "x$OPT_ZLIB" = "xyes"; then OPT_ZLIB="" fi @@ -1399,7 +1468,7 @@ else [ dnl zlib.h was found HAVE_ZLIB_H="1" - dnl if the lib wasn't found already, try again with the new paths + dnl if the lib was not found already, try again with the new paths if test "$HAVE_LIBZ" != "1"; then AC_CHECK_LIB(z, gzread, [ @@ -1449,7 +1518,7 @@ else fi dnl set variable for use in automakefile(s) -AM_CONDITIONAL(HAVE_LIBZ, test x"$AMFIXLIB" = x1) +AM_CONDITIONAL(HAVE_LIBZ, test "$AMFIXLIB" = "1") AC_SUBST(ZLIB_LIBS) dnl ********************************************************************** @@ -1460,12 +1529,13 @@ dnl Brotli project home page: https://github.com/google/brotli dnl Default to compiler & linker defaults for BROTLI files & libraries. OPT_BROTLI=off -AC_ARG_WITH(brotli,dnl -AS_HELP_STRING([--with-brotli=PATH],[Where to look for brotli, PATH points to the BROTLI installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) +AC_ARG_WITH(brotli, +AS_HELP_STRING([--with-brotli=PATH],[Where to look for brotli, PATH points to the BROTLI installation; + when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) AS_HELP_STRING([--without-brotli], [disable BROTLI]), OPT_BROTLI=$withval) -if test X"$OPT_BROTLI" != Xno; then +if test "x$OPT_BROTLI" != "xno"; then dnl backup the pre-brotli variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" @@ -1524,18 +1594,18 @@ if test X"$OPT_BROTLI" != Xno; then AC_DEFINE(HAVE_BROTLI, 1, [if BROTLI is in use]) ) - if test X"$OPT_BROTLI" != Xoff && + if test "x$OPT_BROTLI" != "xoff" && test "$HAVE_BROTLI" != "1"; then AC_MSG_ERROR([BROTLI libs and/or directories were not found where specified!]) fi if test "$HAVE_BROTLI" = "1"; then if test -n "$DIR_BROTLI"; then - dnl when the brotli shared libs were found in a path that the run-time - dnl linker doesn't search through, we need to add it to CURL_LIBRARY_PATH + dnl when the brotli shared libs were found in a path that the runtime + dnl linker does not search through, we need to add it to CURL_LIBRARY_PATH dnl to prevent further configure tests to fail due to this - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_BROTLI" export CURL_LIBRARY_PATH AC_MSG_NOTICE([Added $DIR_BROTLI to CURL_LIBRARY_PATH]) @@ -1557,12 +1627,13 @@ dnl ********************************************************************** dnl Default to compiler & linker defaults for libzstd OPT_ZSTD=off -AC_ARG_WITH(zstd,dnl -AS_HELP_STRING([--with-zstd=PATH],[Where to look for libzstd, PATH points to the libzstd installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) +AC_ARG_WITH(zstd, +AS_HELP_STRING([--with-zstd=PATH],[Where to look for libzstd, PATH points to the libzstd installation; + when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) AS_HELP_STRING([--without-zstd], [disable libzstd]), OPT_ZSTD=$withval) -if test X"$OPT_ZSTD" != Xno; then +if test "x$OPT_ZSTD" != "xno"; then dnl backup the pre-zstd variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" @@ -1613,19 +1684,19 @@ if test X"$OPT_ZSTD" != Xno; then AC_DEFINE(HAVE_ZSTD, 1, [if libzstd is in use]) ) - if test X"$OPT_ZSTD" != Xoff && + if test "x$OPT_ZSTD" != "xoff" && test "$HAVE_ZSTD" != "1"; then AC_MSG_ERROR([libzstd was not found where specified!]) fi if test "$HAVE_ZSTD" = "1"; then if test -n "$DIR_ZSTD"; then - dnl when the zstd shared lib were found in a path that the run-time - dnl linker doesn't search through, we need to add it to + dnl when the zstd shared lib were found in a path that the runtime + dnl linker does not search through, we need to add it to dnl CURL_LIBRARY_PATH to prevent further configure tests to fail due to dnl this - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_ZSTD" export CURL_LIBRARY_PATH AC_MSG_NOTICE([Added $DIR_ZSTD to CURL_LIBRARY_PATH]) @@ -1689,11 +1760,7 @@ AS_HELP_STRING([--disable-ipv6],[Disable IPv6 support]), ) ) -if test "$curl_cv_wince" = 'yes'; then - ipv6=no -fi - -if test "$ipv6" = yes; then +if test "$ipv6" = "yes"; then curl_ipv6_msg="enabled" AC_DEFINE(USE_IPV6, 1, [Define if you want to enable IPv6 support]) IPV6_ENABLED=1 @@ -1741,7 +1808,7 @@ int main(int argc, char **argv) #else (void)argc; argv[0][0] = ' '; - return (argv[0][0] == ' ')?0:1; + return (argv[0][0] == ' ') ? 0 : 1; #endif } ]],[ @@ -1751,7 +1818,7 @@ int main(int argc, char **argv) ],[ curl_cv_writable_argv=cross ]) -if test "$curl_cv_writable_argv" = 'cross' -a "$curl_cv_apple" = 'yes'; then +if test "$curl_cv_writable_argv" = "cross" && test "$curl_cv_apple" = "yes"; then curl_cv_writable_argv=yes fi case $curl_cv_writable_argv in @@ -1792,9 +1859,9 @@ AC_ARG_WITH(gssapi-libs, AC_ARG_WITH(gssapi, AS_HELP_STRING([--with-gssapi=DIR], [Where to look for GSS-API]), [ GSSAPI_ROOT="$withval" - if test x"$GSSAPI_ROOT" != xno; then + if test "$GSSAPI_ROOT" != "no"; then want_gss="yes" - if test x"$GSSAPI_ROOT" = xyes; then + if test "$GSSAPI_ROOT" = "yes"; then dnl if yes, then use default root GSSAPI_ROOT="/usr" fi @@ -1806,16 +1873,16 @@ AC_ARG_WITH(gssapi, save_CPPFLAGS="$CPPFLAGS" AC_MSG_CHECKING([if GSS-API support is requested]) -if test x"$want_gss" = xyes; then +if test "$want_gss" = "yes"; then AC_MSG_RESULT(yes) - if test $GSSAPI_ROOT != "/usr"; then + if test "$GSSAPI_ROOT" != "/usr"; then CURL_CHECK_PKGCONFIG(mit-krb5-gssapi, $GSSAPI_ROOT/lib/pkgconfig) else CURL_CHECK_PKGCONFIG(mit-krb5-gssapi) fi if test -z "$GSSAPI_INCS"; then - if test -n "$host_alias" -a -f "$GSSAPI_ROOT/bin/$host_alias-krb5-config"; then + if test -n "$host_alias" && test -f "$GSSAPI_ROOT/bin/$host_alias-krb5-config"; then GSSAPI_INCS=`$GSSAPI_ROOT/bin/$host_alias-krb5-config --cflags gssapi` elif test "$PKGCONFIG" != "no"; then GSSAPI_INCS=`$PKGCONFIG --cflags mit-krb5-gssapi` @@ -1835,61 +1902,24 @@ if test x"$want_gss" = xyes; then gnu_gss=yes ], [ - dnl not found, check Heimdal or MIT - AC_CHECK_HEADERS([gssapi/gssapi.h], [], [not_mit=1]) + dnl not found, check for MIT AC_CHECK_HEADERS( - [gssapi/gssapi_generic.h gssapi/gssapi_krb5.h], + [gssapi/gssapi.h gssapi/gssapi_generic.h gssapi/gssapi_krb5.h], [], - [not_mit=1], - [ - AC_INCLUDES_DEFAULT - #ifdef HAVE_GSSAPI_GSSAPI_H - #include - #endif - ]) - if test "x$not_mit" = "x1"; then - dnl MIT not found, check for Heimdal - AC_CHECK_HEADER(gssapi.h, - [], - [ - dnl no header found, disabling GSS - want_gss=no - AC_MSG_WARN(disabling GSS-API support since no header files were found) - ] - ) - else - dnl MIT found - dnl check if we have a really old MIT Kerberos version (<= 1.2) - AC_MSG_CHECKING([if GSS-API headers declare GSS_C_NT_HOSTBASED_SERVICE]) - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - #include - #include - #include - ]],[[ - gss_import_name( - (OM_uint32 *)0, - (gss_buffer_t)0, - GSS_C_NT_HOSTBASED_SERVICE, - (gss_name_t *)0); - ]]) - ],[ - AC_MSG_RESULT([yes]) - ],[ - AC_MSG_RESULT([no]) - AC_DEFINE(HAVE_OLD_GSSMIT, 1, - [if you have an old MIT Kerberos version, lacking GSS_C_NT_HOSTBASED_SERVICE]) - ]) + [not_mit=1]) + if test "$not_mit" = "1"; then + dnl MIT not found + AC_MSG_ERROR([MIT or GNU GSS library required, but not found]) fi ] ) else AC_MSG_RESULT(no) fi -if test x"$want_gss" = xyes; then +if test "$want_gss" = "yes"; then AC_DEFINE(HAVE_GSSAPI, 1, [if you have GSS-API libraries]) HAVE_GSSAPI=1 - curl_gss_msg="enabled (MIT Kerberos/Heimdal)" + curl_gss_msg="enabled (MIT Kerberos)" link_pkgconfig='' if test -n "$gnu_gss"; then @@ -1899,16 +1929,16 @@ if test x"$want_gss" = xyes; then LIBS="-lgss $LIBS" link_pkgconfig=1 elif test -z "$GSSAPI_LIB_DIR"; then - if test "$curl_cv_apple" = 'yes'; then + if test "$curl_cv_apple" = "yes"; then LIBS="-lgssapi_krb5 -lresolv $LIBS" else - if test $GSSAPI_ROOT != "/usr"; then + if test "$GSSAPI_ROOT" != "/usr"; then CURL_CHECK_PKGCONFIG(mit-krb5-gssapi, $GSSAPI_ROOT/lib/pkgconfig) else CURL_CHECK_PKGCONFIG(mit-krb5-gssapi) fi - if test -n "$host_alias" -a -f "$GSSAPI_ROOT/bin/$host_alias-krb5-config"; then - dnl krb5-config doesn't have --libs-only-L or similar, put everything + if test -n "$host_alias" && test -f "$GSSAPI_ROOT/bin/$host_alias-krb5-config"; then + dnl krb5-config does not have --libs-only-L or similar, put everything dnl into LIBS gss_libs=`$GSSAPI_ROOT/bin/$host_alias-krb5-config --libs gssapi` LIBS="$gss_libs $LIBS" @@ -1917,7 +1947,7 @@ if test x"$want_gss" = xyes; then LIBS="$gss_libs $LIBS" link_pkgconfig=1 elif test -f "$KRB5CONFIG"; then - dnl krb5-config doesn't have --libs-only-L or similar, put everything + dnl krb5-config does not have --libs-only-L or similar, put everything dnl into LIBS gss_libs=`$KRB5CONFIG --libs gssapi` LIBS="$gss_libs $LIBS" @@ -1941,6 +1971,18 @@ if test x"$want_gss" = xyes; then fi fi fi + gss_version="" + if test -n "$host_alias" && test -f "$GSSAPI_ROOT/bin/$host_alias-krb5-config"; then + gss_version=`$GSSAPI_ROOT/bin/$host_alias-krb5-config --version | $SED 's/Kerberos 5 release //'` + elif test "$PKGCONFIG" != "no"; then + gss_version=`$PKGCONFIG --modversion mit-krb5-gssapi` + elif test -f "$KRB5CONFIG"; then + gss_version=`$KRB5CONFIG --version | $SED 's/Kerberos 5 release //'` + fi + if test -n "$gss_version"; then + AC_MSG_NOTICE([GSS-API MIT Kerberos version detected: $gss_version]) + AC_DEFINE_UNQUOTED([CURL_KRB5_VERSION], ["$gss_version"], [MIT Kerberos version]) + fi else LDFLAGS="$LDFLAGS $GSSAPI_LIB_DIR" LDFLAGSPC="$LDFLAGSPC $GSSAPI_LIB_DIR" @@ -1956,8 +1998,6 @@ if test x"$want_gss" = xyes; then if test -n "$link_pkgconfig"; then if test -n "$gnu_gss"; then LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE gss" - elif test "x$not_mit" = "x1"; then - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE heimdal-gssapi" else LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE mit-krb5-gssapi" fi @@ -1966,7 +2006,7 @@ else CPPFLAGS="$save_CPPFLAGS" fi -if test x"$want_gss" = xyes; then +if test "$want_gss" = "yes"; then AC_MSG_CHECKING([if we can link against GSS-API library]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([gss_init_sec_context]) @@ -1979,11 +2019,11 @@ if test x"$want_gss" = xyes; then fi build_libstubgss=no -if test x"$want_gss" = "xyes"; then +if test "$want_gss" = "yes"; then build_libstubgss=yes fi -AM_CONDITIONAL(BUILD_STUB_GSS, test "x$build_libstubgss" = "xyes") +AM_CONDITIONAL(BUILD_STUB_GSS, test "$build_libstubgss" = "yes") dnl ------------------------------------------------------------- dnl parse --with-default-ssl-backend so it can be validated below @@ -2017,17 +2057,15 @@ CURL_WITH_GNUTLS CURL_WITH_MBEDTLS CURL_WITH_WOLFSSL CURL_WITH_RUSTLS +CURL_WITH_APPLE_SECTRUST dnl link required libraries for USE_WIN32_CRYPTO or SCHANNEL_ENABLED -if test "x$USE_WIN32_CRYPTO" = "x1" -o "x$SCHANNEL_ENABLED" = "x1"; then - LIBS="-lcrypt32 $LIBS" - if test "$curl_cv_wince" = 'no'; then - LIBS="-ladvapi32 $LIBS" - fi +if test "$USE_WIN32_CRYPTO" = "1" || test "$SCHANNEL_ENABLED" = "1"; then + LIBS="-ladvapi32 -lcrypt32 $LIBS" fi -dnl link bcrypt for BCryptGenRandom() (used when building for Vista or newer) -if test "x$curl_cv_native_windows" = "xyes" -a "$curl_cv_wince" = 'no'; then +if test "$curl_cv_native_windows" = "yes"; then + dnl for BCryptGenRandom() LIBS="-lbcrypt $LIBS" fi @@ -2037,12 +2075,12 @@ case "x$SSL_DISABLED$OPENSSL_ENABLED$GNUTLS_ENABLED$MBEDTLS_ENABLED$WOLFSSL_ENAB Use --with-openssl, --with-gnutls, --with-wolfssl, --with-mbedtls, --with-schannel, --with-amissl or --with-rustls to address this.]) ;; x1) - # one SSL backend is enabled + dnl one SSL backend is enabled SSL_ENABLED="1" AC_MSG_NOTICE([built with one SSL backend]) ;; xD) - # explicitly built without TLS + dnl explicitly built without TLS ;; xD*) AC_MSG_ERROR([--without-ssl has been set together with an explicit option to use an ssl library @@ -2050,7 +2088,7 @@ Use --with-openssl, --with-gnutls, --with-wolfssl, --with-mbedtls, --with-schann Since these are conflicting parameters, verify which is the desired one and drop the other.]) ;; *) - # more than one SSL backend is enabled + dnl more than one SSL backend is enabled SSL_ENABLED="1" CURL_WITH_MULTI_SSL="1" AC_DEFINE(CURL_WITH_MULTI_SSL, 1, [built with multiple SSL backends]) @@ -2062,16 +2100,38 @@ if test -n "$ssl_backends"; then curl_ssl_msg="enabled ($ssl_backends)" fi -if test no = "$VALID_DEFAULT_SSL_BACKEND"; then +if test "$VALID_DEFAULT_SSL_BACKEND" = "no"; then if test -n "$SSL_ENABLED"; then AC_MSG_ERROR([Default SSL backend $DEFAULT_SSL_BACKEND not enabled!]) else AC_MSG_ERROR([Default SSL backend requires SSL!]) fi -elif test yes = "$VALID_DEFAULT_SSL_BACKEND"; then +elif test "$VALID_DEFAULT_SSL_BACKEND" = "yes"; then AC_DEFINE_UNQUOTED([CURL_DEFAULT_SSL_BACKEND], ["$DEFAULT_SSL_BACKEND"], [Default SSL backend]) fi +dnl --------------------- +dnl check native CA store +dnl --------------------- + +ca_native_opt=0 +AC_MSG_CHECKING([whether to use native CA store]) +AC_ARG_ENABLE(ca-native, +AS_HELP_STRING([--enable-ca-native],[Enable native CA store]) +AS_HELP_STRING([--disable-ca-native],[Disable native CA store (default)]), +[ case "$enableval" in + yes) + AC_MSG_RESULT([yes]) + AC_DEFINE(CURL_CA_NATIVE, 1, [If native CA store is enabled]) + ca_native_opt=1 + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac ], + AC_MSG_RESULT([no]) +) + dnl ********************************************************************** dnl Check for the CA bundle dnl ********************************************************************** @@ -2081,28 +2141,45 @@ if test -n "$check_for_ca_bundle"; then CURL_CHECK_CA_EMBED fi -AM_CONDITIONAL(CURL_CA_EMBED_SET, test "x$CURL_CA_EMBED" != "x") +AM_CONDITIONAL(CURL_CA_EMBED_SET, test -n "$CURL_CA_EMBED") dnl ---------------------- dnl check unsafe CA search dnl ---------------------- if test "$curl_cv_native_windows" = "yes"; then + ca_search=1 AC_MSG_CHECKING([whether to enable unsafe CA bundle search in PATH on Windows]) AC_ARG_ENABLE(ca-search, -AS_HELP_STRING([--enable-ca-search],[Enable unsafe CA bundle search in PATH on Windows (default)]) +AS_HELP_STRING([--enable-ca-search],[Enable unsafe CA bundle search in PATH on Windows]) AS_HELP_STRING([--disable-ca-search],[Disable unsafe CA bundle search in PATH on Windows]), [ case "$enableval" in no) AC_MSG_RESULT([no]) - AC_DEFINE(CURL_DISABLE_CA_SEARCH, 1, [If unsafe CA bundle search in PATH on Windows is disabled]) + ca_search=0 + ;; + yes) + AC_MSG_RESULT([yes]) ;; *) - AC_MSG_RESULT([yes]) + if test "$ca_native_opt" = "1"; then + AC_MSG_RESULT([no]) + ca_search=0 + else + AC_MSG_RESULT([yes]) + fi ;; esac ], - AC_MSG_RESULT([yes]) + if test "$ca_native_opt" = "1"; then + AC_MSG_RESULT([no]) + ca_search=0 + else + AC_MSG_RESULT([yes]) + fi ) + if test "$ca_search" = "0"; then + AC_DEFINE(CURL_DISABLE_CA_SEARCH, 1, [If unsafe CA bundle search in PATH on Windows is disabled]) + fi fi dnl -------------------- @@ -2133,12 +2210,13 @@ dnl ********************************************************************** dnl Default to compiler & linker defaults for LIBPSL files & libraries. OPT_LIBPSL=off -AC_ARG_WITH(libpsl,dnl -AS_HELP_STRING([--with-libpsl=PATH],[Where to look for libpsl, PATH points to the LIBPSL installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) +AC_ARG_WITH(libpsl, +AS_HELP_STRING([--with-libpsl=PATH],[Where to look for libpsl, PATH points to the LIBPSL installation; + when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) AS_HELP_STRING([--without-libpsl], [disable LIBPSL]), OPT_LIBPSL=$withval) -if test X"$OPT_LIBPSL" != Xno; then +if test "x$OPT_LIBPSL" != "xno"; then dnl backup the pre-libpsl variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" @@ -2200,25 +2278,72 @@ if test X"$OPT_LIBPSL" != Xno; then fi AM_CONDITIONAL([USE_LIBPSL], [test "$curl_psl_msg" = "enabled"]) - dnl ********************************************************************** dnl Check for libgsasl dnl ********************************************************************** +OPT_LIBGSASL=no AC_ARG_WITH(libgsasl, - AS_HELP_STRING([--without-libgsasl], - [disable libgsasl support for SCRAM]), - with_libgsasl=$withval, - with_libgsasl=yes) -if test $with_libgsasl != "no"; then - AC_SEARCH_LIBS(gsasl_init, gsasl, - [curl_gsasl_msg="enabled"; - AC_DEFINE([USE_GSASL], [1], [GSASL support enabled]) - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE libgsasl" +AS_HELP_STRING([--with-libgsasl=PATH],[Where to look for libgsasl, PATH points to the libgsasl installation; + when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) +AS_HELP_STRING([--without-libgsasl], [disable libgsasl support for SCRAM]), + OPT_LIBGSASL=$withval) + +if test "x$OPT_LIBGSASL" != "xno"; then + dnl backup the pre-libgsasl variables + CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" + CLEANCPPFLAGS="$CPPFLAGS" + CLEANLIBS="$LIBS" + + case "$OPT_LIBGSASL" in + yes) + dnl --with-libgsasl (without path) used + CURL_CHECK_PKGCONFIG(libgsasl) + + if test "$PKGCONFIG" != "no"; then + LIB_GSASL=`$PKGCONFIG --libs-only-l libgsasl` + LD_GSASL=`$PKGCONFIG --libs-only-L libgsasl` + CPP_GSASL=`$PKGCONFIG --cflags-only-I libgsasl` + else + dnl no libgsasl pkg-config found + LIB_GSASL="-lgsasl" + fi + ;; + *) + dnl use the given --with-libgsasl spot + PREFIX_GSASL=$OPT_LIBGSASL + ;; + esac + + dnl if given with a prefix, we set -L and -I based on that + if test -n "$PREFIX_GSASL"; then + LIB_GSASL="-lgsasl" + LD_GSASL=-L${PREFIX_GSASL}/lib$libsuff + CPP_GSASL=-I${PREFIX_GSASL}/include + fi + + LDFLAGS="$LDFLAGS $LD_GSASL" + LDFLAGSPC="$LDFLAGSPC $LD_GSASL" + CPPFLAGS="$CPPFLAGS $CPP_GSASL" + LIBS="$LIB_GSASL $LIBS" + + AC_CHECK_LIB(gsasl, gsasl_init, + [ + AC_CHECK_HEADERS(gsasl.h, + curl_gsasl_msg="enabled" + AC_DEFINE(USE_GSASL, 1, [GSASL support enabled]) + USE_LIBGSASL=1 + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE libgsasl" + ) ], - [curl_gsasl_msg="no (libgsasl not found)"; + dnl not found, revert back to clean variables + LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC + CPPFLAGS=$CLEANCPPFLAGS + LIBS=$CLEANLIBS + curl_gsasl_msg="no (libgsasl not found)" AC_MSG_WARN([libgsasl was not found]) - ] ) fi AM_CONDITIONAL([USE_GSASL], [test "$curl_gsasl_msg" = "enabled"]) @@ -2232,25 +2357,20 @@ dnl ********************************************************************** dnl Default to compiler & linker defaults for libssh2 files & libraries. OPT_LIBSSH2=off -AC_ARG_WITH(libssh2,dnl -AS_HELP_STRING([--with-libssh2=PATH],[Where to look for libssh2, PATH points to the libssh2 installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) +AC_ARG_WITH(libssh2, +AS_HELP_STRING([--with-libssh2=PATH],[Where to look for libssh2, PATH points to the libssh2 installation; + when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) AS_HELP_STRING([--with-libssh2], [enable libssh2]), OPT_LIBSSH2=$withval, OPT_LIBSSH2=no) - OPT_LIBSSH=off -AC_ARG_WITH(libssh,dnl -AS_HELP_STRING([--with-libssh=PATH],[Where to look for libssh, PATH points to the libssh installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) +AC_ARG_WITH(libssh, +AS_HELP_STRING([--with-libssh=PATH],[Where to look for libssh, PATH points to the libssh installation; + when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) AS_HELP_STRING([--with-libssh], [enable libssh]), OPT_LIBSSH=$withval, OPT_LIBSSH=no) -OPT_WOLFSSH=off -AC_ARG_WITH(wolfssh,dnl -AS_HELP_STRING([--with-wolfssh=PATH],[Where to look for wolfssh, PATH points to the wolfSSH installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) -AS_HELP_STRING([--with-wolfssh], [enable wolfssh]), - OPT_WOLFSSH=$withval, OPT_WOLFSSH=no) - -if test X"$OPT_LIBSSH2" != Xno; then +if test "x$OPT_LIBSSH2" != "xno"; then dnl backup the pre-libssh2 variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" @@ -2293,8 +2413,8 @@ if test X"$OPT_LIBSSH2" != Xno; then CPPFLAGS="$CPPFLAGS $CPP_SSH2" LIBS="$LIB_SSH2 $LIBS" - dnl check for function added in libssh2 version 1.2.8 - AC_CHECK_LIB(ssh2, libssh2_free) + dnl check for function added in libssh2 version 1.9.0 + AC_CHECK_LIB(ssh2, libssh2_agent_get_identity_path) AC_CHECK_HEADER(libssh2.h, curl_ssh_msg="enabled (libssh2)" @@ -2302,18 +2422,18 @@ if test X"$OPT_LIBSSH2" != Xno; then USE_LIBSSH2=1 ) - if test X"$OPT_LIBSSH2" != Xoff && + if test "x$OPT_LIBSSH2" != "xoff" && test "$USE_LIBSSH2" != "1"; then AC_MSG_ERROR([libssh2 libs and/or directories were not found where specified!]) fi if test "$USE_LIBSSH2" = "1"; then if test -n "$DIR_SSH2"; then - dnl when the libssh2 shared libs were found in a path that the run-time - dnl linker doesn't search through, we need to add it to CURL_LIBRARY_PATH + dnl when the libssh2 shared libs were found in a path that the runtime + dnl linker does not search through, we need to add it to CURL_LIBRARY_PATH dnl to prevent further configure tests to fail due to this - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_SSH2" export CURL_LIBRARY_PATH AC_MSG_NOTICE([Added $DIR_SSH2 to CURL_LIBRARY_PATH]) @@ -2327,7 +2447,7 @@ if test X"$OPT_LIBSSH2" != Xno; then CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS fi -elif test X"$OPT_LIBSSH" != Xno; then +elif test "x$OPT_LIBSSH" != "xno"; then dnl backup the pre-libssh variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" @@ -2378,7 +2498,7 @@ elif test X"$OPT_LIBSSH" != Xno; then USE_LIBSSH=1 ) - if test X"$OPT_LIBSSH" != Xoff && + if test "x$OPT_LIBSSH" != "xoff" && test "$USE_LIBSSH" != "1"; then AC_MSG_ERROR([libssh libs and/or directories were not found where specified!]) fi @@ -2389,11 +2509,11 @@ elif test X"$OPT_LIBSSH" != Xno; then LIBS="-liphlpapi $LIBS" fi if test -n "$DIR_SSH"; then - dnl when the libssh shared libs were found in a path that the run-time - dnl linker doesn't search through, we need to add it to CURL_LIBRARY_PATH + dnl when the libssh shared libs were found in a path that the runtime + dnl linker does not search through, we need to add it to CURL_LIBRARY_PATH dnl to prevent further configure tests to fail due to this - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_SSH" export CURL_LIBRARY_PATH AC_MSG_NOTICE([Added $DIR_SSH to CURL_LIBRARY_PATH]) @@ -2407,28 +2527,6 @@ elif test X"$OPT_LIBSSH" != Xno; then CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS fi -elif test X"$OPT_WOLFSSH" != Xno; then - dnl backup the pre-wolfssh variables - CLEANLDFLAGS="$LDFLAGS" - CLEANLDFLAGSPC="$LDFLAGSPC" - CLEANCPPFLAGS="$CPPFLAGS" - CLEANLIBS="$LIBS" - - if test "$OPT_WOLFSSH" != yes; then - WOLFCONFIG="$OPT_WOLFSSH/bin/wolfssh-config" - WOLFSSH_LIBS=`$WOLFCONFIG --libs` - LDFLAGS="$LDFLAGS $WOLFSSH_LIBS" - LDFLAGSPC="$LDFLAGSPC $WOLFSSH_LIBS" - CPPFLAGS="$CPPFLAGS `$WOLFCONFIG --cflags`" - fi - - AC_CHECK_LIB(wolfssh, wolfSSH_Init) - - AC_CHECK_HEADERS(wolfssh/ssh.h, - curl_ssh_msg="enabled (wolfSSH)" - AC_DEFINE(USE_WOLFSSH, 1, [if wolfSSH is in use]) - USE_WOLFSSH=1 - ) fi dnl ********************************************************************** @@ -2451,7 +2549,8 @@ clean_LDAP_LDFLAGS=$LDFLAGS clean_LDAP_LIBS=$LIBS OPT_LDAP=off AC_ARG_WITH(ldap, -AS_HELP_STRING([--with-ldap=PATH],[Where to look for LDAP, PATH points to the LDAP installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) +AS_HELP_STRING([--with-ldap=PATH],[Where to look for LDAP, PATH points to the LDAP installation; + when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) AS_HELP_STRING([--without-ldap], [disable LDAP]), OPT_LDAP=$withval) @@ -2465,7 +2564,7 @@ case "$OPT_LDAP" in want_ldap="yes" ;; off) - dnl no --with-ldap option given, don't change anything + dnl no --with-ldap option given, do not change anything want_ldap="default" ;; *) @@ -2485,14 +2584,14 @@ case "$OPT_LDAP" in ;; esac -if test x$CURL_DISABLE_LDAP != x1 && test "$want_ldap" != "no"; then +if test "$CURL_DISABLE_LDAP" != "1" && test "$want_ldap" != "no"; then CURL_CHECK_HEADER_LBER CURL_CHECK_HEADER_LDAP CURL_CHECK_HEADER_LDAP_SSL if test -z "$LDAPLIBNAME"; then - if test "$curl_cv_native_windows" = "yes" -a "$curl_cv_winuwp" != "yes"; then + if test "$curl_cv_native_windows" = "yes" && test "$curl_cv_winuwp" != "yes"; then dnl Windows uses a single and unique LDAP library name LDAPLIBNAME="wldap32" LBERLIBNAME="no" @@ -2501,7 +2600,7 @@ if test x$CURL_DISABLE_LDAP != x1 && test "$want_ldap" != "no"; then if test "$LDAPLIBNAME"; then dnl If we have both LDAP and LBER library names, check if we need both - if test "$LBERLIBNAME" -a "$LBERLIBNAME" != "no"; then + if test "$LBERLIBNAME" && test "$LBERLIBNAME" != "no"; then dnl Try LDAP first, then with LBER if needed AC_CHECK_LIB("$LDAPLIBNAME", ldap_init, [ldap_lib_ok=yes], [ldap_lib_ok=no]) if test "$ldap_lib_ok" = "no"; then @@ -2522,7 +2621,7 @@ if test x$CURL_DISABLE_LDAP != x1 && test "$want_ldap" != "no"; then if test "$ldap_lib_ok" = "no"; then if test -n "$ldap_askedfor"; then - AC_MSG_ERROR([couldn't detect the LDAP libraries]) + AC_MSG_ERROR([could not detect the LDAP libraries]) fi AC_MSG_WARN(["$LDAPLIBNAME" is not an LDAP library: LDAP disabled]) AC_DEFINE(CURL_DISABLE_LDAP, 1, [to disable LDAP]) @@ -2540,7 +2639,7 @@ if test x$CURL_DISABLE_LDAP != x1 && test "$want_ldap" != "no"; then case X-"$curl_cv_ldap_LIBS" in X-unknown) if test -n "$ldap_askedfor"; then - AC_MSG_ERROR([couldn't detect the LDAP libraries]) + AC_MSG_ERROR([could not detect the LDAP libraries]) fi AC_MSG_WARN([Cannot find libraries for LDAP support: LDAP disabled]) AC_DEFINE(CURL_DISABLE_LDAP, 1, [to disable LDAP]) @@ -2556,15 +2655,15 @@ if test x$CURL_DISABLE_LDAP != x1 && test "$want_ldap" != "no"; then fi fi -if test x$CURL_DISABLE_LDAP != x1; then +if test "$CURL_DISABLE_LDAP" != "1"; then dnl Add to library path if needed if test -n "$DIR_LDAP"; then - dnl when the ldap shared lib were found in a path that the run-time - dnl linker doesn't search through, we need to add it to + dnl when the ldap shared lib were found in a path that the runtime + dnl linker does not search through, we need to add it to dnl CURL_LIBRARY_PATH to prevent further configure tests to fail due to dnl this - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_LDAP" export CURL_LIBRARY_PATH AC_MSG_NOTICE([Added $DIR_LDAP to CURL_LIBRARY_PATH]) @@ -2572,10 +2671,10 @@ if test x$CURL_DISABLE_LDAP != x1; then fi if test "$LBERLIBNAME"; then - dnl If name is "no" then don't define this library at all - dnl (it's only needed if libldap.so's dependencies are broken). + dnl If name is "no" then do not define this library at all + dnl (it is only needed if libldap.so's dependencies are broken). dnl Skip this check if we already determined we need both libraries above - if test "$LBERLIBNAME" != "no" -a "$ldap_lib_ok" != "yes"; then + if test "$LBERLIBNAME" != "no" && test "$ldap_lib_ok" != "yes"; then AC_CHECK_LIB("$LBERLIBNAME", ber_free,, [ AC_MSG_WARN(["$LBERLIBNAME" is not an LBER library: LDAP disabled]) AC_DEFINE(CURL_DISABLE_LDAP, 1, [to disable LDAP]) @@ -2592,7 +2691,7 @@ if test x$CURL_DISABLE_LDAP != x1; then fi fi -if test x$CURL_DISABLE_LDAP != x1; then +if test "$CURL_DISABLE_LDAP" != "1"; then AC_CHECK_FUNCS([ldap_url_parse \ ldap_init_fd]) @@ -2600,7 +2699,7 @@ if test x$CURL_DISABLE_LDAP != x1; then curl_ldap_msg="enabled (winldap)" AC_DEFINE(USE_WIN32_LDAP, 1, [Use Windows LDAP implementation]) else - if test "x$ac_cv_func_ldap_init_fd" = "xyes"; then + if test "$ac_cv_func_ldap_init_fd" = "yes"; then curl_ldap_msg="enabled (OpenLDAP)" AC_DEFINE(USE_OPENLDAP, 1, [Use OpenLDAP-specific code]) USE_OPENLDAP=1 @@ -2610,91 +2709,10 @@ if test x$CURL_DISABLE_LDAP != x1; then fi fi -if test x$CURL_DISABLE_LDAPS != x1; then +if test "$CURL_DISABLE_LDAPS" != "1"; then curl_ldaps_msg="enabled" fi -dnl ********************************************************************** -dnl Check for the presence of LIBRTMP libraries and headers -dnl ********************************************************************** - -dnl Default to compiler & linker defaults for LIBRTMP files & libraries. -OPT_LIBRTMP=off -AC_ARG_WITH(librtmp,dnl -AS_HELP_STRING([--with-librtmp=PATH],[Where to look for librtmp, PATH points to the LIBRTMP installation; when possible, set the PKG_CONFIG_PATH environment variable instead of using this option]) -AS_HELP_STRING([--without-librtmp], [disable LIBRTMP]), - OPT_LIBRTMP=$withval) - -if test X"$OPT_LIBRTMP" != Xno; then - dnl backup the pre-librtmp variables - CLEANLDFLAGS="$LDFLAGS" - CLEANLDFLAGSPC="$LDFLAGSPC" - CLEANCPPFLAGS="$CPPFLAGS" - CLEANLIBS="$LIBS" - - case "$OPT_LIBRTMP" in - yes) - dnl --with-librtmp (without path) used - CURL_CHECK_PKGCONFIG(librtmp) - - if test "$PKGCONFIG" != "no"; then - LIB_RTMP=`$PKGCONFIG --libs-only-l librtmp` - LD_RTMP=`$PKGCONFIG --libs-only-L librtmp` - CPP_RTMP=`$PKGCONFIG --cflags-only-I librtmp` - version=`$PKGCONFIG --modversion librtmp` - DIR_RTMP=`echo $LD_RTMP | $SED -e 's/^-L//'` - else - dnl To avoid link errors, we do not allow --librtmp without - dnl a pkgconfig file - AC_MSG_ERROR([--librtmp was specified but could not find librtmp pkgconfig file.]) - fi - - ;; - off) - dnl no --with-librtmp option given, just check default places - LIB_RTMP="-lrtmp" - ;; - *) - dnl use the given --with-librtmp spot - LIB_RTMP="-lrtmp" - PREFIX_RTMP=$OPT_LIBRTMP - ;; - esac - - dnl if given with a prefix, we set -L and -I based on that - if test -n "$PREFIX_RTMP"; then - LD_RTMP=-L${PREFIX_RTMP}/lib$libsuff - CPP_RTMP=-I${PREFIX_RTMP}/include - DIR_RTMP=${PREFIX_RTMP}/lib$libsuff - fi - - LDFLAGS="$LDFLAGS $LD_RTMP" - LDFLAGSPC="$LDFLAGSPC $LD_RTMP" - CPPFLAGS="$CPPFLAGS $CPP_RTMP" - LIBS="$LIB_RTMP $LIBS" - - AC_CHECK_LIB(rtmp, RTMP_Init, - [ - AC_CHECK_HEADERS(librtmp/rtmp.h, - curl_rtmp_msg="enabled (librtmp)" - AC_DEFINE(USE_LIBRTMP, 1, [if librtmp is in use]) - USE_LIBRTMP=1 - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE librtmp" - ) - ], - dnl not found, revert back to clean variables - LDFLAGS=$CLEANLDFLAGS - LDFLAGSPC=$CLEANLDFLAGSPC - CPPFLAGS=$CLEANCPPFLAGS - LIBS=$CLEANLIBS - ) - - if test X"$OPT_LIBRTMP" != Xoff && - test "$USE_LIBRTMP" != "1"; then - AC_MSG_ERROR([librtmp libs and/or directories were not found where specified!]) - fi -fi - dnl ********************************************************************** dnl Check for linker switch for versioned symbols dnl ********************************************************************** @@ -2719,17 +2737,17 @@ AS_HELP_STRING([--disable-versioned-symbols], [Disable versioned symbols in shar AC_MSG_RESULT(yes) if test "x$enableval" != "xyes"; then versioned_symbols_flavour="$enableval" - elif test "x$CURL_WITH_MULTI_SSL" = "x1"; then + elif test "$CURL_WITH_MULTI_SSL" = "1"; then versioned_symbols_flavour="MULTISSL_" - elif test "x$OPENSSL_ENABLED" = "x1"; then + elif test "$OPENSSL_ENABLED" = "1"; then versioned_symbols_flavour="OPENSSL_" - elif test "x$MBEDTLS_ENABLED" = "x1"; then + elif test "$MBEDTLS_ENABLED" = "1"; then versioned_symbols_flavour="MBEDTLS_" - elif test "x$WOLFSSL_ENABLED" = "x1"; then + elif test "$WOLFSSL_ENABLED" = "1"; then versioned_symbols_flavour="WOLFSSL_" - elif test "x$GNUTLS_ENABLED" = "x1"; then + elif test "$GNUTLS_ENABLED" = "1"; then versioned_symbols_flavour="GNUTLS_" - elif test "x$RUSTLS_ENABLED" = "x1"; then + elif test "$RUSTLS_ENABLED" = "1"; then versioned_symbols_flavour="RUSTLS_" else versioned_symbols_flavour="" @@ -2747,19 +2765,15 @@ AS_HELP_STRING([--disable-versioned-symbols], [Disable versioned symbols in shar AC_SUBST([CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX], ["$versioned_symbols_flavour"]) AC_SUBST([CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME], ["4"]) dnl Keep in sync with VERSIONCHANGE - VERSIONDEL in lib/Makefile.soname AM_CONDITIONAL([CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS], - [test "x$versioned_symbols" = 'xyes']) + [test "$versioned_symbols" = "yes"]) dnl ---------------------------- dnl check Windows Unicode option dnl ---------------------------- -if test "$curl_cv_wince" = 'yes'; then - want_winuni="yes" -else - want_winuni="no" -fi +want_winuni="no" if test "$curl_cv_native_windows" = "yes"; then - if test "$curl_cv_winuwp" = 'yes'; then + if test "$curl_cv_winuwp" = "yes"; then want_winuni="yes" else AC_MSG_CHECKING([whether to enable Windows Unicode (Windows native builds only)]) @@ -2791,11 +2805,11 @@ dnl check WinIDN option before other IDN libraries dnl ------------------------------------------------- tst_links_winidn='no' -if test "$curl_cv_native_windows" = 'yes'; then +if test "$curl_cv_native_windows" = "yes"; then AC_MSG_CHECKING([whether to enable Windows native IDN (Windows native builds only)]) OPT_WINIDN="default" AC_ARG_WITH(winidn, -AS_HELP_STRING([--with-winidn=PATH],[enable Windows native IDN]) +AS_HELP_STRING([--with-winidn],[enable Windows native IDN]) AS_HELP_STRING([--without-winidn], [disable Windows native IDN]), OPT_WINIDN=$withval) case "$OPT_WINIDN" in @@ -2804,75 +2818,18 @@ AS_HELP_STRING([--without-winidn], [disable Windows native IDN]), want_winidn="no" AC_MSG_RESULT([no]) ;; - yes) - dnl --with-winidn option used without path - want_winidn="yes" - want_winidn_path="default" - AC_MSG_RESULT([yes]) - ;; *) - dnl --with-winidn option used with path + dnl --with-winidn option want_winidn="yes" - want_winidn_path="$withval" - AC_MSG_RESULT([yes ($withval)]) + AC_MSG_RESULT([yes]) ;; esac if test "$want_winidn" = "yes"; then - dnl WinIDN library support has been requested - clean_CPPFLAGS="$CPPFLAGS" - clean_LDFLAGS="$LDFLAGS" - clean_LDFLAGSPC="$LDFLAGSPC" - clean_LIBS="$LIBS" - WINIDN_LIBS="-lnormaliz" - WINIDN_CPPFLAGS="" - # - if test "$want_winidn_path" != "default"; then - dnl path has been specified - dnl pkg-config not available or provides no info - WINIDN_LDFLAGS="-L$want_winidn_path/lib$libsuff" - WINIDN_CPPFLAGS="-I$want_winidn_path/include" - fi - # - CPPFLAGS="$CPPFLAGS $WINIDN_CPPFLAGS" - LDFLAGS="$LDFLAGS $WINIDN_LDFLAGS" - LDFLAGSPC="$LDFLAGSPC $WINIDN_LDFLAGS" - LIBS="$WINIDN_LIBS $LIBS" - # - AC_MSG_CHECKING([if IdnToUnicode can be linked]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ - #include - ]],[[ - #if (!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600) && \ - (!defined(WINVER) || WINVER < 0x600) - WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags, - const WCHAR *lpASCIICharStr, - int cchASCIIChar, - WCHAR *lpUnicodeCharStr, - int cchUnicodeChar); - #endif - IdnToUnicode(0, NULL, 0, NULL, 0); - ]]) - ],[ - AC_MSG_RESULT([yes]) - tst_links_winidn="yes" - ],[ - AC_MSG_RESULT([no]) - tst_links_winidn="no" - ]) - # - if test "$tst_links_winidn" = "yes"; then - AC_DEFINE(USE_WIN32_IDN, 1, [Define to 1 if you have the `normaliz' (WinIDN) library (-lnormaliz).]) - IDN_ENABLED=1 - curl_idn_msg="enabled (Windows-native)" - else - AC_MSG_WARN([Cannot find libraries for IDN support: IDN disabled]) - CPPFLAGS="$clean_CPPFLAGS" - LDFLAGS="$clean_LDFLAGS" - LDFLAGSPC="$clean_LDFLAGSPC" - LIBS="$clean_LIBS" - fi + LIBS="-lnormaliz $LIBS" + AC_DEFINE(USE_WIN32_IDN, 1, [Define to 1 if you have the `normaliz' (WinIDN) library (-lnormaliz).]) + IDN_ENABLED=1 + curl_idn_msg="enabled (Windows-native)" fi fi @@ -2881,7 +2838,7 @@ dnl Check for the presence of AppleIDN dnl ********************************************************************** tst_links_appleidn='no' -if test "$curl_cv_apple" = 'yes'; then +if test "$curl_cv_apple" = "yes"; then AC_MSG_CHECKING([whether to build with Apple IDN]) OPT_IDN="default" AC_ARG_WITH(apple-idn, @@ -2920,10 +2877,10 @@ AC_ARG_WITH(libidn2, AS_HELP_STRING([--with-libidn2=PATH],[Enable libidn2 usage]) AS_HELP_STRING([--without-libidn2],[Disable libidn2 usage]), [OPT_IDN=$withval]) -if test "x$tst_links_winidn" = "xyes"; then +if test "$tst_links_winidn" = "yes"; then want_idn="no" AC_MSG_RESULT([no (using WinIDN instead)]) -elif test "x$tst_links_appleidn" = "xyes"; then +elif test "$tst_links_appleidn" = "yes"; then want_idn="no" AC_MSG_RESULT([no (using AppleIDN instead)]) else @@ -2961,17 +2918,17 @@ if test "$want_idn" = "yes"; then clean_LDFLAGSPC="$LDFLAGSPC" clean_LIBS="$LIBS" PKGCONFIG="no" - # + if test "$want_idn_path" != "default"; then dnl path has been specified IDN_PCDIR="$want_idn_path/lib$libsuff/pkgconfig" CURL_CHECK_PKGCONFIG(libidn2, [$IDN_PCDIR]) if test "$PKGCONFIG" != "no"; then - IDN_LIBS=`CURL_EXPORT_PCDIR([$IDN_PCDIR]) dnl + IDN_LIBS=`CURL_EXPORT_PCDIR([$IDN_PCDIR]) $PKGCONFIG --libs-only-l libidn2 2>/dev/null` - IDN_LDFLAGS=`CURL_EXPORT_PCDIR([$IDN_PCDIR]) dnl + IDN_LDFLAGS=`CURL_EXPORT_PCDIR([$IDN_PCDIR]) $PKGCONFIG --libs-only-L libidn2 2>/dev/null` - IDN_CPPFLAGS=`CURL_EXPORT_PCDIR([$IDN_PCDIR]) dnl + IDN_CPPFLAGS=`CURL_EXPORT_PCDIR([$IDN_PCDIR]) $PKGCONFIG --cflags-only-I libidn2 2>/dev/null` IDN_DIR=`echo $IDN_LDFLAGS | $SED -e 's/^-L//'` else @@ -2994,7 +2951,7 @@ if test "$want_idn" = "yes"; then IDN_LIBS="-lidn2" fi fi - # + if test "$PKGCONFIG" != "no"; then AC_MSG_NOTICE([pkg-config: IDN_LIBS: "$IDN_LIBS"]) AC_MSG_NOTICE([pkg-config: IDN_LDFLAGS: "$IDN_LDFLAGS"]) @@ -3006,12 +2963,12 @@ if test "$want_idn" = "yes"; then AC_MSG_NOTICE([IDN_CPPFLAGS: "$IDN_CPPFLAGS"]) AC_MSG_NOTICE([IDN_DIR: "$IDN_DIR"]) fi - # + CPPFLAGS="$CPPFLAGS $IDN_CPPFLAGS" LDFLAGS="$LDFLAGS $IDN_LDFLAGS" LDFLAGSPC="$LDFLAGSPC $IDN_LDFLAGS" LIBS="$IDN_LIBS $LIBS" - # + AC_MSG_CHECKING([if idn2_lookup_ul can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([idn2_lookup_ul]) @@ -3022,7 +2979,7 @@ if test "$want_idn" = "yes"; then AC_MSG_RESULT([no]) tst_links_libidn="no" ]) - # + AC_CHECK_HEADERS(idn2.h) if test "$tst_links_libidn" = "yes"; then @@ -3031,7 +2988,7 @@ if test "$want_idn" = "yes"; then IDN_ENABLED=1 curl_idn_msg="enabled (libidn2)" - if test -n "$IDN_DIR" -a "x$cross_compiling" != "xyes"; then + if test -n "$IDN_DIR" && test "$cross_compiling" != "yes"; then CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$IDN_DIR" export CURL_LIBRARY_PATH AC_MSG_NOTICE([Added $IDN_DIR to CURL_LIBRARY_PATH]) @@ -3053,8 +3010,8 @@ dnl ********************************************************************** OPT_H2="yes" -if test "x$disable_http" = "xyes"; then - # without HTTP nghttp2 is no use +if test "$disable_http" = "yes"; then + dnl without HTTP nghttp2 is no use OPT_H2="no" fi @@ -3077,11 +3034,11 @@ case "$OPT_H2" in dnl --with-nghttp2 option used with path want_nghttp2="yes" want_nghttp2_path="$withval" - want_nghttp2_pkg_config_path="$withval/lib/pkgconfig" + want_nghttp2_pkg_config_path="$OPT_H2/lib/pkgconfig" ;; esac -if test X"$want_nghttp2" != Xno; then +if test "$want_nghttp2" != "no"; then dnl backup the pre-nghttp2 variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" @@ -3095,7 +3052,7 @@ if test X"$want_nghttp2" != Xno; then $PKGCONFIG --libs-only-l libnghttp2` AC_MSG_NOTICE([-l is $LIB_H2]) - CPP_H2=`CURL_EXPORT_PCDIR([$want_nghttp2_pkg_config_path]) dnl + CPP_H2=`CURL_EXPORT_PCDIR([$want_nghttp2_pkg_config_path]) $PKGCONFIG --cflags-only-I libnghttp2` AC_MSG_NOTICE([-I is $CPP_H2]) @@ -3104,12 +3061,12 @@ if test X"$want_nghttp2" != Xno; then AC_MSG_NOTICE([-L is $LD_H2]) DIR_H2=`echo $LD_H2 | $SED -e 's/^-L//'` - elif test x"$want_nghttp2_path" != x; then + elif test -n "$want_nghttp2_path"; then LIB_H2="-lnghttp2" LD_H2=-L${want_nghttp2_path}/lib$libsuff CPP_H2=-I${want_nghttp2_path}/include DIR_H2=${want_nghttp2_path}/lib$libsuff - elif test X"$want_nghttp2" != Xdefault; then + elif test "$want_nghttp2" != "default"; then dnl no nghttp2 pkg-config found and no custom directory specified, dnl deal with it AC_MSG_ERROR([--with-nghttp2 was specified but could not find libnghttp2 pkg-config file.]) @@ -3122,8 +3079,8 @@ if test X"$want_nghttp2" != Xno; then CPPFLAGS="$CPPFLAGS $CPP_H2" LIBS="$LIB_H2 $LIBS" - # use nghttp2_session_get_stream_local_window_size to require nghttp2 - # >= 1.15.0 + dnl use nghttp2_session_get_stream_local_window_size to require nghttp2 + dnl >= 1.15.0 AC_CHECK_LIB(nghttp2, nghttp2_session_get_stream_local_window_size, [ AC_CHECK_HEADERS(nghttp2/nghttp2.h, @@ -3151,8 +3108,8 @@ dnl ********************************************************************** OPT_TCP2="no" -if test "x$disable_http" = "xyes"; then - # without HTTP, ngtcp2 is no use +if test "$disable_http" = "yes"; then + dnl without HTTP, ngtcp2 is no use OPT_TCP2="no" fi @@ -3173,12 +3130,12 @@ case "$OPT_TCP2" in *) dnl --with-ngtcp2 option used with path want_tcp2="yes" - want_tcp2_path="$withval/lib/pkgconfig" + want_tcp2_path="$OPT_TCP2/lib/pkgconfig" ;; esac curl_tcp2_msg="no (--with-ngtcp2)" -if test X"$want_tcp2" != Xno; then +if test "$want_tcp2" != "no"; then if test "$QUIC_ENABLED" != "yes"; then AC_MSG_ERROR([the detected TLS library does not support QUIC, making --with-ngtcp2 a no-no]) @@ -3197,7 +3154,7 @@ if test X"$want_tcp2" != Xno; then $PKGCONFIG --libs-only-l libngtcp2` AC_MSG_NOTICE([-l is $LIB_TCP2]) - CPP_TCP2=`CURL_EXPORT_PCDIR([$want_tcp2_path]) dnl + CPP_TCP2=`CURL_EXPORT_PCDIR([$want_tcp2_path]) $PKGCONFIG --cflags-only-I libngtcp2` AC_MSG_NOTICE([-I is $CPP_TCP2]) @@ -3210,7 +3167,7 @@ if test X"$want_tcp2" != Xno; then CPPFLAGS="$CPPFLAGS $CPP_TCP2" LIBS="$LIB_TCP2 $LIBS" - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then DIR_TCP2=`echo $LD_TCP2 | $SED -e 's/^-L//'` fi AC_CHECK_LIB(ngtcp2, ngtcp2_conn_client_new_versioned, @@ -3233,7 +3190,7 @@ if test X"$want_tcp2" != Xno; then else dnl no ngtcp2 pkg-config found, deal with it - if test X"$want_tcp2" != Xdefault; then + if test "$want_tcp2" != "default"; then dnl To avoid link errors, we do not allow --with-ngtcp2 without dnl a pkgconfig file AC_MSG_ERROR([--with-ngtcp2 was specified but could not find ngtcp2 pkg-config file.]) @@ -3241,25 +3198,26 @@ if test X"$want_tcp2" != Xno; then fi fi -if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "$HAVE_LIBRESSL" = "1"; then +if test "$USE_NGTCP2" = "1" && test "$OPENSSL_ENABLED" = "1" && test "$HAVE_LIBRESSL" = "1"; then + dnl backup the pre-ngtcp2_crypto_libressl variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" - CURL_CHECK_PKGCONFIG(libngtcp2_crypto_libressl, $want_tcp2_path) + CURL_CHECK_PKGCONFIG(libngtcp2_crypto_libressl, $want_tcp2_path, 1) if test "$PKGCONFIG" != "no"; then - LIB_NGTCP2_CRYPTO_LIBRESSL=`CURL_EXPORT_PCDIR([$want_tcp2_path]) + LIB_NGTCP2_CRYPTO_LIBRESSL=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --libs-only-l libngtcp2_crypto_libressl` AC_MSG_NOTICE([-l is $LIB_NGTCP2_CRYPTO_LIBRESSL]) - CPP_NGTCP2_CRYPTO_LIBRESSL=`CURL_EXPORT_PCDIR([$want_tcp2_path]) dnl + CPP_NGTCP2_CRYPTO_LIBRESSL=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --cflags-only-I libngtcp2_crypto_libressl` AC_MSG_NOTICE([-I is $CPP_NGTCP2_CRYPTO_LIBRESSL]) - LD_NGTCP2_CRYPTO_LIBRESSL=`CURL_EXPORT_PCDIR([$want_tcp2_path]) + LD_NGTCP2_CRYPTO_LIBRESSL=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --libs-only-L libngtcp2_crypto_libressl` AC_MSG_NOTICE([-L is $LD_NGTCP2_CRYPTO_LIBRESSL]) @@ -3268,7 +3226,7 @@ if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "$HAVE_LIBRESSL" CPPFLAGS="$CPPFLAGS $CPP_NGTCP2_CRYPTO_LIBRESSL" LIBS="$LIB_NGTCP2_CRYPTO_LIBRESSL $LIBS" - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then DIR_NGTCP2_CRYPTO_LIBRESSL=`echo $LD_NGTCP2_CRYPTO_LIBRESSL | $SED -e 's/^-L//'` fi AC_CHECK_LIB(ngtcp2_crypto_libressl, ngtcp2_crypto_recv_client_initial_cb, @@ -3290,7 +3248,7 @@ if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "$HAVE_LIBRESSL" else dnl no ngtcp2_crypto_libressl pkg-config found, deal with it - if test X"$want_tcp2" != Xdefault; then + if test "$want_tcp2" != "default"; then dnl To avoid link errors, we do not allow --with-ngtcp2 without dnl a pkgconfig file AC_MSG_WARN([--with-ngtcp2 was specified but could not find ngtcp2_crypto_libressl pkg-config file.]) @@ -3300,26 +3258,27 @@ if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "$HAVE_LIBRESSL" fi fi -if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "$HAVE_LIBRESSL" != "1" -a \ - "x$OPENSSL_IS_BORINGSSL" != "x1" -a "x$OPENSSL_QUIC_API2" != "x1"; then +if test "$USE_NGTCP2" = "1" && test "$OPENSSL_ENABLED" = "1" && test "$HAVE_LIBRESSL" != "1" && + test "$OPENSSL_IS_AWSLC" != "1" && test "$OPENSSL_IS_BORINGSSL" != "1" && test "$OPENSSL_QUIC_API2" != "1"; then + dnl backup the pre-ngtcp2_crypto_quictls variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" - CURL_CHECK_PKGCONFIG(libngtcp2_crypto_quictls, $want_tcp2_path) + CURL_CHECK_PKGCONFIG(libngtcp2_crypto_quictls, $want_tcp2_path, 1) if test "$PKGCONFIG" != "no"; then - LIB_NGTCP2_CRYPTO_QUICTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path]) + LIB_NGTCP2_CRYPTO_QUICTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --libs-only-l libngtcp2_crypto_quictls` AC_MSG_NOTICE([-l is $LIB_NGTCP2_CRYPTO_QUICTLS]) - CPP_NGTCP2_CRYPTO_QUICTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path]) dnl + CPP_NGTCP2_CRYPTO_QUICTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --cflags-only-I libngtcp2_crypto_quictls` AC_MSG_NOTICE([-I is $CPP_NGTCP2_CRYPTO_QUICTLS]) - LD_NGTCP2_CRYPTO_QUICTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path]) + LD_NGTCP2_CRYPTO_QUICTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --libs-only-L libngtcp2_crypto_quictls` AC_MSG_NOTICE([-L is $LD_NGTCP2_CRYPTO_QUICTLS]) @@ -3328,7 +3287,7 @@ if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "$HAVE_LIBRESSL" CPPFLAGS="$CPPFLAGS $CPP_NGTCP2_CRYPTO_QUICTLS" LIBS="$LIB_NGTCP2_CRYPTO_QUICTLS $LIBS" - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then DIR_NGTCP2_CRYPTO_QUICTLS=`echo $LD_NGTCP2_CRYPTO_QUICTLS | $SED -e 's/^-L//'` fi AC_CHECK_LIB(ngtcp2_crypto_quictls, ngtcp2_crypto_recv_client_initial_cb, @@ -3350,7 +3309,7 @@ if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "$HAVE_LIBRESSL" else dnl no ngtcp2_crypto_quictls pkg-config found, deal with it - if test X"$want_tcp2" != Xdefault; then + if test "$want_tcp2" != "default"; then dnl To avoid link errors, we do not allow --with-ngtcp2 without dnl a pkgconfig file AC_MSG_ERROR([--with-ngtcp2 was specified but could not find ngtcp2_crypto_quictls pkg-config file.]) @@ -3358,26 +3317,27 @@ if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "$HAVE_LIBRESSL" fi fi -if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a \ - "x$OPENSSL_IS_BORINGSSL" != "x1" -a "x$OPENSSL_QUIC_API2" = "x1"; then +if test "$USE_NGTCP2" = "1" && test "$OPENSSL_ENABLED" = "1" && + test "$OPENSSL_IS_AWSLC" != "1" && test "$OPENSSL_IS_BORINGSSL" != "1" && test "$OPENSSL_QUIC_API2" = "1"; then + dnl backup the pre-ngtcp2_crypto_ossl variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" - CURL_CHECK_PKGCONFIG(libngtcp2_crypto_ossl, $want_tcp2_path) + CURL_CHECK_PKGCONFIG(libngtcp2_crypto_ossl, $want_tcp2_path, 1) if test "$PKGCONFIG" != "no"; then - LIB_NGTCP2_CRYPTO_OSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path]) + LIB_NGTCP2_CRYPTO_OSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --libs-only-l libngtcp2_crypto_ossl` AC_MSG_NOTICE([-l is $LIB_NGTCP2_CRYPTO_OSSL]) - CPP_NGTCP2_CRYPTO_OSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path]) dnl + CPP_NGTCP2_CRYPTO_OSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --cflags-only-I libngtcp2_crypto_ossl` AC_MSG_NOTICE([-I is $CPP_NGTCP2_CRYPTO_OSSL]) - LD_NGTCP2_CRYPTO_OSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path]) + LD_NGTCP2_CRYPTO_OSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --libs-only-L libngtcp2_crypto_ossl` AC_MSG_NOTICE([-L is $LD_NGTCP2_CRYPTO_OSSL]) @@ -3386,7 +3346,7 @@ if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a \ CPPFLAGS="$CPPFLAGS $CPP_NGTCP2_CRYPTO_OSSL" LIBS="$LIB_NGTCP2_CRYPTO_OSSL $LIBS" - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then DIR_NGTCP2_CRYPTO_OSSL=`echo $LD_NGTCP2_CRYPTO_OSSL | $SED -e 's/^-L//'` fi AC_CHECK_LIB(ngtcp2_crypto_ossl, ngtcp2_crypto_recv_client_initial_cb, @@ -3409,7 +3369,7 @@ if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a \ else dnl no ngtcp2_crypto_ossl pkg-config found, deal with it - if test X"$want_tcp2" != Xdefault; then + if test "$want_tcp2" != "default"; then dnl To avoid link errors, we do not allow --with-ngtcp2 without dnl a pkgconfig file AC_MSG_ERROR([--with-ngtcp2 was specified but could not find ngtcp2_crypto_ossl pkg-config file.]) @@ -3417,25 +3377,27 @@ if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a \ fi fi -if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "x$OPENSSL_IS_BORINGSSL" = "x1"; then +if test "$USE_NGTCP2" = "1" && test "$OPENSSL_ENABLED" = "1" && + (test "$OPENSSL_IS_AWSLC" = "1" || test "$OPENSSL_IS_BORINGSSL" = "1"); then + dnl backup the pre-ngtcp2_crypto_boringssl variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" - CURL_CHECK_PKGCONFIG(libngtcp2_crypto_boringssl, $want_tcp2_path) + CURL_CHECK_PKGCONFIG(libngtcp2_crypto_boringssl, $want_tcp2_path, 1) if test "$PKGCONFIG" != "no"; then - LIB_NGTCP2_CRYPTO_BORINGSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path]) + LIB_NGTCP2_CRYPTO_BORINGSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --libs-only-l libngtcp2_crypto_boringssl` AC_MSG_NOTICE([-l is $LIB_NGTCP2_CRYPTO_BORINGSSL]) - CPP_NGTCP2_CRYPTO_BORINGSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path]) dnl + CPP_NGTCP2_CRYPTO_BORINGSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --cflags-only-I libngtcp2_crypto_boringssl` AC_MSG_NOTICE([-I is $CPP_NGTCP2_CRYPTO_BORINGSSL]) - LD_NGTCP2_CRYPTO_BORINGSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path]) + LD_NGTCP2_CRYPTO_BORINGSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --libs-only-L libngtcp2_crypto_boringssl` AC_MSG_NOTICE([-L is $LD_NGTCP2_CRYPTO_BORINGSSL]) @@ -3444,7 +3406,7 @@ if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "x$OPENSSL_IS_BOR CPPFLAGS="$CPPFLAGS $CPP_NGTCP2_CRYPTO_BORINGSSL" LIBS="$LIB_NGTCP2_CRYPTO_BORINGSSL $LIBS" - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then DIR_NGTCP2_CRYPTO_BORINGSSL=`echo $LD_NGTCP2_CRYPTO_BORINGSSL | $SED -e 's/^-L//'` fi AC_CHECK_LIB(ngtcp2_crypto_boringssl, ngtcp2_crypto_recv_client_initial_cb, @@ -3466,7 +3428,7 @@ if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "x$OPENSSL_IS_BOR else dnl no ngtcp2_crypto_boringssl pkg-config found, deal with it - if test X"$want_tcp2" != Xdefault; then + if test "$want_tcp2" != "default"; then dnl To avoid link errors, we do not allow --with-ngtcp2 without dnl a pkgconfig file AC_MSG_ERROR([--with-ngtcp2 was specified but could not find ngtcp2_crypto_boringssl pkg-config file.]) @@ -3474,25 +3436,25 @@ if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "x$OPENSSL_IS_BOR fi fi -if test "x$USE_NGTCP2" = "x1" -a "x$GNUTLS_ENABLED" = "x1"; then +if test "$USE_NGTCP2" = "1" && test "$GNUTLS_ENABLED" = "1"; then dnl backup the pre-ngtcp2_crypto_gnutls variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" - CURL_CHECK_PKGCONFIG(libngtcp2_crypto_gnutls, $want_tcp2_path) + CURL_CHECK_PKGCONFIG(libngtcp2_crypto_gnutls, $want_tcp2_path, 1) if test "$PKGCONFIG" != "no"; then - LIB_NGTCP2_CRYPTO_GNUTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path]) + LIB_NGTCP2_CRYPTO_GNUTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --libs-only-l libngtcp2_crypto_gnutls` AC_MSG_NOTICE([-l is $LIB_NGTCP2_CRYPTO_GNUTLS]) - CPP_NGTCP2_CRYPTO_GNUTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path]) dnl + CPP_NGTCP2_CRYPTO_GNUTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --cflags-only-I libngtcp2_crypto_gnutls` AC_MSG_NOTICE([-I is $CPP_NGTCP2_CRYPTO_GNUTLS]) - LD_NGTCP2_CRYPTO_GNUTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path]) + LD_NGTCP2_CRYPTO_GNUTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --libs-only-L libngtcp2_crypto_gnutls` AC_MSG_NOTICE([-L is $LD_NGTCP2_CRYPTO_GNUTLS]) @@ -3501,7 +3463,7 @@ if test "x$USE_NGTCP2" = "x1" -a "x$GNUTLS_ENABLED" = "x1"; then CPPFLAGS="$CPPFLAGS $CPP_NGTCP2_CRYPTO_GNUTLS" LIBS="$LIB_NGTCP2_CRYPTO_GNUTLS $LIBS" - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then DIR_NGTCP2_CRYPTO_GNUTLS=`echo $LD_NGTCP2_CRYPTO_GNUTLS | $SED -e 's/^-L//'` fi AC_CHECK_LIB(ngtcp2_crypto_gnutls, ngtcp2_crypto_recv_client_initial_cb, @@ -3523,7 +3485,7 @@ if test "x$USE_NGTCP2" = "x1" -a "x$GNUTLS_ENABLED" = "x1"; then else dnl no ngtcp2_crypto_gnutls pkg-config found, deal with it - if test X"$want_tcp2" != Xdefault; then + if test "$want_tcp2" != "default"; then dnl To avoid link errors, we do not allow --with-ngtcp2 without dnl a pkgconfig file AC_MSG_ERROR([--with-ngtcp2 was specified but could not find ngtcp2_crypto_gnutls pkg-config file.]) @@ -3531,25 +3493,25 @@ if test "x$USE_NGTCP2" = "x1" -a "x$GNUTLS_ENABLED" = "x1"; then fi fi -if test "x$USE_NGTCP2" = "x1" -a "x$WOLFSSL_ENABLED" = "x1"; then +if test "$USE_NGTCP2" = "1" && test "$WOLFSSL_ENABLED" = "1"; then dnl backup the pre-ngtcp2_crypto_wolfssl variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" - CURL_CHECK_PKGCONFIG(libngtcp2_crypto_wolfssl, $want_tcp2_path) + CURL_CHECK_PKGCONFIG(libngtcp2_crypto_wolfssl, $want_tcp2_path, 1) if test "$PKGCONFIG" != "no"; then - LIB_NGTCP2_CRYPTO_WOLFSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path]) + LIB_NGTCP2_CRYPTO_WOLFSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --libs-only-l libngtcp2_crypto_wolfssl` AC_MSG_NOTICE([-l is $LIB_NGTCP2_CRYPTO_WOLFSSL]) - CPP_NGTCP2_CRYPTO_WOLFSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path]) dnl + CPP_NGTCP2_CRYPTO_WOLFSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --cflags-only-I libngtcp2_crypto_wolfssl` AC_MSG_NOTICE([-I is $CPP_NGTCP2_CRYPTO_WOLFSSL]) - LD_NGTCP2_CRYPTO_WOLFSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path]) + LD_NGTCP2_CRYPTO_WOLFSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path], 1) $PKGCONFIG --libs-only-L libngtcp2_crypto_wolfssl` AC_MSG_NOTICE([-L is $LD_NGTCP2_CRYPTO_WOLFSSL]) @@ -3558,7 +3520,7 @@ if test "x$USE_NGTCP2" = "x1" -a "x$WOLFSSL_ENABLED" = "x1"; then CPPFLAGS="$CPPFLAGS $CPP_NGTCP2_CRYPTO_WOLFSSL" LIBS="$LIB_NGTCP2_CRYPTO_WOLFSSL $LIBS" - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then DIR_NGTCP2_CRYPTO_WOLFSSL=`echo $LD_NGTCP2_CRYPTO_WOLFSSL | $SED -e 's/^-L//'` fi AC_CHECK_LIB(ngtcp2_crypto_wolfssl, ngtcp2_crypto_recv_client_initial_cb, @@ -3580,7 +3542,7 @@ if test "x$USE_NGTCP2" = "x1" -a "x$WOLFSSL_ENABLED" = "x1"; then else dnl no ngtcp2_crypto_wolfssl pkg-config found, deal with it - if test X"$want_tcp2" != Xdefault; then + if test "$want_tcp2" != "default"; then dnl To avoid link errors, we do not allow --with-ngtcp2 without dnl a pkgconfig file AC_MSG_ERROR([--with-ngtcp2 was specified but could not find ngtcp2_crypto_wolfssl pkg-config file.]) @@ -3588,53 +3550,14 @@ if test "x$USE_NGTCP2" = "x1" -a "x$WOLFSSL_ENABLED" = "x1"; then fi fi -dnl ********************************************************************** -dnl Check for OpenSSL QUIC -dnl ********************************************************************** - -OPT_OPENSSL_QUIC="no" - -if test "x$disable_http" = "xyes" -o "x$OPENSSL_ENABLED" != "x1"; then - # without HTTP or without openssl, no use - OPT_OPENSSL_QUIC="no" -fi - -AC_ARG_WITH(openssl-quic, -AS_HELP_STRING([--with-openssl-quic],[Enable OpenSSL QUIC usage]) -AS_HELP_STRING([--without-openssl-quic],[Disable OpenSSL QUIC usage]), - [OPT_OPENSSL_QUIC=$withval]) -case "$OPT_OPENSSL_QUIC" in - no) - dnl --without-openssl-quic option used - want_openssl_quic="no" - ;; - yes) - dnl --with-openssl-quic option used - want_openssl_quic="yes" - ;; -esac - -curl_openssl_quic_msg="no (--with-openssl-quic)" -if test "x$want_openssl_quic" = "xyes"; then - - if test "$USE_NGTCP2" = 1; then - AC_MSG_ERROR([--with-openssl-quic and --with-ngtcp2 are mutually exclusive]) - fi - if test "$have_openssl_quic" != 1; then - AC_MSG_ERROR([--with-openssl-quic requires quic support and OpenSSL >= 3.3.0]) - fi - AC_DEFINE(USE_OPENSSL_QUIC, 1, [if openssl QUIC is in use]) - USE_OPENSSL_QUIC=1 -fi - dnl ********************************************************************** dnl Check for nghttp3 (HTTP/3 with ngtcp2) dnl ********************************************************************** OPT_NGHTTP3="yes" -if test "x$USE_NGTCP2" != "x1" -a "x$USE_OPENSSL_QUIC" != "x1"; then - # without ngtcp2 or openssl quic, nghttp3 is of no use for us +if test "$USE_NGTCP2" != "1"; then + dnl without ngtcp2, nghttp3 is of no use for us OPT_NGHTTP3="no" want_nghttp3="no" fi @@ -3656,16 +3579,16 @@ case "$OPT_NGHTTP3" in *) dnl --with-nghttp3 option used with path want_nghttp3="yes" - want_nghttp3_path="$withval/lib/pkgconfig" + want_nghttp3_path="$OPT_NGHTTP3/lib/pkgconfig" ;; esac curl_http3_msg="no (--with-nghttp3)" -if test X"$want_nghttp3" != Xno; then +if test "$want_nghttp3" != "no"; then - if test "x$USE_NGTCP2" != "x1" -a "x$USE_OPENSSL_QUIC" != "x1"; then - # without ngtcp2 or openssl quic, nghttp3 is of no use for us - AC_MSG_ERROR([nghttp3 enabled without a QUIC library; enable ngtcp2 or OpenSSL-QUIC]) + if test "x$USE_NGTCP2" != "x1"; then + dnl without ngtcp2, nghttp3 is of no use for us + AC_MSG_ERROR([nghttp3 enabled without a QUIC library; enable ngtcp2]) fi dnl backup the pre-nghttp3 variables @@ -3681,7 +3604,7 @@ if test X"$want_nghttp3" != Xno; then $PKGCONFIG --libs-only-l libnghttp3` AC_MSG_NOTICE([-l is $LIB_NGHTTP3]) - CPP_NGHTTP3=`CURL_EXPORT_PCDIR([$want_nghttp3_path]) dnl + CPP_NGHTTP3=`CURL_EXPORT_PCDIR([$want_nghttp3_path]) $PKGCONFIG --cflags-only-I libnghttp3` AC_MSG_NOTICE([-I is $CPP_NGHTTP3]) @@ -3694,7 +3617,7 @@ if test X"$want_nghttp3" != Xno; then CPPFLAGS="$CPPFLAGS $CPP_NGHTTP3" LIBS="$LIB_NGHTTP3 $LIBS" - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then DIR_NGHTTP3=`echo $LD_NGHTTP3 | $SED -e 's/^-L//'` fi AC_CHECK_LIB(nghttp3, nghttp3_conn_client_new_versioned, @@ -3714,10 +3637,9 @@ if test X"$want_nghttp3" != Xno; then CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS ) - else dnl no nghttp3 pkg-config found, deal with it - if test X"$want_nghttp3" != Xdefault; then + if test "$want_nghttp3" != "default"; then dnl To avoid link errors, we do not allow --with-nghttp3 without dnl a pkgconfig file AC_MSG_ERROR([--with-nghttp3 was specified but could not find nghttp3 pkg-config file.]) @@ -3729,31 +3651,19 @@ dnl ********************************************************************** dnl Check for ngtcp2 and nghttp3 (HTTP/3 with ngtcp2 + nghttp3) dnl ********************************************************************** -if test "x$USE_NGTCP2" = "x1" -a "x$USE_NGHTTP3" = "x1"; then +if test "$USE_NGTCP2" = "1" && test "$USE_NGHTTP3" = "1"; then USE_NGTCP2_H3=1 - AC_MSG_NOTICE([HTTP3 support is experimental]) curl_h3_msg="enabled (ngtcp2 + nghttp3)" fi -dnl ********************************************************************** -dnl Check for OpenSSL and nghttp3 (HTTP/3 with nghttp3 using OpenSSL QUIC) -dnl ********************************************************************** - -if test "x$USE_OPENSSL_QUIC" = "x1" -a "x$USE_NGHTTP3" = "x1"; then - experimental="$experimental HTTP3" - USE_OPENSSL_H3=1 - AC_MSG_NOTICE([HTTP3 support is experimental]) - curl_h3_msg="enabled (openssl + nghttp3)" -fi - dnl ********************************************************************** dnl Check for quiche (QUIC) dnl ********************************************************************** OPT_QUICHE="no" -if test "x$disable_http" = "xyes" -o "x$USE_NGTCP" = "x1"; then - # without HTTP or with ngtcp2, quiche is no use +if test "$disable_http" = "yes" || test "$USE_NGTCP" = "1"; then + dnl without HTTP or with ngtcp2, quiche is no use OPT_QUICHE="no" fi @@ -3778,13 +3688,13 @@ case "$OPT_QUICHE" in ;; esac -if test X"$want_quiche" != Xno; then +if test "$want_quiche" != "no"; then if test "$QUIC_ENABLED" != "yes"; then AC_MSG_ERROR([the detected TLS library does not support QUIC, making --with-quiche a no-no]) fi - if test "$NGHTTP3_ENABLED" = 1; then + if test "$NGHTTP3_ENABLED" = "1"; then AC_MSG_ERROR([--with-quiche and --with-ngtcp2 are mutually exclusive]) fi @@ -3801,7 +3711,7 @@ if test X"$want_quiche" != Xno; then $PKGCONFIG --libs-only-l quiche` AC_MSG_NOTICE([-l is $LIB_QUICHE]) - CPP_QUICHE=`CURL_EXPORT_PCDIR([$want_quiche_path]) dnl + CPP_QUICHE=`CURL_EXPORT_PCDIR([$want_quiche_path]) $PKGCONFIG --cflags-only-I quiche` AC_MSG_NOTICE([-I is $CPP_QUICHE]) @@ -3814,7 +3724,7 @@ if test X"$want_quiche" != Xno; then CPPFLAGS="$CPPFLAGS $CPP_QUICHE" LIBS="$LIB_QUICHE $LIBS" - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then DIR_QUICHE=`echo $LD_QUICHE | $SED -e 's/^-L//'` fi AC_CHECK_LIB(quiche, quiche_conn_send_ack_eliciting, @@ -3838,11 +3748,11 @@ if test X"$want_quiche" != Xno; then ) ], dnl not found, revert back to clean variables - AC_MSG_ERROR([couldn't use quiche]) + AC_MSG_ERROR([could not use quiche]) ) else dnl no quiche pkg-config found, deal with it - if test X"$want_quiche" != Xdefault; then + if test "$want_quiche" != "default"; then dnl To avoid link errors, we do not allow --with-quiche without dnl a pkgconfig file AC_MSG_ERROR([--with-quiche was specified but could not find quiche pkg-config file.]) @@ -3876,8 +3786,8 @@ case "$OPT_LIBUV" in ;; esac -if test X"$want_libuv" != Xno; then - if test x$want_debug != xyes; then +if test "$want_libuv" != "no"; then + if test "$want_debug" != "yes"; then AC_MSG_ERROR([Using libuv without debug support enabled is useless]) fi @@ -3894,7 +3804,7 @@ if test X"$want_libuv" != Xno; then $PKGCONFIG --libs-only-l libuv` AC_MSG_NOTICE([-l is $LIB_LIBUV]) - CPP_LIBUV=`CURL_EXPORT_PCDIR([$want_libuv_path]) dnl + CPP_LIBUV=`CURL_EXPORT_PCDIR([$want_libuv_path]) $PKGCONFIG --cflags-only-I libuv` AC_MSG_NOTICE([-I is $CPP_LIBUV]) @@ -3907,7 +3817,7 @@ if test X"$want_libuv" != Xno; then CPPFLAGS="$CPPFLAGS $CPP_LIBUV" LIBS="$LIB_LIBUV $LIBS" - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then DIR_LIBUV=`echo $LD_LIBUV | $SED -e 's/^-L//'` fi AC_CHECK_LIB(uv, uv_default_loop, @@ -3927,10 +3837,9 @@ if test X"$want_libuv" != Xno; then CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS ) - else dnl no libuv pkg-config found, deal with it - if test X"$want_libuv" != Xdefault; then + if test "$want_libuv" != "default"; then dnl To avoid link errors, we do not allow --with-libuv without dnl a pkgconfig file AC_MSG_ERROR([--with-libuv was specified but could not find libuv pkg-config file.]) @@ -3963,11 +3872,11 @@ case "$OPT_ZSH_FPATH" in AC_SUBST(ZSH_FUNCTIONS_DIR) ;; esac -if test -z "$PERL" -a x"$ZSH_FUNCTIONS_DIR" != x; then +if test -z "$PERL" && test -n "$ZSH_FUNCTIONS_DIR"; then AC_MSG_WARN([perl was not found. Will not install zsh completions.]) ZSH_FUNCTIONS_DIR='' fi -AM_CONDITIONAL(USE_ZSH_COMPLETION, test x"$ZSH_FUNCTIONS_DIR" != x) +AM_CONDITIONAL(USE_ZSH_COMPLETION, test -n "$ZSH_FUNCTIONS_DIR") dnl ********************************************************************** dnl Check for fish completion path @@ -3998,13 +3907,13 @@ case "$OPT_FISH_FPATH" in AC_SUBST(FISH_FUNCTIONS_DIR) ;; esac -if test -z "$PERL" -a x"$FISH_FUNCTIONS_DIR" != x; then +if test -z "$PERL" && test -n "$FISH_FUNCTIONS_DIR"; then AC_MSG_WARN([perl was not found. Will not install fish completions.]) FISH_FUNCTIONS_DIR='' fi -AM_CONDITIONAL(USE_FISH_COMPLETION, test x"$FISH_FUNCTIONS_DIR" != x) +AM_CONDITIONAL(USE_FISH_COMPLETION, test -n "$FISH_FUNCTIONS_DIR") -dnl Now check for the very most basic headers. Then we can use these +dnl Now check for the most basic headers. Then we can use these dnl ones as default-headers when checking for the rest! AC_CHECK_HEADERS( sys/types.h \ @@ -4035,14 +3944,11 @@ AC_CHECK_HEADERS( libgen.h \ locale.h \ stdbool.h \ - stdint.h \ sys/filio.h \ sys/eventfd.h, -dnl to do if not found [], -dnl to do if found [], -dnl default includes +/* default includes */ [ #include #ifdef HAVE_SYS_TYPES_H @@ -4071,7 +3977,6 @@ dnl default includes ] ) - dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_TYPE_SIZE_T @@ -4095,21 +4000,15 @@ CURL_SIZEOF(curl_socket_t, [ ]) CPPFLAGS=$o -AC_CHECK_TYPE(long long, - [AC_DEFINE(HAVE_LONGLONG, 1, - [Define to 1 if the compiler supports the 'long long' data type.])] - longlong="yes" -) - -if test ${ac_cv_sizeof_curl_off_t} -lt 8; then - AC_MSG_ERROR([64 bit curl_off_t is required]) +if test "$ac_cv_sizeof_curl_off_t" -lt 8; then + AC_MSG_ERROR([64-bit curl_off_t is required]) fi -# check for ssize_t +dnl check for ssize_t AC_CHECK_TYPE(ssize_t, , AC_DEFINE(ssize_t, int, [the signed version of size_t])) -# check for bool type +dnl check for bool type AC_CHECK_TYPE([bool],[ AC_DEFINE(HAVE_BOOL_T, 1, [Define to 1 if bool is an available type.]) @@ -4122,8 +4021,8 @@ AC_CHECK_TYPE([bool],[ #endif ]) -if test "$curl_cv_native_windows" != 'yes'; then - # check for sa_family_t +if test "$curl_cv_native_windows" != "yes"; then + dnl check for sa_family_t AC_CHECK_TYPE(sa_family_t, AC_DEFINE(HAVE_SA_FAMILY_T, 1, [Define to 1 if symbol `sa_family_t' exists]),, [ @@ -4131,7 +4030,7 @@ if test "$curl_cv_native_windows" != 'yes'; then ]) fi -# check for suseconds_t +dnl check for suseconds_t AC_CHECK_TYPE([suseconds_t],[ AC_DEFINE(HAVE_SUSECONDS_T, 1, [Define to 1 if suseconds_t is an available type.]) @@ -4175,7 +4074,6 @@ CURL_CHECK_FUNC_SELECT CURL_CHECK_FUNC_RECV CURL_CHECK_FUNC_SEND -CURL_CHECK_MSG_NOSIGNAL CURL_CHECK_FUNC_ALARM CURL_CHECK_FUNC_BASENAME @@ -4184,7 +4082,6 @@ CURL_CHECK_FUNC_CLOSESOCKET_CAMEL CURL_CHECK_FUNC_FCNTL CURL_CHECK_FUNC_FREEADDRINFO CURL_CHECK_FUNC_FSETXATTR -CURL_CHECK_FUNC_FTRUNCATE CURL_CHECK_FUNC_GETADDRINFO CURL_CHECK_FUNC_GETHOSTBYNAME_R CURL_CHECK_FUNC_GETHOSTNAME @@ -4195,6 +4092,7 @@ CURL_CHECK_FUNC_GMTIME_R CURL_CHECK_FUNC_IOCTL CURL_CHECK_FUNC_IOCTLSOCKET CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL +CURL_CHECK_FUNC_LOCALTIME_R CURL_CHECK_FUNC_MEMRCHR CURL_CHECK_FUNC_SIGACTION CURL_CHECK_FUNC_SIGINTERRUPT @@ -4202,7 +4100,6 @@ CURL_CHECK_FUNC_SIGNAL CURL_CHECK_FUNC_SIGSETJMP CURL_CHECK_FUNC_SOCKET CURL_CHECK_FUNC_SOCKETPAIR -CURL_CHECK_FUNC_STRDUP CURL_CHECK_FUNC_STRERROR_R case $host in @@ -4226,37 +4123,25 @@ AC_CHECK_FUNCS([\ gettimeofday \ mach_absolute_time \ pipe \ - pipe2 \ poll \ sendmmsg \ sendmsg \ setlocale \ setrlimit \ - snprintf \ utime \ utimes \ ]) -if test "$curl_cv_native_windows" = 'yes'; then - AC_MSG_CHECKING([for if_nametoindex on Windows]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #include - #include /* workaround for mingw-w64 __MINGW64_VERSION_MAJOR <= 5 header bug */ - #include - ]],[[ - if_nametoindex(""); - ]]) - ],[ - AC_MSG_RESULT([yes]) - AC_DEFINE(HAVE_IF_NAMETOINDEX, 1, [if you have the 'if_nametoindex' function]) - ],[ - AC_MSG_RESULT([no]) +if test "$curl_cv_apple" != "yes"; then + dnl Apple platforms do not offer pipe2(), but the iPhone Simulator-specific + dnl /usr/lib/system/libsystem_sim_kernel.dylib exports it. To avoid false + dnl detection, omit this feature check for Apple targets. + AC_CHECK_FUNCS([\ + pipe2 \ ]) -else +fi + +if test "$curl_cv_native_windows" != "yes"; then AC_CHECK_FUNCS([\ if_nametoindex \ realpath \ @@ -4269,18 +4154,11 @@ else CURL_CHECK_FUNC_STRICMP fi -if test "$curl_cv_wince" = 'no'; then - AC_CHECK_FUNCS([setmode]) - if test "$curl_cv_native_windows" = 'yes' -o "$curl_cv_cygwin" = 'yes'; then - AC_CHECK_FUNCS([_setmode]) - fi -fi - if test -z "$ssl_backends"; then AC_CHECK_FUNCS([arc4random]) fi -if test "$curl_cv_native_windows" != 'yes'; then +if test "$curl_cv_native_windows" != "yes"; then AC_CHECK_FUNCS([fseeko]) dnl On Android, the only way to know if fseeko can be used is to see if it is @@ -4296,7 +4174,7 @@ fi CURL_CHECK_NONBLOCKING_SOCKET dnl set variable for use in automakefile(s) -AM_CONDITIONAL(BUILD_DOCS, test x"$BUILD_DOCS" = x1) +AM_CONDITIONAL(BUILD_DOCS, test "$BUILD_DOCS" = "1") dnl ************************************************************************* dnl If the manual variable still is set, then we go with providing a built-in @@ -4307,26 +4185,19 @@ if test "$USE_MANUAL" = "1"; then fi dnl set variable for use in automakefile(s) -AM_CONDITIONAL(USE_MANUAL, test x"$USE_MANUAL" = x1) +AM_CONDITIONAL(USE_MANUAL, test "$USE_MANUAL" = "1") CURL_CHECK_LIB_ARES CURL_CHECK_OPTION_THREADED_RESOLVER -if test "$ipv6" = yes -a "$curl_cv_apple" = 'yes'; then +if test "$ipv6" = "yes" && test "$curl_cv_apple" = "yes"; then CURL_DARWIN_SYSTEMCONFIGURATION fi -dnl Windows threaded resolver check -if test "$want_threaded_resolver" = "yes" && test "$curl_cv_native_windows" = "yes"; then - USE_THREADS_WIN32=1 - AC_DEFINE(USE_THREADS_WIN32, 1, [if you want Win32 threaded DNS lookup]) - curl_res_msg="Win32 threaded" -fi - dnl detect pthreads -if test "$want_threaded_resolver" = "yes" && test "$USE_THREADS_WIN32" != "1"; then +if test "$curl_cv_native_windows" != "yes"; then AC_CHECK_HEADER(pthread.h, - [ AC_DEFINE(HAVE_PTHREAD_H, 1, [if you have ]) + [ save_CFLAGS="$CFLAGS" dnl When statically linking against BoringSSL, -lpthread is added to LIBS. dnl Make sure to that this does not pass the check below, we really want @@ -4337,29 +4208,27 @@ if test "$want_threaded_resolver" = "yes" && test "$USE_THREADS_WIN32" != "1"; t LIBS= dnl Check for libc variants without a separate pthread lib like bionic - AC_CHECK_FUNC(pthread_create, [USE_THREADS_POSIX=1] ) + AC_CHECK_FUNC(pthread_create, [HAVE_THREADS_POSIX=1] ) LIBS="$save_LIBS" - dnl on HP-UX, life is more complicated... case $host in *-hp-hpux*) - dnl it doesn't actually work without -lpthread - USE_THREADS_POSIX="" + CFLAGS="$CFLAGS -mt" ;; *) ;; esac - dnl if it wasn't found without lib, search for it in pthread lib - if test "$USE_THREADS_POSIX" != "1"; then - # assign PTHREAD for pkg-config use + dnl if it was not found without lib, search for it in pthread lib + if test "$HAVE_THREADS_POSIX" != "1"; then + dnl assign PTHREAD for pkg-config use PTHREAD=" -pthread" case $host in *-ibm-aix*) dnl Check if compiler is xlC COMPILER_VERSION=`"$CC" -qversion 2>/dev/null` - if test x"$COMPILER_VERSION" = "x"; then + if test -z "$COMPILER_VERSION"; then CFLAGS="$CFLAGS -pthread" else CFLAGS="$CFLAGS -qthreaded" @@ -4374,20 +4243,32 @@ if test "$want_threaded_resolver" = "yes" && test "$USE_THREADS_WIN32" != "1"; t ;; esac AC_CHECK_LIB(pthread, pthread_create, - [USE_THREADS_POSIX=1], + [HAVE_THREADS_POSIX=1], [ CFLAGS="$save_CFLAGS"]) fi - - if test "x$USE_THREADS_POSIX" = "x1"; then - AC_DEFINE(USE_THREADS_POSIX, 1, [if you want POSIX threaded DNS lookup]) - curl_res_msg="POSIX threaded" - fi ]) + if test "$HAVE_THREADS_POSIX" = "1"; then + AC_DEFINE(HAVE_THREADS_POSIX, 1, [if POSIX pthreads are supported]) + fi fi -dnl Did we find a threading option? -if test "$want_threaded_resolver" != "no" -a "x$USE_THREADS_POSIX" != "x1" -a "x$USE_THREADS_WIN32" != "x1"; then - AC_MSG_ERROR([Threaded resolver enabled but no thread library found]) +dnl threaded resolver check +if test "$want_threaded_resolver" = "yes"; then + if test "$curl_cv_native_windows" = "yes"; then + USE_RESOLV_THREADED=1 + AC_DEFINE(USE_RESOLV_THREADED, 1, [if you want threaded DNS lookup]) + curl_res_msg="Win32 threaded" + elif test "$HAVE_THREADS_POSIX" = "1"; then + USE_RESOLV_THREADED=1 + AC_DEFINE(USE_RESOLV_THREADED, 1, [if you want threaded DNS lookup]) + curl_res_msg="POSIX threaded" + else + AC_MSG_ERROR([Threaded resolver enabled but no thread library found]) + fi +elif test "$USE_ARES" = "1"; then + USE_RESOLV_ARES=1 + AC_DEFINE(USE_RESOLV_ARES, 1, [if you want c-ares for DNS lookup]) + curl_res_msg="c-ares" fi AC_CHECK_HEADER(dirent.h, @@ -4398,6 +4279,27 @@ AC_CHECK_HEADER(dirent.h, CURL_CONVERT_INCLUDE_TO_ISYSTEM +dnl ************************************************************ +dnl disable curl_easy_setopt()/curl_easy_getinfo() type checking +dnl +AC_MSG_CHECKING([whether to enable curl_easy_setopt()/curl_easy_getinfo() type checking]) +AC_ARG_ENABLE(typecheck, +AS_HELP_STRING([--enable-typecheck],[Enable type checking (default)]) +AS_HELP_STRING([--disable-typecheck],[Disable type checking]), +[ case "$enableval" in + no) + AC_MSG_RESULT(no) + dnl Set it via the command-line to make it apply to examples also. + CPPFLAGS="$CPPFLAGS -DCURL_DISABLE_TYPECHECK" + curl_typecheck_msg="no" + ;; + *) + AC_MSG_RESULT(yes) + ;; + esac ], + AC_MSG_RESULT(yes) +) + dnl ************************************************************ dnl disable verbose text strings dnl @@ -4418,14 +4320,14 @@ AS_HELP_STRING([--disable-verbose],[Disable verbose strings]), AC_MSG_RESULT(yes) ) -if test "$curl_cv_winuwp" != 'yes'; then +if test "$curl_cv_winuwp" != "yes"; then dnl ************************************************************ dnl enable SSPI support dnl AC_MSG_CHECKING([whether to enable SSPI support (Windows native builds only)]) AC_ARG_ENABLE(sspi, AS_HELP_STRING([--enable-sspi],[Enable SSPI]) - AS_HELP_STRING([--disable-sspi],[Disable SSPI]), +AS_HELP_STRING([--disable-sspi],[Disable SSPI]), [ case "$enableval" in yes) if test "$curl_cv_native_windows" = "yes"; then @@ -4439,23 +4341,23 @@ if test "$curl_cv_winuwp" != 'yes'; then fi ;; *) - if test "x$SCHANNEL_ENABLED" = "x1"; then - # --with-schannel implies --enable-sspi + if test "$SCHANNEL_ENABLED" = "1"; then + dnl --with-schannel implies --enable-sspi AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) fi ;; esac ], - if test "x$SCHANNEL_ENABLED" = "x1"; then - # --with-schannel implies --enable-sspi + if test "$SCHANNEL_ENABLED" = "1"; then + dnl --with-schannel implies --enable-sspi AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) fi ) - if test "x$USE_WINDOWS_SSPI" = "x1"; then + if test "$USE_WINDOWS_SSPI" = "1"; then LIBS="-lsecur32 $LIBS" fi fi @@ -4584,16 +4486,16 @@ AC_ARG_ENABLE(ntlm, AS_HELP_STRING([--enable-ntlm],[Enable NTLM support]) AS_HELP_STRING([--disable-ntlm],[Disable NTLM support]), [ case "$enableval" in - no) - AC_MSG_RESULT(no) - AC_DEFINE(CURL_DISABLE_NTLM, 1, [to disable NTLM support]) - CURL_DISABLE_NTLM=1 + yes) + AC_MSG_RESULT(yes) + AC_DEFINE(CURL_ENABLE_NTLM, 1, [enable NTLM support]) + CURL_ENABLE_NTLM=1 ;; *) - AC_MSG_RESULT(yes) + AC_MSG_RESULT(no) ;; esac ], - AC_MSG_RESULT(yes) + AC_MSG_RESULT(no) ) dnl ************************************************************ @@ -4617,7 +4519,7 @@ AS_HELP_STRING([--disable-tls-srp],[Disable TLS-SRP authentication]), want_tls_srp=yes ) -if test "$want_tls_srp" = "yes" && ( test "x$HAVE_GNUTLS_SRP" = "x1" || test "x$HAVE_OPENSSL_SRP" = "x1"); then +if test "$want_tls_srp" = "yes" && (test "$HAVE_GNUTLS_SRP" = "1" || test "$HAVE_OPENSSL_SRP" = "1"); then AC_DEFINE(USE_TLS_SRP, 1, [Use TLS-SRP authentication]) USE_TLS_SRP=1 curl_tls_srp_msg="enabled" @@ -4644,8 +4546,8 @@ AS_HELP_STRING([--disable-unix-sockets],[Disable Unix domain sockets]), want_unix_sockets=auto ] ) -if test "x$want_unix_sockets" != "xno" -a "$curl_cv_wince" = 'no'; then - if test "x$curl_cv_native_windows" = "xyes"; then +if test "$want_unix_sockets" != "no"; then + if test "$curl_cv_native_windows" = "yes"; then USE_UNIX_SOCKETS=1 AC_DEFINE(USE_UNIX_SOCKETS, 1, [Use Unix domain sockets]) curl_unix_sockets_msg="enabled" @@ -4655,7 +4557,7 @@ if test "x$want_unix_sockets" != "xno" -a "$curl_cv_wince" = 'no'; then USE_UNIX_SOCKETS=1 curl_unix_sockets_msg="enabled" ], [ - if test "x$want_unix_sockets" = "xyes"; then + if test "$want_unix_sockets" = "yes"; then AC_MSG_ERROR([--enable-unix-sockets is not available on this platform!]) fi ], [ @@ -4792,12 +4694,12 @@ AS_HELP_STRING([--disable-form-api],[Disable form API support]), ;; *) AC_MSG_RESULT(yes) - test "$enable_mime" = no && + test "x$enable_mime" = "xno" && AC_MSG_ERROR(MIME support needs to be enabled in order to enable form API support) ;; esac ], [ - if test "$enable_mime" = no; then + if test "x$enable_mime" = "xno"; then enable_form_api=no AC_MSG_RESULT(no) AC_DEFINE(CURL_DISABLE_FORM_API, 1, [disable form API]) @@ -4961,7 +4863,7 @@ AS_HELP_STRING([--disable-headers-api],[Disable headers-api support]), AC_MSG_RESULT(yes) ) -dnl only check for HSTS if there's SSL present +dnl only check for HSTS if there is SSL present if test -n "$SSL_ENABLED"; then dnl ************************************************************ dnl switch on/off hsts @@ -4986,16 +4888,15 @@ else hsts="no" fi -if test "x$hsts" != "xyes"; then +if test "$hsts" != "yes"; then curl_hsts_msg="no (--enable-hsts)"; AC_DEFINE(CURL_DISABLE_HSTS, 1, [disable alt-svc]) fi - dnl ************************************************************* dnl check whether ECH support, if desired, is actually available dnl -if test "x$want_ech" != "xno"; then +if test "$want_ech" != "no"; then AC_MSG_CHECKING([whether ECH support is available]) dnl assume NOT and look for sufficient condition @@ -5006,25 +4907,25 @@ if test "x$want_ech" != "xno"; then ECH_SUPPORT='' dnl check for OpenSSL equivalent - if test "x$OPENSSL_ENABLED" = "x1"; then + if test "$OPENSSL_ENABLED" = "1"; then AC_CHECK_FUNCS(SSL_set1_ech_config_list, ECH_SUPPORT="$ECH_SUPPORT OpenSSL" ECH_ENABLED_OPENSSL=1) fi - if test "x$WOLFSSL_ENABLED" = "x1"; then + if test "$WOLFSSL_ENABLED" = "1"; then AC_CHECK_FUNCS(wolfSSL_CTX_GenerateEchConfig, ECH_SUPPORT="$ECH_SUPPORT wolfSSL" ECH_ENABLED_WOLFSSL=1) fi - if test "x$RUSTLS_ENABLED" = "x1"; then - ECH_SUPPORT="$ECH_SUPPORT rustls-ffi" - ECH_ENABLED_RUSTLS=1 + if test "$RUSTLS_ENABLED" = "1"; then + ECH_SUPPORT="$ECH_SUPPORT rustls-ffi" + ECH_ENABLED_RUSTLS=1 fi dnl now deal with whatever we found - if test "x$ECH_ENABLED_OPENSSL" = "x1" -o \ - "x$ECH_ENABLED_WOLFSSL" = "x1" -o \ - "x$ECH_ENABLED_RUSTLS" = "x1"; then + if test "$ECH_ENABLED_OPENSSL" = "1" || + test "$ECH_ENABLED_WOLFSSL" = "1" || + test "$ECH_ENABLED_RUSTLS" = "1"; then AC_DEFINE(USE_ECH, 1, [if ECH support is available]) AC_MSG_RESULT(ECH support available via:$ECH_SUPPORT) experimental="$experimental ECH" @@ -5040,38 +4941,37 @@ AC_MSG_CHECKING([whether to enable HTTPS-RR support]) dnl ************************************************************* dnl check whether HTTPSRR support if desired dnl -if test "x$want_httpsrr" != "xno"; then +if test "$want_httpsrr" != "no"; then AC_MSG_RESULT([yes]) AC_DEFINE(USE_HTTPSRR, 1, [enable HTTPS RR support]) experimental="$experimental HTTPSRR" curl_httpsrr_msg="enabled (--disable-httpsrr)" else AC_MSG_RESULT([no]) - # no HTTPSRR wanted + dnl no HTTPSRR wanted if test "$want_threaded_resolver" = "yes"; then - # and using the threaded resolver - if test "x$USE_ARES" = "x1"; then + dnl and using the threaded resolver + if test "$USE_ARES" = "1"; then AC_MSG_ERROR([without HTTPS-RR support, asking for both threaded resolver and c-ares support is ambivalent. Please drop one of them.]) fi fi fi - dnl ************************************************************* dnl check whether OpenSSL (lookalikes) have SSL_set0_wbio dnl -if test "x$OPENSSL_ENABLED" = "x1"; then +if test "$OPENSSL_ENABLED" = "1"; then AC_CHECK_FUNCS([SSL_set0_wbio]) fi -if test "x$CURL_DISABLE_HTTP" != "x1"; then - dnl ************************************************************* - dnl WebSockets - dnl +dnl ************************************************************* +dnl WebSockets +dnl +if test "$CURL_DISABLE_HTTP" != "1"; then AC_MSG_CHECKING([whether to support WebSockets]) AC_ARG_ENABLE(websockets, AS_HELP_STRING([--enable-websockets],[Enable WebSockets support]) - AS_HELP_STRING([--disable-websockets],[Disable WebSockets support]), +AS_HELP_STRING([--disable-websockets],[Disable WebSockets support]), [ case "$enableval" in no) AC_MSG_RESULT(no) @@ -5079,12 +4979,12 @@ if test "x$CURL_DISABLE_HTTP" != "x1"; then CURL_DISABLE_WEBSOCKETS=1 ;; *) - if test ${ac_cv_sizeof_curl_off_t} -gt 4; then + if test "$ac_cv_sizeof_curl_off_t" -gt 4; then AC_MSG_RESULT(yes) else - dnl WebSockets requires >32 bit curl_off_t + dnl WebSockets requires >32-bit curl_off_t AC_MSG_RESULT(no) - AC_MSG_WARN([WebSockets disabled due to lack of >32 bit curl_off_t]) + AC_MSG_WARN([WebSockets disabled due to lack of >32-bit curl_off_t]) AC_DEFINE(CURL_DISABLE_WEBSOCKETS, [1], [disable WebSockets]) CURL_DISABLE_WEBSOCKETS=1 fi @@ -5101,14 +5001,14 @@ fi dnl ************************************************************* dnl check whether experimental SSL Session Im-/Export is enabled dnl -if test "x$want_ssls_export" != "xno"; then +if test "$want_ssls_export" != "no"; then AC_MSG_CHECKING([whether SSL session export support is available]) dnl assume NOT and look for sufficient condition SSLS_EXPORT_ENABLED=0 SSLS_EXPORT_SUPPORT='' - if test "x$SSL_ENABLED" != "x1"; then + if test "$SSL_ENABLED" != "1"; then AC_MSG_WARN([--enable-ssls-export ignored: No SSL support]) else SSLS_EXPORT_ENABLED=1 @@ -5157,7 +5057,7 @@ dnl all link targets in given makefile. BLANK_AT_MAKETIME= AC_SUBST(BLANK_AT_MAKETIME) -AM_CONDITIONAL(CROSSCOMPILING, test x$cross_compiling = xyes) +AM_CONDITIONAL(CROSSCOMPILING, test "$cross_compiling" = "yes") dnl yes or no ENABLE_SHARED="$enable_shared" @@ -5173,7 +5073,7 @@ LIBCURL_PC_REQUIRES_PRIVATE=`echo $LIBCURL_PC_REQUIRES_PRIVATE | tr ' ' ','` AC_SUBST(LIBCURL_PC_REQUIRES_PRIVATE) dnl Merge pkg-config private fields into public ones when static-only -if test "x$enable_shared" = "xno"; then +if test "$enable_shared" = "no"; then LIBCURL_PC_REQUIRES=$LIBCURL_PC_REQUIRES_PRIVATE LIBCURL_PC_LIBS=$LIBCURL_PC_LIBS_PRIVATE else @@ -5190,118 +5090,117 @@ dnl For keeping supported features and protocols also in pkg-config file dnl since it is more cross-compile friendly than curl-config dnl -if test "x$OPENSSL_ENABLED" = "x1"; then +if test "$OPENSSL_ENABLED" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES SSL" elif test -n "$SSL_ENABLED"; then SUPPORT_FEATURES="$SUPPORT_FEATURES SSL" fi -if test "x$IPV6_ENABLED" = "x1"; then +if test "$IPV6_ENABLED" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES IPv6" fi -if test "x$USE_UNIX_SOCKETS" = "x1"; then +if test "$USE_UNIX_SOCKETS" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES UnixSockets" fi -if test "x$HAVE_LIBZ" = "x1"; then +if test "$HAVE_LIBZ" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES libz" fi -if test "x$HAVE_BROTLI" = "x1"; then +if test "$HAVE_BROTLI" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES brotli" fi -if test "x$HAVE_ZSTD" = "x1"; then +if test "$HAVE_ZSTD" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES zstd" fi -if test "x$USE_ARES" = "x1" -o "x$USE_THREADS_POSIX" = "x1" \ - -o "x$USE_THREADS_WIN32" = "x1"; then +if test "$USE_RESOLV_ARES" = "1" || test "$USE_RESOLV_THREADED" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES AsynchDNS" fi -if test "x$USE_ARES" = "x1" -a "$want_threaded_resolver" = "yes" -a "x$want_httpsrr" != "xno"; then +if test "$USE_ARES" = "1" && test "$want_threaded_resolver" = "yes" && test "$want_httpsrr" != "no"; then SUPPORT_FEATURES="$SUPPORT_FEATURES asyn-rr" fi -if test "x$IDN_ENABLED" = "x1"; then +if test "$IDN_ENABLED" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES IDN" fi -if test "x$USE_WINDOWS_SSPI" = "x1"; then +if test "$USE_WINDOWS_SSPI" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES SSPI" fi -if test "x$HAVE_GSSAPI" = "x1"; then +if test "$HAVE_GSSAPI" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES GSS-API" fi -if test "x$curl_psl_msg" = "xenabled"; then +if test "$curl_psl_msg" = "enabled"; then SUPPORT_FEATURES="$SUPPORT_FEATURES PSL" fi -if test "x$curl_gsasl_msg" = "xenabled"; then +if test "$curl_gsasl_msg" = "enabled"; then SUPPORT_FEATURES="$SUPPORT_FEATURES gsasl" fi -if test "x$enable_altsvc" = "xyes"; then +if test "$enable_altsvc" = "yes"; then SUPPORT_FEATURES="$SUPPORT_FEATURES alt-svc" fi -if test "x$hsts" = "xyes"; then +if test "$hsts" = "yes"; then SUPPORT_FEATURES="$SUPPORT_FEATURES HSTS" fi -if test "x$CURL_DISABLE_NEGOTIATE_AUTH" != "x1" -a \ - \( "x$HAVE_GSSAPI" = "x1" -o "x$USE_WINDOWS_SSPI" = "x1" \); then +if test "$CURL_DISABLE_NEGOTIATE_AUTH" != "1" && (test "$HAVE_GSSAPI" = "1" || test "$USE_WINDOWS_SSPI" = "1"); then SUPPORT_FEATURES="$SUPPORT_FEATURES SPNEGO" fi -if test "x$CURL_DISABLE_KERBEROS_AUTH" != "x1" -a \ - \( "x$HAVE_GSSAPI" = "x1" -o "x$USE_WINDOWS_SSPI" = "x1" \); then +if test "$CURL_DISABLE_KERBEROS_AUTH" != "1" && (test "$HAVE_GSSAPI" = "1" || test "$USE_WINDOWS_SSPI" = "1"); then SUPPORT_FEATURES="$SUPPORT_FEATURES Kerberos" fi use_curl_ntlm_core=no -if test "x$CURL_DISABLE_NTLM" != "x1"; then - if test "x$OPENSSL_ENABLED" = "x1" -o "x$MBEDTLS_ENABLED" = "x1" \ - -o "x$GNUTLS_ENABLED" = "x1" \ - -o "x$USE_WIN32_CRYPTO" = "x1" \ - -o "x$HAVE_WOLFSSL_DES_ECB_ENCRYPT" = "x1"; then +if test "$CURL_ENABLE_NTLM" = "1"; then + if test "$HAVE_DES_ECB_ENCRYPT" = "1" || + test "$GNUTLS_ENABLED" = "1" || + test "$USE_WIN32_CRYPTO" = "1" || + test "$HAVE_WC_DES_ECBENCRYPT" = "1" || + test "$HAVE_MBEDTLS_DES_CRYPT_ECB" = "1"; then use_curl_ntlm_core=yes fi - if test "x$use_curl_ntlm_core" = "xyes" \ - -o "x$USE_WINDOWS_SSPI" = "x1"; then + if test "$use_curl_ntlm_core" = "yes" || + test "$USE_WINDOWS_SSPI" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES NTLM" fi fi -if test "x$USE_TLS_SRP" = "x1"; then +if test "$USE_TLS_SRP" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES TLS-SRP" fi -if test "x$USE_NGHTTP2" = "x1"; then +if test "$USE_NGHTTP2" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES HTTP2" fi -if test "x$USE_NGTCP2_H3" = "x1" -o "x$USE_QUICHE" = "x1" \ - -o "x$USE_OPENSSL_H3" = "x1"; then - if test "x$CURL_WITH_MULTI_SSL" = "x1"; then +if test "$USE_NGTCP2_H3" = "1" || + test "$USE_QUICHE" = "1" || + test "$USE_OPENSSL_H3" = "1"; then + if test "$CURL_WITH_MULTI_SSL" = "1"; then AC_MSG_ERROR([MultiSSL cannot be enabled with HTTP/3 and vice versa]) fi SUPPORT_FEATURES="$SUPPORT_FEATURES HTTP3" fi -if test "x$CURL_WITH_MULTI_SSL" = "x1"; then +if test "$CURL_WITH_MULTI_SSL" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES MultiSSL" fi AC_MSG_CHECKING([if this build supports HTTPS-proxy]) dnl if not explicitly turned off, HTTPS-proxy comes with some TLS backends -if test "x$CURL_DISABLE_HTTP" != "x1"; then - if test "x$https_proxy" != "xno"; then - if test "x$OPENSSL_ENABLED" = "x1" \ - -o "x$GNUTLS_ENABLED" = "x1" \ - -o "x$RUSTLS_ENABLED" = "x1" \ - -o "x$SCHANNEL_ENABLED" = "x1" \ - -o "x$GNUTLS_ENABLED" = "x1" \ - -o "x$MBEDTLS_ENABLED" = "x1"; then +if test "$CURL_DISABLE_HTTP" != "1"; then + if test "$https_proxy" != "no"; then + if test "$OPENSSL_ENABLED" = "1" || + test "$GNUTLS_ENABLED" = "1" || + test "$RUSTLS_ENABLED" = "1" || + test "$SCHANNEL_ENABLED" = "1" || + test "$GNUTLS_ENABLED" = "1" || + test "$MBEDTLS_ENABLED" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPS-proxy" AC_MSG_RESULT([yes]) - elif test "x$WOLFSSL_ENABLED" = "x1" -a "x$HAVE_WOLFSSL_BIO_NEW" = "x1"; then + elif test "$WOLFSSL_ENABLED" = "1" && test "$HAVE_WOLFSSL_BIO_NEW" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPS-proxy" AC_MSG_RESULT([yes]) else @@ -5314,57 +5213,50 @@ else AC_MSG_RESULT([no]) fi -if test "x$OPENSSL_ENABLED" = "x1" -o -n "$SSL_ENABLED"; then - if test "x$ECH_ENABLED" = "x1"; then +if test "$OPENSSL_ENABLED" = "1" || test -n "$SSL_ENABLED"; then + if test "$ECH_ENABLED" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES ECH" fi fi -if test "x$want_httpsrr" != "xno"; then +if test -n "$SSL_ENABLED"; then + if test "$APPLE_SECTRUST_ENABLED" = "1"; then + SUPPORT_FEATURES="$SUPPORT_FEATURES AppleSecTrust" + elif test "$ca_native_opt" = "1"; then + SUPPORT_FEATURES="$SUPPORT_FEATURES NativeCA" + fi +fi + +if test "$want_httpsrr" != "no"; then SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPSRR" fi -if test "x$SSLS_EXPORT_ENABLED" = "x1"; then +if test "$SSLS_EXPORT_ENABLED" = "1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES SSLS-EXPORT" fi -if test ${ac_cv_sizeof_curl_off_t} -gt 4; then - if test ${ac_cv_sizeof_off_t} -gt 4 -o \ - "$curl_win32_file_api" = "win32_large_files"; then +if test "$ac_cv_sizeof_curl_off_t" -gt 4; then + if test "$ac_cv_sizeof_off_t" -gt 4 || + test "$curl_cv_native_windows" = "yes"; then SUPPORT_FEATURES="$SUPPORT_FEATURES Largefile" fi fi if test "$tst_atomic" = "yes"; then SUPPORT_FEATURES="$SUPPORT_FEATURES threadsafe" -elif test "x$USE_THREADS_POSIX" = "x1" -a \ - "x$ac_cv_header_pthread_h" = "xyes"; then +elif test "$HAVE_THREADS_POSIX" = "1" && test "$ac_cv_header_pthread_h" = "yes"; then + SUPPORT_FEATURES="$SUPPORT_FEATURES threadsafe" +elif test "$curl_cv_native_windows" = "yes"; then SUPPORT_FEATURES="$SUPPORT_FEATURES threadsafe" -else - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - #include - ]],[[ - #if (_WIN32_WINNT < 0x600) - #error - #endif - ]]) - ],[ - SUPPORT_FEATURES="$SUPPORT_FEATURES threadsafe" - ],[ - ]) fi -if test "x$want_winuni" = "xyes"; then +if test "$want_winuni" = "yes"; then SUPPORT_FEATURES="$SUPPORT_FEATURES Unicode" fi -if test "x$want_debug" = "xyes"; then +if test "$want_debug" = "yes"; then SUPPORT_FEATURES="$SUPPORT_FEATURES Debug" fi -if test "x$want_curldebug" = "xyes"; then - SUPPORT_FEATURES="$SUPPORT_FEATURES TrackMemory" -fi -if test "x$CURL_CA_EMBED" != "x"; then +if test -n "$CURL_CA_EMBED"; then SUPPORT_FEATURES="$SUPPORT_FEATURES CAcert" CURL_CA_EMBED_msg="$CURL_CA_EMBED" else @@ -5382,96 +5274,92 @@ fi AC_SUBST(SUPPORT_FEATURES) dnl For supported protocols in pkg-config file -if test "x$CURL_DISABLE_HTTP" != "x1"; then +if test "$CURL_DISABLE_HTTP" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS HTTP" - if test "x$SSL_ENABLED" = "x1"; then + if test "$SSL_ENABLED" = "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS HTTPS" fi fi -if test "x$CURL_DISABLE_FTP" != "x1"; then +if test "$CURL_DISABLE_FTP" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS FTP" - if test "x$SSL_ENABLED" = "x1"; then + if test "$SSL_ENABLED" = "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS FTPS" fi fi -if test "x$CURL_DISABLE_FILE" != "x1"; then +if test "$CURL_DISABLE_FILE" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS FILE" fi -if test "x$CURL_DISABLE_TELNET" != "x1"; then +if test "$CURL_DISABLE_TELNET" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS TELNET" fi -if test "x$CURL_DISABLE_LDAP" != "x1"; then +if test "$CURL_DISABLE_LDAP" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS LDAP" - if test "x$CURL_DISABLE_LDAPS" != "x1"; then - if (test "x$USE_OPENLDAP" = "x1" && test "x$SSL_ENABLED" = "x1") || - (test "x$USE_OPENLDAP" != "x1" && test "x$HAVE_LDAP_SSL" = "x1"); then + if test "$CURL_DISABLE_LDAPS" != "1"; then + if (test "$USE_OPENLDAP" = "1" && test "$SSL_ENABLED" = "1") || + (test "$USE_OPENLDAP" != "1" && test "$HAVE_LDAP_SSL" = "1"); then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS LDAPS" fi fi fi -if test "x$CURL_DISABLE_DICT" != "x1"; then +if test "$CURL_DISABLE_DICT" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS DICT" fi -if test "x$CURL_DISABLE_TFTP" != "x1"; then +if test "$CURL_DISABLE_TFTP" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS TFTP" fi -if test "x$CURL_DISABLE_GOPHER" != "x1"; then +if test "$CURL_DISABLE_GOPHER" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS GOPHER" - if test "x$SSL_ENABLED" = "x1"; then + if test "$SSL_ENABLED" = "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS GOPHERS" fi fi -if test "x$CURL_DISABLE_MQTT" != "x1"; then +if test "$CURL_DISABLE_MQTT" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS MQTT" + if test "$SSL_ENABLED" = "1"; then + SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS MQTTS" + fi fi -if test "x$CURL_DISABLE_POP3" != "x1"; then +if test "$CURL_DISABLE_POP3" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS POP3" - if test "x$SSL_ENABLED" = "x1"; then + if test "$SSL_ENABLED" = "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS POP3S" fi fi -if test "x$CURL_DISABLE_IMAP" != "x1"; then +if test "$CURL_DISABLE_IMAP" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS IMAP" - if test "x$SSL_ENABLED" = "x1"; then + if test "$SSL_ENABLED" = "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS IMAPS" fi fi -if test "x$CURL_DISABLE_SMB" != "x1" \ - -a "x$use_curl_ntlm_core" = "xyes"; then +if test "$CURL_ENABLE_SMB" = "1" && test "$use_curl_ntlm_core" = "yes"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SMB" - if test "x$SSL_ENABLED" = "x1"; then + if test "$SSL_ENABLED" = "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SMBS" fi fi -if test "x$CURL_DISABLE_SMTP" != "x1"; then +if test "$CURL_DISABLE_SMTP" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SMTP" - if test "x$SSL_ENABLED" = "x1"; then + if test "$SSL_ENABLED" = "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SMTPS" fi fi -if test "x$USE_LIBSSH2" = "x1"; then +if test "$USE_LIBSSH2" = "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SCP" SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SFTP" fi -if test "x$USE_LIBSSH" = "x1"; then +if test "$USE_LIBSSH" = "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SCP" SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SFTP" fi -if test "x$USE_WOLFSSH" = "x1"; then - SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS SFTP" -fi -if test "x$CURL_DISABLE_IPFS" != "x1"; then +if test "$CURL_DISABLE_IPFS" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS IPFS IPNS" fi -if test "x$CURL_DISABLE_RTSP" != "x1"; then +if test "$CURL_DISABLE_RTSP" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS RTSP" fi -if test "x$USE_LIBRTMP" = "x1"; then - SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS RTMP" -fi -if test "x$CURL_DISABLE_WEBSOCKETS" != "x1"; then +if test "$CURL_DISABLE_WEBSOCKETS" != "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS WS" - if test "x$SSL_ENABLED" = "x1"; then + if test "$SSL_ENABLED" = "1"; then SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS WSS" fi fi @@ -5503,11 +5391,6 @@ XC_CHECK_BUILD_FLAGS SSL_BACKENDS=${ssl_backends} AC_SUBST(SSL_BACKENDS) -if test "x$want_curldebug_assumed" = "xyes" && - test "x$want_curldebug" = "xyes" && test "x$USE_ARES" = "x1"; then - ac_configure_args="$ac_configure_args --enable-curldebug" -fi - CURL_PREPARE_CONFIGUREHELP_PM AC_CONFIG_FILES([\ @@ -5534,8 +5417,8 @@ AC_CONFIG_FILES([\ tests/tunit/Makefile \ tests/http/config.ini \ tests/http/Makefile \ - packages/Makefile \ - packages/vms/Makefile \ + projects/Makefile \ + projects/vms/Makefile \ libcurl.pc ]) AC_CONFIG_FILES([curl-config], [chmod +x curl-config]) @@ -5572,9 +5455,11 @@ AC_MSG_NOTICE([Configured to build curl/libcurl: Build libcurl: Shared=${enable_shared}, Static=${enable_static} Built-in manual: ${curl_manual_msg} --libcurl option: ${curl_libcurl_msg} + Type checking: ${curl_typecheck_msg} Verbose errors: ${curl_verbose_msg} Code coverage: ${curl_coverage_msg} SSPI: ${curl_sspi_msg} + ca native: ${ca_native} ca cert bundle: ${ca}${ca_warning} ca cert path: ${capath}${capath_warning} ca cert embed: ${CURL_CA_EMBED_msg} @@ -5583,7 +5468,6 @@ AC_MSG_NOTICE([Configured to build curl/libcurl: LDAPS: ${curl_ldaps_msg} IPFS/IPNS: ${curl_ipfs_msg} RTSP: ${curl_rtsp_msg} - RTMP: ${curl_rtmp_msg} PSL: ${curl_psl_msg} Alt-svc: ${curl_altsvc_msg} Headers API: ${curl_headers_msg} diff --git a/curl-config.in b/curl-config.in index ce23519c33..a1c8185875 100644 --- a/curl-config.in +++ b/curl-config.in @@ -149,7 +149,7 @@ while test "$#" -gt 0; do ;; --libs) - if test "@libdir@" != '/usr/lib' -a "@libdir@" != '/usr/lib64'; then + if test "@libdir@" != '/usr/lib' && test "@libdir@" != '/usr/lib64'; then curllibdir="-L@libdir@ " else curllibdir='' diff --git a/docs/ALTSVC.md b/docs/ALTSVC.md index bcdf565074..24b961a3fb 100644 --- a/docs/ALTSVC.md +++ b/docs/ALTSVC.md @@ -35,7 +35,8 @@ space separated fields. 4. The ALPN id for the destination host 5. The hostname for the destination host 6. The port number for the destination host -7. The expiration date and time of this entry within double quotes. The date format is "YYYYMMDD HH:MM:SS" and the time zone is GMT. +7. The expiration date and time of this entry within double quotes. + The date format is "YYYYMMDD HH:MM:SS" and the time zone is GMT. 8. Boolean (1 or 0) if "persist" was set for this entry 9. Integer priority value (not currently used) diff --git a/docs/BINDINGS.md b/docs/BINDINGS.md index 8bba1d9c9a..9a53f81f29 100644 --- a/docs/BINDINGS.md +++ b/docs/BINDINGS.md @@ -7,14 +7,14 @@ SPDX-License-Identifier: curl libcurl bindings ================ - Creative people have written bindings or interfaces for various environments - and programming languages. Using one of these allows you to take advantage of - curl powers from within your favourite language or system. +Creative people have written bindings or interfaces for various environments +and programming languages. Using one of these allows you to take advantage of +curl powers from within your favorite language or system. - This is a list of all known interfaces as of this writing. +This is a list of all known interfaces as of this writing. - The bindings listed below are not part of the curl/libcurl distribution - archives, but must be downloaded and installed separately. +The bindings listed below are not part of the curl/libcurl distribution +archives, but must be downloaded and installed separately. @@ -22,9 +22,9 @@ libcurl bindings [Basic](https://scriptbasic.com/) ScriptBasic bindings written by Peter Verhas -C++: [curlpp](https://github.com/jpbarrette/curlpp/) Written by Jean-Philippe Barrette-LaPierre, -[curlcpp](https://github.com/JosephP91/curlcpp) by Giuseppe Persico and [C++ -Requests](https://github.com/libcpr/cpr) by Huu Nguyen +C++: [curlpp](https://github.com/jpbarrette/curlpp) Written by Jean-Philippe Barrette-LaPierre, +[curlcpp](https://github.com/JosephP91/curlcpp) by Giuseppe Persico and +[C++ Requests](https://github.com/libcpr/cpr) by Huu Nguyen [Ch](https://chcurl.sourceforge.net/) Written by Stephen Nestinger and Jonathan Rogado @@ -49,7 +49,7 @@ Clojure: [clj-curl](https://github.com/lsevero/clj-curl) by Lucas Severo [Fortran](https://github.com/interkosmos/fortran-curl) Written by Philipp Engel -[Gambas](https://gambas.sourceforge.net/) +[Gambas](https://gambaswiki.org/website/en/main.html) [glib/GTK+](https://web.archive.org/web/20100526203452/atterer.net/glibcurl) Written by Richard Atterer @@ -61,7 +61,7 @@ Go: [go-curl](https://github.com/andelf/go-curl) by ShuYu Wang [Haskell](https://hackage.haskell.org/package/curl) Written by Galois, Inc -[Hollywood](https://www.hollywood-mal.com/download.html) hURL by Andreas Falkenhahn +[Hollywood](https://web.archive.org/web/20250116185836/www.hollywood-mal.com/download.html) hURL by Andreas Falkenhahn [Java](https://github.com/covers1624/curl4j) @@ -73,9 +73,9 @@ Go: [go-curl](https://github.com/andelf/go-curl) by ShuYu Wang [LibQurl](https://github.com/Qriist/LibQurl) a feature rich AutoHotKey v2 (AHKv2) wrapper around libcurl. -Lua: [luacurl](https://web.archive.org/web/20201205052437/luacurl.luaforge.net/) by Alexander Marinov, [Lua-cURL](https://github.com/Lua-cURL) by Jürgen Hötzel +Lua: [luacurl](https://web.archive.org/web/20201205052437/luacurl.luaforge.net/) by Alexander Marinov, [Lua-curl](https://github.com/Lua-cURL) by Jürgen Hötzel -[Mono](https://web.archive.org/web/20070606064500/https://forge.novell.com/modules/xfmod/project/?libcurl-mono) Written by Jeffrey Phillips +[Mono](https://web.archive.org/web/20070606064500/forge.novell.com/modules/xfmod/project/?libcurl-mono) Written by Jeffrey Phillips [.NET](https://sourceforge.net/projects/libcurl-net/) libcurl-net by Jeffrey Phillips @@ -98,11 +98,11 @@ Bailiff and Bálint Szilakszi, [PostgreSQL](https://github.com/pramsey/pgsql-http) - HTTP client for PostgreSQL -[PostgreSQL](https://github.com/RekGRpth/pg_curl) - cURL client for PostgreSQL +[PostgreSQL](https://github.com/RekGRpth/pg_curl) - curl client for PostgreSQL -[PureBasic](https://www.purebasic.com/documentation/http/index.html) uses libcurl in its "native" HTTP subsystem +[PureBasic](https://web.archive.org/web/20250325015028/www.purebasic.com/documentation/http/index.html) uses libcurl in its "native" HTTP subsystem -[Python](http://pycurl.io/) PycURL by Kjetil Jacobsen +[Python](https://github.com/pycurl/pycurl) PycURL by Kjetil Jacobsen [Python](https://pypi.org/project/pymcurl/) mcurl by Ganesh Viswanathan @@ -112,7 +112,7 @@ Bailiff and Bálint Szilakszi, [Rexx](https://rexxcurl.sourceforge.net/) Written Mark Hessling -[Ring](https://ring-lang.sourceforge.io/doc1.3/libcurl.html) RingLibCurl by Mahmoud Fayed +[Ring](https://ring-lang.github.io/doc1.24/libcurl.html) RingLibCurl by Mahmoud Fayed RPG, support for ILE/RPG on OS/400 is included in source distribution @@ -121,7 +121,7 @@ Ruby: [curb](https://github.com/taf2/curb) written by Ross Bamford, [Rust](https://github.com/alexcrichton/curl-rust) curl-rust - by Carl Lerche -[Scheme](https://www.metapaper.net/lisovsky/web/curl/) Bigloo binding by Kirill Lisovsky +[Scheme](https://metapaper.net/lisovsky/web/curl/) Bigloo binding by Kirill Lisovsky [Scilab](https://help.scilab.org/docs/current/fr_FR/getURL.html) binding by Sylvestre Ledru diff --git a/docs/BUG-BOUNTY.md b/docs/BUG-BOUNTY.md index 6933c61b34..765cf493e6 100644 --- a/docs/BUG-BOUNTY.md +++ b/docs/BUG-BOUNTY.md @@ -4,90 +4,13 @@ Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl --> -# The curl bug bounty +# No curl bug bounty -The curl project runs a bug bounty program in association with -[HackerOne](https://www.hackerone.com) and the [Internet Bug -Bounty](https://internetbugbounty.org). +The curl project does not offer any rewards for reported bugs or +vulnerabilities. We do not aid security researchers to get such rewards for +curl problems from other sources. -## How does it work? +A bug bounty gives people too strong incentives to find and make up "problems" +in bad faith that cause overload and abuse. -Start out by posting your suspected security vulnerability directly to [curl's -HackerOne program](https://hackerone.com/curl). - -After you have reported a security issue, it has been deemed credible, and a -patch and advisory has been made public, you may be eligible for a bounty from -this program. See the [Security Process](https://curl.se/dev/secprocess.html) -document for how we work with security issues. - -## What are the reward amounts? - -The curl project offers monetary compensation for reported and published -security vulnerabilities. The amount of money that is rewarded depends on how -serious the flaw is determined to be. - -Since 2021, the Bug Bounty is managed in association with the Internet Bug -Bounty and they set the reward amounts. If it would turn out that they set -amounts that are way lower than we can accept, the curl project intends to -"top up" rewards. - -In 2025, typical "Medium" rated vulnerabilities are rewarded 2,500 USD each. - -## Who is eligible for a reward? - -Everyone and anyone who reports a security problem in a released curl version -that has not already been reported can ask for a bounty. - -Dedicated - paid for - security audits that are performed in collaboration -with curl developers are not eligible for bounties. - -Vulnerabilities in features that are off by default and documented as -experimental are not eligible for a reward. - -The vulnerability has to be fixed and publicly announced (by the curl project) -before a bug bounty is considered. - -Once the vulnerability has been published by curl, the researcher can request -their bounty from the [Internet Bug Bounty](https://hackerone.com/ibb). - -Bounties need to be requested within twelve months from the publication of the -vulnerability. - -The curl security team reserves themselves the right to deny or allow bug -bounty payouts on its own discretion. There is no appeals process. - -## Product vulnerabilities only - -This bug bounty only concerns the curl and libcurl products and thus their -respective source codes - when running on existing hardware. It does not -include curl documentation, curl websites, or other curl related -infrastructure. - -The curl security team is the sole arbiter if a reported flaw is subject to a -bounty or not. - -## Third parties - -The curl bug bounty does not cover flaws in third party dependencies -(libraries) used by curl or libcurl. If the bug triggers because of curl -behaving wrongly or abusing a third party dependency, the problem is rather in -curl and not in the dependency and then the bounty might cover the problem. - -## How are vulnerabilities graded? - -The grading of each reported vulnerability that makes a reward claim is -performed by the curl security team. The grading is based on the CVSS (Common -Vulnerability Scoring System) 3.0. - -## How are reward amounts determined? - -The curl security team gives the vulnerability a score or severity level, as -mentioned above. The actual monetary reward amount is decided and paid by the -Internet Bug Bounty.. - -## Regarding taxes, etc. on the bounties - -In the event that the individual receiving a bug bounty needs to pay taxes on -the reward money, the responsibility lies with the receiver. The curl project -or its security team never actually receive any of this money, hold the money, -or pay out the money. +We still appreciate and value valid vulnerability reports. diff --git a/docs/BUGS.md b/docs/BUGS.md index 68d7fc3479..5f3704771b 100644 --- a/docs/BUGS.md +++ b/docs/BUGS.md @@ -8,263 +8,260 @@ SPDX-License-Identifier: curl ## There are still bugs - curl and libcurl keep being developed. Adding features and changing code - means that bugs sneak in, no matter how hard we try to keep them out. +curl and libcurl keep being developed. Adding features and changing code +means that bugs sneak in, no matter how hard we try to keep them out. - Of course there are lots of bugs left. Not to mention misfeatures. +Of course there are lots of bugs left. Not to mention misfeatures. - To help us make curl the stable and solid product we want it to be, we need - bug reports and bug fixes. +To help us make curl the stable and solid product we want it to be, we need +bug reports and bug fixes. ## Where to report - If you cannot fix a bug yourself and submit a fix for it, try to report an as - detailed report as possible to a curl mailing list to allow one of us to have - a go at a solution. You can optionally also submit your problem in [curl's - bug tracking system](https://github.com/curl/curl/issues). +If you cannot fix a bug yourself and submit a fix for it, try to report an as +detailed report as possible to a curl mailing list to allow one of us to have +a go at a solution. You can optionally also submit your problem in +[curl's bug tracking system](https://github.com/curl/curl/issues). - Please read the rest of this document below first before doing that. +Please read the rest of this document below first before doing that. - If you feel you need to ask around first, find a suitable [mailing - list](https://curl.se/mail/) and post your questions there. +If you feel you need to ask around first, find a suitable +[mailing list](https://curl.se/mail/) and post your questions there. ## Security bugs - If you find a bug or problem in curl or libcurl that you think has a security - impact, for example a bug that can put users in danger or make them - vulnerable if the bug becomes public knowledge, then please report that bug - using our security development process. +If you find a bug or problem in curl or libcurl that you think has a security +impact, for example a bug that can put users in danger or make them +vulnerable if the bug becomes public knowledge, then please report that bug +using our security development process. - Security related bugs or bugs that are suspected to have a security impact, - should be reported on the [curl security tracker at - HackerOne](https://hackerone.com/curl). +Security related bugs or bugs that are suspected to have a security impact, +should be reported [privately](https://curl.se/dev/vuln-disclosure.html). - This ensures that the report reaches the curl security team so that they - first can deal with the report away from the public to minimize the harm and - impact it has on existing users out there who might be using the vulnerable - versions. +This ensures that the report reaches the curl security team so that they first +can deal with the report away from the public to minimize the harm and impact +it has on existing users out there who might be using the vulnerable versions. - The curl project's process for handling security related issues is - [documented separately](https://curl.se/dev/secprocess.html). +The curl project's process for handling security related issues is +[documented separately](https://curl.se/dev/secprocess.html). ## What to report - When reporting a bug, you should include all information to help us - understand what is wrong, what you expected to happen and how to repeat the - bad behavior. You therefore need to tell us: +When reporting a bug, you should include all information to help us +understand what is wrong, what you expected to happen and how to repeat the +bad behavior. You therefore need to tell us: - - your operating system's name and version number +- your operating system's name and version number - - what version of curl you are using (`curl -V` is fine) +- what version of curl you are using (`curl -V` is fine) - - versions of the used libraries that libcurl is built to use +- versions of the used libraries that libcurl is built to use - - what URL you were working with (if possible), at least which protocol +- what URL you were working with (if possible), at least which protocol - and anything and everything else you think matters. Tell us what you expected - to happen, tell us what did happen, tell us how you could make it work - another way. Dig around, try out, test. Then include all the tiny bits and - pieces in your report. You benefit from this yourself, as it enables us to - help you quicker and more accurately. +and anything and everything else you think matters. Tell us what you expected +to happen, tell us what did happen, tell us how you could make it work +another way. Dig around, try out, test. Then include all the tiny bits and +pieces in your report. You benefit from this yourself, as it enables us to +help you quicker and more accurately. - Since curl deals with networks, it often helps us if you include a protocol - debug dump with your bug report. The output you get by using the `-v` or - `--trace` options. +Since curl deals with networks, it often helps us if you include a protocol +debug dump with your bug report. The output you get by using the `-v` or +`--trace` options. - If curl crashed, causing a core dump (in Unix), there is hardly any use to - send that huge file to anyone of us. Unless we have the same system setup as - you, we cannot do much with it. Instead, we ask you to get a stack trace and - send that (much smaller) output to us instead. +If curl crashed, causing a core dump (in Unix), there is hardly any use to +send that huge file to anyone of us. Unless we have the same system setup as +you, we cannot do much with it. Instead, we ask you to get a stack trace and +send that (much smaller) output to us instead. - The address and how to subscribe to the mailing lists are detailed in the - `MANUAL.md` file. +The address and how to subscribe to the mailing lists are detailed in the +`MANUAL.md` file. ## libcurl problems - When you have written your own application with libcurl to perform transfers, - it is even more important to be specific and detailed when reporting bugs. +When you have written your own application with libcurl to perform transfers, +it is even more important to be specific and detailed when reporting bugs. - Tell us the libcurl version and your operating system. Tell us the name and - version of all relevant sub-components like for example the SSL library - you are using and what name resolving your libcurl uses. If you use SFTP or - SCP, the libssh2 version is relevant etc. +Tell us the libcurl version and your operating system. Tell us the name and +version of all relevant sub-components like for example the SSL library +you are using and what name resolving your libcurl uses. If you use SFTP or +SCP, the libssh2 version is relevant etc. - Showing us a real source code example repeating your problem is the best way - to get our attention and it greatly increases our chances to understand your - problem and to work on a fix (if we agree it truly is a problem). +Showing us a real source code example repeating your problem is the best way +to get our attention and it greatly increases our chances to understand your +problem and to work on a fix (if we agree it truly is a problem). - Lots of problems that appear to be libcurl problems are actually just abuses - of the libcurl API or other malfunctions in your applications. It is advised - that you run your problematic program using a memory debug tool like valgrind - or similar before you post memory-related or "crashing" problems to us. +Lots of problems that appear to be libcurl problems are instead abuses of the +libcurl API or other malfunctions in your applications. It is advised that you +run your problematic program using a memory debug tool like valgrind or +similar before you post memory-related or "crashing" problems to us. ## Who fixes the problems - If the problems or bugs you describe are considered to be bugs, we want to - have the problems fixed. +If the problems or bugs you describe are considered to be bugs, we want to +have the problems fixed. - There are no developers in the curl project that are paid to work on bugs. - All developers that take on reported bugs do this on a voluntary basis. We do - it out of an ambition to keep curl and libcurl excellent products and out of - pride. +There are no developers in the curl project that are paid to work on bugs. +All developers that take on reported bugs do this on a voluntary basis. We do +it out of an ambition to keep curl and libcurl excellent products and out of +pride. - Please do not assume that you can just lump over something to us and it then - magically gets fixed after some given time. Most often we need feedback and - help to understand what you have experienced and how to repeat a problem. - Then we may only be able to assist YOU to debug the problem and to track down - the proper fix. +Please do not assume that you can lump over something to us and it then +automatically gets fixed after some given time. Most often we need feedback +and help to understand what you have experienced and how to repeat a problem. +Then we may only be able to assist YOU to debug the problem and to track down +the proper fix. - We get reports from many people every month and each report can take a - considerable amount of time to really go to the bottom with. +We get reports from many people every month and each report can take a +considerable amount of time to really go to the bottom with. ## How to get a stack trace - First, you must make sure that you compile all sources with `-g` and that you - do not 'strip' the final executable. Try to avoid optimizing the code as well, - remove `-O`, `-O2` etc from the compiler options. +First, you must make sure that you compile all sources with `-g` and that you +do not 'strip' the final executable. Try to avoid optimizing the code as well, +remove `-O`, `-O2` etc from the compiler options. - Run the program until it cores. +Run the program until it cores. - Run your debugger on the core file, like ` curl core`. `` - should be replaced with the name of your debugger, in most cases that is - `gdb`, but `dbx` and others also occur. +Run your debugger on the core file, like ` curl core`. `` +should be replaced with the name of your debugger, in most cases that is +`gdb`, but `dbx` and others also occur. - When the debugger has finished loading the core file and presents you a - prompt, enter `where` (without quotes) and press return. +When the debugger has finished loading the core file and presents you a +prompt, enter `where` (without quotes) and press return. - The list that is presented is the stack trace. If everything worked, it is - supposed to contain the chain of functions that were called when curl - crashed. Include the stack trace with your detailed bug report, it helps a - lot. +The list that is presented is the stack trace. If everything worked, it is +supposed to contain the chain of functions that were called when curl +crashed. Include the stack trace with your detailed bug report, it helps a +lot. ## Bugs in libcurl bindings - There are of course bugs in libcurl bindings. You should then primarily - approach the team that works on that particular binding and see what you can - do to help them fix the problem. +There are of course bugs in libcurl bindings. You should then primarily +approach the team that works on that particular binding and see what you can +do to help them fix the problem. - If you suspect that the problem exists in the underlying libcurl, then please - convert your program over to plain C and follow the steps outlined above. +If you suspect that the problem exists in the underlying libcurl, then please +convert your program over to plain C and follow the steps outlined above. ## Bugs in old versions - The curl project typically releases new versions every other month, and we - fix several hundred bugs per year. For a huge table of releases, number of - bug fixes and more, see: https://curl.se/docs/releases.html +The curl project typically releases new versions every other month, and we +fix several hundred bugs per year. For a huge table of releases, number of +bug fixes and more, see: https://curl.se/docs/releases.html - The developers in the curl project do not have bandwidth or energy enough to - maintain several branches or to spend much time on hunting down problems in - old versions when chances are we already fixed them or at least that they have - changed nature and appearance in later versions. +The developers in the curl project do not have bandwidth or energy enough to +maintain several branches or to spend much time on hunting down problems in +old versions when chances are we already fixed them or at least that they have +changed nature and appearance in later versions. - When you experience a problem and want to report it, you really SHOULD - include the version number of the curl you are using when you experience the - issue. If that version number shows us that you are using an out-of-date curl, - you should also try out a modern curl version to see if the problem persists - or how/if it has changed in appearance. +When you experience a problem and want to report it, you really SHOULD +include the version number of the curl you are using when you experience the +issue. If that version number shows us that you are using an out-of-date curl, +you should also try out a modern curl version to see if the problem persists +or how/if it has changed in appearance. - Even if you cannot immediately upgrade your application/system to run the - latest curl version, you can most often at least run a test version or - experimental build or similar, to get this confirmed or not. +Even if you cannot immediately upgrade your application/system to run the +latest curl version, you can most often at least run a test version or +experimental build or similar, to get this confirmed or not. - At times people insist that they cannot upgrade to a modern curl version, but - instead, they "just want the bug fixed". That is fine, just do not count on us - spending many cycles on trying to identify which single commit, if that is - even possible, that at some point in the past fixed the problem you are now - experiencing. +At times people insist that they cannot upgrade to a modern curl version, they +only "want the bug fixed". That is fine, but do not count on us spending many +cycles on trying to identify which single commit, if that is even possible, +that at some point in the past fixed the problem you are now experiencing. - Security wise, it is almost always a bad idea to lag behind the current curl - versions by a lot. We keep discovering and reporting security problems - over time see you can see in [this - table](https://curl.se/docs/vulnerabilities.html) +Security wise, it is almost always a bad idea to lag behind the current curl +versions by a lot. We keep discovering and reporting security problems +over time see you can see in +[this table](https://curl.se/docs/vulnerabilities.html) # Bug fixing procedure ## What happens on first filing - When a new issue is posted in the issue tracker or on the mailing list, the - team of developers first needs to see the report. Maybe they took the day off, - maybe they are off in the woods hunting. Have patience. Allow at least a few - days before expecting someone to have responded. +When a new issue is posted in the issue tracker or on the mailing list, the +team of developers first needs to see the report. Maybe they took the day off, +maybe they are off in the woods hunting. Have patience. Allow at least a few +days before expecting someone to have responded. - In the issue tracker, you can expect that some labels are set on the issue to - help categorize it. +In the issue tracker, you can expect that some labels are set on the issue to +help categorize it. ## First response - If your issue/bug report was not perfect at once (and few are), chances are - that someone asks follow-up questions. Which version did you use? Which - options did you use? How often does the problem occur? How can we reproduce - this problem? Which protocols does it involve? Or perhaps much more specific - and deep diving questions. It all depends on your specific issue. +If your issue/bug report was not perfect at once (and few are), chances are +that someone asks follow-up questions. Which version did you use? Which +options did you use? How often does the problem occur? How can we reproduce +this problem? Which protocols does it involve? Or perhaps much more specific +and deep diving questions. It all depends on your specific issue. - You should then respond to these follow-up questions and provide more info - about the problem, so that we can help you figure it out. Or maybe you can - help us figure it out. An active back-and-forth communication is important - and the key for finding a cure and landing a fix. +You should then respond to these follow-up questions and provide more info +about the problem, so that we can help you figure it out. Or maybe you can +help us figure it out. An active back-and-forth communication is important +and the key for finding a cure and landing a fix. ## Not reproducible - We may require further work from you who actually see or experience the - problem if we cannot reproduce it and cannot understand it even after having - gotten all the info we need and having studied the source code over again. +We may require further work from you who actually see or experience the +problem if we cannot reproduce it and cannot understand it even after having +gotten all the info we need and having studied the source code over again. ## Unresponsive - If the problem have not been understood or reproduced, and there is nobody - responding to follow-up questions or questions asking for clarifications or - for discussing possible ways to move forward with the task, we take that as a - strong suggestion that the bug is unimportant. +If the problem have not been understood or reproduced, and there is nobody +responding to follow-up questions or questions asking for clarifications or +for discussing possible ways to move forward with the task, we take that as a +strong suggestion that the bug is unimportant. - Unimportant issues are closed as inactive sooner or later as they cannot be - fixed. The inactivity period (waiting for responses) should not be shorter - than two weeks but may extend months. +Unimportant issues are closed as inactive sooner or later as they cannot be +fixed. The inactivity period (waiting for responses) should not be shorter +than two weeks but may extend months. ## Lack of time/interest - Bugs that are filed and are understood can unfortunately end up in the - "nobody cares enough about it to work on it" category. Such bugs are - perfectly valid problems that *should* get fixed but apparently are not. We - try to mark such bugs as `KNOWN_BUGS material` after a time of inactivity and - if no activity is noticed after yet some time those bugs are added to the - `KNOWN_BUGS` document and are closed in the issue tracker. +Bugs that are filed and are understood can unfortunately end up in the +"nobody cares enough about it to work on it" category. Such bugs are +perfectly valid problems that *should* get fixed but apparently are not. We +try to mark such bugs as `KNOWN_BUGS material` after a time of inactivity and +if no activity is noticed after yet some time those bugs are added to the +`KNOWN_BUGS` document and are closed in the issue tracker. ## `KNOWN_BUGS` - This is a list of known bugs. Bugs we know exist and that have been pointed - out but that have not yet been fixed. The reasons for why they have not been - fixed can involve anything really, but the primary reason is that nobody has - considered these problems to be important enough to spend the necessary time - and effort to have them fixed. +This is a list of known bugs. Bugs we know exist and that have been pointed +out but that have not yet been fixed. The reasons for why they have not been +fixed can involve anything really, but the primary reason is that nobody has +considered these problems to be important enough to spend the necessary time +and effort to have them fixed. - The `KNOWN_BUGS` items are always up for grabs and we love the ones who bring - one of them back to life and offer solutions to them. +The `KNOWN_BUGS` items are always up for grabs and we love the ones who bring +one of them back to life and offer solutions to them. - The `KNOWN_BUGS` document has a sibling document known as `TODO`. +The `KNOWN_BUGS` document has a sibling document known as `TODO`. ## `TODO` - Issues that are filed or reported that are not really bugs but more missing - features or ideas for future improvements and so on are marked as - *enhancement* or *feature-request* and get added to the `TODO` document and - the issues are closed. We do not keep TODO items open in the issue tracker. +Issues that are filed or reported that are not really bugs but more missing +features or ideas for future improvements and so on are marked as +*enhancement* or *feature-request* and get added to the `TODO` document and +the issues are closed. We do not keep TODO items open in the issue tracker. - The `TODO` document is full of ideas and suggestions of what we can add or - fix one day. You are always encouraged and free to grab one of those items and - take up a discussion with the curl development team on how that could be - implemented or provided in the project so that you can work on ticking it odd - that document. +The `TODO` document is full of ideas and suggestions of what we can add or +fix one day. You are always encouraged and free to grab one of those items and +take up a discussion with the curl development team on how that could be +implemented or provided in the project so that you can work on ticking it odd +that document. - If an issue is rather a bug and not a missing feature or functionality, it is - listed in `KNOWN_BUGS` instead. +If an issue is rather a bug and not a missing feature or functionality, it is +listed in `KNOWN_BUGS` instead. ## Closing off stalled bugs - The [issue and pull request trackers](https://github.com/curl/curl) only hold - "active" entries open (using a non-precise definition of what active actually - is, but they are at least not completely dead). Those that are abandoned or - in other ways dormant are closed and sometimes added to `TODO` and - `KNOWN_BUGS` instead. +The [issue and pull request trackers](https://github.com/curl/curl) only hold +"active" entries open (using a non-precise definition of what active actually +is, but they are at least not completely dead). Those that are abandoned or +in other ways dormant are closed and sometimes added to `TODO` and +`KNOWN_BUGS` instead. - This way, we only have "active" issues open on GitHub. Irrelevant issues and - pull requests do not distract developers or casual visitors. +This way, we only have "active" issues open on GitHub. Irrelevant issues and +pull requests do not distract developers or casual visitors. diff --git a/docs/CIPHERS.md b/docs/CIPHERS.md index 8ce938d9c2..060d3da949 100644 --- a/docs/CIPHERS.md +++ b/docs/CIPHERS.md @@ -78,6 +78,7 @@ OpenSSL (1.1.1+, curl 7.61.0+), LibreSSL (3.4.1+, curl 8.3.0+), wolfSSL (curl 8.10.0+) and mbedTLS (3.6.0+, curl 8.10.0+). The list of cipher suites that can be used for the `--tls13-ciphers` option: + ``` TLS_AES_128_GCM_SHA256 TLS_AES_256_GCM_SHA384 @@ -109,15 +110,15 @@ TLS 1.2 cipher suites with curl it is recommended that you use OpenSSL names as these are most widely recognized by the supported SSL backends. The complete list of cipher suites that may be considered for the `--ciphers` -option is extensive, it consists of more than 300 ciphers suites. However, -nowadays for most of them their usage is discouraged, and support for a lot of -them have been removed from the various SSL backends, if ever implemented at -all. +option is extensive, it consists of more than 300 ciphers suites. Nowadays, +most of them are discouraged, and support for a lot of them has been removed +from the various SSL backends, if ever implemented at all. A shortened list (based on [recommendations by Mozilla](https://wiki.mozilla.org/Security/Server_Side_TLS)) of cipher suites, which are (mostly) supported by all SSL backends, that can be used for the `--ciphers` option: + ``` ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-GCM-SHA256 @@ -163,11 +164,11 @@ for further information on that format. Schannel does not support setting individual TLS 1.2 cipher suites directly. It only allows the enabling and disabling of encryption algorithms. These are in the form of `CALG_xxx`, see the [Schannel `ALG_ID` -documentation](https://docs.microsoft.com/windows/desktop/SecCrypto/alg-id) +documentation](https://learn.microsoft.com/windows/win32/seccrypto/alg-id) for a list of these algorithms. Also, (since curl 7.77.0) `SCH_USE_STRONG_CRYPTO` can be given to pass that flag to Schannel, lookup the [documentation for the Windows version in -use](https://learn.microsoft.com/en-us/windows/win32/secauthn/cipher-suites-in-schannel) +use](https://learn.microsoft.com/windows/win32/secauthn/cipher-suites-in-schannel) to see how that affects the cipher suite selection. When not specifying the `--ciphers` and `--tls13-ciphers` options curl passes this flag by default. @@ -180,6 +181,7 @@ curl \ ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305 \ https://example.com/ ``` + Restrict ciphers to `aes128-gcm` and `chacha20`. Works with OpenSSL, LibreSSL, mbedTLS and wolfSSL. @@ -189,6 +191,7 @@ curl \ --tls13-ciphers TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256 \ https://example.com/ ``` + Restrict to only TLS 1.3 with `aes128-gcm` and `chacha20` ciphers. Works with OpenSSL, LibreSSL, mbedTLS, wolfSSL and Schannel. @@ -198,6 +201,7 @@ curl \ ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305 \ https://example.com/ ``` + Restrict TLS 1.2 ciphers to `aes128-gcm` and `chacha20`, use default TLS 1.3 ciphers (if TLS 1.3 is available). Works with OpenSSL, LibreSSL, BoringSSL, mbedTLS and wolfSSL. @@ -244,6 +248,7 @@ curl \ --ciphers '-CIPHER_ALL:+AES-128-GCM:+CHACHA20-POLY1305' \ https://example.com/ ``` + Restrict ciphers to `aes128-gcm` and `chacha20` in GnuTLS. ```sh @@ -251,6 +256,7 @@ curl \ --ciphers 'NORMAL:-VERS-ALL:+TLS1.3:-AES-256-GCM' \ https://example.com/ ``` + Restrict to only TLS 1.3 without the `aes256-gcm` cipher. ```sh @@ -258,13 +264,15 @@ curl \ --ciphers 'NORMAL:-VERS-ALL:+TLS1.2:-CIPHER_ALL:+CAMELLIA-128-GCM' \ https://example.com/ ``` + Restrict to only TLS 1.2 with the `CAMELLIA-128-GCM` cipher. ## Further reading + - [OpenSSL cipher suite names documentation](https://docs.openssl.org/master/man1/openssl-ciphers/#cipher-suite-names) - [wolfSSL cipher support documentation](https://www.wolfssl.com/documentation/manuals/wolfssl/chapter04.html#cipher-support) - [mbedTLS cipher suites reference](https://mbed-tls.readthedocs.io/projects/api/en/development/api/file/ssl__ciphersuites_8h/) -- [Schannel cipher suites documentation](https://learn.microsoft.com/en-us/windows/win32/secauthn/cipher-suites-in-schannel) +- [Schannel cipher suites documentation](https://learn.microsoft.com/windows/win32/secauthn/cipher-suites-in-schannel) - [IANA cipher suites list](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4) - [Wikipedia cipher suite article](https://en.wikipedia.org/wiki/Cipher_suite) - [GnuTLS Priority Strings](https://gnutls.org/manual/html_node/Priority-Strings.html) diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md index 5c8878189c..f42fd493d0 100644 --- a/docs/CODE_OF_CONDUCT.md +++ b/docs/CODE_OF_CONDUCT.md @@ -4,8 +4,7 @@ Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl --> -Contributor Code of Conduct -=========================== +# Contributor Code of Conduct As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, @@ -33,6 +32,7 @@ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. -This Code of Conduct is adapted from the [Contributor -Covenant](https://contributor-covenant.org/), version 1.1.0, available at +This Code of Conduct is adapted from the +[Contributor Covenant](https://contributor-covenant.org/), version 1.1.0, +available at [https://contributor-covenant.org/version/1/1/0/](https://contributor-covenant.org/version/1/1/0/) diff --git a/docs/CONTRIBUTE.md b/docs/CONTRIBUTE.md index 710b3a1ef8..3168091930 100644 --- a/docs/CONTRIBUTE.md +++ b/docs/CONTRIBUTE.md @@ -48,7 +48,7 @@ By submitting a patch to the curl project, you are assumed to have the right to the code and to be allowed by your employer or whatever to hand over that patch/code to us. We credit you for your changes as far as possible, to give credit but also to keep a trace back to who made what changes. Please always -provide us with your full real name when contributing, +provide us with your full real name when contributing. ## What To Read @@ -56,7 +56,7 @@ Source code, the man pages, the [INTERNALS document](https://curl.se/dev/internals.html), [TODO](https://curl.se/docs/todo.html), [KNOWN_BUGS](https://curl.se/docs/knownbugs.html) and the [most recent -changes](https://curl.se/dev/sourceactivity.html) in git. Just lurking on the +changes](https://curl.se/dev/sourceactivity.html) in git. Lurking on the [curl-library mailing list](https://curl.se/mail/list.cgi?list=curl-library) gives you a lot of insights on what's going on right now. Asking there is a good idea too. @@ -102,7 +102,7 @@ and regression in the future. Please try to get the latest available sources to make your patches against. It makes the lives of the developers so much easier. The best is if you get the most up-to-date sources from the git repository, but the latest release -archive is quite OK as well. +archive is OK as well. ### Documentation @@ -111,8 +111,8 @@ projects but someone's gotta do it. It makes things a lot easier if you submit a small description of your fix or your new features with every contribution so that it can be swiftly added to the package documentation. -Documentation is mostly provided as manpages or plain ASCII files. The -manpages are rendered from their source files that are usually written using +Documentation is mostly provided as man pages or plain ASCII files. The +man pages are rendered from their source files that are usually written using markdown. Most HTML files on the website and in the release archives are generated from corresponding markdown and ASCII files. @@ -145,12 +145,12 @@ then come on GitHub. Your changes be reviewed and discussed and you are expected to correct flaws pointed out and update accordingly, or the change risks stalling and -eventually just getting deleted without action. As a submitter of a change, -you are the owner of that change until it has been merged. +eventually getting deleted without action. As a submitter of a change, you are +the owner of that change until it has been merged. Respond on the list or on GitHub about the change and answer questions and/or fix nits/flaws. This is important. We take lack of replies as a sign that you -are not anxious to get your patch accepted and we tend to simply drop such +are not anxious to get your patch accepted and we tend to drop such changes. ## About pull requests @@ -169,8 +169,8 @@ ways. [See the CI document for more information](https://github.com/curl/curl/blob/master/docs/tests/CI.md). Sometimes the tests fail due to a dependency service temporarily being offline -or otherwise unavailable, e.g. package downloads. In this case you can just -try to update your pull requests to rerun the tests later as described below. +or otherwise unavailable, e.g. package downloads. In this case you can try to +update your pull requests to rerun the tests later as described below. You can update your pull requests by pushing new commits or force-pushing changes to existing commits. Force-pushing an amended commit without any @@ -195,7 +195,7 @@ Once your pull request has been approved it can be merged by a maintainer. For new features, or changes, we require that the *feature window* is open for the pull request to be merged. This is typically a three week period that starts ten days after a previous release. New features submitted as pull -requests while the window is closed simply have to wait until it opens to get +requests while the window is closed have to wait until it opens to get merged. If time passes without your approved pull request gets merged: feel free to @@ -224,9 +224,9 @@ How to write git commit messages in the curl project. The first line is a succinct description of the change and should ideally work as a single line in the RELEASE NOTES. - - use the imperative, present tense: **change** not "changed" nor "changes" - - do not capitalize the first letter - - no period (.) at the end +- use the imperative, present tense: **change** not "changed" nor "changes" +- do not capitalize the first letter +- no period (.) at the end The `[area]` in the first line can be `http2`, `cookies`, `openssl` or similar. There is no fixed list to select from but using the same "area" as @@ -238,36 +238,36 @@ Use the following ways to improve the message and provide pointers to related work. - `Follow-up to {shorthash}` - if this fixes or continues a previous commit; -add a `Ref:` that commit's PR or issue if it is not a small, obvious fix; -followed by an empty line + add a `Ref:` that commit's PR or issue if it is not a small, obvious fix; + followed by an empty line - `Bug: URL` to the source of the report or more related discussion; use -`Fixes` for GitHub issues instead when that is appropriate. + `Fixes` for GitHub issues instead when that is appropriate. - `Approved-by: John Doe` - credit someone who approved the PR. - `Authored-by: John Doe` - credit the original author of the code; only use -this if you cannot use `git commit --author=...`. + this if you cannot use `git commit --author=...`. - `Signed-off-by: John Doe` - we do not use this, but do not bother removing it. - `whatever-else-by:` credit all helpers, finders, doers; try to use one of -the following keywords if at all possible, for consistency: `Acked-by:`, -`Assisted-by:`, `Co-authored-by:`, `Found-by:`, `Reported-by:`, -`Reviewed-by:`, `Suggested-by:`, `Tested-by:`. + the following keywords if at all possible, for consistency: `Acked-by:`, + `Assisted-by:`, `Co-authored-by:`, `Found-by:`, `Reported-by:`, + `Reviewed-by:`, `Suggested-by:`, `Tested-by:`. - `Ref: #1234` - if this is related to a GitHub issue or PR, possibly one that -has already been closed. + has already been closed. - `Ref: URL` to more information about the commit; use `Bug:` instead for a -reference to a bug on another bug tracker] + reference to a bug on another bug tracker] - `Fixes #1234` - if this fixes a GitHub issue; GitHub closes the issue once -this commit is merged. + this commit is merged. - `Closes #1234` - if this merges a GitHub PR; GitHub closes the PR once this -commit is merged. + commit is merged. Do not forget to use commit with `--author` if you commit someone else's work, and make sure that you have your own user and email setup correctly in git @@ -285,11 +285,13 @@ If you are a frequent contributor, you may be given push access to the git repository and then you are able to push your changes straight into the git repository instead of sending changes as pull requests or by mail as patches. -Just ask if this is what you would want. You are required to have posted -several high quality patches first, before you can be granted push access. +Feel free to ask for this, if this is what you want. You are required to have +posted several high quality patches first, before you can be granted push +access. ## Useful resources - - [Webinar on getting code into cURL](https://www.youtube.com/watch?v=QmZ3W1d6LQI) + +- [Webinar on getting code into curl](https://youtu.be/QmZ3W1d6LQI) # Update copyright and license information @@ -319,13 +321,13 @@ You must also double-check the findings carefully before reporting them to us to validate that the issues are indeed existing and working exactly as the AI says. AI-based tools frequently generate inaccurate or fabricated results. -Further: it is *rarely* a good idea to just copy and paste an AI generated -report to the project. Those generated reports typically are too wordy and -rarely to the point (in addition to the common fabricated details). If you -actually find a problem with an AI and you have verified it yourself to be -true: write the report yourself and explain the problem as you have learned -it. This makes sure the AI-generated inaccuracies and invented issues are -filtered out early before they waste more people's time. +Further: it is *rarely* a good idea to copy and paste an AI generated report +to the project. Those generated reports typically are too wordy and rarely to +the point (in addition to the common fabricated details). If you actually find +a problem with an AI and you have verified it yourself to be true: write the +report yourself and explain the problem as you have learned it. This makes +sure the AI-generated inaccuracies and invented issues are filtered out early +before they waste more people's time. As we take security reports seriously, we investigate each report with priority. This work is both time and energy consuming and pulls us away from diff --git a/docs/CURL-DISABLE.md b/docs/CURL-DISABLE.md index 63de4026a6..a6a1ea1661 100644 --- a/docs/CURL-DISABLE.md +++ b/docs/CURL-DISABLE.md @@ -68,8 +68,8 @@ Disable the FTP (and FTPS) protocol ## `CURL_DISABLE_GETOPTIONS` -Disable the `curl_easy_options` API calls that lets users get information -about existing options to `curl_easy_setopt`. +Disable the `curl_easy_options()` API calls that lets users get information +about existing options to `curl_easy_setopt()`. ## `CURL_DISABLE_GOPHER` @@ -120,10 +120,6 @@ Disable MQTT support. Disable the netrc parser. -## `CURL_DISABLE_NTLM` - -Disable support for NTLM. - ## `CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG` Disable the auto load config support in the OpenSSL backend. @@ -161,9 +157,9 @@ Disable the SHA-512/256 hash algorithm. Disable the shuffle DNS feature -## `CURL_DISABLE_SMB` +## `CURL_ENABLE_SMB` -Disable the SMB(S) protocols +Enable the SMB(S) protocols ## `CURL_DISABLE_SMTP` @@ -182,6 +178,12 @@ Disable the TELNET protocol Disable the TFTP protocol +## `CURL_DISABLE_TYPECHECK` + +Disable `curl_easy_setopt()`/`curl_easy_getinfo()` type checking. + +Useful to improve build performance for the `tests/libtest` test tool. + ## `CURL_DISABLE_VERBOSE_STRINGS` Disable verbose strings and error messages. diff --git a/docs/CURLDOWN.md b/docs/CURLDOWN.md index 6726b3946c..ce19b5f5d6 100644 --- a/docs/CURLDOWN.md +++ b/docs/CURLDOWN.md @@ -119,7 +119,7 @@ syntax: ~~~ Quoted source code should start with `~~~c` and end with `~~~` while regular -quotes can start with `~~~` or just be indented with 4 spaces. +quotes can start with `~~~` or be indented with 4 spaces. Headers at top-level `#` get converted to `.SH`. @@ -134,8 +134,7 @@ Write italics like: This is *italics*. Due to how man pages do not support backticks especially formatted, such -occurrences in the source are instead just using italics in the generated -output: +occurrences in the source are instead using italics in the generated output: This `word` appears in italics. diff --git a/docs/DEPRECATE.md b/docs/DEPRECATE.md index 65c630cbee..fe00189182 100644 --- a/docs/DEPRECATE.md +++ b/docs/DEPRECATE.md @@ -12,70 +12,74 @@ email the as soon as possible and explain to us why this is a problem for you and how your use case cannot be satisfied properly using a workaround. -## winbuild build system +## TLS-SRP Authentication -curl drops support for the winbuild build method after September 2025. +Transport Layer Security Secure Remote Password is a TLS feature that does not +work with TLS 1.3 or QUIC and is virtually unused by curl users and in +general. -We recommend migrating to CMake. See the migration guide in -`docs/INSTALL-CMAKE.md`. +TLS-SRP support gets removed in August 2026. -## Windows CE +## drop SMB support -Windows CE "mainstream support" ended on October 9, 2018, and "Extended -Support" ended on October 10, 2023. +The SMB protocol has weak security and is rarely used these days. -curl drops all support in November 2025. +SMB support gets removed in September 2026. -## VS2008 +## drop NTLM support -curl drops support for getting built with Microsoft Visual Studio 2008 in -November 2025. +The NTLM authentication method has weak security and is rarely used these +days. It has been deprecated by Microsoft and does not work over HTTP/2 or +HTTP/3. -The only reason we kept support for this version is for Windows CE - and we -intend to remove support for that Operating System in this time frame as well. -Bumping the minimum to VS2010. VS2008 is a pain to support. +NTLM support gets removed in September 2026 -Previous discussion and details: https://github.com/curl/curl/discussions/15972 +## Local crypto implementations -## Windows XP +Since the dawn of time, curl bundles code for a few crypto and hash algorithms +in order to enable functionality for builds without TLS libraries. This list +includes MD4, MD5, SHA256, SHA256_512 and perhaps something more. -In January 2026, curl drops support for Windows XP and Server 2003. Their -"mainstream support" ended in 2014, with final updates on May 14, 2019. +Meanwhile, curl is almost always built to use a TLS/crypto library which for +sure has better maintained and better performing versions of these algorithms. -Making the new minimum target Windows version Vista / Server 2008. +Also, the local curl implementations are not as widely tested since curl +builds without TLS are rare. -## c-ares 1.16.0 +Since these implementations are going away, a good idea is to verify ahead of +time that builds using your preferred TLS library use the crypto functions +provided by that library and are not bundled by curl. -In March 2026, we drop support for all c-ares versions before 1.16.0. +The removal of local crypto functions subsequently disables some functions in +future curl versions when built without TLS support. For example Digest. -## OpenSSL 1.0.2 - -OpenSSL and others only ship fixes for this version to paying customers, -meaning users of the free version risk being vulnerable. - -We remove support for this OpenSSL version from curl in December 2025. - -## OpenSSL 1.1.1 - -OpenSSL and others only ship fixes to paying customers, meaning users of the -free version risk being vulnerable. - -We remove support for this OpenSSL version from curl in June 2026. +Local crypto gets removed in October 2026. ## Past removals - - axTLS (removed in 7.63.0) - - Pipelining (removed in 7.65.0) - - PolarSSL (removed in 7.69.0) - - NPN (removed in 7.86.0) - - Support for systems without 64-bit data types (removed in 8.0.0) - - NSS (removed in 8.3.0) - - gskit (removed in 8.3.0) - - MinGW v1 (removed in 8.4.0) - - NTLM_WB (removed in 8.8.0) - - space-separated `NOPROXY` patterns (removed in 8.9.0) - - hyper (removed in 8.12.0) - - Support for Visual Studio 2005 and older (removed in 8.13.0) - - Secure Transport (removed in 8.15.0) - - BearSSL (removed in 8.15.0) - - msh3 (removed in 8.16.0) +- axTLS (removed in 7.63.0) +- Pipelining (removed in 7.65.0) +- PolarSSL (removed in 7.69.0) +- NPN (removed in 7.86.0) +- Support for systems without 64-bit data types (removed in 8.0.0) +- NSS (removed in 8.3.0) +- gskit (removed in 8.3.0) +- MinGW v1 (removed in 8.4.0) +- NTLM_WB (removed in 8.8.0) +- space-separated `NOPROXY` patterns (removed in 8.9.0) +- hyper (removed in 8.12.0) +- Support for Visual Studio 2005 and older (removed in 8.13.0) +- Secure Transport (removed in 8.15.0) +- BearSSL (removed in 8.15.0) +- msh3 (removed in 8.16.0) +- winbuild build system (removed in 8.17.0) +- Windows CE (removed in 8.18.0) +- Support for Visual Studio 2008 (removed in 8.18.0) +- OpenSSL 1.1.1 and older (removed in 8.18.0) +- Support for Windows XP (removed in 8.19.0) +- OpenSSL-QUIC (removed in 8.19.0) +- CMake 3.17 and older (removed in 8.20.0) +- RTMP (removed in 8.20.0) +- SMB (became opt-in in 8.20.0) +- NTLM (became opt-in in 8.20.0) +- c-ares < 1.16.0 (removed in 8.20.0) diff --git a/docs/DISTROS.md b/docs/DISTROS.md index e852b797d2..3a433445da 100644 --- a/docs/DISTROS.md +++ b/docs/DISTROS.md @@ -49,8 +49,8 @@ archives](https://curl.se/mail/list.cgi?list=curl-distros)). *Rolling Release* -- curl package source and patches: https://git.buildroot.net/buildroot/tree/package/libcurl -- curl issues: https://bugs.buildroot.org/buglist.cgi?quicksearch=curl +- curl package source and patches: **missing URL** +- curl issues: **missing URL** ## Chimera @@ -152,6 +152,12 @@ Issues and patches for this are managed in the main curl project. Homebrew's policy is that all patches and issues should be submitted upstream unless it is specific to Homebrew's way of packaging software. +## LibreELEC + +- curl: https://github.com/LibreELEC/LibreELEC.tv/blob/master/packages/web/curl/ +- curl issues: https://github.com/LibreELEC/LibreELEC.tv/issues?q=is%3Aissue%20state%3Aopen%20curl +- curl patches: https://github.com/LibreELEC/LibreELEC.tv/blob/master/packages/web/curl/patches/ + ## MacPorts *Rolling Release* @@ -165,7 +171,7 @@ unless it is specific to Homebrew's way of packaging software. - curl: https://svnweb.mageia.org/packages/cauldron/curl/current/SPECS/curl.spec?view=markup - curl issues: https://bugs.mageia.org/buglist.cgi?bug_status=NEW&bug_status=UNCONFIRMED&bug_status=NEEDINFO&bug_status=UPSTREAM&bug_status=ASSIGNED&component=RPM%20Packages&f1=cf_rpmpkg&list_id=176576&o1=casesubstring&product=Mageia&query_format=advanced&v1=curl - curl patches: https://svnweb.mageia.org/packages/cauldron/curl/current/SOURCES/ -- curl patches in stable distro releases: https://svnweb.mageia.org/packages/updates//curl/current/SOURCES/ +- curl patches in stable distro releases: https://svnweb.mageia.org/packages/updates/9/curl/current/SOURCES/ - curl security: https://advisories.mageia.org/src_curl.html ## MSYS2 @@ -188,9 +194,9 @@ unless it is specific to Homebrew's way of packaging software. *Rolling Release* -- curl: https://github.com/lordmulder/cURL-build-win32 -- curl issues: https://github.com/lordmulder/cURL-build-win32/issues -- curl patches: https://github.com/lordmulder/cURL-build-win32/tree/master/patch +- curl: https://github.com/lordmulder/curl-build-win32 +- curl issues: https://github.com/lordmulder/curl-build-win32/issues +- curl patches: https://github.com/lordmulder/curl-build-win32/tree/master/patch ## NixOS @@ -222,6 +228,12 @@ can also be used on other distributions - curl issues: https://support.oracle.com/ (requires support contract) - curl patches: https://github.com/oracle/solaris-userland/tree/master/components/curl/patches +## OpenBSD + +- curl: https://github.com/openbsd/ports/tree/master/net/curl +- curl issues: https://www.openbsd.org/mail.html (ports mailing list) +- curl patches: https://github.com/openbsd/ports/tree/master/net/curl/patches + ## OpenEmbedded / Yocto Project *Rolling Release* @@ -250,7 +262,7 @@ can also be used on other distributions ## Rocky Linux - curl: https://git.rockylinux.org/staging/rpms/curl/-/blob/r9/SPECS/curl.spec -- curl issues: https://bugs.rockylinux.org +- curl issues: https://bugs.rockylinux.org/ - curl patches: https://git.rockylinux.org/staging/rpms/curl/-/tree/r9/SOURCES ## SerenityOS diff --git a/docs/EARLY-RELEASE.md b/docs/EARLY-RELEASE.md index e66dbbd444..8ec74c3e20 100644 --- a/docs/EARLY-RELEASE.md +++ b/docs/EARLY-RELEASE.md @@ -27,14 +27,14 @@ in the git master branch. An early patch release means that we ship a new, complete and full release called `major.minor.patch` where the `patch` part is increased by one since the previous release. A curl release is a curl release. There is no small or -big and we never release just a patch. There is only "release". +big and we never ship stand-alone separate patches. There is only "release". ## Questions to ask - - Is there a security advisory rated high or critical? - - Is there a data corruption bug? - - Did the bug cause an API/ABI breakage? - - Does the problem annoy a significant share of the user population? +- Is there a security advisory rated high or critical? +- Is there a data corruption bug? +- Did the bug cause an API/ABI breakage? +- Does the problem annoy a significant share of the user population? If the answer is yes to one or more of the above, an early release might be warranted. @@ -42,25 +42,25 @@ warranted. More questions to ask ourselves when doing the assessment if the answers to the three ones above are all 'no'. - - Does the bug cause curl to prematurely terminate? - - How common is the affected buggy option/feature/protocol/platform to get - used? - - How large is the estimated impacted user base? - - Does the bug block something crucial for applications or other adoption of - curl "out there" ? - - Does the bug cause problems for curl developers or others on "the curl - team" ? - - Is the bug limited to the curl tool only? That might have a smaller impact - than a bug also present in libcurl. - - Is there a (decent) workaround? - - Is it a regression? Is the bug introduced in this release? - - Can the bug be fixed "easily" by applying a patch? - - Does the bug break the build? Most users do not build curl themselves. - - How long is it until the already scheduled next release? - - Can affected users safely rather revert to a former release until the next - scheduled release? - - Is it a performance regression with no functionality side-effects? If so it - has to be substantial. +- Does the bug cause curl to prematurely terminate? +- How common is the affected buggy option/feature/protocol/platform to get + used? +- How large is the estimated impacted user base? +- Does the bug block something crucial for applications or other adoption of + curl "out there" ? +- Does the bug cause problems for curl developers or others on "the curl + team" ? +- Is the bug limited to the curl tool only? That might have a smaller impact + than a bug also present in libcurl. +- Is there a (decent) workaround? +- Is it a regression? Is the bug introduced in this release? +- Can the bug be fixed "easily" by applying a patch? +- Does the bug break the build? Most users do not build curl themselves. +- How long is it until the already scheduled next release? +- Can affected users safely rather revert to a former release until the next + scheduled release? +- Is it a performance regression with no functionality side-effects? If so it + has to be substantial. ## If an early release is deemed necessary diff --git a/docs/ECH.md b/docs/ECH.md index 969bbfa27f..32beba267e 100644 --- a/docs/ECH.md +++ b/docs/ECH.md @@ -20,41 +20,40 @@ discussion about a good path forward for ECH support in curl. To build the OpenSSL project's ECH feature branch: -```bash - cd $HOME/code - git clone https://github.com/openssl/openssl - cd openssl - git checkout feature/ech - ./config --libdir=lib --prefix=$HOME/code/openssl-local-inst - ...stuff... - make -j8 - ...more stuff... - make install_sw - ...a little bit of stuff... +```sh +cd $HOME/code +git clone https://github.com/openssl/openssl --branch feature/ech +cd openssl +./config --libdir=lib --prefix=$HOME/code/openssl-local-inst +...stuff... +make -j8 +...more stuff... +make install_sw +...a little bit of stuff... ``` To build curl ECH-enabled, making use of the above: -```bash - cd $HOME/code - git clone https://github.com/curl/curl - cd curl - autoreconf -fi - LDFLAGS="-Wl,-rpath,$HOME/code/openssl-local-inst/lib/" ./configure --with-ssl=$HOME/code/openssl-local-inst --enable-ech - ...lots of output... - WARNING: ECH HTTPSRR enabled but marked EXPERIMENTAL... - make - ...lots more output... +```sh +cd $HOME/code +git clone https://github.com/curl/curl +cd curl +autoreconf -fi +LDFLAGS="-Wl,-rpath,$HOME/code/openssl-local-inst/lib/" ./configure --with-ssl=$HOME/code/openssl-local-inst --enable-ech +...lots of output... +WARNING: ECH HTTPSRR enabled but marked EXPERIMENTAL... +make +...lots more output... ``` -If you do not get that WARNING at the end of the ``configure`` command, then +If you do not get that WARNING at the end of the `configure` command, then ECH is not enabled, so go back some steps and re-do whatever needs re-doing:-) -If you want to debug curl then you should add ``--enable-debug`` to the -``configure`` command. +If you want to debug curl then you should add `--enable-debug` to the +`configure` command. In a recent (2024-05-20) build on one machine, configure failed to find the ECH-enabled SSL library, apparently due to the existence of -``$HOME/code/openssl-local-inst/lib/pkgconfig`` as a directory containing +`$HOME/code/openssl-local-inst/lib/pkgconfig` as a directory containing various settings. Deleting that directory worked around the problem but may not be the best solution. @@ -63,24 +62,22 @@ not be the best solution. curl supports using DoH for A/AAAA lookups so it was relatively easy to add retrieval of HTTPS RRs in that situation. To use ECH and DoH together: -```bash - cd $HOME/code/curl - LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl --ech true --doh-url https://one.one.one.one/dns-query https://defo.ie/ech-check.php - ... - SSL_ECH_STATUS: success good
- ... +```sh +cd $HOME/code/curl +LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl --ech true --doh-url https://one.one.one.one/dns-query https://defo.ie/ech-check.php +... +SSL_ECH_STATUS: success good
+... ``` The output snippet above is within the HTML for the webpage, when things work. The above works for these test sites: -```bash - https://defo.ie/ech-check.php - https://draft-13.esni.defo.ie:8413/stats - https://draft-13.esni.defo.ie:8414/stats - https://crypto.cloudflare.com/cdn-cgi/trace - https://tls-ech.dev +```sh +https://defo.ie/ech-check.php +https://crypto.cloudflare.com/cdn-cgi/trace +https://tls-ech.dev/ ``` The list above has 4 different server technologies, implemented by 3 different @@ -89,13 +86,13 @@ parties, and includes a case (the port 8414 server) where HelloRetryRequest We currently support the following new curl command line arguments/options: -- ``--ech `` - the ``config`` value can be one of: - - ``false`` says to not attempt ECH - - ``true`` says to attempt ECH, if possible - - ``grease`` if attempting ECH is not possible, then send a GREASE ECH extension - - ``hard`` hard-fail the connection if ECH cannot be attempted - - ``ecl:`` a base64 encoded ECHConfigList, rather than one accessed from the DNS - - ``pn:`` override the ``public_name`` from an ECHConfigList +- `--ech ` - the `config` value can be one of: + - `false` says to not attempt ECH + - `true` says to attempt ECH, if possible + - `grease` if attempting ECH is not possible, then send a GREASE ECH extension + - `hard` hard-fail the connection if ECH cannot be attempted + - `ecl:` a base64 encoded ECHConfigList, rather than one accessed from the DNS + - `pn:` override the `public_name` from an ECHConfigList Note that in the above "attempt ECH" means the client emitting a TLS ClientHello with a "real" ECH extension, but that does not mean that the @@ -107,30 +104,33 @@ reasons. To supply the ECHConfigList on the command line, you might need a bit of cut-and-paste, e.g.: -```bash - dig +short https defo.ie - 1 . ipv4hint=213.108.108.101 ech=AED+DQA8PAAgACD8WhlS7VwEt5bf3lekhHvXrQBGDrZh03n/LsNtAodbUAAEAAEAAQANY292ZXIuZGVmby5pZQAA ipv6hint=2a00:c6c0:0:116:5::10 +```sh +dig +short https defo.ie +1 . ipv4hint=213.108.108.101 ech=AED+DQA8PAAgACD8WhlS7VwEt5bf3lekhHvXrQBGDrZh03n/LsNtAodbUAAEAAEAAQANY292ZXIuZGVmby5pZQAA + ipv6hint=2a00:c6c0:0:116:5::10 ``` Then paste the base64 encoded ECHConfigList onto the curl command line: -```bash - LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl --ech ecl:AED+DQA8PAAgACD8WhlS7VwEt5bf3lekhHvXrQBGDrZh03n/LsNtAodbUAAEAAEAAQANY292ZXIuZGVmby5pZQAA https://defo.ie/ech-check.php - ... - SSL_ECH_STATUS: success good
- ... +```sh +LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl --ech ecl:AED+DQA8PAAgACD8WhlS7VwEt5bf3lekhHvXrQBGDrZh03n/LsNtAodbUAAEAAEAAQANY292ZXIuZGVmby5pZQAA \ + https://defo.ie/ech-check.php +... +SSL_ECH_STATUS: success good
+... ``` The output snippet above is within the HTML for the webpage. -If you paste in the wrong ECHConfigList (it changes hourly for ``defo.ie``) you +If you paste in the wrong ECHConfigList (it changes hourly for `defo.ie`) you should get an error like this: -```bash - LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl -vvv --ech ecl:AED+DQA8yAAgACDRMQo+qYNsNRNj+vfuQfFIkrrUFmM4vogucxKj/4nzYgAEAAEAAQANY292ZXIuZGVmby5pZQAA https://defo.ie/ech-check.php - ... - * OpenSSL/3.3.0: error:0A00054B:SSL routines::ech required - ... +```sh +LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl -vvv --ech ecl:AED+DQA8yAAgACDRMQo+qYNsNRNj+vfuQfFIkrrUFmM4vogucxKj/4nzYgAEAAEAAQANY292ZXIuZGVmby5pZQAA \ + https://defo.ie/ech-check.php +... +* OpenSSL/3.3.0: error:0A00054B:SSL routines::ech required +... ``` There is a reason to want this command line option - for use before publishing @@ -138,15 +138,18 @@ an ECHConfigList in the DNS as per the Internet-draft [A well-known URI for publishing ECHConfigList values](https://datatracker.ietf.org/doc/draft-ietf-tls-wkech/). If you do use a wrong ECHConfigList value, then the server might return a -good value, via the ``retry_configs`` mechanism. You can see that value in +good value, via the `retry_configs` mechanism. You can see that value in the verbose output, e.g.: -```bash - LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl -vvv --ech ecl:AED+DQA8yAAgACDRMQo+qYNsNRNj+vfuQfFIkrrUFmM4vogucxKj/4nzYgAEAAEAAQANY292ZXIuZGVmby5pZQAA https://defo.ie/ech-check.php - ... -* ECH: retry_configs AQD+DQA8DAAgACBvYqJy+Hgk33wh/ZLBzKSPgwxeop7gvojQzfASq7zeZQAEAAEAAQANY292ZXIuZGVmby5pZQAA/g0APEMAIAAgXkT5r4cYs8z19q5rdittyIX8gfQ3ENW4wj1fVoiJZBoABAABAAEADWNvdmVyLmRlZm8uaWUAAP4NADw2ACAAINXSE9EdXzEQIJZA7vpwCIQsWqsFohZARXChgPsnfI1kAAQAAQABAA1jb3Zlci5kZWZvLmllAAD+DQA8cQAgACASeiD5F+UoSnVoHvA2l1EifUVMFtbVZ76xwDqmMPraHQAEAAEAAQANY292ZXIuZGVmby5pZQAA +```sh +LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl -vvv --ech ecl:AED+DQA8yAAgACDRMQo+qYNsNRNj+vfuQfFIkrrUFmM4vogucxKj/4nzYgAEAAEAAQANY292ZXIuZGVmby5pZQAA \ + https://defo.ie/ech-check.php +... +* ECH: retry_configs AQD+DQA8DAAgACBvYqJy+Hgk33wh/ZLBzKSPgwxeop7gvojQzfASq7zeZQAEAAEAAQANY292ZXIuZGVmby5pZQAA/g0APEMAIAAgXkT5r4cYs8z19q5rdittyIX + 8gfQ3ENW4wj1fVoiJZBoABAABAAEADWNvdmVyLmRlZm8uaWUAAP4NADw2ACAAINXSE9EdXzEQIJZA7vpwCIQsWqsFohZARXChgPsnfI1kAAQAAQABAA1jb3Zlci5kZWZvLmllAAD+DQA8c + QAgACASeiD5F+UoSnVoHvA2l1EifUVMFtbVZ76xwDqmMPraHQAEAAEAAQANY292ZXIuZGVmby5pZQAA * ECH: retry_configs for defo.ie from cover.defo.ie, 319 - ... +... ``` At that point, you could copy the base64 encoded value above and try again. @@ -154,229 +157,229 @@ For now, this only works for the OpenSSL and BoringSSL/AWS-LC builds. ## Default settings -curl has various ways to configure default settings, e.g. in ``$HOME/.curlrc``, +curl has various ways to configure default settings, e.g. in `$HOME/.curlrc`, so one can set the DoH URL and enable ECH that way: -```bash - cat ~/.curlrc - doh-url=https://one.one.one.one/dns-query - silent - ech=true +```sh +cat ~/.curlrc +doh-url=https://one.one.one.one/dns-query +silent +ech=true ``` Note that when you use the system's curl command (rather than our ECH-enabled -build), it is liable to warn that ``ech`` is an unknown option. If that is an +build), it is liable to warn that `ech` is an unknown option. If that is an issue (e.g. if some script re-directs stdout and stderr somewhere) then adding -the ``silent`` line above seems to be a good enough fix. (Though of +the `silent` line above seems to be a good enough fix. (Though of course, yet another script could depend on non-silent behavior, so you may have to figure out what you prefer yourself.) That seems to have changed with the -latest build, previously ``silent=TRUE`` was what I used in ``~/.curlrc`` but +latest build, previously `silent=TRUE` was what I used in `~/.curlrc` but now that seems to cause a problem, so that the following line(s) are ignored. -If you want to always use our OpenSSL build you can set ``LD_LIBRARY_PATH`` +If you want to always use our OpenSSL build you can set `LD_LIBRARY_PATH` in the environment: -```bash - export LD_LIBRARY_PATH=$HOME/code/openssl +```sh +export LD_LIBRARY_PATH=$HOME/code/openssl ``` When you do the above, there can be a mismatch between OpenSSL versions -for applications that check that. A ``git push`` for example fails so you -should unset ``LD_LIBRARY_PATH`` before doing that or use a different shell. +for applications that check that. A `git push` for example fails so you +should unset `LD_LIBRARY_PATH` before doing that or use a different shell. -```bash - git push - OpenSSL version mismatch. Built against 30000080, you have 30200000 - ... +```sh +git push +OpenSSL version mismatch. Built against 30000080, you have 30200000 +... ``` With all that setup as above the command line gets simpler: -```bash - ./src/curl https://defo.ie/ech-check.php - ... - SSL_ECH_STATUS: success good
- ... +```sh +./src/curl https://defo.ie/ech-check.php +... +SSL_ECH_STATUS: success good
+... ``` -The ``--ech true`` option is opportunistic, so tries to do ECH but does not fail if -the client for example cannot find any ECHConfig values. The ``--ech hard`` +The `--ech true` option is opportunistic, so tries to do ECH but does not fail if +the client for example cannot find any ECHConfig values. The `--ech hard` option hard-fails if there is no ECHConfig found in DNS, so for now, that is not a good option to set as a default. Once ECH has really been attempted by the client, if decryption on the server side fails, then curl fails. ## Code changes for ECH support when using DoH -Code changes are ``#ifdef`` protected via ``USE_ECH`` or ``USE_HTTPSRR``: +Code changes are `#ifdef` protected via `USE_ECH` or `USE_HTTPSRR`: -- ``USE_HTTPSRR`` is used for HTTPS RR retrieval code that could be generically +- `USE_HTTPSRR` is used for HTTPS RR retrieval code that could be generically used should non-ECH uses for HTTPS RRs be identified, e.g. use of ALPN values or IP address hints. -- ``USE_ECH`` protects ECH specific code. +- `USE_ECH` protects ECH specific code. There are various obvious code blocks for handling the new command line arguments which are not described here, but should be fairly clear. -As shown in the ``configure`` usage above, there are ``configure.ac`` changes -that allow separately dis/enabling ``USE_HTTPSRR`` and ``USE_ECH``. If ``USE_ECH`` -is enabled, then ``USE_HTTPSRR`` is forced. In both cases ``CURL_DISABLE_DOH`` +As shown in the `configure` usage above, there are `configure.ac` changes +that allow separately dis/enabling `USE_HTTPSRR` and `USE_ECH`. If `USE_ECH` +is enabled, then `USE_HTTPSRR` is forced. In both cases `CURL_DISABLE_DOH` must not be enabled. (There may be some configuration conflicts available for the determined :-) -The main functional change, as you would expect, is in ``lib/vtls/openssl.c`` +The main functional change, as you would expect, is in `lib/vtls/openssl.c` where an ECHConfig, if available from command line or DNS cache, is fed into the OpenSSL library via the new APIs implemented in our OpenSSL fork for that -purpose. This code also implements the opportunistic (``--ech true``) or hard-fail -(``--ech hard``) logic. +purpose. This code also implements the opportunistic (`--ech true`) or hard-fail +(`--ech hard`) logic. -Other than that, the main additions are in ``lib/doh.c`` -where we reuse ``dohprobe()`` to retrieve an HTTPS RR value for the target -domain. If such a value is found, that is stored using a new ``doh_store_https()`` -function in a new field in the ``dohentry`` structure. +Other than that, the main additions are in `lib/doh.c` +where we reuse `dohprobe()` to retrieve an HTTPS RR value for the target +domain. If such a value is found, that is stored using a new `doh_store_https()` +function in a new field in the `dohentry` structure. The qname for the DoH query is modified if the port number is not 443, as defined in the SVCB specification. -When the DoH process has worked, ``Curl_doh_is_resolved()`` now also returns -the relevant HTTPS RR value data in the ``Curl_dns_entry`` structure. +When the DoH process has worked, `Curl_doh_take_result()` now also returns +the relevant HTTPS RR value data in the `Curl_dns_entry` structure. That is later accessed when the TLS session is being established, if ECH is -enabled (from ``lib/vtls/openssl.c`` as described above). +enabled (from `lib/vtls/openssl.c` as described above). ## Limitations Things that need fixing, but that can probably be ignored for the moment: -- We could easily add code to make use of an ``alpn=`` value found in an HTTPS +- We could easily add code to make use of an `alpn=` value found in an HTTPS RR, passing that on to OpenSSL for use as the "inner" ALPN value, but have -yet to do that. + yet to do that. Current limitations (more interesting than the above): - Only the first HTTPS RR value retrieved is actually processed as described above, that could be extended in future, though picking the "right" HTTPS RR -could be non-trivial if multiple RRs are published - matching IP address hints -versus A/AAAA values might be a good basis for that. Last I checked though, -browsers supporting ECH did not handle multiple HTTPS RRs well, though that -needs re-checking as it has been a while. + could be non-trivial if multiple RRs are published - matching IP address + hints versus A/AAAA values might be a good basis for that. Last I checked + though, browsers supporting ECH did not handle multiple HTTPS RRs well, though + that needs re-checking as it has been a while. - It is unclear how one should handle any IP address hints found in an HTTPS RR. It may be that a bit of consideration of how "multi-CDN" deployments might -emerge would provide good answers there, but for now, it is not clear how best -curl might handle those values when present in the DNS. + emerge would provide good answers there, but for now, it is not clear how best + curl might handle those values when present in the DNS. - The SVCB/HTTPS RR specification supports a new "CNAME at apex" indirection ("aliasMode") - the current code takes no account of that at all. One could -envisage implementing the equivalent of following CNAMEs in such cases, but -it is not clear if that'd be a good plan. (As of now, chrome browsers do not seem -to have any support for that "aliasMode" and we have not checked Firefox for that -recently.) + envisage implementing the equivalent of following CNAMEs in such cases, but + it is not clear if that'd be a good plan. (As of now, chrome browsers do not + seem to have any support for that "aliasMode" and we have not checked Firefox + for that recently.) - We have not investigated what related changes or additions might be needed for applications using libcurl, as opposed to use of curl as a command line -tool. + tool. - We have not yet implemented tests as part of the usual curl test harness as -doing so would seem to require re-implementing an ECH-enabled server as part -of the curl test harness. For now, we have a ``./tests/ech_test.sh`` script -that attempts ECH with various test servers and with many combinations of the -allowed command line options. While that is a useful test and has find issues, -it is not comprehensive and we are not (as yet) sure what would be the right -level of coverage. When running that script you should not have a -``$HOME/.curlrc`` file that affects ECH or some of the negative tests could -produce spurious failures. + doing so would seem to require re-implementing an ECH-enabled server as part + of the curl test harness. For now, we have a `./tests/ech_test.sh` script + that attempts ECH with various test servers and with many combinations of the + allowed command line options. While that is a useful test and has find + issues, it is not comprehensive and we are not (as yet) sure what would be + the right level of coverage. When running that script you should not have a + `$HOME/.curlrc` file that affects ECH or some of the negative tests could + produce spurious failures. ## Building with cmake To build with cmake, assuming our ECH-enabled OpenSSL is as before: -```bash - cd $HOME/code - git clone https://github.com/curl/curl - cd curl - mkdir build - cd build - cmake -DOPENSSL_ROOT_DIR=$HOME/code/openssl -DUSE_ECH=1 .. - ... - make - ... - [100%] Built target curl +```sh +cd $HOME/code +git clone https://github.com/curl/curl +cd curl +mkdir build +cd build +cmake -DOPENSSL_ROOT_DIR=$HOME/code/openssl -DUSE_ECH=1 .. +... +make +... +[100%] Built target curl ``` The binary produced by the cmake build does not need any ECH-specific -``LD_LIBRARY_PATH`` setting. +`LD_LIBRARY_PATH` setting. ## BoringSSL build BoringSSL is also supported by curl and also supports ECH, so to build with that, instead of our ECH-enabled OpenSSL: -```bash - cd $HOME/code - git clone https://boringssl.googlesource.com/boringssl - cd boringssl - cmake -DCMAKE_INSTALL_PREFIX:PATH=$HOME/code/boringssl/inst -DBUILD_SHARED_LIBS=1 - make - ... - make install +```sh +cd $HOME/code +git clone https://boringssl.googlesource.com/boringssl +cd boringssl +cmake -DCMAKE_INSTALL_PREFIX:PATH=$HOME/code/boringssl/inst -DBUILD_SHARED_LIBS=1 +make +... +make install ``` Then: -```bash - cd $HOME/code - git clone https://github.com/curl/curl - cd curl - autoreconf -fi - LDFLAGS="-Wl,-rpath,$HOME/code/boringssl/inst/lib" ./configure --with-ssl=$HOME/code/boringssl/inst --enable-ech - ...lots of output... - WARNING: ECH HTTPSRR enabled but marked EXPERIMENTAL. Use with caution. - make +```sh +cd $HOME/code +git clone https://github.com/curl/curl +cd curl +autoreconf -fi +LDFLAGS="-Wl,-rpath,$HOME/code/boringssl/inst/lib" ./configure --with-ssl=$HOME/code/boringssl/inst --enable-ech +...lots of output... +WARNING: ECH HTTPSRR enabled but marked EXPERIMENTAL. Use with caution. +make ``` The BoringSSL/AWS-LC APIs are fairly similar to those in our ECH-enabled -OpenSSL fork, so code changes are also in ``lib/vtls/openssl.c``, protected -via ``#ifdef OPENSSL_IS_BORINGSSL`` and are mostly obvious API variations. +OpenSSL fork, so code changes are also in `lib/vtls/openssl.c`, protected +via `#ifdef OPENSSL_IS_BORINGSSL` and are mostly obvious API variations. -The BoringSSL/AWS-LC APIs however do not support the ``--ech pn:`` command +The BoringSSL/AWS-LC APIs however do not support the `--ech pn:` command line variant as of now. ## wolfSSL build wolfSSL also supports ECH and can be used by curl, so here's how: -```bash - cd $HOME/code - git clone https://github.com/wolfSSL/wolfssl - cd wolfssl - ./autogen.sh - ./configure --prefix=$HOME/code/wolfssl/inst --enable-ech --enable-debug --enable-opensslextra - make - make install +```sh +cd $HOME/code +git clone https://github.com/wolfSSL/wolfssl +cd wolfssl +./autogen.sh +./configure --prefix=$HOME/code/wolfssl/inst --enable-ech --enable-debug --enable-opensslextra +make +make install ``` -The install prefix (``inst``) in the above causes wolfSSL to be installed there +The install prefix (`inst`) in the above causes wolfSSL to be installed there and we seem to need that for the curl configure command to work out. The -``--enable-opensslextra`` turns out (after much faffing about;-) to be +`--enable-opensslextra` turns out (after much faffing about;-) to be important or else we get build problems with curl below. -```bash - cd $HOME/code - git clone https://github.com/curl/curl - cd curl - autoreconf -fi - ./configure --with-wolfssl=$HOME/code/wolfssl/inst --enable-ech - make +```sh +cd $HOME/code +git clone https://github.com/curl/curl +cd curl +autoreconf -fi +./configure --with-wolfssl=$HOME/code/wolfssl/inst --enable-ech +make ``` There are some known issues with the ECH implementation in wolfSSL: - The main issue is that the client currently handles HelloRetryRequest - incorrectly. [HRR issue](https://github.com/wolfSSL/wolfssl/issues/6802).) + incorrectly. [HRR issue](https://github.com/wolfSSL/wolfssl/issues/6802).) The HRR issue means that the client does not work for - [this ECH test web site](https://tls-ech.dev) and any other similarly configured - sites. + [this ECH test web site](https://tls-ech.dev/) and any other similarly + configured sites. - There is also an issue related to so-called middlebox compatibility mode. [middlebox compatibility issue](https://github.com/wolfSSL/wolfssl/issues/6774) @@ -384,22 +387,22 @@ There are some known issues with the ECH implementation in wolfSSL: There are what seem like oddball differences: -- The DoH URL in``$HOME/.curlrc`` can use `1.1.1.1` for OpenSSL but has to be +- The DoH URL in`$HOME/.curlrc` can use `1.1.1.1` for OpenSSL but has to be `one.one.one.one` for wolfSSL. The latter works for both, so OK, we us that. - There seems to be some difference in CA databases too - the wolfSSL version - does not like ``defo.ie``, whereas the system and OpenSSL ones do. We can - ignore that for our purposes via ``--insecure``/``-k`` but would need to fix + does not like `defo.ie`, whereas the system and OpenSSL ones do. We can + ignore that for our purposes via `--insecure`/`-k` but would need to fix for a real setup. (Browsers do like those certificates though.) Then there are some functional code changes: -- tweak to ``configure.ac`` to check if wolfSSL has ECH or not -- added code to ``lib/vtls/wolfssl.c`` mirroring what's done in the +- tweak to `configure.ac` to check if wolfSSL has ECH or not +- added code to `lib/vtls/wolfssl.c` mirroring what's done in the OpenSSL equivalent above. -- wolfSSL does not support ``--ech false`` or the ``--ech pn:`` command line +- wolfSSL does not support `--ech false` or the `--ech pn:` command line argument. -The lack of support for ``--ech false`` is because wolfSSL has decided to +The lack of support for `--ech false` is because wolfSSL has decided to always at least GREASE if built to support ECH. In other words, GREASE is a compile time choice for wolfSSL, but a runtime choice for OpenSSL or BoringSSL/AWS-LC. (Both are reasonable.) @@ -413,19 +416,19 @@ for ECH when DoH is not used by curl - if a system stub resolver supports DoT or DoH, then, considering only ECH and the network threat model, it would make sense for curl to support ECH without curl itself using DoH. The author for example uses a combination of stubby+unbound as the system resolver listening -on localhost:53, so would fit this use-case. That said, it is unclear if -this is a niche that is worth trying to address. (The author is just as happy to -let curl use DoH to talk to the same public recursive that stubby might use:-) +on localhost:53, so would fit this use-case. That said, it is unclear if this +is a niche that is worth trying to address. (The author is happy to let curl +use DoH to talk to the same public recursive that stubby might use:-) Assuming for the moment this is a use-case we would like to support, then if DoH is not being used by curl, it is not clear at this time how to provide -support for ECH. One option would seem to be to extend the ``c-ares`` library +support for ECH. One option would seem to be to extend the `c-ares` library to support HTTPS RRs, but in that case it is not now clear whether such -changes would be attractive to the ``c-ares`` maintainers, nor whether the +changes would be attractive to the `c-ares` maintainers, nor whether the "tag=value" extensibility inherent in the HTTPS/SVCB specification is a good -match for the ``c-ares`` approach of defining structures specific to decoded +match for the `c-ares` approach of defining structures specific to decoded answers for each supported RRtype. We are also not sure how many downstream -curl deployments actually make use of the ``c-ares`` library, which would +curl deployments actually make use of the `c-ares` library, which would affect the utility of such changes. Another option might be to consider using some other generic DNS library that does support HTTPS RRs, but it is unclear if such a library could or would be used by all or almost all curl builds and @@ -435,48 +438,41 @@ Our current conclusion is that doing the above is likely best left until we have some experience with the "using DoH" approach, so we are going to punt on this for now. -### Debugging - -Just a note to self as remembering this is a nuisance: - -```bash -LD_LIBRARY_PATH=$HOME/code/openssl:./lib/.libs gdb ./src/.libs/curl -``` - ### Localhost testing -It can be useful to be able to run against a localhost OpenSSL ``s_server`` +It can be useful to be able to run against a localhost OpenSSL `s_server` for testing. We have published instructions for such [localhost tests](https://github.com/defo-project/ech-dev-utils/blob/main/howtos/localhost-tests.md) in another repository. Once you have that set up, you can start a server and then run curl against that: -```bash - cd $HOME/code/ech-dev-utils - ./scripts/echsvr.sh -d - ... +```sh +cd $HOME/code/ech-dev-utils +./scripts/echsvr.sh -d +... ``` -The ``echsvr.sh`` script supports many ECH-related options. Use ``echsvr.sh -h`` +The `echsvr.sh` script supports many ECH-related options. Use `echsvr.sh -h` for details. In another window: -```bash - cd $HOME/code/curl/ - ./src/curl -vvv --insecure --connect-to foo.example.com:8443:localhost:8443 --ech ecl:AD7+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAA== +```sh +cd $HOME/code/curl/ +./src/curl -vvv --insecure --connect-to foo.example.com:8443:localhost:8443 \ + --ech ecl:AD7+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAA== ``` -### Automated use of ``retry_configs`` not supported so far... +### Automated use of `retry_configs` not supported so far... -As of now we have not added support for using ``retry_config`` handling in the -application - for a command line tool, one can just use ``dig`` (or ``kdig``) -to get the HTTPS RR and pass the ECHConfigList from that on the command line, -if needed, or one can access the value from command line output in verbose more +As of now we have not added support for using `retry_config` handling in the +application - for a command line tool, one can use `dig` (or `kdig`) to +get the HTTPS RR and pass the ECHConfigList from that on the command line, if +needed, or one can access the value from command line output in verbose more and then reuse that in another invocation. Both our OpenSSL fork and BoringSSL/AWS-LC have APIs for both controlling GREASE -and accessing and logging ``retry_configs``, it seems wolfSSL has neither. +and accessing and logging `retry_configs`, it seems wolfSSL has neither. ### Testing ECH diff --git a/docs/FAQ b/docs/FAQ deleted file mode 100644 index 640dc1ab2a..0000000000 --- a/docs/FAQ +++ /dev/null @@ -1,1559 +0,0 @@ - _ _ ____ _ - ___| | | | _ \| | - / __| | | | |_) | | - | (__| |_| | _ <| |___ - \___|\___/|_| \_\_____| - -FAQ - - 1. Philosophy - 1.1 What is cURL? - 1.2 What is libcurl? - 1.3 What is curl not? - 1.4 When will you make curl do XXXX ? - 1.5 Who makes curl? - 1.6 What do you get for making curl? - 1.7 What about CURL from curl.com? - 1.8 I have a problem, who do I mail? - 1.9 Where do I buy commercial support for curl? - 1.10 How many are using curl? - 1.11 Why do you not update ca-bundle.crt - 1.12 I have a problem, who can I chat with? - 1.13 curl's ECCN number? - 1.14 How do I submit my patch? - 1.15 How do I port libcurl to my OS? - - 2. Install Related Problems - 2.1 configure fails when using static libraries - 2.2 Does curl work/build with other SSL libraries? - 2.3 How do I upgrade curl.exe in Windows? - 2.4 Does curl support SOCKS (RFC 1928) ? - - 3. Usage Problems - 3.1 curl: (1) SSL is disabled, https: not supported - 3.2 How do I tell curl to resume a transfer? - 3.3 Why does my posting using -F not work? - 3.4 How do I tell curl to run custom FTP commands? - 3.5 How can I disable the Accept: */* header? - 3.6 Does curl support ASP, XML, XHTML or HTML version Y? - 3.7 Can I use curl to delete/rename a file through FTP? - 3.8 How do I tell curl to follow HTTP redirects? - 3.9 How do I use curl in my favorite programming language? - 3.10 What about SOAP, WebDAV, XML-RPC or similar protocols over HTTP? - 3.11 How do I POST with a different Content-Type? - 3.12 Why do FTP-specific features over HTTP proxy fail? - 3.13 Why do my single/double quotes fail? - 3.14 Does curl support JavaScript or PAC (automated proxy config)? - 3.15 Can I do recursive fetches with curl? - 3.16 What certificates do I need when I use SSL? - 3.17 How do I list the root directory of an FTP server? - 3.18 Can I use curl to send a POST/PUT and not wait for a response? - 3.19 How do I get HTTP from a host using a specific IP address? - 3.20 How to SFTP from my user's home directory? - 3.21 Protocol xxx not supported or disabled in libcurl - 3.22 curl -X gives me HTTP problems - - 4. Running Problems - 4.2 Why do I get problems when I use & or % in the URL? - 4.3 How can I use {, }, [ or ] to specify multiple URLs? - 4.4 Why do I get downloaded data even though the webpage does not exist? - 4.5 Why do I get return code XXX from an HTTP server? - 4.5.1 "400 Bad Request" - 4.5.2 "401 Unauthorized" - 4.5.3 "403 Forbidden" - 4.5.4 "404 Not Found" - 4.5.5 "405 Method Not Allowed" - 4.5.6 "301 Moved Permanently" - 4.6 Can you tell me what error code 142 means? - 4.7 How do I keep usernames and passwords secret in curl command lines? - 4.8 I found a bug - 4.9 curl cannot authenticate to a server that requires NTLM? - 4.10 My HTTP request using HEAD, PUT or DELETE does not work - 4.11 Why do my HTTP range requests return the full document? - 4.12 Why do I get "certificate verify failed" ? - 4.13 Why is curl -R on Windows one hour off? - 4.14 Redirects work in browser but not with curl - 4.15 FTPS does not work - 4.16 My HTTP POST or PUT requests are slow - 4.17 Non-functional connect timeouts on Windows - 4.18 file:// URLs containing drive letters (Windows, NetWare) - 4.19 Why does not curl return an error when the network cable is unplugged? - 4.20 curl does not return error for HTTP non-200 responses - - 5. libcurl Issues - 5.1 Is libcurl thread-safe? - 5.2 How can I receive all data into a large memory chunk? - 5.3 How do I fetch multiple files with libcurl? - 5.4 Does libcurl do Winsock initialization on Win32 systems? - 5.5 Does CURLOPT_WRITEDATA and CURLOPT_READDATA work on Win32 ? - 5.6 What about Keep-Alive or persistent connections? - 5.7 Link errors when building libcurl on Windows - 5.8 libcurl.so.X: open failed: No such file or directory - 5.9 How does libcurl resolve hostnames? - 5.10 How do I prevent libcurl from writing the response to stdout? - 5.11 How do I make libcurl not receive the whole HTTP response? - 5.12 Can I make libcurl fake or hide my real IP address? - 5.13 How do I stop an ongoing transfer? - 5.14 Using C++ non-static functions for callbacks? - 5.15 How do I get an FTP directory listing? - 5.16 I want a different time-out - 5.17 Can I write a server with libcurl? - 5.18 Does libcurl use threads? - - 6. License Issues - 6.1 I have a GPL program, can I use the libcurl library? - 6.2 I have a closed-source program, can I use the libcurl library? - 6.3 I have a BSD licensed program, can I use the libcurl library? - 6.4 I have a program that uses LGPL libraries, can I use libcurl? - 6.5 Can I modify curl/libcurl for my program and keep the changes secret? - 6.6 Can you please change the curl/libcurl license to XXXX? - 6.7 What are my obligations when using libcurl in my commercial apps? - - 7. PHP/CURL Issues - 7.1 What is PHP/CURL? - 7.2 Who wrote PHP/CURL? - 7.3 Can I perform multiple requests using the same handle? - 7.4 Does PHP/CURL have dependencies? - - 8. Development - 8.1 Why does curl use C89? - 8.2 Will curl be rewritten? - -============================================================================== - -1. Philosophy - - 1.1 What is cURL? - - cURL is the name of the project. The name is a play on 'Client for URLs', - originally with URL spelled in uppercase to make it obvious it deals with - URLs. The fact it can also be read as 'see URL' also helped, it works as - an abbreviation for "Client URL Request Library" or why not the recursive - version: "curl URL Request Library". - - The cURL project produces two products: - - libcurl - - A client-side URL transfer library, supporting DICT, FILE, FTP, FTPS, - GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, POP3, POP3S, - RTMP, RTMPS, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS - and WSS. - - libcurl supports HTTPS certificates, HTTP POST, HTTP PUT, FTP uploading, - Kerberos, SPNEGO, HTTP form based upload, proxies, cookies, user+password - authentication, file transfer resume, http proxy tunneling and more. - - libcurl is highly portable, it builds and works identically on numerous - platforms, including Solaris, NetBSD, FreeBSD, OpenBSD, Darwin, HP-UX, - IRIX, AIX, Tru64, Linux, UnixWare, HURD, Windows, Amiga, OS/2, macOS, - Ultrix, QNX, OpenVMS, RISC OS, Novell NetWare, DOS, Symbian, OSF, Android, - Minix, IBM TPF and more... - - libcurl is free, thread-safe, IPv6 compatible, feature rich, well - supported and fast. - - curl - - A command line tool for getting or sending data using URL syntax. - - Since curl uses libcurl, curl supports the same wide range of common - Internet protocols that libcurl does. - - We pronounce curl with an initial k sound. It rhymes with words like girl - and earl. This is a short WAV file to help you: - - https://media.merriam-webster.com/soundc11/c/curl0001.wav - - There are numerous sub-projects and related projects that also use the word - curl in the project names in various combinations, but you should take - notice that this FAQ is directed at the command-line tool named curl (and - libcurl the library), and may therefore not be valid for other curl-related - projects. (There is however a small section for the PHP/CURL in this FAQ.) - - 1.2 What is libcurl? - - libcurl is a reliable and portable library for doing Internet data transfers - using one or more of its supported Internet protocols. - - You can use libcurl freely in your application, be it open source, - commercial or closed-source. - - libcurl is most probably the most portable, most powerful and most often - used C-based multi-platform file transfer library on this planet - be it - open source or commercial. - - 1.3 What is curl not? - - curl is not a wget clone. That is a common misconception. Never, during - curl's development, have we intended curl to replace wget or compete on its - market. curl is targeted at single-shot file transfers. - - curl is not a website mirroring program. If you want to use curl to mirror - something: fine, go ahead and write a script that wraps around curl or use - libcurl to make it reality. - - curl is not an FTP site mirroring program. Sure, get and send FTP with curl - but if you want systematic and sequential behavior you should write a - script (or write a new program that interfaces libcurl) and do it. - - curl is not a PHP tool, even though it works perfectly well when used from - or with PHP (when using the PHP/CURL module). - - curl is not a program for a single operating system. curl exists, compiles, - builds and runs under a wide range of operating systems, including all - modern Unixes (and a bunch of older ones too), Windows, Amiga, OS/2, macOS, - QNX etc. - - 1.4 When will you make curl do XXXX ? - - We love suggestions of what to change in order to make curl and libcurl - better. We do however believe in a few rules when it comes to the future of - curl: - - curl -- the command line tool -- is to remain a non-graphical command line - tool. If you want GUIs or fancy scripting capabilities, you should look for - another tool that uses libcurl. - - We do not add things to curl that other small and available tools already do - well at the side. curl's output can be piped into another program or - redirected to another file for the next program to interpret. - - We focus on protocol related issues and improvements. If you want to do more - magic with the supported protocols than curl currently does, chances are - good we will agree. If you want to add more protocols, we may agree. - - If you want someone else to do all the work while you wait for us to - implement it for you, that is not a friendly attitude. We spend a - considerable time already on maintaining and developing curl. In order to - get more out of us, you should consider trading in some of your time and - effort in return. Simply go to the GitHub repository which resides at - https://github.com/curl/curl, fork the project, and create pull requests - with your proposed changes. - - If you write the code, chances are better that it will get into curl faster. - - 1.5 Who makes curl? - - curl and libcurl are not made by any single individual. Daniel Stenberg is - project leader and main developer, but other persons' submissions are - important and crucial. Anyone can contribute and post their changes and - improvements and have them inserted in the main sources (of course on the - condition that developers agree that the fixes are good). - - The full list of all contributors is found in the docs/THANKS file. - - curl is developed by a community, with Daniel at the wheel. - - 1.6 What do you get for making curl? - - Project cURL is entirely free and open. We do this voluntarily, mostly in - our spare time. Companies may pay individual developers to work on curl. - This is not controlled by nor supervised in any way by the curl project. - - We get help from companies. Haxx provides website, bandwidth, mailing lists - etc, GitHub hosts the primary git repository and other services like the bug - tracker at https://github.com/curl/curl. Also again, some companies have - sponsored certain parts of the development in the past and I hope some will - continue to do so in the future. - - If you want to support our project, consider a donation or a banner-program - or even better: by helping us with coding, documenting or testing etc. - - See also: https://curl.se/sponsors.html - - 1.7 What about CURL from curl.com? - - During the summer of 2001, curl.com was busy advertising their client-side - programming language for the web, named CURL. - - We are in no way associated with curl.com or their CURL programming - language. - - Our project name curl has been in effective use since 1998. We were not the - first computer related project to use the name "curl" and do not claim any - rights to the name. - - We recognize that we will be living in parallel with curl.com and wish them - every success. - - 1.8 I have a problem, who do I mail? - - Please do not mail any single individual unless you really need to. Keep - curl-related questions on a suitable mailing list. All available mailing - lists are listed in the MANUAL document and online at - https://curl.se/mail/ - - Keeping curl-related questions and discussions on mailing lists allows - others to join in and help, to share their ideas, to contribute their - suggestions and to spread their wisdom. Keeping discussions on public mailing - lists also allows for others to learn from this (both current and future - users thanks to the web based archives of the mailing lists), thus saving us - from having to repeat ourselves even more. Thanks for respecting this. - - If you have found or simply suspect a security problem in curl or libcurl, - submit all the details at https://hackerone.one/curl. On there we keep the - issue private while we investigate, confirm it, work and validate a fix and - agree on a time schedule for publication etc. That way we produce a fix in a - timely manner before the flaw is announced to the world, reducing the impact - the problem risks having on existing users. - - Security issues can also be taking to the curl security team by emailing - security at curl.se (closed list of receivers, mails are not disclosed). - - 1.9 Where do I buy commercial support for curl? - - curl is fully open source. It means you can hire any skilled engineer to fix - your curl-related problems. - - We list available alternatives on the curl website: - https://curl.se/support.html - - 1.10 How many are using curl? - - It is impossible to tell. - - We do not know how many users that knowingly have installed and use curl. - - We do not know how many users that use curl without knowing that they are in - fact using it. - - We do not know how many users that downloaded or installed curl and then - never use it. - - In 2020, we estimate that curl runs in roughly ten billion installations - world wide. - - 1.11 Why do you not update ca-bundle.crt - - In the cURL project we have decided not to attempt to keep this file updated - (or even present) since deciding what to add to a ca cert bundle is an - undertaking we have not been ready to accept, and the one we can get from - Mozilla is perfectly fine so there is no need to duplicate that work. - - Today, with many services performed over HTTPS, every operating system - should come with a default ca cert bundle that can be deemed somewhat - trustworthy and that collection (if reasonably updated) should be deemed to - be a lot better than a private curl version. - - If you want the most recent collection of ca certs that Mozilla Firefox - uses, we recommend that you extract the collection yourself from Mozilla - Firefox (by running 'make ca-bundle), or by using our online service setup - for this purpose: https://curl.se/docs/caextract.html - - 1.12 I have a problem who, can I chat with? - - There is a bunch of friendly people hanging out in the #curl channel on the - IRC network libera.chat. If you are polite and nice, chances are good that - you can get -- or provide -- help instantly. - - 1.13 curl's ECCN number? - - The US government restricts exports of software that contains or uses - cryptography. When doing so, the Export Control Classification Number (ECCN) - is used to identify the level of export control etc. - - Apache Software Foundation gives a good explanation of ECCNs at - https://www.apache.org/dev/crypto.html - - We believe curl's number might be ECCN 5D002, another possibility is - 5D992. It seems necessary to write them (the authority that administers ECCN - numbers), asking to confirm. - - Comprehensible explanations of the meaning of such numbers and how to obtain - them (resp.) are here - - https://www.bis.doc.gov/licensing/exportingbasics.htm - https://www.bis.doc.gov/licensing/do_i_needaneccn.html - - An incomprehensible description of the two numbers above is here - https://www.bis.doc.gov/index.php/documents/new-encryption/1653-ccl5-pt2-3 - - 1.14 How do I submit my patch? - - We strongly encourage you to submit changes and improvements directly as - "pull requests" on GitHub: https://github.com/curl/curl/pulls - - If you for any reason cannot or will not deal with GitHub, send your patch to - the curl-library mailing list. We are many subscribers there and there are - lots of people who can review patches, comment on them and "receive" them - properly. - - Lots of more details are found in the CONTRIBUTE.md and INTERNALS.md - documents. - - 1.15 How do I port libcurl to my OS? - - Here's a rough step-by-step: - - 1. copy a suitable lib/config-*.h file as a start to lib/config-[youros].h - - 2. edit lib/config-[youros].h to match your OS and setup - - 3. edit lib/curl_setup.h to include config-[youros].h when your OS is - detected by the preprocessor, in the style others already exist - - 4. compile lib/*.c and make them into a library - - -2. Install Related Problems - - 2.1 configure fails when using static libraries - - You may find that configure fails to properly detect the entire dependency - chain of libraries when you provide static versions of the libraries that - configure checks for. - - The reason why static libraries is much harder to deal with is that for them - we do not get any help but the script itself must know or check what more - libraries that are needed (with shared libraries, that dependency "chain" is - handled automatically). This is an error-prone process and one that also - tends to vary over time depending on the release versions of the involved - components and may also differ between operating systems. - - For that reason, configure does few attempts to actually figure this out and - you are instead encouraged to set LIBS and LDFLAGS accordingly when you - invoke configure, and point out the needed libraries and set the necessary - flags yourself. - - 2.2 Does curl work with other SSL libraries? - - curl has been written to use a generic SSL function layer internally, and - that SSL functionality can then be provided by one out of many different SSL - backends. - - curl can be built to use one of the following SSL alternatives: OpenSSL, - LibreSSL, BoringSSL, AWS-LC, GnuTLS, wolfSSL, mbedTLS, Schannel (native - Windows) or Rustls. They all have their pros and cons, and we try to - maintain a comparison of them here: https://curl.se/docs/ssl-compared.html - - 2.3 How do I upgrade curl.exe in Windows? - - The curl tool that is shipped as an integrated component of Windows 10 and - Windows 11 is managed by Microsoft. If you were to delete the file or - replace it with a newer version downloaded from https://curl.se/windows, - then Windows Update will cease to work on your system. - - There is no way to independently force an upgrade of the curl.exe that is - part of Windows other than through the regular Windows update process. There - is also nothing the curl project itself can do about this, since this is - managed and controlled entirely by Microsoft as owners of the operating - system. - - You can always download and install the latest version of curl for Windows - from https://curl.se/windows into a separate location. - - 2.4 Does curl support SOCKS (RFC 1928) ? - - Yes, SOCKS 4 and 5 are supported. - -3. Usage problems - - 3.1 curl: (1) SSL is disabled, https: not supported - - If you get this output when trying to get anything from an HTTPS server, it - means that the instance of curl/libcurl that you are using was built without - support for this protocol. - - This could have happened if the configure script that was run at build time - could not find all libs and include files curl requires for SSL to work. If - the configure script fails to find them, curl is simply built without SSL - support. - - To get HTTPS support into a curl that was previously built but that reports - that HTTPS is not supported, you should dig through the document and logs - and check out why the configure script does not find the SSL libs and/or - include files. - - Also, check out the other paragraph in this FAQ labeled "configure does not - find OpenSSL even when it is installed". - - 3.2 How do I tell curl to resume a transfer? - - curl supports resumed transfers both ways on both FTP and HTTP. - Try the -C option. - - 3.3 Why does my posting using -F not work? - - You cannot arbitrarily use -F or -d, the choice between -F or -d depends on - the HTTP operation you need curl to do and what the web server that will - receive your post expects. - - If the form you are trying to submit uses the type 'multipart/form-data', - then and only then you must use the -F type. In all the most common cases, - you should use -d which then causes a posting with the type - 'application/x-www-form-urlencoded'. - - This is described in some detail in the MANUAL and TheArtOfHttpScripting - documents, and if you do not understand it the first time, read it again - before you post questions about this to the mailing list. Also, try reading - through the mailing list archives for old postings and questions regarding - this. - - 3.4 How do I tell curl to run custom FTP commands? - - You can tell curl to perform optional commands both before and/or after a - file transfer. Study the -Q/--quote option. - - Since curl is used for file transfers, you do not normally use curl to - perform FTP commands without transferring anything. Therefore you must - always specify a URL to transfer to/from even when doing custom FTP - commands, or use -I which implies the "no body" option sent to libcurl. - - 3.5 How can I disable the Accept: */* header? - - You can change all internally generated headers by adding a replacement with - the -H/--header option. By adding a header with empty contents you safely - disable that one. Use -H "Accept:" to disable that specific header. - - 3.6 Does curl support ASP, XML, XHTML or HTML version Y? - - To curl, all contents are alike. It does not matter how the page was - generated. It may be ASP, PHP, Perl, shell-script, SSI or plain HTML - files. There is no difference to curl and it does not even know what kind of - language that generated the page. - - See also item 3.14 regarding JavaScript. - - 3.7 Can I use curl to delete/rename a file through FTP? - - Yes. You specify custom FTP commands with -Q/--quote. - - One example would be to delete a file after you have downloaded it: - - curl -O ftp://example.com/coolfile -Q '-DELE coolfile' - - or rename a file after upload: - - curl -T infile ftp://example.com/dir/ -Q "-RNFR infile" -Q "-RNTO newname" - - 3.8 How do I tell curl to follow HTTP redirects? - - curl does not follow so-called redirects by default. The Location: header - that informs the client about this is only interpreted if you are using the - -L/--location option. As in: - - curl -L http://example.com - - Not all redirects are HTTP ones, see 4.14 - - 3.9 How do I use curl in my favorite programming language? - - Many programming languages have interfaces/bindings that allow you to use - curl without having to use the command line tool. If you are fluent in such - a language, you may prefer to use one of these interfaces instead. - - Find out more about which languages that support curl directly, and how to - install and use them, in the libcurl section of the curl website: - https://curl.se/libcurl/ - - All the various bindings to libcurl are made by other projects and people, - outside of the cURL project. The cURL project itself only produces libcurl - with its plain C API. If you do not find anywhere else to ask you can ask - about bindings on the curl-library list too, but be prepared that people on - that list may not know anything about bindings. - - In December 2021, there were interfaces available for the following - languages: Ada95, Basic, C, C++, Ch, Cocoa, D, Delphi, Dylan, Eiffel, - Euphoria, Falcon, Ferite, Gambas, glib/GTK+, Go, Guile, Harbour, Haskell, - Java, Julia, Lisp, Lua, Mono, .NET, node.js, Object-Pascal, OCaml, Pascal, - Perl, PHP, PostgreSQL, Python, R, Rexx, Ring, RPG, Ruby, Rust, Scheme, - Scilab, S-Lang, Smalltalk, SP-Forth, SPL, Tcl, Visual Basic, Visual FoxPro, - Q, wxwidgets, XBLite and Xoho. By the time you read this, additional ones - may have appeared. - - 3.10 What about SOAP, WebDAV, XML-RPC or similar protocols over HTTP? - - curl adheres to the HTTP spec, which basically means you can play with *any* - protocol that is built on top of HTTP. Protocols such as SOAP, WebDAV and - XML-RPC are all such ones. You can use -X to set custom requests and -H to - set custom headers (or replace internally generated ones). - - Using libcurl is of course just as good and you would just use the proper - library options to do the same. - - 3.11 How do I POST with a different Content-Type? - - You can always replace the internally generated headers with -H/--header. - To make a simple HTTP POST with text/xml as content-type, do something like: - - curl -d "datatopost" -H "Content-Type: text/xml" [URL] - - 3.12 Why do FTP-specific features over HTTP proxy fail? - - Because when you use an HTTP proxy, the protocol spoken on the network will - be HTTP, even if you specify an FTP URL. This effectively means that you - normally cannot use FTP-specific features such as FTP upload and FTP quote - etc. - - There is one exception to this rule, and that is if you can "tunnel through" - the given HTTP proxy. Proxy tunneling is enabled with a special option (-p) - and is generally not available as proxy admins usually disable tunneling to - ports other than 443 (which is used for HTTPS access through proxies). - - 3.13 Why do my single/double quotes fail? - - To specify a command line option that includes spaces, you might need to - put the entire option within quotes. Like in: - - curl -d " with spaces " example.com - - or perhaps - - curl -d ' with spaces ' example.com - - Exactly what kind of quotes and how to do this is entirely up to the shell - or command line interpreter that you are using. For most Unix shells, you - can more or less pick either single (') or double (") quotes. For - Windows/DOS command prompts you must use double (") quotes, and if the - option string contains inner double quotes you can escape them with a - backslash. - - For Windows powershell the arguments are not always passed on as expected - because curl is not a powershell script. You may or may not be able to use - single quotes. To escape inner double quotes seems to require a - backslash-backtick escape sequence and the outer quotes as double quotes. - - Please study the documentation for your particular environment. Examples in - the curl docs will use a mix of both of these as shown above. You must - adjust them to work in your environment. - - Remember that curl works and runs on more operating systems than most single - individuals have ever tried. - - 3.14 Does curl support JavaScript or PAC (automated proxy config)? - - Many webpages do magic stuff using embedded JavaScript. curl and libcurl - have no built-in support for that, so it will be treated just like any other - contents. - - .pac files are a Netscape invention and are sometimes used by organizations - to allow them to differentiate which proxies to use. The .pac contents is - just a JavaScript program that gets invoked by the browser and that returns - the name of the proxy to connect to. Since curl does not support JavaScript, - it cannot support .pac proxy configuration either. - - Some workarounds usually suggested to overcome this JavaScript dependency: - - Depending on the JavaScript complexity, write up a script that translates it - to another language and execute that. - - Read the JavaScript code and rewrite the same logic in another language. - - Implement a JavaScript interpreter, people have successfully used the - Mozilla JavaScript engine in the past. - - Ask your admins to stop this, for a static proxy setup or similar. - - 3.15 Can I do recursive fetches with curl? - - No. curl itself has no code that performs recursive operations, such as - those performed by wget and similar tools. - - There exists wrapper scripts with that functionality (for example the - curlmirror perl script), and you can write programs based on libcurl to do - it, but the command line tool curl itself cannot. - - 3.16 What certificates do I need when I use SSL? - - There are three different kinds of "certificates" to keep track of when we - talk about using SSL-based protocols (HTTPS or FTPS) using curl or libcurl. - - CLIENT CERTIFICATE - - The server you communicate with may require that you can provide this in - order to prove that you actually are who you claim to be. If the server - does not require this, you do not need a client certificate. - - A client certificate is always used together with a private key, and the - private key has a pass phrase that protects it. - - SERVER CERTIFICATE - - The server you communicate with has a server certificate. You can and should - verify this certificate to make sure that you are truly talking to the real - server and not a server impersonating it. - - CERTIFICATE AUTHORITY CERTIFICATE ("CA cert") - - You often have several CA certs in a CA cert bundle that can be used to - verify a server certificate that was signed by one of the authorities in the - bundle. curl does not come with a CA cert bundle but most curl installs - provide one. You can also override the default. - - The server certificate verification process is made by using a Certificate - Authority certificate ("CA cert") that was used to sign the server - certificate. Server certificate verification is enabled by default in curl - and libcurl and is often the reason for problems as explained in FAQ entry - 4.12 and the SSLCERTS document - (https://curl.se/docs/sslcerts.html). Server certificates that are - "self-signed" or otherwise signed by a CA that you do not have a CA cert - for, cannot be verified. If the verification during a connect fails, you are - refused access. You then need to explicitly disable the verification to - connect to the server. - - 3.17 How do I list the root directory of an FTP server? - - There are two ways. The way defined in the RFC is to use an encoded slash - in the first path part. List the "/tmp" directory like this: - - curl ftp://ftp.example.com/%2ftmp/ - - or the not-quite-kosher-but-more-readable way, by simply starting the path - section of the URL with a slash: - - curl ftp://ftp.example.com//tmp/ - - 3.18 Can I use curl to send a POST/PUT and not wait for a response? - - No. - - You can easily write your own program using libcurl to do such stunts. - - 3.19 How do I get HTTP from a host using a specific IP address? - - For example, you may be trying out a website installation that is not yet in - the DNS. Or you have a site using multiple IP addresses for a given host - name and you want to address a specific one out of the set. - - Set a custom Host: header that identifies the server name you want to reach - but use the target IP address in the URL: - - curl --header "Host: www.example.com" http://127.0.0.1/ - - You can also opt to add faked hostname entries to curl with the --resolve - option. That has the added benefit that things like redirects will also work - properly. The above operation would instead be done as: - - curl --resolve www.example.com:80:127.0.0.1 http://www.example.com/ - - 3.20 How to SFTP from my user's home directory? - - Contrary to how FTP works, SFTP and SCP URLs specify the exact directory to - work with. It means that if you do not specify that you want the user's home - directory, you get the actual root directory. - - To specify a file in your user's home directory, you need to use the correct - URL syntax which for SFTP might look similar to: - - curl -O -u user:password sftp://example.com/~/file.txt - - and for SCP it is just a different protocol prefix: - - curl -O -u user:password scp://example.com/~/file.txt - - 3.21 Protocol xxx not supported or disabled in libcurl - - When passing on a URL to curl to use, it may respond that the particular - protocol is not supported or disabled. The particular way this error message - is phrased is because curl does not make a distinction internally of whether - a particular protocol is not supported (i.e. never got any code added that - knows how to speak that protocol) or if it was explicitly disabled. curl can - be built to only support a given set of protocols, and the rest would then - be disabled or not supported. - - Note that this error will also occur if you pass a wrongly spelled protocol - part as in "htpt://example.com" or as in the less evident case if you prefix - the protocol part with a space as in " http://example.com/". - - 3.22 curl -X gives me HTTP problems - - In normal circumstances, -X should hardly ever be used. - - By default you use curl without explicitly saying which request method to - use when the URL identifies an HTTP transfer. If you just pass in a URL like - "curl http://example.com" it will use GET. If you use -d or -F curl will use - POST, -I will cause a HEAD and -T will make it a PUT. - - If for whatever reason you are not happy with these default choices that curl - does for you, you can override those request methods by specifying -X - [WHATEVER]. This way you can for example send a DELETE by doing "curl -X - DELETE [URL]". - - It is thus pointless to do "curl -XGET [URL]" as GET would be used anyway. - In the same vein it is pointless to do "curl -X POST -d data [URL]". You can - make a fun and somewhat rare request that sends a request-body in a GET - request with something like "curl -X GET -d data [URL]" - - Note that -X does not actually change curl's behavior as it only modifies the - actual string sent in the request, but that may of course trigger a - different set of events. - - Accordingly, by using -XPOST on a command line that for example would follow - a 303 redirect, you will effectively prevent curl from behaving - correctly. Be aware. - - -4. Running Problems - - 4.2 Why do I get problems when I use & or % in the URL? - - In general Unix shells, the & symbol is treated specially and when used, it - runs the specified command in the background. To safely send the & as a part - of a URL, you should quote the entire URL by using single (') or double (") - quotes around it. Similar problems can also occur on some shells with other - characters, including ?*!$~(){}<>\|;`. When in doubt, quote the URL. - - An example that would invoke a remote CGI that uses &-symbols could be: - - curl 'http://www.example.com/cgi-bin/query?text=yes&q=curl' - - In Windows, the standard DOS shell treats the percent sign specially and you - need to use TWO percent signs for each single one you want to use in the - URL. - - If you want a literal percent sign to be part of the data you pass in a POST - using -d/--data you must encode it as '%25' (which then also needs the - percent sign doubled on Windows machines). - - 4.3 How can I use {, }, [ or ] to specify multiple URLs? - - Because those letters have a special meaning to the shell, to be used in - a URL specified to curl you must quote them. - - An example that downloads two URLs (sequentially) would be: - - curl '{curl,www}.haxx.se' - - To be able to use those characters as actual parts of the URL (without using - them for the curl URL "globbing" system), use the -g/--globoff option: - - curl -g 'www.example.com/weirdname[].html' - - 4.4 Why do I get downloaded data even though the webpage does not exist? - - curl asks remote servers for the page you specify. If the page does not exist - at the server, the HTTP protocol defines how the server should respond and - that means that headers and a "page" will be returned. That is simply how - HTTP works. - - By using the --fail option you can tell curl explicitly to not get any data - if the HTTP return code does not say success. - - 4.5 Why do I get return code XXX from an HTTP server? - - RFC 2616 clearly explains the return codes. This is a short transcript. Go - read the RFC for exact details: - - 4.5.1 "400 Bad Request" - - The request could not be understood by the server due to malformed - syntax. The client SHOULD NOT repeat the request without modifications. - - 4.5.2 "401 Unauthorized" - - The request requires user authentication. - - 4.5.3 "403 Forbidden" - - The server understood the request, but is refusing to fulfill it. - Authorization will not help and the request SHOULD NOT be repeated. - - 4.5.4 "404 Not Found" - - The server has not found anything matching the Request-URI. No indication - is given as to whether the condition is temporary or permanent. - - 4.5.5 "405 Method Not Allowed" - - The method specified in the Request-Line is not allowed for the resource - identified by the Request-URI. The response MUST include an Allow header - containing a list of valid methods for the requested resource. - - 4.5.6 "301 Moved Permanently" - - If you get this return code and an HTML output similar to this: - -

Moved Permanently

The document has moved here. - - it might be because you requested a directory URL but without the trailing - slash. Try the same operation again _with_ the trailing URL, or use the - -L/--location option to follow the redirection. - - 4.6 Can you tell me what error code 142 means? - - All curl error codes are described at the end of the man page, in the - section called "EXIT CODES". - - Error codes that are larger than the highest documented error code means - that curl has exited due to a crash. This is a serious error, and we - appreciate a detailed bug report from you that describes how we could go - ahead and repeat this. - - 4.7 How do I keep usernames and passwords secret in curl command lines? - - This problem has two sides: - - The first part is to avoid having clear-text passwords in the command line - so that they do not appear in 'ps' outputs and similar. That is easily - avoided by using the "-K" option to tell curl to read parameters from a file - or stdin to which you can pass the secret info. curl itself will also - attempt to "hide" the given password by blanking out the option - this - does not work on all platforms. - - To keep the passwords in your account secret from the rest of the world is - not a task that curl addresses. You could of course encrypt them somehow to - at least hide them from being read by human eyes, but that is not what - anyone would call security. - - Also note that regular HTTP (using Basic authentication) and FTP passwords - are sent as cleartext across the network. All it takes for anyone to fetch - them is to listen on the network. Eavesdropping is easy. Use more secure - authentication methods (like Digest, Negotiate or even NTLM) or consider the - SSL-based alternatives HTTPS and FTPS. - - 4.8 I found a bug - - It is not a bug if the behavior is documented. Read the docs first. - Especially check out the KNOWN_BUGS file, it may be a documented bug. - - If it is a problem with a binary you have downloaded or a package for your - particular platform, try contacting the person who built the package/archive - you have. - - If there is a bug, read the BUGS document first. Then report it as described - in there. - - 4.9 curl cannot authenticate to a server that requires NTLM? - - NTLM support requires OpenSSL, GnuTLS, mbedTLS or Microsoft Windows - libraries at build-time to provide this functionality. - - 4.10 My HTTP request using HEAD, PUT or DELETE does not work - - Many web servers allow or demand that the administrator configures the - server properly for these requests to work on the web server. - - Some servers seem to support HEAD only on certain kinds of URLs. - - To fully grasp this, try the documentation for the particular server - software you are trying to interact with. This is not anything curl can do - anything about. - - 4.11 Why do my HTTP range requests return the full document? - - Because the range may not be supported by the server, or the server may - choose to ignore it and return the full document anyway. - - 4.12 Why do I get "certificate verify failed" ? - - When you invoke curl and get an error 60 error back it means that curl - could not verify that the server's certificate was good. curl verifies the - certificate using the CA cert bundle and verifying for which names the - certificate has been granted. - - To completely disable the certificate verification, use -k. This does - however enable man-in-the-middle attacks and makes the transfer INSECURE. - We strongly advise against doing this for more than experiments. - - If you get this failure with a CA cert bundle installed and used, the - server's certificate might not be signed by one of the CA's in your CA - store. It might for example be self-signed. You then correct this problem by - obtaining a valid CA cert for the server. Or again, decrease the security by - disabling this check. - - At times, you find that the verification works in your favorite browser but - fails in curl. When this happens, the reason is usually that the server - sends an incomplete cert chain. The server is mandated to send all - "intermediate certificates" but does not. This typically works with browsers - anyway since they A) cache such certs and B) supports AIA which downloads - such missing certificates on demand. This is a server misconfiguration. A - good way to figure out if this is the case it to use the SSL Labs server - test and check the certificate chain: https://www.ssllabs.com/ssltest/ - - Details are also in the SSLCERTS.md document, found online here: - https://curl.se/docs/sslcerts.html - - 4.13 Why is curl -R on Windows one hour off? - - Since curl 7.53.0 this issue should be fixed as long as curl was built with - any modern compiler that allows for a 64-bit curl_off_t type. For older - compilers or prior curl versions it may set a time that appears one hour off. - This happens due to a flaw in how Windows stores and uses file modification - times and it is not easily worked around. For more details read this: - https://www.codeproject.com/Articles/1144/Beating-the-Daylight-Savings-Time-bug-and-getting - - 4.14 Redirects work in browser but not with curl - - curl supports HTTP redirects well (see item 3.8). Browsers generally support - at least two other ways to perform redirects that curl does not: - - Meta tags. You can write an HTML tag that will cause the browser to redirect - to another given URL after a certain time. - - JavaScript. You can write a JavaScript program embedded in an HTML page that - redirects the browser to another given URL. - - There is no way to make curl follow these redirects. You must either - manually figure out what the page is set to do, or write a script that parses - the results and fetches the new URL. - - 4.15 FTPS does not work - - curl supports FTPS (sometimes known as FTP-SSL) both implicit and explicit - mode. - - When a URL is used that starts with FTPS://, curl assumes implicit SSL on - the control connection and will therefore immediately connect and try to - speak SSL. FTPS:// connections default to port 990. - - To use explicit FTPS, you use an FTP:// URL and the --ssl-reqd option (or one - of its related flavors). This is the most common method, and the one - mandated by RFC 4217. This kind of connection will then of course use the - standard FTP port 21 by default. - - 4.16 My HTTP POST or PUT requests are slow - - libcurl makes all POST and PUT requests (except for requests with a small - request body) use the "Expect: 100-continue" header. This header allows the - server to deny the operation early so that libcurl can bail out before having - to send any data. This is useful in authentication cases and others. - - However, many servers do not implement the Expect: stuff properly and if the - server does not respond (positively) within 1 second libcurl will continue - and send off the data anyway. - - You can disable libcurl's use of the Expect: header the same way you disable - any header, using -H / CURLOPT_HTTPHEADER, or by forcing it to use HTTP 1.0. - - 4.17 Non-functional connect timeouts - - In most Windows setups having a timeout longer than 21 seconds make no - difference, as it will only send 3 TCP SYN packets and no more. The second - packet sent three seconds after the first and the third six seconds after - the second. No more than three packets are sent, no matter how long the - timeout is set. - - See option TcpMaxConnectRetransmissions on this page: - https://web.archive.org/web/20160819015101/support.microsoft.com/en-us/kb/175523 - - Also, even on non-Windows systems there may run a firewall or anti-virus - software or similar that accepts the connection but does not actually do - anything else. This will make (lib)curl to consider the connection connected - and thus the connect timeout will not trigger. - - 4.18 file:// URLs containing drive letters (Windows, NetWare) - - When using curl to try to download a local file, one might use a URL - in this format: - - file://D:/blah.txt - - you will find that even if D:\blah.txt does exist, curl returns a 'file - not found' error. - - According to RFC 1738 (https://www.ietf.org/rfc/rfc1738.txt), - file:// URLs must contain a host component, but it is ignored by - most implementations. In the above example, 'D:' is treated as the - host component, and is taken away. Thus, curl tries to open '/blah.txt'. - If your system is installed to drive C:, that will resolve to 'C:\blah.txt', - and if that does not exist you will get the not found error. - - To fix this problem, use file:// URLs with *three* leading slashes: - - file:///D:/blah.txt - - Alternatively, if it makes more sense, specify 'localhost' as the host - component: - - file://localhost/D:/blah.txt - - In either case, curl should now be looking for the correct file. - - 4.19 Why does not curl return an error when the network cable is unplugged? - - Unplugging a cable is not an error situation. The TCP/IP protocol stack - was designed to be fault tolerant, so even though there may be a physical - break somewhere the connection should not be affected, just possibly - delayed. Eventually, the physical break will be fixed or the data will be - re-routed around the physical problem through another path. - - In such cases, the TCP/IP stack is responsible for detecting when the - network connection is irrevocably lost. Since with some protocols it is - perfectly legal for the client to wait indefinitely for data, the stack may - never report a problem, and even when it does, it can take up to 20 minutes - for it to detect an issue. The curl option --keepalive-time enables - keep-alive support in the TCP/IP stack which makes it periodically probe the - connection to make sure it is still available to send data. That should - reliably detect any TCP/IP network failure. - - TCP keep alive will not detect the network going down before the TCP/IP - connection is established (e.g. during a DNS lookup) or using protocols that - do not use TCP. To handle those situations, curl offers a number of timeouts - on its own. --speed-limit/--speed-time will abort if the data transfer rate - falls too low, and --connect-timeout and --max-time can be used to put an - overall timeout on the connection phase or the entire transfer. - - A libcurl-using application running in a known physical environment (e.g. - an embedded device with only a single network connection) may want to act - immediately if its lone network connection goes down. That can be achieved - by having the application monitor the network connection on its own using an - OS-specific mechanism, then signaling libcurl to abort (see also item 5.13). - - 4.20 curl does not return error for HTTP non-200 responses - - Correct. Unless you use -f (--fail). - - When doing HTTP transfers, curl will perform exactly what you are asking it - to do and if successful it will not return an error. You can use curl to - test your web server's "file not found" page (that gets 404 back), you can - use it to check your authentication protected webpages (that gets a 401 - back) and so on. - - The specific HTTP response code does not constitute a problem or error for - curl. It simply sends and delivers HTTP as you asked and if that worked, - everything is fine and dandy. The response code is generally providing more - higher level error information that curl does not care about. The error was - not in the HTTP transfer. - - If you want your command line to treat error codes in the 400 and up range - as errors and thus return a non-zero value and possibly show an error - message, curl has a dedicated option for that: -f (CURLOPT_FAILONERROR in - libcurl speak). - - You can also use the -w option and the variable %{response_code} to extract - the exact response code that was returned in the response. - -5. libcurl Issues - - 5.1 Is libcurl thread-safe? - - Yes. - - We have written the libcurl code specifically adjusted for multi-threaded - programs. libcurl will use thread-safe functions instead of non-safe ones if - your system has such. Note that you must never share the same handle in - multiple threads. - - There may be some exceptions to thread safety depending on how libcurl was - built. Please review the guidelines for thread safety to learn more: - https://curl.se/libcurl/c/threadsafe.html - - 5.2 How can I receive all data into a large memory chunk? - - [ See also the examples/getinmemory.c source ] - - You are in full control of the callback function that gets called every time - there is data received from the remote server. You can make that callback do - whatever you want. You do not have to write the received data to a file. - - One solution to this problem could be to have a pointer to a struct that you - pass to the callback function. You set the pointer using the - CURLOPT_WRITEDATA option. Then that pointer will be passed to the callback - instead of a FILE * to a file: - - /* imaginary struct */ - struct MemoryStruct { - char *memory; - size_t size; - }; - - /* imaginary callback function */ - size_t - WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) - { - size_t realsize = size * nmemb; - struct MemoryStruct *mem = (struct MemoryStruct *)data; - - mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1); - if (mem->memory) { - memcpy(&(mem->memory[mem->size]), ptr, realsize); - mem->size += realsize; - mem->memory[mem->size] = 0; - } - return realsize; - } - - 5.3 How do I fetch multiple files with libcurl? - - libcurl has excellent support for transferring multiple files. You should - just repeatedly set new URLs with curl_easy_setopt() and then transfer it - with curl_easy_perform(). The handle you get from curl_easy_init() is not - only reusable, but you are even encouraged to reuse it if you can, as that - will enable libcurl to use persistent connections. - - 5.4 Does libcurl do Winsock initialization on Win32 systems? - - Yes, if told to in the curl_global_init() call. - - 5.5 Does CURLOPT_WRITEDATA and CURLOPT_READDATA work on Win32 ? - - Yes, but you cannot open a FILE * and pass the pointer to a DLL and have - that DLL use the FILE * (as the DLL and the client application cannot access - each others' variable memory areas). If you set CURLOPT_WRITEDATA you must - also use CURLOPT_WRITEFUNCTION as well to set a function that writes the - file, even if that simply writes the data to the specified FILE *. - Similarly, if you use CURLOPT_READDATA you must also specify - CURLOPT_READFUNCTION. - - 5.6 What about Keep-Alive or persistent connections? - - curl and libcurl have excellent support for persistent connections when - transferring several files from the same server. curl will attempt to reuse - connections for all URLs specified on the same command line/config file, and - libcurl will reuse connections for all transfers that are made using the - same libcurl handle. - - When you use the easy interface the connection cache is kept within the easy - handle. If you instead use the multi interface, the connection cache will be - kept within the multi handle and will be shared among all the easy handles - that are used within the same multi handle. - - 5.7 Link errors when building libcurl on Windows - - You need to make sure that your project, and all the libraries (both static - and dynamic) that it links against, are compiled/linked against the same run - time library. - - This is determined by the /MD, /ML, /MT (and their corresponding /M?d) - options to the command line compiler. /MD (linking against MSVCRT dll) seems - to be the most commonly used option. - - When building an application that uses the static libcurl library, you must - add -DCURL_STATICLIB to your CFLAGS. Otherwise the linker will look for - dynamic import symbols. If you are using Visual Studio, you need to instead - add CURL_STATICLIB in the "Preprocessor Definitions" section. - - If you get a linker error like "unknown symbol __imp__curl_easy_init ..." you - have linked against the wrong (static) library. If you want to use the - libcurl.dll and import lib, you do not need any extra CFLAGS, but use one of - the import libraries below. These are the libraries produced by the various - lib/Makefile.* files: - - Target: static lib. import lib for libcurl*.dll. - ----------------------------------------------------------- - MinGW: libcurl.a libcurldll.a - MSVC (release): libcurl.lib libcurl_imp.lib - MSVC (debug): libcurld.lib libcurld_imp.lib - Borland: libcurl.lib libcurl_imp.lib - - 5.8 libcurl.so.X: open failed: No such file or directory - - This is an error message you might get when you try to run a program linked - with a shared version of libcurl and your runtime linker (ld.so) could not - find the shared library named libcurl.so.X. (Where X is the number of the - current libcurl ABI, typically 3 or 4). - - You need to make sure that ld.so finds libcurl.so.X. You can do that - multiple ways, and it differs somewhat between different operating systems. - They are usually: - - * Add an option to the linker command line that specify the hard-coded path - the runtime linker should check for the lib (usually -R) - - * Set an environment variable (LD_LIBRARY_PATH for example) where ld.so - should check for libs - - * Adjust the system's config to check for libs in the directory where you have - put the library (like Linux's /etc/ld.so.conf) - - 'man ld.so' and 'man ld' will tell you more details - - 5.9 How does libcurl resolve hostnames? - - libcurl supports a large number of name resolve functions. One of them is - picked at build-time and will be used unconditionally. Thus, if you want to - change name resolver function you must rebuild libcurl and tell it to use a - different function. - - - The non-IPv6 resolver that can use one of four different hostname resolve - calls (depending on what your system supports): - - A - gethostbyname() - B - gethostbyname_r() with 3 arguments - C - gethostbyname_r() with 5 arguments - D - gethostbyname_r() with 6 arguments - - - The IPv6-resolver that uses getaddrinfo() - - - The c-ares based name resolver that uses the c-ares library for resolves. - Using this offers asynchronous name resolves. - - - The threaded resolver (default option on Windows). It uses: - - A - gethostbyname() on plain IPv4 hosts - B - getaddrinfo() on IPv6 enabled hosts - - Also note that libcurl never resolves or reverse-lookups addresses given as - pure numbers, such as 127.0.0.1 or ::1. - - 5.10 How do I prevent libcurl from writing the response to stdout? - - libcurl provides a default built-in write function that writes received data - to stdout. Set the CURLOPT_WRITEFUNCTION to receive the data, or possibly - set CURLOPT_WRITEDATA to a different FILE * handle. - - 5.11 How do I make libcurl not receive the whole HTTP response? - - You make the write callback (or progress callback) return an error and - libcurl will then abort the transfer. - - 5.12 Can I make libcurl fake or hide my real IP address? - - No. libcurl operates on a higher level. Besides, faking IP address would - imply sending IP packets with a made-up source address, and then you normally - get a problem with receiving the packet sent back as they would then not be - routed to you. - - If you use a proxy to access remote sites, the sites will not see your local - IP address but instead the address of the proxy. - - Also note that on many networks NATs or other IP-munging techniques are used - that makes you see and use a different IP address locally than what the - remote server will see you coming from. You may also consider using - https://www.torproject.org/ . - - 5.13 How do I stop an ongoing transfer? - - With the easy interface you make sure to return the correct error code from - one of the callbacks, but none of them are instant. There is no function you - can call from another thread or similar that will stop it immediately. - Instead, you need to make sure that one of the callbacks you use returns an - appropriate value that will stop the transfer. Suitable callbacks that you - can do this with include the progress callback, the read callback and the - write callback. - - If you are using the multi interface, you can also stop a transfer by - removing the particular easy handle from the multi stack at any moment you - think the transfer is done or when you wish to abort the transfer. - - 5.14 Using C++ non-static functions for callbacks? - - libcurl is a C library, it does not know anything about C++ member functions. - - You can overcome this "limitation" with relative ease using a static - member function that is passed a pointer to the class: - - // f is the pointer to your object. - static size_t YourClass::func(void *buffer, size_t sz, size_t n, void *f) - { - // Call non-static member function. - static_cast(f)->nonStaticFunction(); - } - - // This is how you pass pointer to the static function: - curl_easy_setopt(hcurl, CURLOPT_WRITEFUNCTION, YourClass::func); - curl_easy_setopt(hcurl, CURLOPT_WRITEDATA, this); - - 5.15 How do I get an FTP directory listing? - - If you end the FTP URL you request with a slash, libcurl will provide you - with a directory listing of that given directory. You can also set - CURLOPT_CUSTOMREQUEST to alter what exact listing command libcurl would use - to list the files. - - The follow-up question tends to be how is a program supposed to parse the - directory listing. How does it know what's a file and what's a directory and - what's a symlink etc. If the FTP server supports the MLSD command then it - will return data in a machine-readable format that can be parsed for type. - The types are specified by RFC 3659 section 7.5.1. If MLSD is not supported - then you have to work with what you are given. The LIST output format is - entirely at the server's own liking and the NLST output does not reveal any - types and in many cases does not even include all the directory entries. - Also, both LIST and NLST tend to hide Unix-style hidden files (those that - start with a dot) by default so you need to do "LIST -a" or similar to see - them. - - Example - List only directories. - ftp.funet.fi supports MLSD and ftp.kernel.org does not: - - curl -s ftp.funet.fi/pub/ -X MLSD | \ - perl -lne 'print if s/(?:^|;)type=dir;[^ ]+ (.+)$/$1/' - - curl -s ftp.kernel.org/pub/linux/kernel/ | \ - perl -lne 'print if s/^d[-rwx]{9}(?: +[^ ]+){7} (.+)$/$1/' - - If you need to parse LIST output in libcurl one such existing - list parser is available at https://cr.yp.to/ftpparse.html Versions of - libcurl since 7.21.0 also provide the ability to specify a wildcard to - download multiple files from one FTP directory. - - 5.16 I want a different time-out - - Sometimes users realize that CURLOPT_TIMEOUT and CURLOPT_CONNECTIMEOUT are - not sufficiently advanced or flexible to cover all the various use cases and - scenarios applications end up with. - - libcurl offers many more ways to time-out operations. A common alternative - is to use the CURLOPT_LOW_SPEED_LIMIT and CURLOPT_LOW_SPEED_TIME options to - specify the lowest possible speed to accept before to consider the transfer - timed out. - - The most flexible way is by writing your own time-out logic and using - CURLOPT_XFERINFOFUNCTION (perhaps in combination with other callbacks) and - use that to figure out exactly when the right condition is met when the - transfer should get stopped. - - 5.17 Can I write a server with libcurl? - - No. libcurl offers no functions or building blocks to build any kind of - Internet protocol server. libcurl is only a client-side library. For server - libraries, you need to continue your search elsewhere but there exist many - good open source ones out there for most protocols you could want a server - for. There are also really good stand-alone servers that have been tested - and proven for many years. There is no need for you to reinvent them. - - 5.18 Does libcurl use threads? - - Put simply: no, libcurl will execute in the same thread you call it in. All - callbacks will be called in the same thread as the one you call libcurl in. - - If you want to avoid your thread to be blocked by the libcurl call, you make - sure you use the non-blocking multi API which will do transfers - asynchronously - still in the same single thread. - - libcurl will potentially internally use threads for name resolving, if it - was built to work like that, but in those cases it will create the child - threads by itself and they will only be used and then killed internally by - libcurl and never exposed to the outside. - -6. License Issues - - curl and libcurl are released under an MIT/X derivative license. The license - is liberal and should not impose a problem for your project. This section is - just a brief summary for the cases we get the most questions. (Parts of this - section was much enhanced by Bjorn Reese.) - - We are not lawyers and this is not legal advice. You should probably consult - one if you want true and accurate legal insights without our prejudice. Note - especially that this section concerns the libcurl license only; compiling in - features of libcurl that depend on other libraries (e.g. OpenSSL) may affect - the licensing obligations of your application. - - 6.1 I have a GPL program, can I use the libcurl library? - - Yes - - Since libcurl may be distributed under the MIT/X derivative license, it can - be used together with GPL in any software. - - 6.2 I have a closed-source program, can I use the libcurl library? - - Yes - - libcurl does not put any restrictions on the program that uses the library. - - 6.3 I have a BSD licensed program, can I use the libcurl library? - - Yes - - libcurl does not put any restrictions on the program that uses the library. - - 6.4 I have a program that uses LGPL libraries, can I use libcurl? - - Yes - - The LGPL license does not clash with other licenses. - - 6.5 Can I modify curl/libcurl for my program and keep the changes secret? - - Yes - - The MIT/X derivative license practically allows you to do almost anything - with the sources, on the condition that the copyright texts in the sources - are left intact. - - 6.6 Can you please change the curl/libcurl license to XXXX? - - No. - - We have carefully picked this license after years of development and - discussions and a large amount of people have contributed with source code - knowing that this is the license we use. This license puts the restrictions - we want on curl/libcurl and it does not spread to other programs or - libraries that use it. It should be possible for everyone to use libcurl or - curl in their projects, no matter what license they already have in use. - - 6.7 What are my obligations when using libcurl in my commercial apps? - - Next to none. All you need to adhere to is the MIT-style license (stated in - the COPYING file) which basically says you have to include the copyright - notice in "all copies" and that you may not use the copyright holder's name - when promoting your software. - - You do not have to release any of your source code. - - You do not have to reveal or make public any changes to the libcurl source - code. - - You do not have to broadcast to the world that you are using libcurl within - your app. - - All we ask is that you disclose "the copyright notice and this permission - notice" somewhere. Most probably like in the documentation or in the section - where other third party dependencies already are mentioned and acknowledged. - - As can be seen here: https://curl.se/docs/companies.html and elsewhere, - more and more companies are discovering the power of libcurl and take - advantage of it even in commercial environments. - - -7. PHP/CURL Issues - - 7.1 What is PHP/CURL? - - The module for PHP that makes it possible for PHP programs to access curl- - functions from within PHP. - - In the cURL project we call this module PHP/CURL to differentiate it from - curl the command line tool and libcurl the library. The PHP team however - does not refer to it like this (for unknown reasons). They call it plain - CURL (often using all caps) or sometimes ext/curl, but both cause much - confusion to users which in turn gives us a higher question load. - - 7.2 Who wrote PHP/CURL? - - PHP/CURL was initially written by Sterling Hughes. - - 7.3 Can I perform multiple requests using the same handle? - - Yes - at least in PHP version 4.3.8 and later (this has been known to not - work in earlier versions, but the exact version when it started to work is - unknown to me). - - After a transfer, you just set new options in the handle and make another - transfer. This will make libcurl reuse the same connection if it can. - - 7.4 Does PHP/CURL have dependencies? - - PHP/CURL is a module that comes with the regular PHP package. It depends on - and uses libcurl, so you need to have libcurl installed properly before - PHP/CURL can be used. - -8. Development - - 8.1 Why does curl use C89? - - As with everything in curl, there is a history and we keep using what we have - used before until someone brings up the subject and argues for and works on - changing it. - - We started out using C89 in the 1990s because that was the only way to write - a truly portable C program and have it run as widely as possible. C89 was for - a long time even necessary to make things work on otherwise considered modern - platforms such as Windows. Today, we do not really know how many users that - still require the use of a C89 compiler. - - We will continue to use C89 for as long as nobody brings up a strong enough - reason for us to change our minds. The core developers of the project do not - feel restricted by this and we are not convinced that going C99 will offer us - enough of a benefit to warrant the risk of cutting off a share of users. - - 8.2 Will curl be rewritten? - - In one go: no. Little by little over time? Maybe. - - Over the years, new languages and clever operating environments come and go. - Every now and then the urge apparently arises to request that we rewrite curl - in another language. - - Some the most important properties in curl are maintaining the API and ABI - for libcurl and keeping the behavior for the command line tool. As long as we - can do that, everything else is up for discussion. To maintain the ABI, we - probably have to maintain a certain amount of code in C, and to remain rock - stable, we will never risk anything by rewriting a lot of things in one go. - That said, we can certainly offer more and more optional backends written in - other languages, as long as those backends can be plugged in at build-time. - Backends can be written in any language, but should probably provide APIs - usable from C to ease integration and transition. diff --git a/docs/FAQ.md b/docs/FAQ.md new file mode 100644 index 0000000000..05f7eda382 --- /dev/null +++ b/docs/FAQ.md @@ -0,0 +1,1427 @@ + + +# Frequently Asked Questions + +# Philosophy + +## What is curl? + +curl is the name of the project. The name is a play on *Client for URLs*, +originally with URL spelled in uppercase to make it obvious it deals with +URLs. The fact it can also be read as *see URL* also helped, it works as an +abbreviation for *Client URL Request Library* or why not the recursive +version: *curl URL Request Library*. + +The curl project produces two products: + +### libcurl + +A client-side URL transfer library, supporting DICT, FILE, FTP, FTPS, GOPHER, +GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, +RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. + +libcurl supports HTTPS certificates, HTTP POST, HTTP PUT, FTP uploading, +Kerberos, SPNEGO, HTTP form based upload, proxies, cookies, user+password +authentication, file transfer resume, http proxy tunneling and more. + +libcurl is highly portable, it builds and works identically on numerous +platforms. The [internals document](https://curl.se/docs/install.html#Ports) +lists more than 110 operating systems and 28 CPU architectures on which curl +has been reported to run. + +libcurl is free, thread-safe, IPv6 compatible, feature rich, well supported +and fast. + +### curl + +A command line tool for getting or sending data using URL syntax. + +Since curl uses libcurl, curl supports the same wide range of common Internet +protocols that libcurl does. + +We pronounce curl with an initial k sound. It rhymes with words like girl and +earl. [This is a short WAV +file](https://media.merriam-webster.com/soundc11/c/curl0001.wav) to help you. + +There are numerous sub-projects and related projects that also use the word +curl in the project names in various combinations, but you should take notice +that this FAQ is directed at the command-line tool named curl (and libcurl the +library), and may therefore not be valid for other curl-related projects. +(There is however a small section for the PHP/CURL in this FAQ.) + +## What is libcurl? + +libcurl is a reliable and portable library for doing Internet data transfers +using one or more of its supported Internet protocols. + +You can use libcurl freely in your application, be it open source, commercial +or closed-source. + +libcurl is most probably the most portable, most powerful and most often used +C-based multi-platform file transfer library on this planet - be it open +source or commercial. + +## What is curl not? + +curl is not a Wget clone. That is a common misconception. Never, during curl's +development, have we intended curl to replace Wget or compete on its market. +curl is targeted at single-shot file transfers. + +curl is not a website mirroring program. If you want to use curl to mirror +something: fine, go ahead and write a script that wraps around curl or use +libcurl to make it reality. + +curl is not an FTP site mirroring program. Sure, get and send FTP with curl +but if you want systematic and sequential behavior you should write a script +(or write a new program that interfaces libcurl) and do it. + +curl is not a PHP tool, even though it works perfectly well when used from or +with PHP (when using the PHP/CURL module). + +curl is not a program for a single operating system. curl exists, compiles, +builds and runs under a wide range of operating systems, including all modern +Unixes (and a bunch of older ones too), Windows, Amiga, OS/2, macOS, QNX etc. + +## When would you make curl do ... ? + +We love suggestions of what to change in order to make curl and libcurl +better. We do however believe in a few rules when it comes to the future of +curl: + +curl the command line tool is to remain a non-graphical command line tool. If +you want GUIs or fancy scripting capabilities, you should look for another +tool that uses libcurl. + +We do not add things to curl that other small and available tools already do +well at the side. curl's output can be piped into another program or +redirected to another file for the next program to interpret. + +We focus on protocol related issues and improvements. If you want to do more +with the supported protocols than curl currently does, chances are good we +would agree. If you want to add more protocols, we may agree. + +If you want someone else to do all the work while you wait for us to implement +it for you, that is not a friendly attitude. We spend a considerable time +already on maintaining and developing curl. In order to get more out of us, +you should consider trading in some of your time and effort in return. Go to +the [GitHub repository](https://github.com/curl/curl), fork the project, +and create pull requests with your proposed changes. + +If you write the code, chances are better that it gets into curl faster. + +## Who makes curl? + +curl and libcurl are not made by any single individual. Daniel Stenberg is +project leader and main developer, but other persons' submissions are +important and crucial. Anyone can contribute and post their changes and +improvements and have them inserted in the main sources (of course on the +condition that developers agree that the fixes are good). + +The full list of all contributors is found in the +[docs/THANKS](https://curl.se/docs/thanks.html) file. + +curl is developed by a community, with Daniel at the wheel. + +## What do you get for making curl? + +Project curl is entirely free and open. We do this voluntarily, mostly in our +spare time. Companies may pay individual developers to work on curl. This is +not controlled by nor supervised in any way by the curl project. + +We get help from companies. Haxx provides website, bandwidth, mailing lists +etc, GitHub hosts [the primary git repository](https://github.com/curl/curl) +and other services like the bug tracker. Also again, some companies have +sponsored certain parts of the development in the past and I hope some +continue to do so in the future. + +If you want to [support our project](https://curl.se/sponsors.html), consider +a donation or a banner-program or even better: by helping us with coding, +documenting or testing etc. + +## What about CURL from curl.com? + +During the summer of 2001, curl.com was busy advertising their client-side +programming language for the web, named CURL. + +We are in no way associated with curl.com or their CURL programming language. + +Our project name curl has been in effective use since 1998. We were not the +first computer related project to use the name *curl* and do not claim any +rights to the name. + +We recognize that we are living in parallel with curl.com and wish them +every success. + +## I have a problem, who do I mail? + +Please do not mail any single individual unless you really need to. Keep +curl-related questions on a suitable mailing list. All available mailing lists +are listed [online](https://curl.se/mail/). + +Keeping curl-related questions and discussions on mailing lists allows others +to join in and help, to share their ideas, to contribute their suggestions and +to spread their wisdom. Keeping discussions on public mailing lists also +allows for others to learn from this (both current and future users thanks to +the web based archives of the mailing lists), thus saving us from having to +repeat ourselves even more. Thanks for respecting this. + +If you have found or suspect a security problem in curl or libcurl, +[submit all the details to us](https://curl.se/dev/vuln-disclosure.html). We +keep the issue private while we investigate, confirm it, work and validate a +fix and agree on a time schedule for publication etc. That way we produce a +fix in a timely manner before the flaw is announced to the world, reducing the +impact the problem risks having on existing users. + +Security issues can also be taking to the curl security team by emailing +security at curl.se (closed list of receivers, mails are not disclosed). + +## Where do I buy commercial support for curl? + +curl is fully open source. It means you can hire any skilled engineer to fix +your curl-related problems. + +We list [available alternatives](https://curl.se/support.html). + +## How many are using curl? + +It is impossible to tell. + +We do not know how many users that knowingly have installed and use curl. + +We do not know how many users that use curl without knowing that they are in +fact using it. + +We do not know how many users that downloaded or installed curl and then never +use it. + +In 2025, we estimate that curl runs in roughly thirty billion installations +world wide. + +## Why do you not update ca-bundle.crt + +In the curl project we have decided not to attempt to keep this file updated +(or even present) since deciding what to add to a ca cert bundle is an +undertaking we have not been ready to accept, and the one we can get from +Mozilla is perfectly fine so there is no need to duplicate that work. + +Today, with many services performed over HTTPS, every operating system should +come with a default ca cert bundle that can be deemed somewhat trustworthy and +that collection (if reasonably updated) should be deemed to be a lot better +than a private curl version. + +If you want the most recent collection of ca certs that Mozilla Firefox uses, +we recommend that using our online [CA certificate +service](https://curl.se/docs/caextract.html) setup for this purpose. + +## I have a problem who, can I chat with? + +There is a bunch of friendly people hanging out in the #curl channel on the +IRC network libera.chat. If you are polite and nice, chances are good that you +can get -- or provide -- help instantly. + +## curl's ECCN number? + +The US government restricts exports of software that contains or uses +cryptography. When doing so, the Export Control Classification Number (ECCN) +is used to identify the level of export control etc. + +Apache Software Foundation has [a good explanation of +ECCN](https://www.apache.org/dev/crypto.html). + +We believe curl's number might be ECCN 5D002, another possibility is 5D992. It +seems necessary to write them (the authority that administers ECCN numbers), +asking to confirm. + +Comprehensible explanations of the meaning of such numbers and how to obtain +them (resp.) are [here](https://www.bis.gov/licensing/classify-your-item) +and [here](https://www.bis.gov/licensing/classify-your-item/publicly-available-classification-information). + +An incomprehensible description of the two numbers above is available on +[bis.doc.gov](https://www.bis.doc.gov/index.php/documents/new-encryption/1653-ccl5-pt2-3) + +## How do I submit my patch? + +We strongly encourage you to submit changes and improvements directly as [pull +requests on GitHub](https://github.com/curl/curl/pulls). + +If you cannot or choose not to engage with GitHub, send your patch +to the curl-library mailing list. We are many subscribers there and there are +lots of people who can review patches, comment on them and receive them +properly. + +Lots of more details are found in the +[contribute](https://curl.se/dev/contribute.html) and +[internals](https://curl.se/dev/internals.html) +documents. + +## How do I port libcurl to my OS? + +Here's a rough step-by-step: + +1. copy a suitable `lib/config-*.h` file as a start to `lib/config-[youros].h` +2. edit `lib/config-[youros].h` to match your OS and setup +3. edit `lib/curl_setup.h` to include `config-[youros].h` when your OS is + detected by the preprocessor, in the style others already exist +4. compile `lib/*.c` and make them into a library + +# Install + +## configure fails when using static libraries + +You may find that configure fails to properly detect the entire dependency +chain of libraries when you provide static versions of the libraries that +configure checks for. + +The reason why static libraries is much harder to deal with is that for them +we do not get any help but the script itself must know or check what more +libraries that are needed (with shared libraries, that dependency chain is +handled automatically). This is an error-prone process and one that also tends +to vary over time depending on the release versions of the involved components +and may also differ between operating systems. + +For that reason, configure does few attempts to actually figure this out and +you are instead encouraged to set `LIBS` and `LDFLAGS` accordingly when you invoke +configure, and point out the needed libraries and set the necessary flags +yourself. + +## Does curl work with other SSL libraries? + +curl has been written to use a generic SSL function layer internally, and +that SSL functionality can then be provided by one out of many different SSL +backends. + +curl can be built to use one of the following SSL alternatives: OpenSSL, +LibreSSL, BoringSSL, AWS-LC, GnuTLS, wolfSSL, mbedTLS, Schannel (native +Windows) or Rustls. They all have their pros and cons, and we maintain [a TLS +library comparison](https://curl.se/docs/ssl-compared.html). + +## How do I upgrade curl.exe in Windows? + +The curl tool that is shipped as an integrated component of Windows 10 and +Windows 11 is managed by Microsoft. If you were to delete the file or replace +it with a newer version downloaded from [the curl +website](https://curl.se/windows/), then Windows Update ceases to work on +your system. + +There is no way to independently force an upgrade of the curl.exe that is part +of Windows other than through the regular Windows update process. There is +also nothing the curl project itself can do about this, since this is managed +and controlled entirely by Microsoft as owners of the operating system. + +You can always download and install [the latest version of curl for +Windows](https://curl.se/windows/) into a separate location. + +## Does curl support SOCKS (RFC 1928) ? + +Yes, SOCKS 4 and 5 are supported. + +# Usage + +## curl: (1) SSL is disabled, https: not supported + +If you get this output when trying to get anything from an HTTPS server, it +means that the instance of curl/libcurl that you are using was built without +support for this protocol. + +This could have happened if the configure script that was run at build time +could not find all libs and include files curl requires for SSL to work. If +the configure script fails to find them, curl is built without SSL +support. + +To get HTTPS support into a curl that was previously built but that reports +that HTTPS is not supported, you should dig through the document and logs and +check out why the configure script does not find the SSL libs and/or include +files. + +## How do I tell curl to resume a transfer? + +curl supports resumed transfers both ways on both FTP and HTTP. Try the `-C` +option. + +## Why does my posting using -F not work? + +You cannot arbitrarily use `-F` or `-d`, the choice between `-F` or `-d` +depends on the HTTP operation you need curl to do and what the web server that +receives your post expects. + +If the form you are trying to submit uses the type 'multipart/form-data', +then and only then you must use the -F type. In all the most common cases, +you should use `-d` which then causes a posting with the type +`application/x-www-form-urlencoded`. + +This is described in some detail in the +[Manual](https://curl.se/docs/tutorial.html) and [The Art Of HTTP +Scripting](https://curl.se/docs/httpscripting.html) documents, and if you do +not understand it the first time, read it again before you post questions +about this to the mailing list. Also, try reading through the mailing list +archives for old postings and questions regarding this. + +## How do I tell curl to run custom FTP commands? + +You can tell curl to perform optional commands both before and/or after a file +transfer. Study the `-Q`/`--quote` option. + +Since curl is used for file transfers, you do not normally use curl to perform +FTP commands without transferring anything. Therefore you must always specify +a URL to transfer to/from even when doing custom FTP commands, or use `-I` +which implies the *no body*" option sent to libcurl. + +## How can I disable the Accept: header? + +You can change this and all internally generated headers by adding a +replacement with the `-H`/`--header` option. By adding a header with empty +contents you safely disable that one. Use `-H Accept:` to disable that +specific header. + +## Does curl support ASP, XML, XHTML or HTML version Y? + +To curl, all contents are alike. It does not matter how the page was +generated. It may be ASP, PHP, Perl, shell-script, SSI or plain HTML +files. There is no difference to curl and it does not even know what kind of +language that generated the page. + +See also the separate question about JavaScript. + +## Can I use curl to delete/rename a file through FTP? + +Yes. You specify custom FTP commands with `-Q`/`--quote`. + +One example would be to delete a file after you have downloaded it: + + curl -O ftp://example.com/coolfile -Q '-DELE coolfile' + +or rename a file after upload: + + curl -T infile ftp://example.com/dir/ -Q "-RNFR infile" -Q "-RNTO newname" + +## How do I tell curl to follow HTTP redirects? + +curl does not follow so-called redirects by default. The `Location:` header that +informs the client about this is only interpreted if you are using the +`-L`/`--location` option. As in: + + curl -L https://example.com + +Not all redirects are HTTP ones. See [Redirects work in browser but not with +curl](#redirects-work-in-browser-but-not-with-curl) + +## How do I use curl in my favorite programming language? + +Many programming languages have interfaces and bindings that allow you to use +curl without having to use the command line tool. If you are fluent in such a +language, you may prefer to use one of these interfaces instead. + +Find out more about which languages that support curl directly, and how to +install and use them, in the [libcurl section of the curl +website](https://curl.se/libcurl/). + +All the various bindings to libcurl are made by other projects and people, +outside of the curl project. The curl project itself only produces libcurl +with its plain C API. If you do not find anywhere else to ask you can ask +about bindings on the curl-library list too, but be prepared that people on +that list may not know anything about bindings. + +In December 2025 there were around **60** different [interfaces +available](https://curl.se/libcurl/bindings.html) for almost any language you +can imagine. + +## What about SOAP, WebDAV, XML-RPC or similar protocols over HTTP? + +curl adheres to the HTTP spec, which means you can play with *any* protocol +that is built on top of HTTP. Protocols such as SOAP, WebDAV and XML-RPC are +all such ones. You can use `-X` to set custom requests and -H to set custom +headers (or replace internally generated ones). + +Using libcurl of course also works and you would use the proper library +options to do the same. + +## How do I POST with a different Content-Type? + +You can always replace the internally generated headers with `-H`/`--header`. +To make a simple HTTP POST with `text/xml` as content-type, do something like: + + curl -d "datatopost" -H "Content-Type: text/xml" [URL] + +## Why do FTP-specific features over HTTP proxy fail? + +Because when you use an HTTP proxy, the protocol spoken on the network is +HTTP, even if you specify an FTP URL. This effectively means that you normally +cannot use FTP-specific features such as FTP upload and FTP quote etc. + +There is one exception to this rule, and that is if you can *tunnel through* +the given HTTP proxy. Proxy tunneling is enabled with a special option (`-p`) +and is generally not available as proxy admins usually disable tunneling to +ports other than 443 (which is used for HTTPS access through proxies). + +## Why do my single/double quotes fail? + +To specify a command line option that includes spaces, you might need to put +the entire option within quotes. Like in: + + curl -d " with spaces " example.com + +or perhaps + + curl -d ' with spaces ' example.com + +Exactly what kind of quotes and how to do this is entirely up to the shell or +command line interpreter that you are using. For most Unix shells, you can +more or less pick either single (`'`) or double (`"`) quotes. For Windows/DOS +command prompts you must use double (") quotes, and if the option string +contains inner double quotes you can escape them with a backslash. + +For Windows PowerShell the arguments are not always passed on as expected +because curl is not a PowerShell script. You may or may not be able to use +single quotes. To escape inner double quotes seems to require a +backslash-backtick escape sequence and the outer quotes as double quotes. + +Please study the documentation for your particular environment. Examples in +the curl docs use a mix of both of these as shown above. You must adjust them +to work in your environment. + +Remember that curl works and runs on more operating systems than most single +individuals have ever tried. + +## Does curl support JavaScript or PAC (automated proxy config)? + +Many webpages do stuff using embedded JavaScript. curl and libcurl have +no built-in support for that, so it is treated like any other contents. + +`.pac` files are a Netscape invention and are sometimes used by organizations +to allow them to differentiate which proxies to use. The `.pac` contents is a +JavaScript program that gets invoked by the browser and that returns the name +of the proxy to connect to. Since curl does not support JavaScript, it cannot +support .pac proxy configuration either. + +Some workarounds usually suggested to overcome this JavaScript dependency: + +Depending on the JavaScript complexity, write up a script that translates it +to another language and execute that. + +Read the JavaScript code and rewrite the same logic in another language. + +Implement a JavaScript interpreter, people have successfully used the +Mozilla JavaScript engine in the past. + +Ask your admins to stop this, for a static proxy setup or similar. + +## Can I do recursive fetches with curl? + +No. curl itself has no code that performs recursive operations, such as those +performed by Wget and similar tools. + +There exists curl using scripts with that functionality, and you can write +programs based on libcurl to do it, but the command line tool curl itself +cannot. + +## What certificates do I need when I use SSL? + +There are three different kinds of certificates to keep track of when we talk +about using SSL-based protocols (HTTPS or FTPS) using curl or libcurl. + +### Client certificate + +The server you communicate with may require that you can provide this in +order to prove that you actually are who you claim to be. If the server +does not require this, you do not need a client certificate. + +A client certificate is always used together with a private key, and the +private key has a passphrase that protects it. + +### Server certificate + +The server you communicate with has a server certificate. You can and should +verify this certificate to make sure that you are truly talking to the real +server and not a server impersonating it. + +Servers often also provide an intermediate certificate. It acts as a bridge +between a website's SSL certificate and a Certificate Authority's (CA) root +certificate, creating a "chain of trust". + +### Certificate Authority Certificate ("CA cert") + +You often have several CA certs in a CA cert bundle that can be used to verify +a server certificate that was signed by one of the authorities in the bundle. +curl does not come with a CA cert bundle but most curl installs provide one. +You can also override the default. + +Server certificate verification is enabled by default in curl and libcurl. +Server certificates that are *self-signed* or otherwise signed by a CA that +you do not have a CA cert for, cannot be verified. If the verification during +a connect fails, you are refused access. You then might have to explicitly +disable the verification to connect to the server. + +## How do I list the root directory of an FTP server? + +There are two ways. The way defined in the RFC is to use an encoded slash in +the first path part. List the `/tmp` directory like this: + + curl ftp://ftp.example.com/%2ftmp/ + +The second way is non-standard but more readable; start the path section of the +URL with a slash: + + curl ftp://ftp.example.com//tmp/ + +## Can I use curl to send a POST/PUT and not wait for a response? + +No. + +You can easily write your own program using libcurl to do such stunts. + +## How do I get HTTP from a host using a specific IP address? + +For example, you may be trying out a website installation that is not yet in +the DNS. Or you have a site using multiple IP addresses for a given host +name and you want to address a specific one out of the set. + +Set a custom `Host:` header that identifies the server name you want to reach +but use the target IP address in the URL: + + curl --header "Host: www.example.com" https://somewhere.example/ + +You can also opt to add faked hostname entries to curl with the --resolve +option. That has the added benefit to make things like redirects also work +properly. The above operation would instead be done as: + + curl --resolve www.example.com:80:127.0.0.1 https://www.example.com/ + +## How to SFTP from my user's home directory? + +Contrary to how FTP works, SFTP and SCP URLs specify the exact directory to +work with. It means that if you do not specify that you want the user's home +directory, you get the actual root directory. + +To specify a file in your user's home directory, you need to use the correct +URL syntax which for SFTP might look similar to: + + curl -O -u user:password sftp://example.com/~/file.txt + +and for SCP it is a different protocol prefix: + + curl -O -u user:password scp://example.com/~/file.txt + +## Protocol xxx not supported or disabled in libcurl + +When passing on a URL to curl to use, it may respond that the particular +protocol is not supported or disabled. The particular way this error message +is phrased is because curl does not make a distinction internally of whether a +particular protocol is not supported (i.e. never got any code added that knows +how to speak that protocol) or if it was explicitly disabled. curl can be +built to only support a given set of protocols, and the rest would then be +disabled or not supported. + +Note that this error also occurs if you pass a wrongly spelled protocol part +as in `htpts://example.com` or as in the less evident case if you prefix +the protocol part with a space as in `" https://example.com/"`. + +## curl `-X` gives me HTTP problems + +In normal circumstances, `-X` should hardly ever be used. + +By default you use curl without explicitly saying which request method to use +when the URL identifies an HTTP transfer. If you pass in a URL like `curl +https://example.com` it uses GET. If you use `-d` or `-F`, curl uses POST, +`-I` causes a HEAD and `-T` makes it a PUT. + +If for whatever reason you are not happy with these default choices that curl +does for you, you can override those request methods by specifying `-X +[WHATEVER]`. This way you can for example send a DELETE by doing +`curl -X DELETE [URL]`. + +It is thus pointless to do `curl -XGET [URL]` as GET would be used anyway. In +the same vein it is pointless to do `curl -X POST -d data [URL`. You can make +a fun and somewhat rare request that sends a request-body in a GET request +with something like `curl -X GET -d data [URL]`. + +Note that `-X` does not actually change curl's behavior as it only modifies +the actual string sent in the request, but that may of course trigger a +different set of events. + +Accordingly, by using `-XPOST` on a command line that for example would follow +a 303 redirect, you effectively prevent curl from behaving correctly. Be aware. + +# Running + +## Why do I get problems when I use & or % in the URL? + +In general Unix shells, the & symbol is treated specially and when used, it +runs the specified command in the background. To safely send the & as a part +of a URL, you should quote the entire URL by using single (`'`) or double +(`"`) quotes around it. Similar problems can also occur on some shells with +other characters, including ?*!$~(){}<>\|;`. When in doubt, quote the URL. + +An example that would invoke a remote CGI that uses &-symbols could be: + + curl 'https://www.example.com/cgi-bin/query?text=yes&q=curl' + +In Windows, the standard DOS shell treats the percent sign specially and you +need to use TWO percent signs for each single one you want to use in the URL. + +If you want a literal percent sign to be part of the data you pass in a POST +using `-d`/`--data` you must encode it as `%25` (which then also needs the +percent sign doubled on Windows machines). + +## How can I use {, }, [ or ] to specify multiple URLs? + +Because those letters have a special meaning to the shell, to be used in a URL +specified to curl you must quote them. + +An example that downloads two URLs (sequentially) would be: + + curl '{curl,www}.haxx.se' + +To be able to use those characters as actual parts of the URL (without using +them for the curl URL *globbing* system), use the `-g`/`--globoff` option: + + curl -g 'www.example.com/weirdname[].html' + +## Why do I get downloaded data even though the webpage does not exist? + +curl asks remote servers for the page you specify. If the page does not exist +at the server, the HTTP protocol defines how the server should respond and +that means that headers and a page get returned. That is how HTTP works. + +By using the `--fail` option you can tell curl explicitly to not get any data +if the HTTP return code does not say success. + +## Why do I get return code XXX from an HTTP server? + +RFC 2616 clearly explains the return codes. This is a short transcript. Go +read the RFC for exact details: + +### 400 Bad Request + +The request could not be understood by the server due to malformed +syntax. The client SHOULD NOT repeat the request without modifications. + +### 401 Unauthorized + +The request requires user authentication. + +### 403 Forbidden + +The server understood the request, but is refusing to fulfill it. +Authorization cannot help and the request SHOULD NOT be repeated. + +### 404 Not Found + +The server has not found anything matching the Request-URI. No indication is +given as to whether the condition is temporary or permanent. + +### 405 Method Not Allowed + +The method specified in the Request-Line is not allowed for the resource +identified by the Request-URI. The response MUST include an `Allow:` header +containing a list of valid methods for the requested resource. + +### 301 Moved Permanently + +If you get this return code and an HTML output similar to this: + +

Moved Permanently

The document has moved here. + +it might be because you requested a directory URL but without the trailing +slash. Try the same operation again _with_ the trailing URL, or use the +`-L`/`--location` option to follow the redirection. + +## Can you tell me what error code 142 means? + +All curl error codes are described at the end of the man page, in the section +called **EXIT CODES**. + +Error codes that are larger than the highest documented error code means that +curl has exited due to a crash. This is a serious error, and we appreciate a +detailed bug report from you that describes how we could go ahead and repeat +this. + +## How do I keep usernames and passwords secret in curl command lines? + +This problem has two sides: + +The first part is to avoid having clear-text passwords in the command line so +that they do not appear in *ps* outputs and similar. That is easily avoided by +using the `-K` option to tell curl to read parameters from a file or stdin to +which you can pass the secret info. curl itself also attempts to hide the given +password by blanking out the option - this does not work on all platforms. + +To keep the passwords in your account secret from the rest of the world is +not a task that curl addresses. You could of course encrypt them somehow to +at least hide them from being read by human eyes, but that is not what +anyone would call security. + +Also note that regular HTTP (using Basic authentication) and FTP passwords are +sent as cleartext across the network. All it takes for anyone to fetch them is +to listen on the network. Eavesdropping is easy. Use more secure +authentication methods (like Digest, Negotiate or even NTLM) or consider the +SSL-based alternatives HTTPS and FTPS. + +## I found a bug + +It is not a bug if the behavior is documented. Read the docs first. Especially +check out the KNOWN_BUGS file, it may be a documented bug. + +If it is a problem with a binary you have downloaded or a package for your +particular platform, try contacting the person who built the package/archive +you have. + +If there is a bug, read the BUGS document first. Then report it as described +in there. + +## curl cannot authenticate to a server that requires NTLM? + +NTLM support requires OpenSSL, GnuTLS, mbedTLS or Microsoft Windows libraries +at build-time to provide this functionality. + +## My HTTP request using HEAD, PUT or DELETE does not work + +Many web servers allow or demand that the administrator configures the server +properly for these requests to work on the web server. + +Some servers seem to support HEAD only on certain kinds of URLs. + +To fully grasp this, try the documentation for the particular server software +you are trying to interact with. This is not anything curl can do anything +about. + +## Why do my HTTP range requests return the full document? + +Because the range may not be supported by the server, or the server may choose +to ignore it and return the full document anyway. + +## Why do I get "certificate verify failed" ? + +When you invoke curl and get an error 60 error back it means that curl could +not verify that the server's certificate was good. curl verifies the +certificate using the CA cert bundle and verifying for which names the +certificate has been granted. + +To completely disable the certificate verification, use `-k`. This does +however enable man-in-the-middle attacks and makes the transfer **insecure**. +We strongly advise against doing this for more than experiments. + +If you get this failure with a CA cert bundle installed and used, the server's +certificate might not be signed by one of the certificate authorities in your +CA store. It might for example be self-signed. You then correct this problem +by obtaining a valid CA cert for the server. Or again, decrease the security +by disabling this check. + +At times, you find that the verification works in your favorite browser but +fails in curl. When this happens, the reason is usually that the server sends +an incomplete cert chain. The server is mandated to send all *intermediate +certificates* but does not. This typically works with browsers anyway since +they A) cache such certs and B) supports AIA which downloads such missing +certificates on demand. This is a bad server configuration. A good way to +figure out if this is the case it to use [the SSL Labs +server](https://www.ssllabs.com/ssltest/) test and check the certificate +chain. + +Details are also in [the SSL certificates +document](https://curl.se/docs/sslcerts.html). + +## Why is curl -R on Windows one hour off? + +Since curl 7.53.0 this issue should be fixed as long as curl was built with +any modern compiler that allows for a 64-bit curl_off_t type. For older +compilers or prior curl versions it may set a time that appears one hour off. +This happens due to a flaw in how Windows stores and uses file modification +times and it is not easily worked around. For more details +[read this](https://web.archive.org/web/20050715084352/codeproject.com/datetime/dstbugs.asp). + +## Redirects work in browser but not with curl + +curl supports HTTP redirects well (see a previous question above). Browsers +generally support at least two other ways to perform redirects that curl does +not: + +Meta tags. You can write an HTML tag that causes the browser to redirect +to another given URL after a certain time. + +JavaScript. You can write a JavaScript program embedded in an HTML page that +redirects the browser to another given URL. + +There is no way to make curl follow these redirects. You must either manually +figure out what the page is set to do, or write a script that parses the +results and fetches the new URL. + +## FTPS does not work + +curl supports FTPS (sometimes known as FTP-SSL) both implicit and explicit +mode. + +When a URL is used that starts with `FTPS://`, curl assumes implicit SSL on +the control connection and therefore immediately connects and tries to speak +SSL. `FTPS://` connections default to port 990. + +To use explicit FTPS, you use an `FTP://` URL and the `--ssl-reqd` option (or +one of its related flavors). This is the most common method, and the one +mandated by RFC 4217. This kind of connection then of course uses the standard +FTP port 21 by default. + +## My HTTP POST or PUT requests are slow + +libcurl makes all POST and PUT requests (except for requests with a small +request body) use the `Expect: 100-continue` header. This header allows the +server to deny the operation early so that libcurl can bail out before having +to send any data. This is useful in authentication cases and others. + +Many servers do not implement the `Expect:` stuff properly and if the server +does not respond (positively) within 1 second libcurl continues and sends +off the data anyway. + +You can disable libcurl's use of the `Expect:` header the same way you disable +any header, using `-H` / `CURLOPT_HTTPHEADER`, or by forcing it to use HTTP +1.0. + +## Non-functional connect timeouts + +In most Windows setups having a timeout longer than 21 seconds makes no +difference, as it only sends 3 TCP SYN packets and no more. The second +packet sent three seconds after the first and the third six seconds after +the second. No more than three packets are sent, no matter how long the +timeout is set. + +See Windows option `TcpMaxConnectRetransmissions` for more. + +Also, even on non-Windows systems there may run a firewall or anti-virus +software or similar that accepts the connection but does not actually do +anything else. This makes (lib)curl to consider the connection connected +and thus the connect timeout does not trigger. + +## file:// URLs containing drive letters (Windows, NetWare) + +When using curl to try to download a local file, one might use a URL in this +format: + + file://D:/blah.txt + +you find that even if `D:\blah.txt` does exist, curl returns a 'file not +found' error. + +According to [RFC 1738](https://datatracker.ietf.org/doc/html/rfc1738), +`file://` URLs must contain a host component, but it is ignored by most +implementations. In the above example, `D:` is treated as the host component, +and is taken away. Thus, curl tries to open `/blah.txt`. If your system is +installed to drive C:, that resolves to `C:\blah.txt`, and if that does +not exist you get the not found error. + +To fix this problem, use `file://` URLs with *three* leading slashes: + + file:///D:/blah.txt + +Alternatively, if it makes more sense, specify `localhost` as the host +component: + + file://localhost/D:/blah.txt + +In either case, curl should now be looking for the correct file. + +## Why does not curl return an error when the network cable is unplugged? + +Unplugging a cable is not an error situation. The TCP/IP protocol stack was +designed to be fault tolerant, so even though there may be a physical break +somewhere the connection should not be affected, but possibly delayed. +Eventually, the physical break gets fixed or the data re-routed around +the physical problem through another path. + +In such cases, the TCP/IP stack is responsible for detecting when the network +connection is irrevocably lost. Since with some protocols it is perfectly +legal for the client to wait indefinitely for data, the stack may never report +a problem, and even when it does, it can take up to 20 minutes for it to +detect an issue. The curl option `--keepalive-time` enables keep-alive support +in the TCP/IP stack which makes it periodically probe the connection to make +sure it is still available to send data. That should reliably detect any +TCP/IP network failure. + +TCP keep alive does not detect the network going down before the TCP/IP +connection is established (e.g. during a DNS lookup) or using protocols that +do not use TCP. To handle those situations, curl offers a number of timeouts +on its own. `--speed-limit`/`--speed-time` aborts if the data transfer rate +falls too low, and `--connect-timeout` and `--max-time` can be used to put +an overall timeout on the connection phase or the entire transfer. + +A libcurl-using application running in a known physical environment (e.g. an +embedded device with only a single network connection) may want to act +immediately if its lone network connection goes down. That can be achieved by +having the application monitor the network connection on its own using an +OS-specific mechanism, then signaling libcurl to abort. + +## curl does not return error for HTTP non-200 responses + +Correct. Unless you use `-f` (`--fail`) or `--fail-with-body`. + +When doing HTTP transfers, curl performs exactly what you are asking it to +do and if successful it does not return an error. You can use curl to test +your web server's "file not found" page (that gets 404 back), you can use it +to check your authentication protected webpages (that gets a 401 back) and so +on. + +The specific HTTP response code does not constitute a problem or error for +curl. It sends and delivers HTTP as you asked and if that worked, +everything is fine and dandy. The response code is generally providing more +higher level error information that curl does not care about. The error was +not in the HTTP transfer. + +If you want your command line to treat error codes in the 400 and up range as +errors and thus return a non-zero value and possibly show an error message, +curl has a dedicated option for that: `-f` (`CURLOPT_FAILONERROR` in libcurl +speak). + +You can also use the `-w` option and the variable `%{response_code}` to +extract the exact response code that was returned in the response. + +# libcurl + +## Is libcurl thread-safe? + +Yes. + +We have written the libcurl code specifically adjusted for multi-threaded +programs. libcurl uses thread-safe functions instead of non-safe ones if your +system has such. Note that you must never share the same handle in multiple +threads. + +There may be some exceptions to thread safety depending on how libcurl was +built. Please review [the guidelines for thread +safety](https://curl.se/libcurl/c/threadsafe.html) to learn more. + +## How can I receive all data into a large memory chunk? + +(See the [get in memory](https://curl.se/libcurl/c/getinmemory.html) example.) + +You are in full control of the callback function that gets called every time +there is data received from the remote server. You can make that callback do +whatever you want. You do not have to write the received data to a file. + +One solution to this problem could be to have a pointer to a struct that you +pass to the callback function. You set the pointer using the CURLOPT_WRITEDATA +option. Then that pointer is passed to the callback instead of a FILE * +to a file: + +~~~c +/* store data this struct */ +struct MemoryStruct { + char *memory; + size_t size; +}; + +/* imaginary callback function */ +size_t +WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data) +{ + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)data; + + mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1); + if(mem->memory) { + memcpy(&(mem->memory[mem->size]), ptr, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + } + return realsize; +} +~~~ + +## How do I fetch multiple files with libcurl? + +libcurl has excellent support for transferring multiple files. You should +repeatedly set new URLs with `curl_easy_setopt()` and then transfer it with +`curl_easy_perform()`. The handle you get from curl_easy_init() is not only +reusable, but you are even encouraged to reuse it if you can, as that +enables libcurl to use persistent connections. + +## Does libcurl do Winsock initialization on Win32 systems? + +Yes, if told to in the `curl_global_init()` call. + +## Does CURLOPT_WRITEDATA and CURLOPT_READDATA work on Win32 ? + +Yes, but you cannot open a FILE * and pass the pointer to a DLL and have that +DLL use the FILE * (as the DLL and the client application cannot access each +others' variable memory areas). If you set `CURLOPT_WRITEDATA` you must also use +`CURLOPT_WRITEFUNCTION` as well to set a function that writes the file, even if +all it does is write the data to the specified FILE *. Similarly, if you use +`CURLOPT_READDATA` you must also specify `CURLOPT_READFUNCTION`. + +## What about Keep-Alive or persistent connections? + +curl and libcurl have excellent support for persistent connections when +transferring several files from the same server. curl attempts to reuse +connections for all URLs specified on the same command line/config file, and +libcurl reuses connections for all transfers that are made using the same +libcurl handle. + +When you use the easy interface the connection cache is kept within the easy +handle. If you instead use the multi interface, the connection cache is kept +within the multi handle and shared among all the easy handles that are used +within the same multi handle. + +## Link errors when building libcurl on Windows + +You need to make sure that your project, and all the libraries (both static +and dynamic) that it links against, are compiled/linked against the same run +time library. + +This is determined by the `/MD`, `/ML`, `/MT` (and their corresponding `/M?d`) +options to the command line compiler. `/MD` (linking against `MSVCRT.dll`) +seems to be the most commonly used option. + +When building an application that uses the static libcurl library, you must +add `-DCURL_STATICLIB` to your `CFLAGS`. Otherwise the linker looks for +dynamic import symbols. If you are using Visual Studio, you need to instead +add `CURL_STATICLIB` in the "Preprocessor Definitions" section. + +If you get a linker error like `unknown symbol __imp__curl_easy_init ...` you +have linked against the wrong (static) library. If you want to use the +libcurl.dll and import lib, you do not need any extra `CFLAGS`, but use one of +the import libraries below. These are the libraries produced by the various +lib/Makefile.* files: + +| Target | static lib | import lib for DLL | +|----------------|----------------|--------------------| +| MinGW | `libcurl.a` | `libcurldll.a` | +| MSVC (release) | `libcurl.lib` | `libcurl_imp.lib` | +| MSVC (debug) | `libcurld.lib` | `libcurld_imp.lib` | + +## libcurl.so.X: open failed: No such file or directory + +This is an error message you might get when you try to run a program linked +with a shared version of libcurl and your runtime linker (`ld.so`) could not +find the shared library named `libcurl.so.X`. (Where X is the number of the +current libcurl ABI, typically 3 or 4). + +You need to make sure that `ld.so` finds `libcurl.so.X`. You can do that +multiple ways, and it differs somewhat between different operating systems. +They are usually: + +* Add an option to the linker command line that specify the hard-coded path + the runtime linker should check for the lib (usually `-R`) +* Set an environment variable (`LD_LIBRARY_PATH` for example) where `ld.so` + should check for libs +* Adjust the system's config to check for libs in the directory where you have + put the library (like Linux's `/etc/ld.so.conf`) + +`man ld.so` and `man ld` tells you more details + +## How does libcurl resolve hostnames? + +libcurl supports a large number of name resolve functions. One of them is +picked at build-time and used unconditionally. Thus, if you want to change +name resolver function you must rebuild libcurl and tell it to use +a different function. + +### The non-IPv6 resolver + +The non-IPv6 resolver that can use one of four different hostname resolve +calls depending on what your system supports: + +1. gethostbyname() +2. gethostbyname_r() with 3 arguments +3. gethostbyname_r() with 5 arguments +4. gethostbyname_r() with 6 arguments + +### The IPv6 resolver + +Uses getaddrinfo() + +### The cares resolver + +The c-ares based name resolver that uses the c-ares library for resolves. +Using this offers asynchronous name resolves. + +## The threaded resolver + +It uses the IPv6 or the non-IPv6 resolver solution in a temporary thread. + +## How do I prevent libcurl from writing the response to stdout? + +libcurl provides a default built-in write function that writes received data +to stdout. Set the `CURLOPT_WRITEFUNCTION` to receive the data, or possibly +set `CURLOPT_WRITEDATA` to a different FILE * handle. + +## How do I make libcurl not receive the whole HTTP response? + +You make the write callback (or progress callback) return an error and libcurl +then aborts the transfer. + +## Can I make libcurl fake or hide my real IP address? + +No. libcurl operates on a higher level. Besides, faking IP address would +imply sending IP packets with a made-up source address, and then you normally +get a problem with receiving the packet sent back as they would then not be +routed to you. + +If you use a proxy to access remote sites, the sites do not see your local +IP address but instead the address of the proxy. + +Also note that on many networks NATs or other IP-munging techniques are used +that makes you see and use a different IP address locally than what the remote +server is seeing you coming from. You may also consider using +[Tor](https://www.torproject.org/). + +## How do I stop an ongoing transfer? + +With the easy interface you make sure to return the correct error code from +one of the callbacks, but none of them are instant. There is no function you +can call from another thread or similar that stops it immediately. +Instead, you need to make sure that one of the callbacks you use returns an +appropriate value that stops the transfer. Suitable callbacks that you can +do this with include the progress callback, the read callback and the write +callback. + +If you are using the multi interface, you can also stop a transfer by removing +the particular easy handle from the multi stack at any moment you think the +transfer is done or when you wish to abort the transfer. + +## Using C++ non-static functions for callbacks? + +libcurl is a C library, it does not know anything about C++ member functions. + +You can overcome this limitation with relative ease using a static member +function that is passed a pointer to the class: + +~~~c++ +// f is the pointer to your object. +static size_t YourClass::func(char *buffer, size_t sz, size_t n, void *f) +{ + // Call non-static member function. + static_cast(f)->nonStaticFunction(); +} + +// This is how you pass pointer to the static function: +curl_easy_setopt(hcurl, CURLOPT_WRITEFUNCTION, YourClass::func); +curl_easy_setopt(hcurl, CURLOPT_WRITEDATA, this); +~~~ + +## How do I get an FTP directory listing? + +If you end the FTP URL you request with a slash, libcurl provides you with +a directory listing of that given directory. You can also set +`CURLOPT_CUSTOMREQUEST` to alter what exact listing command libcurl would use +to list the files. + +The follow-up question tends to be how is a program supposed to parse the +directory listing. How does it know what's a file and what's a directory and +what's a symlink etc. If the FTP server supports the `MLSD` command then it +returns data in a machine-readable format that can be parsed for type. The +types are specified by RFC 3659 section 7.5.1. If `MLSD` is not supported then +you have to work with what you are given. The `LIST` output format is entirely +at the server's own liking and the `NLST` output does not reveal any types and +in many cases does not even include all the directory entries. Also, both +`LIST` and `NLST` tend to hide Unix-style hidden files (those that start with +a dot) by default so you need to do `LIST -a` or similar to see them. + +Example - List only directories. `ftp.funet.fi` supports `MLSD` and +`ftp.kernel.org` does not: + + curl -s ftp.funet.fi/pub/ -X MLSD | \ + perl -lne 'print if s/(?:^|;)type=dir;[^ ]+ (.+)$/$1/' + + curl -s ftp.kernel.org/pub/linux/kernel/ | \ + perl -lne 'print if s/^d[-rwx]{9}(?: +[^ ]+){7} (.+)$/$1/' + +If you need to parse LIST output, libcurl provides the ability to specify a +wildcard to download multiple files from an FTP directory. + +## I want a different time-out + +Sometimes users realize that `CURLOPT_TIMEOUT` and `CURLOPT_CONNECTIMEOUT` are +not sufficiently advanced or flexible to cover all the various use cases and +scenarios applications end up with. + +libcurl offers many more ways to time-out operations. A common alternative is +to use the `CURLOPT_LOW_SPEED_LIMIT` and `CURLOPT_LOW_SPEED_TIME` options to +specify the lowest possible speed to accept before to consider the transfer +timed out. + +The most flexible way is by writing your own time-out logic and using +`CURLOPT_XFERINFOFUNCTION` (perhaps in combination with other callbacks) and +use that to figure out exactly when the right condition is met when the +transfer should get stopped. + +## Can I write a server with libcurl? + +No. libcurl offers no functions or building blocks to build any kind of +Internet protocol server. libcurl is only a client-side library. For server +libraries, you need to continue your search elsewhere but there exist many +good open source ones out there for most protocols you could want a server +for. There are also really good stand-alone servers that have been tested and +proven for many years. There is no need for you to reinvent them. + +## Does libcurl use threads? + +No, libcurl executes in the same thread you call it in. All callbacks are +called in the same thread as the one you call libcurl in. + +If you want to avoid your thread to be blocked by the libcurl call, you make +sure you use the non-blocking multi API which does transfers +asynchronously - still in the same single thread. + +libcurl does potentially internally use threads for name resolving, if it was +built to work like that, but in those cases it creates the child threads by +itself and they are only used and then killed internally by libcurl and never +exposed to the outside. + +# License + +curl and libcurl are released under an MIT/X derivative license. The license +is liberal and should not impose a problem for your project. This section is a +brief summary for the cases we get the most questions. + +We are not lawyers and this is not legal advice. You should probably consult +one if you want true and accurate legal insights without our prejudice. Note +especially that this section concerns the libcurl license only; compiling in +features of libcurl that depend on other libraries (e.g. OpenSSL) may affect +the licensing obligations of your application. + +## I have a GPL program, can I use the libcurl library? + +Yes + +Since libcurl may be distributed under the MIT/X derivative license, it can be +used together with GPL in any software. + +## I have a closed-source program, can I use the libcurl library? + +Yes + +libcurl does not put any restrictions on the program that uses the library. + +## I have a BSD licensed program, can I use the libcurl library? + +Yes + +libcurl does not put any restrictions on the program that uses the library. + +## I have a program that uses LGPL libraries, can I use libcurl? + +Yes + +The LGPL license does not clash with other licenses. + +## Can I modify curl/libcurl for my program and keep the changes secret? + +Yes + +The MIT/X derivative license practically allows you to do almost anything with +the sources, on the condition that the copyright texts in the sources are left +intact. + +## Can you please change the curl/libcurl license? + +No. + +We have carefully picked this license after years of development and +discussions and a large amount of people have contributed with source code +knowing that this is the license we use. This license puts the restrictions we +want on curl/libcurl and it does not spread to other programs or libraries +that use it. It should be possible for everyone to use libcurl or curl in +their projects, no matter what license they already have in use. + +## What are my obligations when using libcurl in my commercial apps? + +Next to none. All you need to adhere to is the MIT-style license (stated in +the COPYING file) which says you have to include the copyright notice in *all +copies* and that you may not use the copyright holder's name when promoting +your software. + +You do not have to release any of your source code. + +You do not have to reveal or make public any changes to the libcurl source +code. + +You do not have to broadcast to the world that you are using libcurl within +your app. + +All we ask is that you disclose *the copyright notice and this permission +notice* somewhere. Most probably like in the documentation or in the section +where other third party dependencies already are mentioned and acknowledged. + +As can be seen [here](https://curl.se/docs/companies.html) and elsewhere, more +and more companies are discovering the power of libcurl and take advantage of +it even in commercial environments. + +## What license does curl use exactly? + +curl is released under an [MIT derivative +license](https://curl.se/docs/copyright.html). It is similar but not identical +to the MIT license. + +The difference is considered big enough to make SPDX list it under its own +identifier: [curl](https://spdx.org/licenses/curl.html). + +The changes done to the license that make it uniquely curl were tiny and +well-intended, but the reasons for them have been forgotten and we strongly +discourage others from doing the same thing. + +# PHP/CURL + +## What is PHP/CURL? + +The module for PHP that makes it possible for PHP programs to access curl +functions from within PHP. + +In the curl project we call this module PHP/CURL to differentiate it from curl +the command line tool and libcurl the library. The PHP team however does not +refer to it like this (for unknown reasons). They call it plain CURL (often +using all caps) or sometimes ext/curl, but both cause much confusion to users +which in turn gives us a higher question load. + +## Who wrote PHP/CURL? + +PHP/CURL was initially written by Sterling Hughes. + +## Can I perform multiple requests using the same handle? + +Yes. + +After a transfer, you set new options in the handle and make another transfer. +This makes libcurl reuse the same connection if it can. + +## Does PHP/CURL have dependencies? + +PHP/CURL is a module that comes with the regular PHP package. It depends on +and uses libcurl, so you need to have libcurl installed properly before +PHP/CURL can be used. + +# Development + +## Why does curl use C89? + +As with everything in curl, there is a history and we keep using what we have +used before until someone brings up the subject and argues for and works on +changing it. + +We started out using C89 in the 1990s because that was the only way to write a +truly portable C program and have it run as widely as possible. C89 was for a +long time even necessary to make things work on otherwise considered modern +platforms such as Windows. Today, we do not really know how many users that +still require the use of a C89 compiler. + +We continue to use C89 for as long as nobody brings up a strong enough reason +for us to change our minds. The core developers of the project do not feel +restricted by this and we are not convinced that going C99 offers us enough +of a benefit to warrant the risk of cutting off a share of users. + +## Would curl be rewritten? + +In one go: no. Little by little over time? Sure. + +Over the years, new languages and clever operating environments come and go. +Every now and then the urge apparently arises to request that we rewrite curl +in another language. + +Some the most important properties in curl are maintaining the API and ABI for +libcurl and keeping the behavior for the command line tool. As long as we can +do that, everything else is up for discussion. To maintain the ABI, we +probably have to maintain a certain amount of code in C, and to remain rock +stable, we never risk anything by rewriting a lot of things in one go. +That said, we can certainly offer more and more optional backends written in +other languages, as long as those backends can be plugged in at build-time. +Backends can be written in any language, but should probably provide APIs +usable from C to ease integration and transition. diff --git a/docs/FEATURES.md b/docs/FEATURES.md index f366154943..a7f1845056 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -8,242 +8,242 @@ SPDX-License-Identifier: curl ## curl tool - - config file support - - multiple URLs in a single command line - - range "globbing" support: [0-13], {one,two,three} - - multiple file upload on a single command line - - redirect stderr - - parallel transfers +- config file support +- multiple URLs in a single command line +- range "globbing" support: [0-13], {one,two,three} +- multiple file upload on a single command line +- redirect stderr +- parallel transfers ## libcurl - - URL RFC 3986 syntax - - custom maximum download time - - custom lowest download speed acceptable - - custom output result after completion - - guesses protocol from hostname unless specified - - supports .netrc - - progress bar with time statistics while downloading - - standard proxy environment variables support - - have run on 101 operating systems and 28 CPU architectures - - selectable network interface for outgoing traffic - - IPv6 support on Unix and Windows - - happy eyeballs dual-stack IPv4 + IPv6 connects - - persistent connections - - SOCKS 4 + 5 support, with or without local name resolving - - *pre-proxy* support, for *proxy chaining* - - supports username and password in proxy environment variables - - operations through HTTP proxy "tunnel" (using CONNECT) - - replaceable memory functions (malloc, free, realloc, etc) - - asynchronous name resolving - - both a push and a pull style interface - - international domain names (IDN) - - transfer rate limiting - - stable API and ABI - - TCP keep alive - - TCP Fast Open - - DNS cache (that can be shared between transfers) - - non-blocking single-threaded parallel transfers - - Unix domain sockets to server or proxy - - DNS-over-HTTPS - - uses non-blocking name resolves - - selectable name resolver backend +- URL RFC 3986 syntax +- custom maximum download time +- custom lowest download speed acceptable +- custom output result after completion +- guesses protocol from hostname unless specified +- supports .netrc +- progress bar with time statistics while downloading +- standard proxy environment variables support +- have run on 101 operating systems and 28 CPU architectures +- selectable network interface for outgoing traffic +- IPv6 support on Unix and Windows +- happy eyeballs dual-stack IPv4 + IPv6 connects +- persistent connections +- SOCKS 4 + 5 support, with or without local name resolving +- *pre-proxy* support, for *proxy chaining* +- supports username and password in proxy environment variables +- operations through HTTP proxy "tunnel" (using CONNECT) +- replaceable memory functions (malloc, free, realloc, etc) +- asynchronous name resolving +- both a push and a pull style interface +- international domain names (IDN) +- transfer rate limiting +- stable API and ABI +- TCP keep alive +- TCP Fast Open +- DNS cache (that can be shared between transfers) +- non-blocking single-threaded parallel transfers +- Unix domain sockets to server or proxy +- DNS-over-HTTPS +- uses non-blocking name resolves +- selectable name resolver backend ## URL API - - parses RFC 3986 URLs - - generates URLs from individual components - - manages "redirects" +- parses RFC 3986 URLs +- generates URLs from individual components +- manages "redirects" ## Header API - - easy access to HTTP response headers, from all contexts - - named headers - - iterate over headers +- easy access to HTTP response headers, from all contexts +- named headers +- iterate over headers ## TLS - - selectable TLS backend(s) - - TLS False Start - - TLS version control - - TLS session resumption - - key pinning - - mutual authentication - - Use dedicated CA cert bundle - - Use OS-provided CA store - - separate TLS options for HTTPS proxy +- selectable TLS backend(s) +- TLS False Start +- TLS version control +- TLS session resumption +- key pinning +- mutual authentication +- Use dedicated CA cert bundle +- Use OS-provided CA store +- separate TLS options for HTTPS proxy ## HTTP - - HTTP/0.9 responses are optionally accepted - - HTTP/1.0 - - HTTP/1.1 - - HTTP/2, including multiplexing and server push - - GET - - PUT - - HEAD - - POST - - multipart formpost (RFC 1867-style) - - authentication: Basic, Digest, NTLM (9) and Negotiate (SPNEGO) - to server and proxy - - resume transfers - - follow redirects - - maximum amount of redirects to follow - - custom HTTP request - - cookie get/send fully parsed - - reads/writes the Netscape cookie file format - - custom headers (replace/remove internally generated headers) - - custom user-agent string - - custom referrer string - - range - - proxy authentication - - time conditions - - via HTTP proxy, HTTPS proxy or SOCKS proxy - - HTTP/2 or HTTP/1.1 to HTTPS proxy - - retrieve file modification date - - Content-Encoding support for deflate, gzip, brotli and zstd - - "Transfer-Encoding: chunked" support in uploads - - HSTS - - alt-svc - - ETags - - HTTP/1.1 trailers, both sending and getting +- HTTP/0.9 responses are optionally accepted +- HTTP/1.0 +- HTTP/1.1 +- HTTP/2, including multiplexing and server push +- GET +- PUT +- HEAD +- POST +- multipart formpost (RFC 1867-style) +- authentication: Basic, Digest, NTLM (9) and Negotiate (SPNEGO) + to server and proxy +- resume transfers +- follow redirects +- maximum amount of redirects to follow +- custom HTTP request +- cookie get/send fully parsed +- reads/writes the Netscape cookie file format +- custom headers (replace/remove internally generated headers) +- custom user-agent string +- custom referrer string +- range +- proxy authentication +- time conditions +- via HTTP proxy, HTTPS proxy or SOCKS proxy +- HTTP/2 or HTTP/1.1 to HTTPS proxy +- retrieve file modification date +- Content-Encoding support for deflate, gzip, brotli and zstd +- "Transfer-Encoding: chunked" support in uploads +- HSTS +- alt-svc +- ETags +- HTTP/1.1 trailers, both sending and getting ## HTTPS - - HTTP/3 - - using client certificates - - verify server certificate - - via HTTP proxy, HTTPS proxy or SOCKS proxy - - select desired encryption - - select usage of a specific TLS version - - ECH +- HTTP/3 +- using client certificates +- verify server certificate +- via HTTP proxy, HTTPS proxy or SOCKS proxy +- select desired encryption +- select usage of a specific TLS version +- ECH ## FTP - - download - - authentication - - Kerberos 5 - - active/passive using PORT, EPRT, PASV or EPSV - - single file size information (compare to HTTP HEAD) - - 'type=' URL support - - directory listing - - directory listing names-only - - upload - - upload append - - upload via http-proxy as HTTP PUT - - download resume - - upload resume - - custom ftp commands (before and/or after the transfer) - - simple "range" support - - via HTTP proxy, HTTPS proxy or SOCKS proxy - - all operations can be tunneled through proxy - - customizable to retrieve file modification date - - no directory depth limit +- download +- authentication +- Kerberos 5 +- active/passive using PORT, EPRT, PASV or EPSV +- single file size information (compare to HTTP HEAD) +- 'type=' URL support +- directory listing +- directory listing names-only +- upload +- upload append +- upload via http-proxy as HTTP PUT +- download resume +- upload resume +- custom ftp commands (before and/or after the transfer) +- simple "range" support +- via HTTP proxy, HTTPS proxy or SOCKS proxy +- all operations can be tunneled through proxy +- customizable to retrieve file modification date +- no directory depth limit ## FTPS - - implicit `ftps://` support that use SSL on both connections - - explicit "AUTH TLS" and "AUTH SSL" usage to "upgrade" plain `ftp://` - connection to use SSL for both or one of the connections +- implicit `ftps://` support that use SSL on both connections +- explicit "AUTH TLS" and "AUTH SSL" usage to "upgrade" plain `ftp://` + connection to use SSL for both or one of the connections ## SSH (both SCP and SFTP) - - selectable SSH backend - - known hosts support - - public key fingerprinting - - both password and public key auth +- selectable SSH backend +- known hosts support +- public key fingerprinting +- both password and public key auth ## SFTP - - both password and public key auth - - with custom commands sent before/after the transfer - - directory listing +- both password and public key auth +- with custom commands sent before/after the transfer +- directory listing ## TFTP - - download - - upload +- download +- upload ## TELNET - - connection negotiation - - custom telnet options - - stdin/stdout I/O +- connection negotiation +- custom telnet options +- stdin/stdout I/O ## LDAP - - full LDAP URL support +- full LDAP URL support ## DICT - - extended DICT URL support +- extended DICT URL support ## FILE - - URL support - - upload - - resume +- URL support +- upload +- resume ## SMB - - SMBv1 over TCP and SSL - - download - - upload - - authentication with NTLMv1 +- SMBv1 over TCP and SSL +- download +- upload +- authentication with NTLMv1 ## SMTP - - authentication: Plain, Login, CRAM-MD5, Digest-MD5, NTLM, Kerberos 5 and - External - - send emails - - mail from support - - mail size support - - mail auth support for trusted server-to-server relaying - - multiple recipients - - via http-proxy +- authentication: Plain, Login, CRAM-MD5, Digest-MD5, NTLM, Kerberos 5 and + External +- send emails +- mail from support +- mail size support +- mail auth support for trusted server-to-server relaying +- multiple recipients +- via http-proxy ## SMTPS - - implicit `smtps://` support - - explicit "STARTTLS" usage to "upgrade" plain `smtp://` connections to use SSL - - via http-proxy +- implicit `smtps://` support +- explicit "STARTTLS" usage to "upgrade" plain `smtp://` connections to use SSL +- via http-proxy ## POP3 - - authentication: Clear Text, APOP and SASL - - SASL based authentication: Plain, Login, CRAM-MD5, Digest-MD5, NTLM, - Kerberos 5 and External - - list emails - - retrieve emails - - enhanced command support for: CAPA, DELE, TOP, STAT, UIDL and NOOP via - custom requests - - via http-proxy +- authentication: Clear Text, APOP and SASL +- SASL based authentication: Plain, Login, CRAM-MD5, Digest-MD5, NTLM, + Kerberos 5 and External +- list emails +- retrieve emails +- enhanced command support for: CAPA, DELE, TOP, STAT, UIDL and NOOP via + custom requests +- via http-proxy ## POP3S - - implicit `pop3s://` support - - explicit `STLS` usage to "upgrade" plain `pop3://` connections to use SSL - - via http-proxy +- implicit `pop3s://` support +- explicit `STLS` usage to "upgrade" plain `pop3://` connections to use SSL +- via http-proxy ## IMAP - - authentication: Clear Text and SASL - - SASL based authentication: Plain, Login, CRAM-MD5, Digest-MD5, NTLM, - Kerberos 5 and External - - list the folders of a mailbox - - select a mailbox with support for verifying the `UIDVALIDITY` - - fetch emails with support for specifying the UID and SECTION - - upload emails via the append command - - enhanced command support for: EXAMINE, CREATE, DELETE, RENAME, STATUS, - STORE, COPY and UID via custom requests - - via http-proxy +- authentication: Clear Text and SASL +- SASL based authentication: Plain, Login, CRAM-MD5, Digest-MD5, NTLM, + Kerberos 5 and External +- list the folders of a mailbox +- select a mailbox with support for verifying the `UIDVALIDITY` +- fetch emails with support for specifying the UID and SECTION +- upload emails via the append command +- enhanced command support for: EXAMINE, CREATE, DELETE, RENAME, STATUS, + STORE, COPY and UID via custom requests +- via http-proxy ## IMAPS - - implicit `imaps://` support - - explicit "STARTTLS" usage to "upgrade" plain `imap://` connections to use SSL - - via http-proxy +- implicit `imaps://` support +- explicit "STARTTLS" usage to "upgrade" plain `imap://` connections to use SSL +- via http-proxy ## MQTT - - Subscribe to and publish topics using URL scheme `mqtt://broker/topic` +- Subscribe to and publish topics using URL scheme `mqtt://broker/topic` diff --git a/docs/GOVERNANCE.md b/docs/GOVERNANCE.md index 4ab52a9a01..bae06009b9 100644 --- a/docs/GOVERNANCE.md +++ b/docs/GOVERNANCE.md @@ -21,7 +21,7 @@ what the project and the general user population wants and expects from us. ## Legal entity -There is no legal entity. The curl project is just a bunch of people scattered +There is no legal entity. The curl project is a bunch of people scattered around the globe with the common goal to produce source code that creates great products. We are not part of any umbrella organization and we are not located in any specific country. We are totally independent. @@ -46,9 +46,8 @@ the project. Donating plain money to curl is best done to curl's [Open Collective fund](https://opencollective.com/curl). Open Collective is a US based -non-profit organization that holds on to funds for us. This fund is then used -for paying the curl security bug bounties, to reimburse project related -expenses etc. +non-profit organization that holds on to funds for us. This fund is used to +reimburse and pay for project related expenses etc. Donations to the project can also come in the form of server hosting, providing services and paying for people to work on curl related code etc. Usually, such @@ -62,7 +61,7 @@ they can be mentioned on the Sponsors page on the curl website. The curl project does not do or offer commercial support. It only hosts mailing lists, runs bug trackers etc to facilitate communication and work. -However, Daniel works for wolfSSL and we offer commercial curl support there. +Daniel works for wolfSSL, which offers commercial curl support. # Key roles @@ -111,7 +110,7 @@ developers familiar with the curl project. The security team works best when it consists of a small set of active persons. We invite new members when the team seems to need it, and we also expect to retire security team members as they "drift off" from the project or -just find themselves unable to perform their duties there. +find themselves unable to perform their duties there. ## Core team @@ -200,3 +199,18 @@ You need to have gotten a few quality patches merged as a proof of this. If you (appear to) not be active in the project anymore, you may be removed as a maintainer. Thank you for your service. + +# Post-Daniel BDFL + +At the point in a future when Daniel steps away from the project and stops +being the project lead, there is reason to reconsider how to keep driving the +project forward. Countries, companies and organizations have a single +president or CEO for a reason; having a single leader that responsibly can +take quick daily decisions is efficient. Without Daniel as BDFL, either +someone else needs to step up and become president, or the project needs to +adopt a council-driven process. Maybe both? Maybe vote a new project leader +for one year or a few years at a time? + +Deciding the replacement person and governance is a subject for the curl core +team to vote on. When that happens, this section of this document should also +get updated. diff --git a/docs/HELP-US.md b/docs/HELP-US.md index 0619aec531..e676dcf08f 100644 --- a/docs/HELP-US.md +++ b/docs/HELP-US.md @@ -33,12 +33,12 @@ If you are looking for a smaller or simpler task in the project to help out with as an entry-point into the project, perhaps because you are a newcomer or even maybe not a terribly experienced developer, here's our advice: - - Read through this document to get a grasp on a general approach to use - - Consider adding a test case for something not currently tested (correctly) - - Consider updating or adding documentation - - One way to get started gently in the project, is to participate in an - existing issue/PR and help out by reproducing the issue, review the code in - the PR etc. +- Read through this document to get a grasp on a general approach to use +- Consider adding a test case for something not currently tested (correctly) +- Consider updating or adding documentation +- One way to get started gently in the project, is to participate in an + existing issue/PR and help out by reproducing the issue, review the code in + the PR etc. ## Help wanted diff --git a/docs/HISTORY.md b/docs/HISTORY.md index 21df56f85f..c376905ebb 100644 --- a/docs/HISTORY.md +++ b/docs/HISTORY.md @@ -4,28 +4,25 @@ Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl --> -How curl Became Like This -========================= +# How curl Became Like This Towards the end of 1996, Daniel Stenberg was spending time writing an IRC bot for an Amiga related channel on EFnet. He then came up with the idea to make -currency-exchange calculations available to Internet Relay Chat (IRC) -users. All the necessary data were published on the Web; he just needed to -automate their retrieval. +currency-exchange calculations available to Internet Relay Chat (IRC) users. +All the necessary data were published on the Web; he only needed to automate +their retrieval. -1996 ----- +## 1996 On November 11, 1996 the Brazilian developer Rafael Sagula wrote and released HttpGet version 0.1. Daniel extended this existing command-line open-source tool. After a few minor -adjustments, it did just what he needed. The first release with Daniel's -additions was 0.2, released on December 17, 1996. Daniel quickly became the -new maintainer of the project. +adjustments, it did what he needed. The first release with Daniel's additions +was 0.2, released on December 17, 1996. Daniel quickly became the new +maintainer of the project. -1997 ----- +## 1997 HttpGet 0.3 was released in January 1997 and now it accepted HTTP URLs on the command line. @@ -43,8 +40,7 @@ November 24 1997: Version 3.1 added FTP upload support. Version 3.5 added support for HTTP POST. -1998 ----- +## 1998 February 4: urlget 3.10 @@ -71,14 +67,14 @@ code, we switched over to the MPL license to restrict the effects of "copyleft". November: configure script and reported successful compiles on several -major operating systems. The never-quite-understood -F option was added and -curl could now simulate quite a lot of a browser. TELNET support was added. +major operating systems. The often-misunderstood -F option was added, and +curl could now simulate significant browser functionality. TELNET support was +added. curl 5 was released in December 1998 and introduced the first ever curl man page. People started making Linux RPM packages out of it. -1999 ----- +## 1999 January: DICT support added. @@ -94,8 +90,7 @@ September: Released curl 6.0. 15000 lines of code. December 28: added the project on Sourceforge and started using its services for managing the project. -2000 ----- +## 2000 Spring: major internal overhaul to provide a suitable library interface. The first non-beta release was named 7.1 and arrived in August. This offered @@ -117,8 +112,7 @@ September: kerberos4 support was added. November: started the work on a test suite for curl. It was later re-written from scratch again. The libcurl major SONAME number was set to 1. -2001 ----- +## 2001 January: Daniel released curl 7.5.2 under a new license again: MIT (or MPL). The MIT license is extremely liberal and can be combined with GPL @@ -144,8 +138,7 @@ September 25: curl (7.7.2) is bundled in Mac OS X (10.1) for the first time. It already becoming more and more of a standard utility of Linux distributions and a regular in the BSD ports collections. -2002 ----- +## 2002 June: the curl website gets 13000 visits weekly. curl and libcurl is 35000 lines of code. Reported successful compiles on more than 40 combinations @@ -161,8 +154,7 @@ only. Starting with 7.10, curl verifies SSL server certificates by default. -2003 ----- +## 2003 January: Started working on the distributed curl tests. The autobuilds. @@ -177,8 +169,7 @@ to the website. Five official web mirrors. December: full-fledged SSL for FTP is supported. -2004 ----- +## 2004 January: curl 7.11.0 introduced large file support. @@ -197,8 +188,7 @@ August: curl and libcurl 7.12.1 Amount of public website mirrors: 12 Number of known libcurl bindings: 26 -2005 ----- +## 2005 April: GnuTLS can now optionally be used for the secure layer when curl is built. @@ -211,8 +201,7 @@ More than 100,000 unique visitors of the curl website. 25 mirrors. December: security vulnerability: libcurl URL Buffer Overflow -2006 ----- +## 2006 January: We dropped support for Gopher. We found bugs in the implementation that turned out to have been introduced years ago, so with the conclusion that @@ -223,17 +212,18 @@ March: security vulnerability: libcurl TFTP Packet Buffer Overflow September: The major SONAME number for libcurl was bumped to 4 due to the removal of ftp third party transfer support. +October: we started to offer the Mozilla CA cert bundle as a PEM file on the +curl website. + November: Added SCP and SFTP support -2007 ----- +## 2007 February: Added support for the Mozilla NSS library to do the SSL/TLS stuff July: security vulnerability: libcurl GnuTLS insufficient cert verification -2008 ----- +## 2008 November: @@ -243,10 +233,9 @@ November: Known libcurl bindings: 37 Contributors: 683 - 145,000 unique visitors. >100 GB downloaded. +145,000 unique visitors. >100 GB downloaded. -2009 ----- +## 2009 March: security vulnerability: libcurl Arbitrary File Access @@ -256,8 +245,7 @@ August: security vulnerability: libcurl embedded zero in cert name December: Added support for IMAP, POP3 and SMTP -2010 ----- +## 2010 January: Added support for RTSP @@ -279,152 +267,151 @@ August: Known libcurl bindings: 39 Contributors: 808 - Gopher support added (re-added actually, see January 2006) +Gopher support added (re-added actually, see January 2006) -2011 ----- +## 2011 February: added support for the axTLS backend April: added the cyassl backend (later renamed to wolfSSL) -2012 ----- +## 2012 - July: Added support for Schannel (native Windows TLS backend) and Darwin SSL - (Native Mac OS X and iOS TLS backend). +July: Added support for Schannel (native Windows TLS backend) and Darwin SSL +(Native Mac OS X and iOS TLS backend). - Supports Metalink +Supports Metalink - October: SSH-agent support. +October: SSH-agent support. -2013 ----- +## 2013 - February: Cleaned up internals to always uses the "multi" non-blocking - approach internally and only expose the blocking API with a wrapper. +February: Cleaned up internals to always uses the "multi" non-blocking +approach internally and only expose the blocking API with a wrapper. - September: First small steps on supporting HTTP/2 with nghttp2. +September: First small steps on supporting HTTP/2 with nghttp2. - October: Removed krb4 support. +October: Removed krb4 support. - December: Happy eyeballs. +December: Happy eyeballs. -2014 ----- +## 2014 - March: first real release supporting HTTP/2 +March: first real release supporting HTTP/2 - September: Website had 245,000 unique visitors and served 236GB data +September: Website had 245,000 unique visitors and served 236GB data - SMB and SMBS support +SMB and SMBS support -2015 ----- +## 2015 - June: support for multiplexing with HTTP/2 +June: support for multiplexing with HTTP/2 - August: support for HTTP/2 server push +August: support for HTTP/2 server push - December: Public Suffix List +September: started "everything curl". A separate stand-alone book documenting +curl and related info in perhaps a more tutorial style rather than a +reference, -2016 ----- +December: Public Suffix List - January: the curl tool defaults to HTTP/2 for HTTPS URLs +## 2016 - December: curl 7.52.0 introduced support for HTTPS-proxy +January: the curl tool defaults to HTTP/2 for HTTPS URLs - First TLS 1.3 support +June 26: Rafael Sagula, author of the original httpget tool in 1996 died. -2017 ----- +December: curl 7.52.0 introduced support for HTTPS-proxy - July: OSS-Fuzz started fuzzing libcurl +First TLS 1.3 support - September: Added MultiSSL support +## 2017 - The website serves 3100 GB/month +May: Fastly starts hosting the curl website - Public curl releases: 169 - Command line options: 211 - curl_easy_setopt() options: 249 - Public functions in libcurl: 74 - Contributors: 1609 +July: OSS-Fuzz started fuzzing libcurl - October: SSLKEYLOGFILE support, new MIME API +September: Added MultiSSL support - October: Daniel received the Polhem Prize for his work on curl +The website serves 3100 GB/month - November: brotli + Public curl releases: 169 + Command line options: 211 + curl_easy_setopt() options: 249 + Public functions in libcurl: 74 + Contributors: 1609 -2018 ----- +October: SSLKEYLOGFILE support, new MIME API - January: new SSH backend powered by libssh +October: Daniel received the Polhem Prize for his work on curl - March: starting with the 1803 release of Windows 10, curl is shipped bundled - with Microsoft's operating system. +November: brotli - July: curl shows headers using bold type face +## 2018 - October: added DNS-over-HTTPS (DoH) and the URL API +January: new SSH backend powered by libssh - MesaLink is a new supported TLS backend +March: starting with the 1803 release of Windows 10, curl is shipped bundled +with Microsoft's operating system. - libcurl now does HTTP/2 (and multiplexing) by default on HTTPS URLs +July: curl shows headers using bold type face - curl and libcurl are installed in an estimated 5 *billion* instances - world-wide. +October: added DNS-over-HTTPS (DoH) and the URL API - October 31: curl and libcurl 7.62.0 +MesaLink is a new supported TLS backend - Public curl releases: 177 - Command line options: 219 - curl_easy_setopt() options: 261 - Public functions in libcurl: 80 - Contributors: 1808 +libcurl now does HTTP/2 (and multiplexing) by default on HTTPS URLs - December: removed axTLS support +curl and libcurl are installed in an estimated 5 *billion* instances +world-wide. -2019 ----- +October 31: curl and libcurl 7.62.0 - March: added experimental alt-svc support + Public curl releases: 177 + Command line options: 219 + curl_easy_setopt() options: 261 + Public functions in libcurl: 80 + Contributors: 1808 - August: the first HTTP/3 requests with curl. +December: removed axTLS support - September: 7.66.0 is released and the tool offers parallel downloads +## 2019 -2020 ----- +January: Daniel started working full-time on curl, employed by wolfSSL - curl and libcurl are installed in an estimated 10 *billion* instances - world-wide. +March: added experimental alt-svc support - January: added BearSSL support +August: the first HTTP/3 requests with curl. - March: removed support for PolarSSL, added wolfSSH support +September: 7.66.0 is released and the tool offers parallel downloads - April: experimental MQTT support +## 2020 - August: zstd support +curl and libcurl are installed in an estimated 10 *billion* instances +world-wide. - November: the website moves to curl.se. The website serves 10TB data monthly. +January: added BearSSL support - December: alt-svc support +March: removed support for PolarSSL, added wolfSSH support. Created the first +dashboard on the website. -2021 ----- +April: experimental MQTT support - February 3: curl 7.75.0 ships with support for Hyper as an HTTP backend +August: zstd support - March 31: curl 7.76.0 ships with support for Rustls +November: the website moves to curl.se. The website serves 10TB data monthly. - July: HSTS is supported +December: alt-svc support -2022 ----- +## 2021 + +February 3: curl 7.75.0 ships with support for Hyper as an HTTP backend + +March 31: curl 7.76.0 ships with support for Rustls + +July: HSTS is supported + +## 2022 March: added --json, removed mesalink support @@ -434,20 +421,22 @@ March: added --json, removed mesalink support Public functions in libcurl: 86 Contributors: 2601 - The curl.se website serves 16,500 GB/month over 462M requests, the - official docker image has been pulled 4,098,015,431 times. +The curl.se website serves 16,500 GB/month over 462M requests, the +official docker image has been pulled 4,098,015,431 times. + +April: added support for msh3 as another HTTP/3 backend October: initial WebSocket support -2023 ----- +## 2023 March: remove support for curl_off_t < 8 bytes March 31: we started working on a new command line tool for URL parsing and manipulations: trurl. -May: added support for HTTP/2 over HTTPS proxy. Refuse to resolve .onion. +May: added support for HTTP/2 over HTTPS proxy. Refuse to resolve .onion. The +curl GitHub repository reaches 30,000 stars. August: Dropped support for the NSS library @@ -458,8 +447,7 @@ October: added support for IPFS via HTTP gateway December: HTTP/3 support with ngtcp2 is no longer experimental -2024 ----- +## 2024 January: switched to "curldown" for all documentation @@ -475,12 +463,37 @@ November 6: TLS 1.3 early data, WebSocket is official December 21: dropped hyper -2025 ----- +## 2025 February 5: first 0RTT for QUIC, ssl session import/export February: experimental HTTPS RR support -February 22: The website served 62.95 TB/month; 12.43 billion requests - The docker image has been pulled 6373501745 times. +February 22: The website served 62.95 TB/month; 12.43 billion requests. The +docker image has been pulled 6373501745 times. + +June: we removed support for BearSSL, Secure Transport and msh3 + +October: Daniel gets awarded a gold medal by the Swedish Royal Academy of +Engineering Sciences for his work on curl. + +We counted curl having been installed on 110 operating systems and 28 CPU +architectures. + +November: + + Public curl releases: 271 + Command line options: 273 + curl_easy_setopt() options: 308 + Public functions in libcurl: 100 + Contributors: 3534 + +We drop support for krb-ftp, Heimdal, wolfSSH and the winbuild build system. + +Add support for Apple SecTrust, native CA certs on Apple systems. + +December 15: the website served 78 TB over the last month. + +## 2026 + +April: removed support for RTMP diff --git a/docs/HSTS.md b/docs/HSTS.md index 85140ab30f..c6d872077b 100644 --- a/docs/HSTS.md +++ b/docs/HSTS.md @@ -19,16 +19,19 @@ libcurl features an in-memory cache for HSTS hosts, so that subsequent HTTP-only requests to a hostname present in the cache gets internally "redirected" to the HTTPS version. +Since curl 8.20.0, libcurl keeps no more than the most recently added 10,000 +unique HSTS hostnames. + ## `curl_easy_setopt()` options: - - `CURLOPT_HSTS_CTRL` - enable HSTS for this easy handle - - `CURLOPT_HSTS` - specify filename where to store the HSTS cache on close +- `CURLOPT_HSTS_CTRL` - enable HSTS for this easy handle +- `CURLOPT_HSTS` - specify filename where to store the HSTS cache on close (and possibly read from at startup) ## curl command line options - - `--hsts [filename]` - enable HSTS, use the file as HSTS cache. If filename - is `""` (no length) then no file is used, only in-memory cache. +- `--hsts [filename]` - enable HSTS, use the file as HSTS cache. If filename + is `""` (no length) then no file is used, only in-memory cache. ## HSTS cache file format @@ -36,13 +39,13 @@ Lines starting with `#` are ignored. For each hsts entry: - [host name] "YYYYMMDD HH:MM:SS" + [hostname] "YYYYMMDD HH:MM:SS" -The `[host name]` is dot-prefixed if it includes subdomains. +The `[hostname]` is dot-prefixed if it includes subdomains. The time stamp is when the entry expires. ## Possible future additions - - `CURLOPT_HSTS_PRELOAD` - provide a set of HSTS hostnames to load first - - ability to save to something else than a file +- `CURLOPT_HSTS_PRELOAD` - provide a set of HSTS hostnames to load first +- ability to save to something else than a file diff --git a/docs/HTTP-COOKIES.md b/docs/HTTP-COOKIES.md index 62905dbc6c..2a32aae605 100644 --- a/docs/HTTP-COOKIES.md +++ b/docs/HTTP-COOKIES.md @@ -8,116 +8,116 @@ SPDX-License-Identifier: curl ## Cookie overview - Cookies are `name=contents` pairs that an HTTP server tells the client to - hold and then the client sends back those to the server on subsequent - requests to the same domains and paths for which the cookies were set. +Cookies are `name=contents` pairs that an HTTP server tells the client to +hold and then the client sends back those to the server on subsequent +requests to the same domains and paths for which the cookies were set. - Cookies are either "session cookies" which typically are forgotten when the - session is over which is often translated to equal when browser quits, or - the cookies are not session cookies they have expiration dates after which - the client throws them away. +Cookies are either "session cookies" which typically are forgotten when the +session is over which is often translated to equal when browser quits, or +the cookies are not session cookies they have expiration dates after which +the client throws them away. - Cookies are set to the client with the Set-Cookie: header and are sent to - servers with the Cookie: header. +Cookies are set to the client with the Set-Cookie: header and are sent to +servers with the Cookie: header. - For a long time, the only spec explaining how to use cookies was the - original [Netscape spec from 1994](https://curl.se/rfc/cookie_spec.html). +For a long time, the only spec explaining how to use cookies was the +original [Netscape spec from 1994](https://curl.se/rfc/cookie_spec.html). - In 2011, [RFC 6265](https://www.ietf.org/rfc/rfc6265.txt) was finally - published and details how cookies work within HTTP. In 2016, an update which - added support for prefixes was - [proposed](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-prefixes-00), - and in 2017, another update was - [drafted](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-alone-01) - to deprecate modification of 'secure' cookies from non-secure origins. Both - of these drafts have been incorporated into a proposal to - [replace](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-11) - RFC 6265. Cookie prefixes and secure cookie modification protection has been - implemented by curl. +In 2011, [RFC 6265](https://datatracker.ietf.org/doc/html/rfc6265) was finally +published and details how cookies work within HTTP. In 2016, an update which +added support for prefixes was +[proposed](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-prefixes-00), +and in 2017, another update was +[drafted](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-alone-01) +to deprecate modification of 'secure' cookies from non-secure origins. Both +of these drafts have been incorporated into a proposal to +[replace](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-11) +RFC 6265. Cookie prefixes and secure cookie modification protection has been +implemented by curl. - curl considers `http://localhost` to be a *secure context*, meaning that it - allows and uses cookies marked with the `secure` keyword even when done over - plain HTTP for this host. curl does this to match how popular browsers work - with secure cookies. +curl considers `http://localhost` to be a *secure context*, meaning that it +allows and uses cookies marked with the `secure` keyword even when done over +plain HTTP for this host. curl does this to match how popular browsers work +with secure cookies. ## Super cookies - A single cookie can be set for a domain that matches multiple hosts. Like if - set for `example.com` it gets sent to both `aa.example.com` as well as - `bb.example.com`. +A single cookie can be set for a domain that matches multiple hosts. Like if +set for `example.com` it gets sent to both `aa.example.com` as well as +`bb.example.com`. - A challenge with this concept is that there are certain domains for which - cookies should not be allowed at all, because they are *Public - Suffixes*. Similarly, a client never accepts cookies set directly for the - top-level domain like for example `.com`. Cookies set for *too broad* - domains are generally referred to as *super cookies*. +A challenge with this concept is that there are certain domains for which +cookies should not be allowed at all, because they are *Public +Suffixes*. Similarly, a client never accepts cookies set directly for the +top-level domain like for example `.com`. Cookies set for *too broad* +domains are generally referred to as *super cookies*. - If curl is built with PSL (**Public Suffix List**) support, it detects and - discards cookies that are specified for such suffix domains that should not - be allowed to have cookies. +If curl is built with PSL (**Public Suffix List**) support, it detects and +discards cookies that are specified for such suffix domains that should not +be allowed to have cookies. - if curl is *not* built with PSL support, it has no ability to stop super - cookies. +if curl is *not* built with PSL support, it has no ability to stop super +cookies. ## Cookies saved to disk - Netscape once created a file format for storing cookies on disk so that they - would survive browser restarts. curl adopted that file format to allow - sharing the cookies with browsers, only to see browsers move away from that - format. Modern browsers no longer use it, while curl still does. +Netscape once created a file format for storing cookies on disk so that they +would survive browser restarts. curl adopted that file format to allow +sharing the cookies with browsers, only to see browsers move away from that +format. Modern browsers no longer use it, while curl still does. - The Netscape cookie file format stores one cookie per physical line in the - file with a bunch of associated meta data, each field separated with - TAB. That file is called the cookie jar in curl terminology. +The Netscape cookie file format stores one cookie per physical line in the +file with a bunch of associated meta data, each field separated with +TAB. That file is called the cookie jar in curl terminology. - When libcurl saves a cookie jar, it creates a file header of its own in - which there is a URL mention that links to the web version of this document. +When libcurl saves a cookie jar, it creates a file header of its own in +which there is a URL mention that links to the web version of this document. ## Cookie file format - The cookie file format is text based and stores one cookie per line. Lines - that start with `#` are treated as comments. An exception is lines that - start with `#HttpOnly_`, which is a prefix for cookies that have the - `HttpOnly` attribute set. +The cookie file format is text based and stores one cookie per line. Lines +that start with `#` are treated as comments. An exception is lines that +start with `#HttpOnly_`, which is a prefix for cookies that have the +`HttpOnly` attribute set. - Each line that specifies a single cookie consists of seven text fields - separated with TAB characters. A valid line must end with a newline - character. +Each line that specifies a single cookie consists of seven text fields +separated with TAB characters. A valid line must end with a newline +character. ### Fields in the file - Field number, what type and example data and the meaning of it: +Field number, what type and example data and the meaning of it: - 0. string `example.com` - the domain name - 1. boolean `FALSE` - include subdomains - 2. string `/foobar/` - path - 3. boolean `TRUE` - send/receive over HTTPS only - 4. number `1462299217` - expires at - seconds since Jan 1st 1970, or 0 - 5. string `person` - name of the cookie - 6. string `daniel` - value of the cookie +0. string `example.com` - the domain name +1. boolean `FALSE` - include subdomains +2. string `/foobar/` - path +3. boolean `TRUE` - send/receive over HTTPS only +4. number `1462299217` - expires at - seconds since Jan 1st 1970, or 0 +5. string `person` - name of the cookie +6. string `daniel` - value of the cookie ## Cookies with curl the command line tool - curl has a full cookie "engine" built in. If you just activate it, you can - have curl receive and send cookies exactly as mandated in the specs. +curl has a full cookie "engine" built in. If you activate it, you can have +curl receive and send cookies exactly as mandated in the specs. - Command line options: +Command line options: - [`-b, --cookie`](https://curl.se/docs/manpage.html#-b) +[`-b, --cookie`](https://curl.se/docs/manpage.html#-b) - tell curl a file to read cookies from and start the cookie engine, or if it - is not a file it passes on the given string. `-b name=var` works and so does - `-b cookiefile`. +tell curl a file to read cookies from and start the cookie engine, or if it +is not a file it passes on the given string. `-b name=var` works and so does +`-b cookiefile`. - [`-j, --junk-session-cookies`](https://curl.se/docs/manpage.html#-j) +[`-j, --junk-session-cookies`](https://curl.se/docs/manpage.html#-j) - when used in combination with -b, it skips all "session cookies" on load so - as to appear to start a new cookie session. +when used in combination with -b, it skips all "session cookies" on load so +as to appear to start a new cookie session. - [`-c, --cookie-jar`](https://curl.se/docs/manpage.html#-c) +[`-c, --cookie-jar`](https://curl.se/docs/manpage.html#-c) - tell curl to start the cookie engine and write cookies to the given file - after the request(s) +tell curl to start the cookie engine and write cookies to the given file +after the request(s) ## Cookies with libcurl diff --git a/docs/HTTP3.md b/docs/HTTP3.md index 2047d4ad2e..41b1a3fbc1 100644 --- a/docs/HTTP3.md +++ b/docs/HTTP3.md @@ -21,42 +21,42 @@ QUIC libraries we are using: [quiche](https://github.com/cloudflare/quiche) - **EXPERIMENTAL** -[OpenSSL 3.2+ QUIC](https://github.com/openssl/openssl) - **EXPERIMENTAL** - ## Experimental -HTTP/3 support in curl is considered **EXPERIMENTAL** until further notice -when built to use *quiche*. Only the *ngtcp2* backend is not experimental. +HTTP/3 support using *quiche* in curl is considered **EXPERIMENTAL** until +further notice. Only the *ngtcp2* backend is not experimental. Further development and tweaking of the HTTP/3 support in curl happens in the -master branch using pull-requests, just like ordinary changes. +master branch using pull-requests like ordinary changes. To fix before we remove the experimental label: - - the used QUIC library needs to consider itself non-beta - - it is fine to "leave" individual backends as experimental if necessary +- the used QUIC library needs to consider itself non-beta +- it is fine to "leave" individual backends as experimental if necessary # ngtcp2 version -Building curl with ngtcp2 involves 3 components: `ngtcp2` itself, `nghttp3` and a QUIC supporting TLS library. The supported TLS libraries are covered below. +Building curl with ngtcp2 involves 3 components: `ngtcp2` itself, `nghttp3` +and a QUIC supporting TLS library. The supported TLS libraries are covered +below. While any version of `ngtcp2` and `nghttp3` from v1.0.0 on are expected to work, using the latest versions often brings functional and performance improvements. -The build examples use `$NGHTTP3_VERSION` and `$NGTCP2_VERSION` as placeholders -for the version you build. +The build examples use `$NGHTTP3_VERSION` and `$NGTCP2_VERSION` as +placeholders for the version you build. -## Build with OpenSSL +## Build with OpenSSL or fork -OpenSSL v3.5.0+ offers APIs for integration with *ngtcp2* v1.12.0+. Earlier -versions do not work. +OpenSSL v3.5.0+ requires *ngtcp2* v1.12.0+. Earlier versions do not work. -Build OpenSSL (version 3.5.0 or newer): +Build OpenSSL (v3.5.0+) or fork AWS-LC, BoringSSL, LibreSSL or quictls: - % git clone --quiet --depth=1 -b openssl-$OPENSSL_VERSION https://github.com/openssl/openssl + # Instructions for OpenSSL v3.5.0+ + % git clone --depth 1 -b openssl-$OPENSSL_VERSION https://github.com/openssl/openssl % cd openssl - % ./config --prefix= --libdir=lib + % ./config --prefix=/path/to/openssl --libdir=lib % make % make install @@ -67,7 +67,7 @@ Build nghttp3: % cd nghttp3 % git submodule update --init % autoreconf -fi - % ./configure --prefix= --enable-lib-only + % ./configure --prefix=/path/to/nghttp3 --enable-lib-only % make % make install @@ -77,72 +77,40 @@ Build ngtcp2: % git clone -b $NGTCP2_VERSION https://github.com/ngtcp2/ngtcp2 % cd ngtcp2 % autoreconf -fi - % ./configure PKG_CONFIG_PATH=/lib/pkgconfig:/lib/pkgconfig LDFLAGS="-Wl,-rpath,/lib" --prefix= --enable-lib-only --with-openssl + # Change --with-openssl to --with-boringssl for AWS-LC and BoringSSL + % ./configure PKG_CONFIG_PATH=/path/to/openssl/lib/pkgconfig:/path/to/nghttp3/lib/pkgconfig LDFLAGS="-Wl,-rpath,/path/to/openssl/lib" \ + --prefix=/path/to/ngtcp2 --enable-lib-only --with-openssl % make % make install -Build curl: +Build curl (with autotools): % cd .. % git clone https://github.com/curl/curl % cd curl % autoreconf -fi - % LDFLAGS="-Wl,-rpath,/lib" ./configure PKG_CONFIG_PATH=/lib/pkgconfig --with-openssl= --with-nghttp3= --with-ngtcp2 + % ./configure PKG_CONFIG_PATH=/path/to/openssl/lib/pkgconfig LDFLAGS="-Wl,-rpath,/path/to/openssl/lib" \ + --with-openssl=/path/to/openssl --with-ngtcp2=/path/to/ngtcp2 --with-nghttp3=/path/to/nghttp3 % make % make install -## Build with quictls - -OpenSSL does not offer the required APIs for building a QUIC client. You need -to use a TLS library that has such APIs and that works with *ngtcp2*. - -Build quictls (any `+quic` tagged version works): - - % git clone --depth 1 -b openssl-3.1.4+quic https://github.com/quictls/openssl - % cd openssl - % ./config enable-tls1_3 --prefix= --libdir=lib - % make - % make install - -Build nghttp3: - - % cd .. - % git clone -b $NGHTTP3_VERSION https://github.com/ngtcp2/nghttp3 - % cd nghttp3 - % git submodule update --init - % autoreconf -fi - % ./configure --prefix= --enable-lib-only - % make - % make install - -Build ngtcp2: - - % cd .. - % git clone -b $NGTCP2_VERSION https://github.com/ngtcp2/ngtcp2 - % cd ngtcp2 - % autoreconf -fi - % ./configure PKG_CONFIG_PATH=/lib/pkgconfig:/lib/pkgconfig LDFLAGS="-Wl,-rpath,/lib" --prefix= --enable-lib-only - % make - % make install - -Build curl: +Build curl (with CMake): % cd .. % git clone https://github.com/curl/curl % cd curl - % autoreconf -fi - % LDFLAGS="-Wl,-rpath,/lib" ./configure PKG_CONFIG_PATH=/lib/pkgconfig --with-openssl= --with-nghttp3= --with-ngtcp2 - % make - % make install + % PKG_CONFIG_PATH=/path/to/openssl/lib/pkgconfig:/path/to/ngtcp2/lib/pkgconfig:/path/to/nghttp3/lib/pkgconfig cmake -B bld \ + -DOPENSSL_ROOT_DIR=/path/to/openssl -DUSE_NGTCP2=ON + % cmake --build bld ## Build with GnuTLS Build GnuTLS: - % git clone --depth 1 https://gitlab.com/gnutls/gnutls.git + % git clone --depth 1 https://gitlab.com/gnutls/gnutls % cd gnutls % ./bootstrap - % ./configure --prefix= + % ./configure --prefix=/path/to/gnutls % make % make install @@ -153,7 +121,7 @@ Build nghttp3: % cd nghttp3 % git submodule update --init % autoreconf -fi - % ./configure --prefix= --enable-lib-only + % ./configure --prefix=/path/to/nghttp3 --enable-lib-only % make % make install @@ -163,28 +131,37 @@ Build ngtcp2: % git clone -b $NGTCP2_VERSION https://github.com/ngtcp2/ngtcp2 % cd ngtcp2 % autoreconf -fi - % ./configure PKG_CONFIG_PATH=/lib/pkgconfig:/lib/pkgconfig LDFLAGS="-Wl,-rpath,/lib" --prefix= --enable-lib-only --with-gnutls + % ./configure PKG_CONFIG_PATH=/path/to/gnutls/lib/pkgconfig:/path/to/nghttp3/lib/pkgconfig LDFLAGS="-Wl,-rpath,/path/to/gnutls/lib" \ + --prefix=/path/to/ngtcp2 --enable-lib-only --with-gnutls % make % make install -Build curl: +Build curl (with autotools): % cd .. % git clone https://github.com/curl/curl % cd curl % autoreconf -fi - % ./configure PKG_CONFIG_PATH=/lib/pkgconfig --with-gnutls= --with-nghttp3= --with-ngtcp2 + % ./configure PKG_CONFIG_PATH=/path/to/gnutls/lib/pkgconfig --with-gnutls=/path/to/gnutls --with-ngtcp2=/path/to/ngtcp2 --with-nghttp3=/path/to/nghttp3 % make % make install +Build curl (with CMake): + + % cd .. + % git clone https://github.com/curl/curl + % cd curl + % PKG_CONFIG_PATH=/path/to/gnutls/lib/pkgconfig:/path/to/ngtcp2/lib/pkgconfig:/path/to/nghttp3/lib/pkgconfig cmake -B bld -DCURL_USE_GNUTLS=ON -DUSE_NGTCP2=ON + % cmake --build bld + ## Build with wolfSSL Build wolfSSL: - % git clone https://github.com/wolfSSL/wolfssl.git + % git clone https://github.com/wolfSSL/wolfssl % cd wolfssl % autoreconf -fi - % ./configure --prefix= --enable-quic --enable-session-ticket --enable-earlydata --enable-psk --enable-harden --enable-altcertchains + % ./configure --prefix=/path/to/wolfssl --enable-quic --enable-session-ticket --enable-earlydata --enable-psk --enable-harden --enable-altcertchains % make % make install @@ -195,7 +172,7 @@ Build nghttp3: % cd nghttp3 % git submodule update --init % autoreconf -fi - % ./configure --prefix= --enable-lib-only + % ./configure --prefix=/path/to/nghttp3 --enable-lib-only % make % make install @@ -205,25 +182,36 @@ Build ngtcp2: % git clone -b $NGTCP2_VERSION https://github.com/ngtcp2/ngtcp2 % cd ngtcp2 % autoreconf -fi - % ./configure PKG_CONFIG_PATH=/lib/pkgconfig:/lib/pkgconfig LDFLAGS="-Wl,-rpath,/lib" --prefix= --enable-lib-only --with-wolfssl + % ./configure PKG_CONFIG_PATH=/path/to/wolfssl/lib/pkgconfig:/path/to/nghttp3/lib/pkgconfig LDFLAGS="-Wl,-rpath,/path/to/wolfssl/lib" \ + --prefix=/path/to/ngtcp2 --enable-lib-only --with-wolfssl % make % make install -Build curl: +Build curl (with autotools): % cd .. % git clone https://github.com/curl/curl % cd curl % autoreconf -fi - % ./configure PKG_CONFIG_PATH=/lib/pkgconfig --with-wolfssl= --with-nghttp3= --with-ngtcp2 + % ./configure PKG_CONFIG_PATH=/path/to/wolfssl/lib/pkgconfig --with-wolfssl=/path/to/wolfssl --with-ngtcp2=/path/to/ngtcp2 --with-nghttp3=/path/to/nghttp3 % make % make install +Build curl (with CMake): + + % cd .. + % git clone https://github.com/curl/curl + % cd curl + % PKG_CONFIG_PATH=/path/to/wolfssl/lib/pkgconfig:/path/to/ngtcp2/lib/pkgconfig:/path/to/nghttp3/lib/pkgconfig cmake -B bld -DCURL_USE_WOLFSSL=ON -DUSE_NGTCP2=ON + % cmake --build bld + # quiche version quiche support is **EXPERIMENTAL** -Since the quiche build manages its dependencies, curl can be built against the latest version. You are *probably* able to build against their main branch, but in case of problems, we recommend their latest release tag. +Since the quiche build manages its dependencies, curl can be built against the +latest version. You are *probably* able to build against their main branch, +but in case of problems, we recommend their latest release tag. ## Build @@ -242,60 +230,13 @@ Build curl: % git clone https://github.com/curl/curl % cd curl % autoreconf -fi - % ./configure LDFLAGS="-Wl,-rpath,$PWD/../quiche/target/release" --with-openssl=$PWD/../quiche/quiche/deps/boringssl/src --with-quiche=$PWD/../quiche/target/release + % ./configure LDFLAGS="-Wl,-rpath,$PWD/../quiche/target/release" \ + --with-openssl=$PWD/../quiche/quiche/deps/boringssl/src --with-quiche=$PWD/../quiche/target/release % make % make install - If `make install` results in `Permission denied` error, you need to prepend - it with `sudo`. - -# OpenSSL version - -QUIC support is **EXPERIMENTAL** - -Use OpenSSL 3.3.1 or newer (QUIC support was added in 3.3.0, with -shortcomings on some platforms like macOS). 3.4.1 or newer is recommended. -Build via: - - % cd .. - % git clone -b $OPENSSL_VERSION https://github.com/openssl/openssl - % cd openssl - % ./config enable-tls1_3 --prefix= --libdir=lib - % make - % make install - -Build nghttp3: - - % cd .. - % git clone -b $NGHTTP3_VERSION https://github.com/ngtcp2/nghttp3 - % cd nghttp3 - % git submodule update --init - % autoreconf -fi - % ./configure --prefix= --enable-lib-only - % make - % make install - -Build curl: - - % cd .. - % git clone https://github.com/curl/curl - % cd curl - % autoreconf -fi - % LDFLAGS="-Wl,-rpath,/lib" ./configure --with-openssl= --with-openssl-quic --with-nghttp3= - % make - % make install - -You can build curl with cmake: - - % cd .. - % git clone https://github.com/curl/curl - % cd curl - % cmake -B bld -DCURL_USE_OPENSSL=ON -DUSE_OPENSSL_QUIC=ON - % cmake --build bld - % cmake --install bld - - If `make install` results in `Permission denied` error, you need to prepend - it with `sudo`. +If `make install` results in `Permission denied` error, you need to prepend +it with `sudo`. # `--http3` @@ -316,7 +257,7 @@ See this [list of public HTTP/3 servers](https://bagder.github.io/HTTP3-test/) ### HTTPS eyeballing With option `--http3` curl attempts earlier HTTP versions as well should the -connect attempt via HTTP/3 not succeed "fast enough". This strategy is similar +connect attempt via HTTP/3 fail "fast enough". This strategy is similar to IPv4/6 happy eyeballing where the alternate address family is used in parallel after a short delay. @@ -331,25 +272,26 @@ or HTTP/1.1. At half of that value - currently - is the **soft** timeout. The soft timeout fires, when there has been **no data at all** seen from the server on the HTTP/3 connection. -So, without you specifying anything, the hard timeout is 200ms and the soft is 100ms: +Without you specifying anything, the hard timeout is 200ms and the soft is +100ms: - * Ideally, the whole QUIC handshake happens and curl has an HTTP/3 connection - in less than 100ms. - * When QUIC is not supported (or UDP does not work for this network path), no - reply is seen and the HTTP/2 TLS+TCP connection starts 100ms later. - * In the worst case, UDP replies start before 100ms, but drag on. This starts - the TLS+TCP connection after 200ms. - * When the QUIC handshake fails, the TLS+TCP connection is attempted right - away. For example, when the QUIC server presents the wrong certificate. +* Ideally, the whole QUIC handshake happens and curl has an HTTP/3 connection + in less than 100ms. +* When QUIC is not supported (or UDP does not work for this network path), no + reply is seen and the HTTP/2 TLS+TCP connection starts 100ms later. +* In the worst case, UDP replies start before 100ms, but drag on. This starts + the TLS+TCP connection after 200ms. +* When the QUIC handshake fails, the TLS+TCP connection is attempted right + away. For example, when the QUIC server presents the wrong certificate. The whole transfer only fails, when **both** QUIC and TLS+TCP fail to handshake or time out. Note that all this happens in addition to IP version happy eyeballing. If the name resolution for the server gives more than one IP address, curl tries all -those until one succeeds - just as with all other protocols. If those IP -addresses contain both IPv6 and IPv4, those attempts happen, delayed, in -parallel (the actual eyeballing). +those until one succeeds - as with all other protocols. If those IP addresses +contain both IPv6 and IPv4, those attempts happen, delayed, in parallel (the +actual eyeballing). ## Known Bugs @@ -364,8 +306,7 @@ development and experimenting. An existing local HTTP/1.1 server that hosts files. Preferably also a few huge ones. You can easily create huge local files like `truncate -s=8G 8GB` - they -are huge but do not occupy that much space on disk since they are just big -holes. +are huge but do not occupy that much space on disk since they are big holes. In a Debian setup you can install apache2. It runs on port 80 and has a document root in `/var/www/html`. Download the 8GB file from apache with `curl @@ -385,24 +326,26 @@ above. Get, build and install nghttp2: - % git clone https://github.com/nghttp2/nghttp2.git + % git clone https://github.com/nghttp2/nghttp2 % cd nghttp2 % autoreconf -fi - % PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/home/daniel/build-quictls/lib/pkgconfig:/home/daniel/build-nghttp3/lib/pkgconfig:/home/daniel/build-ngtcp2/lib/pkgconfig LDFLAGS=-L/home/daniel/build-quictls/lib CFLAGS=-I/home/daniel/build-quictls/include ./configure --enable-maintainer-mode --prefix=/home/daniel/build-nghttp2 --disable-shared --enable-app --enable-http3 --without-jemalloc --without-libxml2 --without-systemd + % PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/path/to/quictls/lib/pkgconfig:/path/to/nghttp3/lib/pkgconfig:/path/to/ngtcp2/lib/pkgconfig \ + LDFLAGS=-L/path/to/quictls/lib CFLAGS=-I/path/to/quictls/include ./configure --enable-maintainer-mode \ + --prefix=/path/to/nghttp2 --disable-shared --enable-app --enable-http3 --without-jemalloc --without-libxml2 --without-systemd % make && make install Run the local h3 server on port 9443, make it proxy all traffic through to -HTTP/1 on localhost port 80. For local toying, we can just use the test cert -that exists in curl's test dir. +HTTP/1 on localhost port 80. For local toying, we can use the test cert that +exists in curl's test dir. % CERT=/path/to/stunnel.pem % $HOME/bin/nghttpx $CERT $CERT --backend=localhost,80 \ - --frontend="localhost,9443;quic" + --frontend="localhost,9443;quic" ### Caddy -[Install Caddy](https://caddyserver.com/docs/install). For easiest use, the binary -should be either in your PATH or your current directory. +[Install Caddy](https://caddyserver.com/docs/install). For easiest use, the +binary should be either in your PATH or your current directory. Create a `Caddyfile` with the following content: ~~~ @@ -415,7 +358,9 @@ Then run Caddy: % ./caddy start -Making requests to `https://localhost:7443` should tell you which protocol is being used. +Making requests to `https://localhost:7443` should tell you which protocol is +being used. -You can change the hard-coded response to something more useful by replacing `respond` -with `reverse_proxy` or `file_server`, for example: `reverse_proxy localhost:80` +You can change the hard-coded response to something more useful by replacing +`respond` with `reverse_proxy` or `file_server`, for example: `reverse_proxy +localhost:80` diff --git a/docs/HTTPSRR.md b/docs/HTTPSRR.md index 22184a253e..bb96526b39 100644 --- a/docs/HTTPSRR.md +++ b/docs/HTTPSRR.md @@ -6,7 +6,7 @@ SPDX-License-Identifier: curl # HTTPS RR -[RFC 9460](https://www.rfc-editor.org/rfc/rfc9460.html) documents the HTTPS +[RFC 9460](https://datatracker.ietf.org/doc/html/rfc9460) documents the HTTPS DNS Resource Record. curl features **experimental** support for HTTPS RR. @@ -35,7 +35,7 @@ be reused on subsequent uses of the same hostnames. ## limitations We have decided to work on the HTTPS RR support by following what seems to be -(widely) used, and simply wait with implementing the details of the record +(widely) used, and wait with implementing the details of the record that do not seem to be deployed. HTTPS RR is a DNS field with many odd corners and complexities and we might as well avoid them if no one seems to want them. diff --git a/docs/INFRASTRUCTURE.md b/docs/INFRASTRUCTURE.md index 13240b31b5..2f24845cdd 100644 --- a/docs/INFRASTRUCTURE.md +++ b/docs/INFRASTRUCTURE.md @@ -60,11 +60,11 @@ account. We regularly run our code through the [Coverity static code analyzer](https://scan.coverity.com/) thanks to them offering this service to -us for free. +us free of charge. ## CodeSonar -[CodeSonar](https://codesecure.com/our-products/codesonar/) analyzes the curl +[CodeSonar](https://www.adacore.com/codesonar) analyzes the curl source code daily and emails Daniel Stenberg whenever it finds suspected problems in the source code. I hope and expect that we can invite other maintainers to access these reports soon. @@ -76,7 +76,7 @@ domain names, including `curl.se` and `curl.dev`. Daniel Stenberg owns these domain names. Until a few years ago, the curl website was present at `curl.haxx.se`. The -`haxx.se` domain is owned by Haxx AB, administrated by Daniel Stenberg. The +`haxx.se` domain is owned by Haxx AB, administered by Daniel Stenberg. The curl.haxx.se name is meant to keep working and be redirecting to curl.se for the foreseeable future. @@ -100,11 +100,11 @@ company). The machine is physically located in Sweden. curl release tarballs are hosted on https://curl.se/download.html. They are uploaded there at release-time by the release manager. -curl-for-win downloads are hosted on https://curl.se/windows and are uploaded +curl-for-win downloads are hosted on https://curl.se/windows/ and are uploaded to the server by Viktor Szakats. -curl-for-QNX downloads are hosted on and are uploaded to -the server by Daniel Stenberg. +curl-for-QNX downloads are hosted on and are uploaded +to the server by Daniel Stenberg. Daily release tarball-like snapshots are generated automatically and are provided for download at . @@ -172,14 +172,6 @@ instances used for this. We use a few rare additional curl related email aliases in the curl domains. They go through the mail server `mail.haxx.se` maintained by Daniel Stenberg -## Bug-bounty - -We run a [bug-bounty](https://curl.se/docs/bugbounty.html) on HackerOne. The -setup runs entirely at https://hackerone.com/curl. - -The money part for the bug bounty is sponsored by the [Internet Bug -Bounty](https://hackerone.com/ibb). - ## Open Collective We use [Open Collective](https://opencollective.com/curl) as our "fiscal diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index ea761fa99a..a84faf72f2 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -23,42 +23,26 @@ It consists of the following steps after you have unpacked the source. We recommend building with CMake on Windows. For instructions on migrating from the `projects/Windows` Visual Studio solution files, see -[this section](#migrating-from-visual-studio-ide-project-files). For -instructions on migrating from the winbuild builds, see -[the following section](#migrating-from-winbuild-builds). +[this section](#migrating-from-visual-studio-ide-project-files). ## Using `cmake` You can configure for in source tree builds or for a build tree that is apart from the source tree. - - Build in the source tree. +- Build in the source tree. - $ cmake -B . + $ cmake -B . - - Build in a separate directory (parallel to the curl source tree in this - example). The build directory is created for you. This is recommended over - building in the source tree to separate source and build artifacts. +- Build in a separate directory (parallel to the curl source tree in this + example). The build directory is created for you. This is recommended over + building in the source tree to separate source and build artifacts. - $ cmake -B ../curl-build + $ cmake -B ../curl-build For the full list of CMake build configuration variables see [the corresponding section](#cmake-build-options). -### Fallback for CMake before version 3.13 - -CMake before version 3.13 does not support the `-B` option. In that case, -you must create the build directory yourself, `cd` to it and run `cmake` -from there: - - $ mkdir ../curl-build - $ cd ../curl-build - $ cmake ../curl - -If you want to build in the source tree, it is enough to do this: - - $ cmake . - ### Build system generator selection You can override CMake's default by using `-G `. For example @@ -81,18 +65,18 @@ generate the project. After the project is generated, you can run make. CMake also comes with a Qt based GUI called `cmake-gui`. To configure with `cmake-gui`, you run `cmake-gui` and follow these steps: - 1. Fill in the "Where is the source code" combo box with the path to - the curl source tree. - 2. Fill in the "Where to build the binaries" combo box with the path to - the directory for your build tree, ideally this should not be the same - as the source tree, but a parallel directory called curl-build or - something similar. - 3. Once the source and binary directories are specified, press the - "Configure" button. - 4. Select the native build tool that you want to use. - 5. At this point you can change any of the options presented in the GUI. - Once you have selected all the options you want, click the "Generate" - button. +1. Fill in the "Where is the source code" combo box with the path to + the curl source tree. +2. Fill in the "Where to build the binaries" combo box with the path to + the directory for your build tree, ideally this should not be the same + as the source tree, but a parallel directory called curl-build or + something similar. +3. Once the source and binary directories are specified, press the + "Configure" button. +4. Select the native build tool that you want to use. +5. At this point you can change any of the options presented in the GUI. + Once you have selected all the options you want, click the "Generate" + button. # Building @@ -157,8 +141,8 @@ assumes that CMake generates `Makefile`: # CMake usage -Just as curl can be built and installed using CMake, it can also be used from -CMake. +This section describes how to locate and use curl/libcurl from CMake-based +projects. ## Using `find_package` @@ -223,12 +207,18 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `BUILD_EXAMPLES`: Build libcurl examples. Default: `ON` - `BUILD_LIBCURL_DOCS`: Build libcurl man pages. Default: `ON` - `BUILD_MISC_DOCS`: Build misc man pages (e.g. `curl-config` and `mk-ca-bundle`). Default: `ON` -- `BUILD_SHARED_LIBS`: Build shared libraries. Default: `ON` -- `BUILD_STATIC_CURL`: Build curl executable with static libcurl. Default: `OFF` -- `BUILD_STATIC_LIBS`: Build static libraries. Default: `OFF` +- `BUILD_SHARED_LIBS`: Build shared libraries. Default: `ON` (if target platform supports shared libs, otherwise `OFF`) +- `BUILD_STATIC_CURL`: Build curl executable with static libcurl. Default: `OFF` (turns to `ON`, when building static libcurl only) +- `BUILD_STATIC_LIBS`: Build static libraries. Default: `OFF` (turns to `ON` if `BUILD_SHARED_LIBS` is `OFF`) - `BUILD_TESTING`: Build tests. Default: `ON` +- `CURL_BUILD_EVERYTHING`: Build optional build targets (examples, tests) by default. Default: `OFF` + Set `QUICK` to build examples quickly with the `curl-examples-build` target (for build tests). + Set `NOEXAMPLES` to not build examples. - `CURL_CLANG_TIDY`: Run the build through `clang-tidy`. Default: `OFF` + If enabled, it implies `CURL_DISABLE_TYPECHECK=ON` and force-disables unity mode + for libcurl and the curl tool. - `CURL_CLANG_TIDYFLAGS`: Custom options to pass to `clang-tidy`. Default: (empty) +- `CURL_CODE_COVERAGE`: Enable code coverage build options. Default: `OFF` - `CURL_COMPLETION_FISH`: Install fish completions. Default: `OFF` - `CURL_COMPLETION_FISH_DIR`: Custom fish completion install directory. - `CURL_COMPLETION_ZSH`: Install zsh completions. Default: `OFF` @@ -236,16 +226,19 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `CURL_DEFAULT_SSL_BACKEND`: Override default TLS backend in MultiSSL builds. Accepted values in order of default priority: `wolfssl`, `gnutls`, `mbedtls`, `openssl`, `schannel`, `rustls` +- `CURL_DROP_UNUSED`: Drop unused code and data from built binaries. Default: `OFF` - `CURL_ENABLE_EXPORT_TARGET`: Enable CMake export target. Default: `ON` +- `CURL_GCC_ANALYZER`: Enable GCC `--analyzer` option. Default: `OFF` - `CURL_HIDDEN_SYMBOLS`: Hide libcurl internal symbols (=hide all symbols that are not officially external). Default: `ON` - `CURL_LIBCURL_SOVERSION`: Enable libcurl SOVERSION. Default: `ON` for supported platforms - `CURL_LIBCURL_VERSIONED_SYMBOLS`: Enable libcurl versioned symbols. Default: `OFF` - `CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX`: Override default versioned symbol prefix. Default: `_` or `MULTISSL_` +- `CURL_LINT`: Run lint checks while building. Default: `OFF` - `CURL_LTO`: Enable compiler Link Time Optimizations. Default: `OFF` +- `CURL_PATCHSTAMP`: Set security patch string for `curl -V`/`curl --version` output. - `CURL_STATIC_CRT`: Build libcurl with static CRT with MSVC (`/MT`) (requires UCRT, static libcurl or no curl executable). Default: `OFF` -- `CURL_TARGET_WINDOWS_VERSION`: Minimum target Windows version as hex string. +- `CURL_TARGET_WINDOWS_VERSION`: Minimum target Windows version as hex string, e.g. `0x0a00` for Windows 10. - `CURL_WERROR`: Turn compiler warnings into errors. Default: `OFF` -- `ENABLE_CURLDEBUG`: Enable TrackMemory debug feature. Default: =`ENABLE_DEBUG` - `ENABLE_CURL_MANUAL`: Build the man page for curl and enable its `-M`/`--manual` option. Default: `ON` - `ENABLE_DEBUG`: Enable curl debug features (for developing curl itself). Default: `OFF` - `IMPORT_LIB_SUFFIX`: Import library suffix. Default: `_imp` for MSVC-like toolchains, otherwise empty. @@ -254,26 +247,33 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `SHARE_LIB_OBJECT`: Build shared and static libcurl in a single pass (requires CMake 3.12 or newer). Default: `ON` for Windows - `STATIC_LIB_SUFFIX`: Static library suffix. Default: (empty) -## CA bundle options +## Root CA options -- `CURL_CA_BUNDLE`: Path to the CA bundle. Set `none` to disable or `auto` for auto-detection. Default: `auto` -- `CURL_CA_EMBED`: Path to the CA bundle to embed in the curl tool. Default: (disabled) +- `CURL_CA_BUNDLE`: Absolute path to the CA bundle. Set `none` to disable or `auto` for auto-detection. Default: `auto` +- `CURL_CA_EMBED`: Absolute path to the CA bundle to embed in the curl tool. Default: (disabled) - `CURL_CA_FALLBACK`: Use built-in CA store of OpenSSL. Default: `OFF` -- `CURL_CA_PATH`: Location of default CA path. Set `none` to disable or `auto` for auto-detection. Default: `auto` +- `CURL_CA_NATIVE`: Use native CA store. Default: `OFF` + Supported by GnuTLS, OpenSSL (including forks) on Windows, wolfSSL. +- `CURL_CA_PATH`: Absolute path to a directory containing CA certificates stored individually. + Set `none` to disable or `auto` for auto-detection. Default: `auto` - `CURL_CA_SEARCH_SAFE`: Enable safe CA bundle search (within the curl tool directory) on Windows. Default: `OFF` +- `USE_APPLE_SECTRUST`: Use Apple OS-native certificate verification. Default: `OFF` ## Enabling features +- `CURL_ENABLE_NTLM`: Enable NTLM support. Default: `OFF` - `CURL_ENABLE_SSL`: Enable SSL support. Default: `ON` - `CURL_WINDOWS_SSPI`: Enable SSPI on Windows. Default: =`CURL_USE_SCHANNEL` - `ENABLE_IPV6`: Enable IPv6 support. Default: `ON` if target supports IPv6. - `ENABLE_THREADED_RESOLVER`: Enable threaded DNS lookup. Default: `ON` if c-ares is not enabled and target supports threading. - `ENABLE_UNICODE`: Use the Unicode version of the Windows API functions. Default: `OFF` - `ENABLE_UNIX_SOCKETS`: Enable Unix domain sockets support. Default: `ON` +- `USE_APPLE_IDN`: Use Apple built-in IDN support. Default: `OFF` - `USE_ECH`: Enable ECH support. Default: `OFF` - `USE_HTTPSRR`: Enable HTTPS RR support. Default: `OFF` -- `USE_OPENSSL_QUIC`: Use OpenSSL and nghttp3 libraries for HTTP/3 support. Default: `OFF` - `USE_SSLS_EXPORT`: Enable experimental SSL session import/export. Default: `OFF` +- `USE_WIN32_IDN`: Use WinIDN for IDN support. Default: `OFF` +- `USE_WIN32_LDAP`: Use Windows LDAP implementation. Default: `ON` ## Disabling features @@ -282,7 +282,7 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `CURL_DISABLE_BASIC_AUTH`: Disable Basic authentication. Default: `OFF` - `CURL_DISABLE_BEARER_AUTH`: Disable Bearer authentication. Default: `OFF` - `CURL_DISABLE_BINDLOCAL`: Disable local binding support. Default: `OFF` -- `CURL_DISABLE_CA_SEARCH`: Disable unsafe CA bundle search in PATH on Windows. Default: `OFF` +- `CURL_DISABLE_CA_SEARCH`: Disable unsafe CA bundle search in PATH on Windows. Default: `OFF` (turns to `ON`, when `CURL_CA_NATIVE=ON`) - `CURL_DISABLE_COOKIES`: Disable cookies support. Default: `OFF` - `CURL_DISABLE_DICT`: Disable DICT. Default: `OFF` - `CURL_DISABLE_DIGEST_AUTH`: Disable Digest authentication. Default: `OFF` @@ -307,7 +307,6 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `CURL_DISABLE_MQTT`: Disable MQTT. Default: `OFF` - `CURL_DISABLE_NEGOTIATE_AUTH`: Disable negotiate authentication. Default: `OFF` - `CURL_DISABLE_NETRC`: Disable netrc parser. Default: `OFF` -- `CURL_DISABLE_NTLM`: Disable NTLM support. Default: `OFF` - `CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG`: Disable automatic loading of OpenSSL configuration. Default: `OFF` - `CURL_DISABLE_PARSEDATE`: Disable date parsing. Default: `OFF` - `CURL_DISABLE_POP3`: Disable POP3. Default: `OFF` @@ -316,12 +315,13 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `CURL_DISABLE_RTSP`: Disable RTSP. Default: `OFF` - `CURL_DISABLE_SHA512_256`: Disable SHA-512/256 hash algorithm. Default: `OFF` - `CURL_DISABLE_SHUFFLE_DNS`: Disable shuffle DNS feature. Default: `OFF` -- `CURL_DISABLE_SMB`: Disable SMB. Default: `OFF` +- `CURL_ENABLE_SMB`: Enable SMB. Default: `OFF` - `CURL_DISABLE_SMTP`: Disable SMTP. Default: `OFF` -- `CURL_DISABLE_SOCKETPAIR`: Disable use of socketpair for curl_multi_poll. Default: `OFF` +- `CURL_DISABLE_SOCKETPAIR`: Disable use of socketpair for curl_multi_poll(). Default: `OFF` - `CURL_DISABLE_SRP`: Disable TLS-SRP support. Default: `OFF` - `CURL_DISABLE_TELNET`: Disable Telnet. Default: `OFF` - `CURL_DISABLE_TFTP`: Disable TFTP. Default: `OFF` +- `CURL_DISABLE_TYPECHECK`: Disable curl_easy_setopt()/curl_easy_getinfo() type checking. Default: `OFF` - `CURL_DISABLE_VERBOSE_STRINGS`: Disable verbose strings. Default: `OFF` - `CURL_DISABLE_WEBSOCKETS`: Disable WebSocket. Default: `OFF` - `HTTP_ONLY`: Disable all protocols except HTTP (This overrides all `CURL_DISABLE_*` options). Default: `OFF` @@ -343,7 +343,7 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `CMAKE_INSTALL_PREFIX` (see CMake) - `CMAKE_STATIC_LIBRARY_SUFFIX` (see CMake) - `CMAKE_UNITY_BUILD_BATCH_SIZE`: Set the number of sources in a "unity" unit. Default: `0` (all) -- `CMAKE_UNITY_BUILD`: Enable "unity" (aka jumbo) builds. Default: `OFF` +- `CMAKE_UNITY_BUILD`: Enable "unity" (aka "jumbo") builds. Default: `OFF` Details via CMake [variables](https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html) and @@ -352,123 +352,167 @@ Details via CMake ## Dependencies - `CURL_BROTLI`: Use brotli (`ON`, `OFF` or `AUTO`). Default: `AUTO` +- `CURL_USE_CMAKECONFIG`: Enable detecting dependencies via CMake Config. + Default: `ON` for MSVC (except under vcpkg), if not cross-compiling. (experimental) - `CURL_USE_GNUTLS`: Enable GnuTLS for SSL/TLS. Default: `OFF` - `CURL_USE_GSASL`: Use libgsasl. Default: `OFF` - `CURL_USE_GSSAPI`: Use GSSAPI implementation. Default: `OFF` +- `CURL_USE_LIBBACKTRACE`: Use [libbacktrace](https://github.com/ianlancetaylor/libbacktrace). + Requires debug-enabled build and DWARF debug information. Default: `OFF` - `CURL_USE_LIBPSL`: Use libpsl. Default: `ON` - `CURL_USE_LIBSSH2`: Use libssh2. Default: `ON` - `CURL_USE_LIBSSH`: Use libssh. Default: `OFF` - `CURL_USE_LIBUV`: Use libuv for event-based tests. Default: `OFF` - `CURL_USE_MBEDTLS`: Enable mbedTLS for SSL/TLS. Default: `OFF` - `CURL_USE_OPENSSL`: Enable OpenSSL for SSL/TLS. Default: `ON` if no other TLS backend was enabled. -- `CURL_USE_PKGCONFIG`: Enable `pkg-config` to detect dependencies. Default: `ON` for Unix (except Android, Apple devices), vcpkg, MinGW if not cross-compiling. +- `CURL_USE_PKGCONFIG`: Enable `pkg-config` to detect dependencies. + Default: `ON` for Unix (except Android, Apple devices), vcpkg, MinGW if not cross-compiling. - `CURL_USE_RUSTLS`: Enable Rustls for SSL/TLS. Default: `OFF` - `CURL_USE_SCHANNEL`: Enable Windows native SSL/TLS (Schannel). Default: `OFF` -- `CURL_USE_WOLFSSH`: Use wolfSSH. Default: `OFF` - `CURL_USE_WOLFSSL`: Enable wolfSSL for SSL/TLS. Default: `OFF` - `CURL_ZLIB`: Use zlib (`ON`, `OFF` or `AUTO`). Default: `AUTO` - `CURL_ZSTD`: Use zstd (`ON`, `OFF` or `AUTO`). Default: `AUTO` - `ENABLE_ARES`: Enable c-ares support. Default: `OFF` -- `USE_APPLE_IDN`: Use Apple built-in IDN support. Default: `OFF` - `USE_LIBIDN2`: Use libidn2 for IDN support. Default: `ON` -- `USE_LIBRTMP`: Enable librtmp from rtmpdump. Default: `OFF` - `USE_NGHTTP2`: Use nghttp2 library. Default: `ON` - `USE_NGTCP2`: Use ngtcp2 and nghttp3 libraries for HTTP/3 support. Default: `OFF` - `USE_QUICHE`: Use quiche library for HTTP/3 support. Default: `OFF` -- `USE_WIN32_IDN`: Use WinIDN for IDN support. Default: `OFF` -- `USE_WIN32_LDAP`: Use Windows LDAP implementation. Default: `ON` ## Dependency options (via CMake) -- `OPENSSL_ROOT_DIR`: Set this variable to the root installation of OpenSSL (and forks). -- `OPENSSL_INCLUDE_DIR`: The OpenSSL include directory. -- `OPENSSL_SSL_LIBRARY`: Path to `ssl` library. With MSVC, CMake uses variables `SSL_EAY_DEBUG`/`SSL_EAY_RELEASE` instead. -- `OPENSSL_CRYPTO_LIBRARY`: Path to `crypto` library. With MSVC, CMake uses variables `LIB_EAY_DEBUG`/`LIB_EAY_RELEASE` instead. +- `OPENSSL_ROOT_DIR`: Absolute path to the root installation of OpenSSL (and forks). +- `OPENSSL_INCLUDE_DIR`: Absolute path to OpenSSL include directory. +- `OPENSSL_SSL_LIBRARY`: Absolute path to `ssl` library. + With MSVC, CMake uses variables `SSL_EAY_DEBUG`/`SSL_EAY_RELEASE` instead. +- `OPENSSL_CRYPTO_LIBRARY`: Absolute path to `crypto` library. + With MSVC, CMake uses variables `LIB_EAY_DEBUG`/`LIB_EAY_RELEASE` instead. - `OPENSSL_USE_STATIC_LIBS`: Look for static OpenSSL libraries. -- `ZLIB_INCLUDE_DIR`: The zlib include directory. -- `ZLIB_LIBRARY`: Path to `zlib` library. -- `ZLIB_USE_STATIC_LIBS`: Look for static ZLIB library (requires CMake v3.24). +- `ZLIB_INCLUDE_DIR`: Absolute path to zlib include directory. +- `ZLIB_LIBRARY`: Absolute path to `zlib` library. +- `ZLIB_USE_STATIC_LIBS`: Look for static `zlib` library (requires CMake v3.24). +- `_DIR`: Absolute path to `` CMake Config directory where `*.cmake` files reside. + Used when `CURL_USE_CMAKECONFIG` is enabled. + `` may be: + `c-ares`, `Libssh2`, `MbedTLS`, `NGHTTP2`, `NGHTTP3`, + `NGTCP2` for 1.19.0+ (with non-fork OpenSSL only), + `wolfssl` for 5.2.1+, `Zstd` for 1.4.5+. ## Dependency options (tools) -- `CLANG_TIDY`: `clang-tidy` tool used with `CURL_CLANG_TIDY=ON`. Default: `clang-tidy` -- `PERL_EXECUTABLE`: Perl binary used throughout the build and tests. +- `CLANG_TIDY`: Absolute path to `clang-tidy` tool used with `CURL_CLANG_TIDY=ON`. Default: search for `clang-tidy` +- `PERL_EXECUTABLE`: Absolute path to Perl binary used throughout the build and tests. Default: auto-detect ## Dependency options (libraries) -- `AMISSL_INCLUDE_DIR`: The AmiSSL include directory. -- `AMISSL_STUBS_LIBRARY`: Path to `amisslstubs` library. -- `AMISSL_AUTO_LIBRARY`: Path to `amisslauto` library. -- `BROTLI_INCLUDE_DIR`: The brotli include directory. -- `BROTLICOMMON_LIBRARY`: Path to `brotlicommon` library. -- `BROTLIDEC_LIBRARY`: Path to `brotlidec` library. -- `CARES_INCLUDE_DIR`: The c-ares include directory. -- `CARES_LIBRARY`: Path to `cares` library. -- `DL_LIBRARY`: Path to `dl` library. (for Rustls) -- `GSS_ROOT_DIR`: Set this variable to the root installation of GSS. (also supported as environment) -- `LDAP_LIBRARY`: Name or full path to `ldap` library. Default: `ldap` -- `LDAP_LBER_LIBRARY`: Name or full path to `lber` library. Default: `lber` -- `LDAP_INCLUDE_DIR`: Path to LDAP include directory. -- `LIBGSASL_INCLUDE_DIR`: The libgsasl include directory. -- `LIBGSASL_LIBRARY`: Path to `libgsasl` library. -- `LIBIDN2_INCLUDE_DIR`: The libidn2 include directory. -- `LIBIDN2_LIBRARY`: Path to `libidn2` library. -- `LIBPSL_INCLUDE_DIR`: The libpsl include directory. -- `LIBPSL_LIBRARY`: Path to `libpsl` library. -- `LIBRTMP_INCLUDE_DIR`: The librtmp include directory. -- `LIBRTMP_LIBRARY`: Path to `librtmp` library. -- `LIBSSH_INCLUDE_DIR`: The libssh include directory. -- `LIBSSH_LIBRARY`: Path to `libssh` library. -- `LIBSSH2_INCLUDE_DIR`: The libssh2 include directory. -- `LIBSSH2_LIBRARY`: Path to `libssh2` library. -- `LIBUV_INCLUDE_DIR`: The libuv include directory. -- `LIBUV_LIBRARY`: Path to `libuv` library. -- `MATH_LIBRARY`: Path to `m` library. (for Rustls, wolfSSL) -- `MBEDTLS_INCLUDE_DIR`: The mbedTLS include directory. -- `MBEDTLS_LIBRARY`: Path to `mbedtls` library. -- `MBEDX509_LIBRARY`: Path to `mbedx509` library. -- `MBEDCRYPTO_LIBRARY`: Path to `mbedcrypto` library. -- `NGHTTP2_INCLUDE_DIR`: The nghttp2 include directory. -- `NGHTTP2_LIBRARY`: Path to `nghttp2` library. -- `NGHTTP3_INCLUDE_DIR`: The nghttp3 include directory. -- `NGHTTP3_LIBRARY`: Path to `nghttp3` library. -- `NGTCP2_INCLUDE_DIR`: The ngtcp2 include directory. -- `NGTCP2_LIBRARY`: Path to `ngtcp2` library. -- `NGTCP2_CRYPTO_BORINGSSL_LIBRARY`: Path to `ngtcp2_crypto_boringssl` library. (also for AWS-LC) -- `NGTCP2_CRYPTO_GNUTLS_LIBRARY`: Path to `ngtcp2_crypto_gnutls` library. -- `NGTCP2_CRYPTO_LIBRESSL_LIBRARY`: Path to `ngtcp2_crypto_libressl` library. (requires ngtcp2 1.15.0+) -- `NGTCP2_CRYPTO_OSSL_LIBRARY`: Path to `ngtcp2_crypto_ossl` library. -- `NGTCP2_CRYPTO_QUICTLS_LIBRARY`: Path to `ngtcp2_crypto_quictls` library. (also for LibreSSL with ngtcp2 <1.15.0) -- `NGTCP2_CRYPTO_WOLFSSL_LIBRARY`: Path to `ngtcp2_crypto_wolfssl` library. -- `NETTLE_INCLUDE_DIR`: The nettle include directory. -- `NETTLE_LIBRARY`: Path to `nettle` library. -- `PTHREAD_LIBRARY`: Path to `pthread` library. (for Rustls) -- `QUICHE_INCLUDE_DIR`: The quiche include directory. -- `QUICHE_LIBRARY`: Path to `quiche` library. -- `RUSTLS_INCLUDE_DIR`: The Rustls include directory. -- `RUSTLS_LIBRARY`: Path to `rustls` library. -- `WATT_ROOT`: Set this variable to the root installation of Watt-32. -- `WOLFSSH_INCLUDE_DIR`: The wolfSSH include directory. -- `WOLFSSH_LIBRARY`: Path to `wolfssh` library. -- `WOLFSSL_INCLUDE_DIR`: The wolfSSL include directory. -- `WOLFSSL_LIBRARY`: Path to `wolfssl` library. -- `ZSTD_INCLUDE_DIR`: The zstd include directory. -- `ZSTD_LIBRARY`: Path to `zstd` library. +- `AMISSL_INCLUDE_DIR`: Absolute path to AmiSSL include directory. +- `AMISSL_STUBS_LIBRARY`: Absolute path to `amisslstubs` library. +- `AMISSL_AUTO_LIBRARY`: Absolute path to `amisslauto` library. +- `BORINGSSL_VERSION`: Set BoringSSL version for `curl -V`/`curl --version` output. +- `BROTLI_INCLUDE_DIR`: Absolute path to brotli include directory. +- `BROTLICOMMON_LIBRARY`: Absolute path to `brotlicommon` library. +- `BROTLIDEC_LIBRARY`: Absolute path to `brotlidec` library. +- `BROTLI_USE_STATIC_LIBS`: Configure for static brotli libraries. (experimental) +- `CARES_INCLUDE_DIR`: Absolute path to c-ares include directory. +- `CARES_LIBRARY`: Absolute path to `cares` library. +- `CARES_USE_STATIC_LIBS`: Configure for static c-ares libraries. (experimental) +- `DL_LIBRARY`: Absolute path to `dl` library. (for Rustls) +- `GNUTLS_INCLUDE_DIR`: Absolute path to GnuTLS include directory. +- `GNUTLS_LIBRARY`: Absolute path to `gnutls` library. +- `GSS_ROOT_DIR`: Absolute path to the root installation of GSS. (also supported as environment) +- `LDAP_INCLUDE_DIR`: Absolute path to LDAP include directory. +- `LDAP_LIBRARY`: Absolute path to `ldap` library. +- `LDAP_LBER_LIBRARY`: Absolute path to `lber` library. +- `LIBBACKTRACE_INCLUDE_DIR`: Absolute path to libbacktrace include directory (https://github.com/ianlancetaylor/libbacktrace). +- `LIBBACKTRACE_LIBRARY`: Absolute path to `libbacktrace` library. +- `LIBGSASL_INCLUDE_DIR`: Absolute path to libgsasl include directory. +- `LIBGSASL_LIBRARY`: Absolute path to `libgsasl` library. +- `LIBIDN2_INCLUDE_DIR`: Absolute path to libidn2 include directory. +- `LIBIDN2_LIBRARY`: Absolute path to `libidn2` library. +- `LIBPSL_INCLUDE_DIR`: Absolute path to libpsl include directory. +- `LIBPSL_LIBRARY`: Absolute path to `libpsl` library. +- `LIBSSH_INCLUDE_DIR`: Absolute path to libssh include directory. +- `LIBSSH_LIBRARY`: Absolute path to `libssh` library. +- `LIBSSH_USE_STATIC_LIBS`: Configure for static libssh libraries. (experimental) +- `LIBSSH2_INCLUDE_DIR`: Absolute path to libssh2 include directory. +- `LIBSSH2_LIBRARY`: Absolute path to `libssh2` library. +- `LIBSSH2_USE_STATIC_LIBS`: Configure for static libssh2 libraries. (experimental) +- `LIBUV_INCLUDE_DIR`: Absolute path to libuv include directory. +- `LIBUV_LIBRARY`: Absolute path to `libuv` library. +- `MATH_LIBRARY`: Absolute path to `m` library. (for Rustls, wolfSSL) +- `MBEDTLS_INCLUDE_DIR`: Absolute path to mbedTLS include directory. +- `MBEDTLS_LIBRARY`: Absolute path to `mbedtls` library. +- `MBEDX509_LIBRARY`: Absolute path to `mbedx509` library. +- `MBEDCRYPTO_LIBRARY`: Absolute path to `mbedcrypto` library. +- `MBEDTLS_USE_STATIC_LIBS`: Configure for static mbedTLS libraries. (experimental) +- `NGHTTP2_INCLUDE_DIR`: Absolute path to nghttp2 include directory. +- `NGHTTP2_LIBRARY`: Absolute path to `nghttp2` library. +- `NGHTTP2_USE_STATIC_LIBS`: Configure for static nghttp2 libraries. (experimental) +- `NGHTTP3_INCLUDE_DIR`: Absolute path to nghttp3 include directory. +- `NGHTTP3_LIBRARY`: Absolute path to `nghttp3` library. +- `NGHTTP3_USE_STATIC_LIBS`: Configure for static nghttp3 libraries. (experimental) +- `NGTCP2_INCLUDE_DIR`: Absolute path to ngtcp2 include directory. +- `NGTCP2_LIBRARY`: Absolute path to `ngtcp2` library. +- `NGTCP2_CRYPTO_BORINGSSL_LIBRARY`: Absolute path to `ngtcp2_crypto_boringssl` library. (also for AWS-LC) +- `NGTCP2_CRYPTO_GNUTLS_LIBRARY`: Absolute path to `ngtcp2_crypto_gnutls` library. +- `NGTCP2_CRYPTO_LIBRESSL_LIBRARY`: Absolute path to `ngtcp2_crypto_libressl` library. (requires ngtcp2 1.15.0+) +- `NGTCP2_CRYPTO_OSSL_LIBRARY`: Absolute path to `ngtcp2_crypto_ossl` library. +- `NGTCP2_CRYPTO_QUICTLS_LIBRARY`: Absolute path to `ngtcp2_crypto_quictls` library. (also for LibreSSL with ngtcp2 <1.15.0) +- `NGTCP2_CRYPTO_WOLFSSL_LIBRARY`: Absolute path to `ngtcp2_crypto_wolfssl` library. +- `NGTCP2_USE_STATIC_LIBS`: Configure for static ngtcp2 libraries. (experimental) +- `NETTLE_INCLUDE_DIR`: Absolute path to nettle include directory. +- `NETTLE_LIBRARY`: Absolute path to `nettle` library. +- `PTHREAD_LIBRARY`: Absolute path to `pthread` library. (for Rustls) +- `QUICHE_INCLUDE_DIR`: Absolute path to quiche include directory. +- `QUICHE_LIBRARY`: Absolute path to `quiche` library. +- `RUSTLS_INCLUDE_DIR`: Absolute path to Rustls include directory. +- `RUSTLS_LIBRARY`: Absolute path to `rustls` library. +- `WATT_ROOT`: Absolute path to the root installation of Watt-32. +- `WOLFSSL_INCLUDE_DIR`: Absolute path to wolfSSL include directory. +- `WOLFSSL_LIBRARY`: Absolute path to `wolfssl` library. +- `ZSTD_INCLUDE_DIR`: Absolute path to zstd include directory. +- `ZSTD_LIBRARY`: Absolute path to `zstd` library. +- `ZSTD_USE_STATIC_LIBS`: Configure for static zstd libraries. (experimental) + +Examples: + +- `-DLIBPSL_INCLUDE_DIR=/path/to/libpl/include`, + which directory contains `libpsl.h`. + No ending slash or backslash is necessary. + +- `-DNGHTTP3_INCLUDE_DIR=/path/to/libnghttp3/include`, + which directory contains an `nghttp3` subdirectory with `.h` files in it. + +- `-DLIBPSL_LIBRARY=/path/to/libpsl/lib/libpsl.a` + Always a single library, with its complete filename, as-is on the file system. + +- `-DOPENSSL_ROOT_DIR=/path/to/openssl`, + which directory (typically) contains `include` and `lib` subdirectories. + No ending slash or backslash is necessary. + +## Dependency options (Apple frameworks) + +- `COREFOUNDATION_FRAMEWORK`: Absolute path to `CoreFoundation` framework. (for IPv6 non-c-ares, SecTrust, wolfSSL) +- `CORESERVICES_FRAMEWORK`: Absolute path to `CoreServices` framework. (for IPv6 non-c-ares, SecTrust) +- `FOUNDATION_FRAMEWORK`: Absolute path to `Foundation` framework. (for Rustls) +- `SECURITY_FRAMEWORK`: Absolute path to `Security` framework. (for Rustls, SecTrust, wolfSSL) +- `SYSTEMCONFIGURATION_FRAMEWORK`: Absolute path to `SystemConfiguration` framework. (for IPv6 non-c-ares) ## Test tools -- `APXS`: Default: `apxs` -- `CADDY`: Default: `caddy` -- `HTTPD_NGHTTPX`: Default: `nghttpx` -- `HTTPD`: Default: `apache2` -- `DANTED`: Default: `danted` -- `TEST_NGHTTPX`: Default: `nghttpx` -- `VSFTPD`: Default: `vsftps` +- `APXS`: Absolute path. Default: search for `apxs` +- `CADDY`: Absolute path. Default: search for `caddy` +- `HTTPD_NGHTTPX`: Absolute path. Default: search for `nghttpx` +- `HTTPD`: Absolute path. Default: search for `apache2` +- `DANTED`: Absolute path. Default: search for `danted` +- `TEST_NGHTTPX`: Absolute path. Default: search for `nghttpx` +- `VSFTPD`: Absolute path. Default: search for `vsftps` +- `SSHD`: Absolute path. Default: search for `sshd` +- `SFTPD`: Absolute path. Default: search for `sftp-server` ## Feature detection variables -By default this CMake build script detects the version of some dependencies -using `check_symbol_exists`. Those checks do not work in the case that both +By default the curl CMake build script detects the version of some dependencies +using `check_symbol_exists()`. Those checks do not work in the case that both CURL and its dependency are included as sub-projects in a larger build using `FetchContent`. To support that case, additional variables may be defined by the parent project, ideally in the "extra" find package redirect file: @@ -476,10 +520,11 @@ the parent project, ideally in the "extra" find package redirect file: Available variables: +- `HAVE_DES_ECB_ENCRYPT`: `DES_ecb_encrypt` present in OpenSSL (or fork). - `HAVE_GNUTLS_SRP`: `gnutls_srp_verifier` present in GnuTLS. -- `HAVE_GSS_C_NT_HOSTBASED_SERVICE`: `GSS_C_NT_HOSTBASED_SERVICE` present in GSS/Heimdal/Kerberos. - `HAVE_LDAP_INIT_FD`: `ldap_init_fd` present in LDAP library. - `HAVE_LDAP_URL_PARSE`: `ldap_url_parse` present in LDAP library. +- `HAVE_MBEDTLS_DES_CRYPT_ECB`: `mbedtls_des_crypt_ecb` present in mbedTLS <4. - `HAVE_OPENSSL_SRP`: `SSL_CTX_set_srp_username` present in OpenSSL (or fork). - `HAVE_QUICHE_CONN_SET_QLOG_FD`: `quiche_conn_set_qlog_fd` present in quiche. - `HAVE_RUSTLS_SUPPORTED_HPKE`: `rustls_supported_hpke` present in Rustls (unused if Rustls is detected via `pkg-config`). @@ -490,11 +535,11 @@ Available variables: - `HAVE_WOLFSSL_BIO_NEW`: `wolfSSL_BIO_new` present in wolfSSL. - `HAVE_WOLFSSL_BIO_SET_SHUTDOWN`: `wolfSSL_BIO_set_shutdown` present in wolfSSL. - `HAVE_WOLFSSL_CTX_GENERATEECHCONFIG`: `wolfSSL_CTX_GenerateEchConfig` present in wolfSSL. -- `HAVE_WOLFSSL_DES_ECB_ENCRYPT`: `wolfSSL_DES_ecb_encrypt` present in wolfSSL. - `HAVE_WOLFSSL_GET_PEER_CERTIFICATE`: `wolfSSL_get_peer_certificate` present in wolfSSL. - `HAVE_WOLFSSL_SET_QUIC_USE_LEGACY_CODEPOINT`: `wolfSSL_set_quic_use_legacy_codepoint` present in wolfSSL. - `HAVE_WOLFSSL_USEALPN`: `wolfSSL_UseALPN` present in wolfSSL. +- `HAVE_WC_DES_ECBENCRYPT`: `wc_Des_EcbEncrypt` present in wolfSSL. For each of the above variables, if the variable is *defined* (either to `ON` or `OFF`), the symbol detection is skipped. If the variable is *not defined*, @@ -502,6 +547,32 @@ the feature detection is performed. Note: These variables are internal and subject to change. +## Useful build targets + +- `testdeps`: Build test dependencies (test binaries, test certificates). + Test certificates: `build-certs` (clean with `clean-certs`) +- `tests`: Run tests (`runtests.pl`). Customize via the `TFLAGS` environment variable, e.g. `TFLAGS=1621`. + Other flavors: `test-am`, `test-ci`, `test-event`, `test-full`, `test-nonflaky`, `test-quiet`, `test-torture` +- `tt`: Build test binaries (servers, tools). + Individual targets: `curlinfo`, `libtests`, `servers`, `tunits`, `units` +- `curl-pytest`: Run tests (pytest). + Other flavor: `curl-test-ci` +- `curl-examples`: Build examples + Individual targets: `curl-example-`, + where is the .c filename without extension. +- `curl-examples-build`: Build examples quickly but without the ability to run them. (for build tests) +- `curl-man`: Build man pages. (built by default unless disabled) +- `curl`: Build curl tool. +- `curl_uninstall`: Uninstall curl. +- `curl-completion-fish`: Build shell completions for fish. (built by default if enabled) +- `curl-completion-zsh`: Build shell completions for zsh. (built by default if enabled) +- `curl-ca-bundle`: Build the CA bundle via `scripts/mk-ca-bundle.pl`. +- `curl-ca-firefox`: Build the CA bundle via `scripts/firefox-db2pem.sh`. +- `curl-lint`: Run lint checks. +- `curl-listcats`: Generate help category constants for `src/tool_help.h` from documentation. +- `curl-listhelp`: Generate `src/tool_listhelp.c` from documentation. +- `curl-optiontable`: Generate `lib/easyoptions.c` from documentation. + # Migrating from Visual Studio IDE Project Files We recommend using CMake to build curl with MSVC. @@ -533,9 +604,9 @@ Configuration element | Equivalent CMake options For example these commands: - > cd projects + > cd projects/Windows > ./generate.bat VC12 - > msbuild "-property:Configuration=DLL Debug - DLL Windows SSPI - DLL WinIDN" Windows/VC12/curl-all.sln + > msbuild "-property:Configuration=DLL Debug - DLL Windows SSPI - DLL WinIDN" VC12/curl-all.sln translate to: @@ -546,59 +617,3 @@ We do *not* specify `-DCMAKE_BUILD_TYPE=Debug` here as we might do for the `"NMake Makefiles"` generator because the Visual Studio generators are [multi-config generators](https://cmake.org/cmake/help/latest/prop_gbl/GENERATOR_IS_MULTI_CONFIG.html) and therefore ignore the value of `CMAKE_BUILD_TYPE`. - -# Migrating from winbuild builds - -We recommend CMake to build curl with MSVC. The winbuild build system is -deprecated and is going to be removed in September 2025 in favor of the CMake -build system. - -In CMake you can customize the path of dependencies by passing the absolute -header path and the full path of the library via `*_INCLUDE_DIR` and -`*_LIBRARY` options (see the complete list in the option listing above). -The full path to the library can point to a static library or an import -library, which defines if the dependency is linked as a dll or statically. -For OpenSSL this works -[differently](https://cmake.org/cmake/help/latest/module/FindOpenSSL.html): -You can pass the root directory of the OpenSSL installation via -`OPENSSL_ROOT_DIR`, then pass `OPENSSL_USE_STATIC_LIBS=ON` to select static -libs. - -winbuild options | Equivalent CMake options -:-------------------------------- | :-------------------------------- -`DEBUG` | `CMAKE_BUILD_TYPE=Debug` -`GEN_PDB` | `CMAKE_EXE_LINKER_FLAGS=/Fd`, `CMAKE_SHARED_LINKER_FLAGS=/Fd` -`LIB_NAME_DLL`, `LIB_NAME_STATIC` | `IMPORT_LIB_SUFFIX`, `LIBCURL_OUTPUT_NAME`, `STATIC_LIB_SUFFIX` -`VC`: `` | see the CMake [Visual Studio generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html#visual-studio-generators) -`MACHINE`: `x64`, `x86` | `-A x64`, `-A Win32` -`MODE`: `dll`, `static` | `BUILD_SHARED_LIBS=ON/OFF`, `BUILD_STATIC_LIBS=ON/OFF`, `BUILD_STATIC_CURL=ON/OFF` (default: dll) -`RTLIBCFG`: `static` | `CURL_STATIC_CRT=ON` -`ENABLE_IDN` | `USE_WIN32_IDN=ON` -`ENABLE_IPV6` | `ENABLE_IPV6=ON` -`ENABLE_NGHTTP2` | `USE_NGHTTP2=ON` -`ENABLE_OPENSSL_AUTO_LOAD_CONFIG` | `CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG=OFF` (default) -`ENABLE_SCHANNEL` | `CURL_USE_SCHANNEL=ON` -`ENABLE_SSPI` | `CURL_WINDOWS_SSPI=ON` (default with Schannel) -`ENABLE_UNICODE` | `ENABLE_UNICODE=ON` -`WITH_PREFIX` | `CMAKE_INSTALL_PREFIX=` -`WITH_DEVEL` | see individual `*_INCLUDE_DIR` and `*_LIBRARY` options and `OPENSSL_ROOT_DIR` -`WITH_CARES`, `CARES_PATH` | `ENABLE_ARES=ON`, optional: `CARES_INCLUDE_DIR`, `CARES_LIBRARY` -`WITH_MBEDTLS`, `MBEDTLS_PATH` | `CURL_USE_MBEDTLS=ON`, optional: `MBEDTLS_INCLUDE_DIR`, `MBEDTLS_LIBRARY`, `MBEDX509_LIBRARY`, `MBEDCRYPTO_LIBRARY` -`WITH_NGHTTP2`, `NGHTTP2_PATH` | `USE_NGHTTP2=ON`, optional: `NGHTTP2_INCLUDE_DIR`, `NGHTTP2_LIBRARY` -`WITH_SSH`, `SSH_PATH` | `CURL_USE_LIBSSH=ON`, optional: `LIBSSH_INCLUDE_DIR`, `LIBSSH_LIBRARY` -`WITH_SSH2`, `SSH2_PATH` | `CURL_USE_LIBSSH2=ON`, optional: `LIBSSH2_INCLUDE_DIR`, `LIBSSH2_LIBRARY` -`WITH_SSL`, `SSL_PATH` | `CURL_USE_OPENSSL=ON`, optional: `OPENSSL_ROOT_DIR`, `OPENSSL_USE_STATIC_LIBS=ON` -`WITH_WOLFSSL`, `WOLFSSL_PATH` | `CURL_USE_WOLFSSL=ON`, optional: `WOLFSSL_INCLUDE_DIR`, `WOLFSSL_LIBRARY` -`WITH_ZLIB`, `ZLIB_PATH` | `CURL_ZLIB=ON`, optional: `ZLIB_INCLUDE_DIR`, `ZLIB_LIBRARY` - -For example this command-line: - - > nmake -f Makefile.vc VC=17 MACHINE=x64 DEBUG=ON mode=dll SSL_PATH=C:\OpenSSL WITH_SSL=dll ENABLE_UNICODE=ON - -translates to: - - > cmake . -G "Visual Studio 17 2022" -A x64 -DBUILD_SHARED_LIBS=ON -DOPENSSL_ROOT_DIR=C:\OpenSSL -DCURL_USE_OPENSSL=ON -DENABLE_UNICODE=ON -DCURL_USE_LIBPSL=OFF - > cmake --build . --config Debug - -We use `--config` with `cmake --build` because the Visual Studio CMake -generators are multi-config and therefore ignore `CMAKE_BUILD_TYPE`. diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 0c53731710..619df5cc89 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -15,9 +15,10 @@ libcurl from [source code](https://curl.se/download.html). ## Building using vcpkg -You can download and install curl and libcurl using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager: +You can download and install curl and libcurl using +the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: - git clone https://github.com/Microsoft/vcpkg.git + git clone https://github.com/Microsoft/vcpkg cd vcpkg ./bootstrap-vcpkg.sh ./vcpkg integrate install @@ -30,8 +31,8 @@ or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. ## Building from git If you get your code off a git repository instead of a release tarball, see -the [GIT-INFO.md](https://github.com/curl/curl/blob/master/GIT-INFO.md) file in the root directory for specific instructions on how -to proceed. +the [GIT-INFO.md](https://github.com/curl/curl/blob/master/GIT-INFO.md) file in +the root directory for specific instructions on how to proceed. # Unix @@ -45,6 +46,9 @@ unpacked the source archive): (Adjust the configure line accordingly to use the TLS library you want.) +By default curl builds with libpsl (Public Suffix List) support. If libpsl is +not available on your system, install it or disable it with `--without-libpsl`. + You probably need to be root when doing the last command. Get a full listing of all available configure options by invoking it like: @@ -64,10 +68,9 @@ your own home directory: make make install -The configure script always tries to find a working SSL library unless -explicitly told not to. If you have OpenSSL installed in the default search -path for your compiler/linker, you do not need to do anything special. If you -have OpenSSL installed in `/usr/local/ssl`, you can run configure like: +The configure script requires you to select a TLS backend explicitly unless +you disable TLS with `--without-ssl`. If you have OpenSSL installed in the +default search path for your compiler/linker, you can run configure like: ./configure --with-openssl @@ -90,7 +93,7 @@ header files somewhere else, you have to set the `LDFLAGS` and `CPPFLAGS` environment variables prior to running configure. Something like this should work: - CPPFLAGS="-I/path/to/ssl/include" LDFLAGS="-L/path/to/ssl/lib" ./configure + CPPFLAGS="-I/path/to/ssl/include" LDFLAGS="-L/path/to/ssl/lib" ./configure --with-openssl If you have shared SSL libs installed in a directory where your runtime linker does not find them (which usually causes configure failures), you can @@ -132,26 +135,49 @@ curl can be built to use a whole range of libraries to provide various useful services, and configure tries to auto-detect a decent default. If you want to alter it, you can select how to deal with each individual library. +To debug the build itself, you can set the environment variable +`CURL_TRACE_PKG_CONFIG` to a non-empty value to enable detailed trace +information and verbose errors from `pkg-config` module detection invocations. + ## Select TLS backend These options are provided to select the TLS backend to use. - - AmiSSL: `--with-amissl` - - GnuTLS: `--with-gnutls`. - - mbedTLS: `--with-mbedtls` - - OpenSSL: `--with-openssl` (also for BoringSSL, AWS-LC, LibreSSL, and quictls) - - rustls: `--with-rustls` - - Schannel: `--with-schannel` - - wolfSSL: `--with-wolfssl` +- AmiSSL: `--with-amissl` +- GnuTLS: `--with-gnutls`. +- mbedTLS: `--with-mbedtls` +- OpenSSL: `--with-openssl` (also for BoringSSL, AWS-LC, LibreSSL, and quictls) +- Rustls: `--with-rustls` +- Schannel: `--with-schannel` +- wolfSSL: `--with-wolfssl` You can build curl with *multiple* TLS backends at your choice, but some TLS backends cannot be combined: if you build with an OpenSSL fork (or wolfSSL), -you cannot add another OpenSSL fork (or wolfSSL) simply because they have +you cannot add another OpenSSL fork (or wolfSSL) because they have conflicting identical symbol names. When you build with multiple TLS backends, you can select the active one at runtime when curl starts up. +### Selecting TLS Trust Anchors Defaults + +Verifying a server certificate established a chain of trust that needs to +start somewhere. Those "root" certificates make the set of Trust Anchors. + +While the build system tries to find good defaults on the platform you +use, you may specify these explicitly. The following options are provided: + +- `--with-ca-bundle=FILE`: the file that libcurl loads default root + certificates from. +- `--with-ca-path=DIRECTORY`: a directory in which root certificates files + are found. +- `--with-ca-embed=FILE`: a file read *at build time* and added to `libcurl`. +- `--with-ca-fallback`: an OpenSSL specific option for delegating default + trust anchor selection to what OpenSSL thinks is best, *if* there are + no other certificates configured by the application. +- `--with-apple-sectrust`: use the system "SecTrust" service on Apple + operating systems for verification. (Added in 8.17.0) + ## MultiSSL and HTTP/3 HTTP/3 needs QUIC and QUIC needs TLS. Building libcurl with HTTP/3 and QUIC @@ -176,162 +202,175 @@ library check. # Windows -Building for Windows XP is required as a minimum. +Building for Windows Vista/Server 2008 is required as a minimum. You can build curl with: -- Microsoft Visual Studio 2008 v9.0 or later (`_MSC_VER >= 1500`) +- Microsoft Visual Studio 2010 v10.0 or later (`_MSC_VER >= 1600`) - MinGW-w64 3.0 or later (`__MINGW64_VERSION_MAJOR >= 3`) ## Building Windows DLLs and C runtime (CRT) linkage issues - As a general rule, building a DLL with static CRT linkage is highly - discouraged, and intermixing CRTs in the same app is something to avoid at - any cost. +As a general rule, building a DLL with static CRT linkage is highly +discouraged, and intermixing CRTs in the same app is something to avoid at +any cost. - Reading and comprehending Microsoft Knowledge Base articles KB94248 and - KB140584 is a must for any Windows developer. Especially important is full - understanding if you are not going to follow the advice given above. +Reading and comprehending Microsoft Knowledge Base articles KB94248 and +KB140584 is a must for any Windows developer. Especially important is full +understanding if you are not going to follow the advice given above. - - [How To Use the C Runtime](https://support.microsoft.com/help/94248/how-to-use-the-c-run-time) - - [Runtime Library Compiler Options](https://docs.microsoft.com/cpp/build/reference/md-mt-ld-use-run-time-library) - - [Potential Errors Passing CRT Objects Across DLL Boundaries](https://docs.microsoft.com/cpp/c-runtime-library/potential-errors-passing-crt-objects-across-dll-boundaries) +- [How To Use the C Runtime](https://learn.microsoft.com/troubleshoot/developer/visualstudio/cpp/libraries/use-c-run-time) +- [Runtime Library Compiler Options](https://learn.microsoft.com/cpp/build/reference/md-mt-ld-use-run-time-library) +- [Potential Errors Passing CRT Objects + Across DLL Boundaries](https://learn.microsoft.com/cpp/c-runtime-library/potential-errors-passing-crt-objects-across-dll-boundaries) If your app is misbehaving in some strange way, or it is suffering from memory corruption, before asking for further help, please try first to rebuild every single library your app uses as well as your app using the debug multi-threaded dynamic C runtime. - If you get linkage errors read section 5.7 of the FAQ document. +If you get linkage errors read section 5.7 of the FAQ document. ## Cygwin -Almost identical to the Unix installation. Essentially run the configure script in the -curl source tree root with `sh configure`, then run `make`. +Almost identical to the Unix installation. Download the `curl` source code, +then essentially run the configure script in the curl source tree root with +`sh configure`, then run `make`. -To expand on building with `cygwin` first ensure it is in your path, and there are no -conflicting tools (*i.e. Chocolatey with sed package*). If so move `cygwin` ahead of any items -in your path that would conflict with `cygwin` commands, making sure you have the `sh` -executable in `/bin/` or you see the configure fail toward the end. +To expand on building with Cygwin first ensure it is in your path, and +there are no conflicting tools (*i.e. Chocolatey with sed package*). If so +move Cygwin ahead of any items in your path that would conflict with +Cygwin commands, making sure you have the `sh` executable in `/bin/` or +you see the configure fail toward the end. -Download the setup installer from -[`cygwin`](https://cygwin.com/) to begin. Additional `cygwin` -packages are needed for the install. For more on installing packages visit -[`cygwin setup`](https://www.cygwin.com/faq/faq.html#faq.setup.cli). +Download the setup installer from [Cygwin](https://cygwin.com/) to begin. +Additional Cygwin packages are needed for the install. For more on +installing packages visit +[Cygwin Setup](https://cygwin.com/faq/faq.html#faq.setup.cli). -Either run setup-x86_64.exe, then search and select packages individually, or try: +Either run `setup-x86_64.exe`, then search and select packages individually, +or try: - setup-x86_64.exe -P binutils -P gcc-core -P libpsl-devel -P libtool -P perl -P make + setup-x86_64.exe --no-admin -q -I -P binutils,gcc-core,libpsl-devel,libtool,perl,make -If the latter, matching packages should appear in the install rows (*is fickle though*) after selecting -the download site i.e. `https://mirrors.kernel.org`. In either case, follow the GUI prompts -until you reach the "Select Packages" window; then select packages, click next, and finish -the `cygwin` package installation. - -Download the latest version of the `cygwin` packages required (*and suggested*) for a successful install: +Expand the below details to show the list of required packages for a +successful Cygwin install:
Package List ``` - binutil - required - gcc-core - required - libpsl-devel - required - libtool - required - perl - required - make - required - - NOTE - if there is an error regarding make, open the cygwin terminal, and run: - ln -s /usr/bin/make /usr/bin/gmake +binutils +gcc-core +libpsl-devel +libtool +perl +make ```
-Once all the packages have been installed, begin the process of installing curl from the source code: +Once all the packages have been installed, begin the process of installing +curl from the source code: -
- configure_options +
+ configure_options ``` - --with-gnutls - --with-mbedtls - --with-openssl (also works for OpenSSL forks) - --with-rustls - --with-wolfssl - --without-ssl +--with-gnutls +--with-mbedtls +--with-openssl (also works for OpenSSL forks) +--with-rustls +--with-wolfssl +--without-ssl ``` -
+
- 1. `sh configure ` - 2. `make` +1. `sh configure ` +2. `make` -If any error occurs during curl installation, try: - - reinstalling the required `cygwin` packages from the list above - - temporarily move `cygwin` to the top of your path - - install all of the suggested `cygwin` packages +> [!Note] +> If an error occurs during the installation, then try: + +- Use `cmake` to configure and/or build +- Use `ninja` to build (***much** faster*) +- Reinstalling the required Cygwin packages from the list above without + passing `-I` to `setup-x86_64` +- Temporarily move Cygwin to the top of your path +- Install all of the Cygwin build packages using + `setup-x86_64 --build-depends curl` ## MS-DOS You can use either autotools or cmake: - ./configure \ - CC=/path/to/djgpp/bin/i586-pc-msdosdjgpp-gcc \ - AR=/path/to/djgpp/bin/i586-pc-msdosdjgpp-ar \ - RANLIB=/path/to/djgpp/bin/i586-pc-msdosdjgpp-ranlib \ - WATT_ROOT=/path/to/djgpp/net/watt \ - --host=i586-pc-msdosdjgpp \ - --with-openssl=/path/to/djgpp \ - --with-zlib=/path/to/djgpp \ - --without-libpsl \ - --disable-shared +```sh +./configure \ + CC=/path/to/djgpp/bin/i586-pc-msdosdjgpp-gcc \ + AR=/path/to/djgpp/bin/i586-pc-msdosdjgpp-ar \ + RANLIB=/path/to/djgpp/bin/i586-pc-msdosdjgpp-ranlib \ + WATT_ROOT=/path/to/djgpp/net/watt \ + --host=i586-pc-msdosdjgpp \ + --with-openssl=/path/to/djgpp \ + --with-zlib=/path/to/djgpp \ + --without-libpsl \ + --disable-shared +``` - cmake . \ - -DCMAKE_SYSTEM_NAME=DOS \ - -DCMAKE_C_COMPILER_TARGET=i586-pc-msdosdjgpp \ - -DCMAKE_C_COMPILER=/path/to/djgpp/bin/i586-pc-msdosdjgpp-gcc \ - -DWATT_ROOT=/path/to/djgpp/net/watt \ - -DOPENSSL_INCLUDE_DIR=/path/to/djgpp/include \ - -DOPENSSL_SSL_LIBRARY=/path/to/djgpp/lib/libssl.a \ - -DOPENSSL_CRYPTO_LIBRARY=/path/to/djgpp/lib/libcrypto.a \ - -DZLIB_INCLUDE_DIR=/path/to/djgpp/include \ - -DZLIB_LIBRARY=/path/to/djgpp/lib/libz.a \ - -DCURL_USE_LIBPSL=OFF +```sh +cmake . \ + -DCMAKE_SYSTEM_NAME=DOS \ + -DCMAKE_C_COMPILER_TARGET=i586-pc-msdosdjgpp \ + -DCMAKE_C_COMPILER=/path/to/djgpp/bin/i586-pc-msdosdjgpp-gcc \ + -DWATT_ROOT=/path/to/djgpp/net/watt \ + -DOPENSSL_INCLUDE_DIR=/path/to/djgpp/include \ + -DOPENSSL_SSL_LIBRARY=/path/to/djgpp/lib/libssl.a \ + -DOPENSSL_CRYPTO_LIBRARY=/path/to/djgpp/lib/libcrypto.a \ + -DZLIB_INCLUDE_DIR=/path/to/djgpp/include \ + -DZLIB_LIBRARY=/path/to/djgpp/lib/libz.a \ + -DCURL_USE_LIBPSL=OFF +``` Notes: - - Requires DJGPP 2.04 or upper. +- Requires DJGPP 2.04 or upper. - - Compile Watt-32 (and OpenSSL) with the same version of DJGPP. Otherwise - things go wrong because things like FS-extensions and `errno` values have - been changed between releases. +- Compile Watt-32 (and OpenSSL) with the same version of DJGPP. Otherwise + things go wrong because things like FS-extensions and `errno` values have + been changed between releases. ## AmigaOS You can use either autotools or cmake: - ./configure \ - CC=/opt/amiga/bin/m68k-amigaos-gcc \ - AR=/opt/amiga/bin/m68k-amigaos-ar \ - RANLIB=/opt/amiga/bin/m68k-amigaos-ranlib \ - --host=m68k-amigaos \ - --with-amissl \ - CFLAGS='-O0 -msoft-float -mcrt=clib2' \ - CPPFLAGS=-I/path/to/AmiSSL/Developer/include \ - LDFLAGS=-L/path/to/AmiSSL/Developer/lib/AmigaOS3 \ - LIBS='-lnet -lm -latomic' \ - --without-libpsl \ - --disable-shared +```sh +./configure \ + CC=/opt/amiga/bin/m68k-amigaos-gcc \ + AR=/opt/amiga/bin/m68k-amigaos-ar \ + RANLIB=/opt/amiga/bin/m68k-amigaos-ranlib \ + --host=m68k-amigaos \ + --with-amissl \ + CFLAGS='-O0 -msoft-float -mcrt=clib2' \ + CPPFLAGS=-I/path/to/AmiSSL/Developer/include \ + LDFLAGS=-L/path/to/AmiSSL/Developer/lib/AmigaOS3 \ + LIBS='-lnet -lm -latomic' \ + --without-libpsl \ + --disable-shared +``` - cmake . \ - -DAMIGA=1 \ - -DCMAKE_SYSTEM_NAME=Generic \ - -DCMAKE_C_COMPILER_TARGET=m68k-unknown-amigaos \ - -DCMAKE_C_COMPILER=/opt/amiga/bin/m68k-amigaos-gcc \ - -DCMAKE_C_FLAGS='-O0 -msoft-float -mcrt=clib2' \ - -DAMISSL_INCLUDE_DIR=/path/to/AmiSSL/Developer/include \ - -DAMISSL_STUBS_LIBRARY=/path/to/AmiSSL/Developer/lib/AmigaOS3/libamisslstubs.a \ - -DAMISSL_AUTO_LIBRARY=/path/to/AmiSSL/Developer/lib/AmigaOS3/libamisslauto.a \ - -DCURL_USE_LIBPSL=OFF +```sh +cmake . \ + -DAMIGA=1 \ + -DCMAKE_SYSTEM_NAME=Generic \ + -DCMAKE_C_COMPILER_TARGET=m68k-unknown-amigaos \ + -DCMAKE_C_COMPILER=/opt/amiga/bin/m68k-amigaos-gcc \ + -DCMAKE_C_FLAGS='-O0 -msoft-float -mcrt=clib2' \ + -DAMISSL_INCLUDE_DIR=/path/to/AmiSSL/Developer/include \ + -DAMISSL_STUBS_LIBRARY=/path/to/AmiSSL/Developer/lib/AmigaOS3/libamisslstubs.a \ + -DAMISSL_AUTO_LIBRARY=/path/to/AmiSSL/Developer/lib/AmigaOS3/libamisslauto.a \ + -DCURL_USE_LIBPSL=OFF +``` ## Disabling Specific Protocols in Windows builds @@ -345,10 +384,9 @@ for the full list. If you want to set any of these defines you have the following options: - - Modify `lib/config-win32.h` - - Modify `lib/curl_setup.h` - - Modify `winbuild/Makefile.vc` - - Modify the "Preprocessor Definitions" in the libcurl project +- Modify `lib/config-win32.h` +- Modify `lib/curl_setup.h` +- Modify the "Preprocessor Definitions" in the libcurl project Note: The pre-processor settings can be found using the Visual Studio IDE under "Project -> Properties -> Configuration Properties -> C/C++ -> @@ -361,9 +399,8 @@ necessary to make the definition of the preprocessor symbol `USE_LWIPSOCK` visible to libcurl and curl compilation processes. To set this definition you have the following alternatives: - - Modify `lib/config-win32.h` - - Modify `winbuild/Makefile.vc` - - Modify the "Preprocessor Definitions" in the libcurl project +- Modify `lib/config-win32.h` +- Modify the "Preprocessor Definitions" in the libcurl project Note: The pre-processor settings can be found using the Visual Studio IDE under "Project -> Properties -> Configuration Properties -> C/C++ -> @@ -389,15 +426,15 @@ for dynamic import symbols. ## Legacy Windows and SSL -Schannel (from Windows SSPI), is the native SSL library in Windows. However, -Schannel in Windows <= XP is unable to connect to servers that no longer -support the legacy handshakes and algorithms used by those versions. If you -are using curl in one of those earlier versions of Windows you should choose -another SSL backend such as OpenSSL. +Schannel (from Windows SSPI), is the native SSL library in Windows. Schannel +in Windows <= XP is unable to connect to servers that no longer support the +legacy handshakes and algorithms used by those versions. If you are using curl +in one of those earlier versions of Windows you should choose another SSL +backend such as OpenSSL. # Android -When building curl for Android you can you CMake or curl's `configure` script. +When building curl for Android you can either use CMake or `configure`. Before you can build curl for Android, you need to install the Android NDK first. This can be done using the SDK Manager that is part of Android Studio. @@ -409,18 +446,22 @@ Examples to compile for `aarch64` and API level 29: with CMake, where `ANDROID_NDK_HOME` points into your NDK: - cmake . \ - -DANDROID_ABI=arm64-v8a \ - -DANDROID_PLATFORM=android-29 \ - -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake" \ - -DCURL_ENABLE_SSL=OFF \ - -DCURL_USE_LIBPSL=OFF +```sh +cmake . \ + -DANDROID_ABI=arm64-v8a \ + -DANDROID_PLATFORM=android-29 \ + -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake" \ + -DCURL_ENABLE_SSL=OFF \ + -DCURL_USE_LIBPSL=OFF +``` with `configure`, on macOS: -```bash +```sh export ANDROID_NDK_HOME=~/Library/Android/sdk/ndk/25.1.8937393 # Point into your NDK. -export HOST_TAG=darwin-x86_64 # Same tag for Apple Silicon. Other OS values here: https://developer.android.com/ndk/guides/other_build_systems#overview +# Same tag for Apple Silicon. Other OS values here: +# https://developer.android.com/ndk/guides/other_build_systems#overview +export HOST_TAG=darwin-x86_64 export TOOLCHAIN=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/$HOST_TAG export AR=$TOOLCHAIN/bin/llvm-ar export AS=$TOOLCHAIN/bin/llvm-as @@ -444,8 +485,10 @@ install `libssl.a` and `libcrypto.a` to `$TOOLCHAIN/sysroot/usr/lib` and copy `include/openssl` to `$TOOLCHAIN/sysroot/usr/include`. Now you can build curl for Android using OpenSSL like this: -```bash -LIBS="-lssl -lcrypto -lc++" # For OpenSSL/BoringSSL. In general, you need to the SSL/TLS layer's transitive dependencies if you are linking statically. +```sh +# For OpenSSL/BoringSSL. In general, you need to the SSL/TLS layer's transitive +# dependencies if you are linking statically. +LIBS='-lssl -lcrypto -lc++' ./configure --host aarch64-linux-android --with-pic --disable-shared --with-openssl="$TOOLCHAIN/sysroot/usr" ``` @@ -455,7 +498,7 @@ For IBM i (formerly OS/400), you can use curl in two different ways: - Natively, running in the **ILE**. The obvious use is being able to call curl from ILE C or RPG applications. -- You need to build this from source. See `packages/OS400/README` for the ILE +- You need to build this from source. See `projects/OS400/README` for the ILE specific build instructions. - In the **PASE** environment, which runs AIX programs. curl is built as it would be on AIX. @@ -494,9 +537,7 @@ configure with any options you need. Be sure and specify the `--host` and of cross-compiling for the IBM 405GP PowerPC processor using the toolchain on Linux. -```bash -#! /bin/sh - +```sh export PATH=$PATH:/opt/hardhat/devkit/ppc/405/bin export CPPFLAGS="-I/opt/hardhat/devkit/ppc/405/target/usr/include" export AR=ppc_405-ar @@ -506,11 +547,12 @@ export RANLIB=ppc_405-ranlib export CC=ppc_405-gcc export NM=ppc_405-nm -./configure --target=powerpc-hardhat-linux - --host=powerpc-hardhat-linux - --build=i586-pc-linux-gnu - --prefix=/opt/hardhat/devkit/ppc/405/target/usr/local - --exec-prefix=/usr/local +./configure \ + --target=powerpc-hardhat-linux + --host=powerpc-hardhat-linux + --build=i586-pc-linux-gnu + --prefix=/opt/hardhat/devkit/ppc/405/target/usr/local + --exec-prefix=/usr/local ``` The `--prefix` parameter specifies where curl gets installed. If `configure` @@ -531,7 +573,7 @@ may be relevant in some environments: `-march=X`, `-mthumb`, `-m32`, `-mdynamic-no-pic`, `-flto`, `-fdata-sections`, `-ffunction-sections`, `-fno-unwind-tables`, `-fno-asynchronous-unwind-tables`, `-fno-record-gcc-switches`, `-fsection-anchors`, `-fno-plt`, -`-Wl,--gc-sections`, `-Wl,-Bsymbolic`, `-Wl,-s`, +`-Wl,--gc-sections`, `-Wl,-dead_strip` (Apple), `-Wl,-Bsymbolic`, `-Wl,-s` For example, this is how to combine a few of these options: @@ -547,47 +589,46 @@ know your application is not going to need. Besides specifying the use, here are some other flags that can reduce the size of the library by disabling support for some features (run `./configure --help` to see them all): - - `--disable-aws` (cryptographic authentication) - - `--disable-basic-auth` (cryptographic authentication) - - `--disable-bearer-auth` (cryptographic authentication) - - `--disable-digest-auth` (cryptographic authentication) - - `--disable-http-auth` (all HTTP authentication) - - `--disable-kerberos-auth` (cryptographic authentication) - - `--disable-negotiate-auth` (cryptographic authentication) - - `--disable-ntlm` (NTLM authentication) - - `--disable-alt-svc` (HTTP Alt-Svc) - - `--disable-ares` (the C-ARES DNS library) - - `--disable-cookies` (HTTP cookies) - - `--disable-dateparse` (date parsing for time conditionals) - - `--disable-dnsshuffle` (internal server load spreading) - - `--disable-doh` (DNS-over-HTTP) - - `--disable-form-api` (POST form API) - - `--disable-get-easy-options` (lookup easy options at runtime) - - `--disable-headers-api` (API to access headers) - - `--disable-hsts` (HTTP Strict Transport Security) - - `--disable-ipv6` (IPv6) - - `--disable-libcurl-option` (--libcurl C code generation support) - - `--disable-manual` (--manual built-in documentation) - - `--disable-mime` (MIME API) - - `--disable-netrc` (.netrc file) - - `--disable-progress-meter` (graphical progress meter in library) - - `--disable-proxy` (HTTP and SOCKS proxies) - - `--disable-socketpair` (socketpair for asynchronous name resolving) - - `--disable-threaded-resolver` (threaded name resolver) - - `--disable-tls-srp` (Secure Remote Password authentication for TLS) - - `--disable-unix-sockets` (Unix sockets) - - `--disable-verbose` (eliminates debugging strings and error code strings) - - `--disable-versioned-symbols` (versioned symbols) - - `--enable-symbol-hiding` (eliminates unneeded symbols in the shared library) - - `--without-brotli` (Brotli on-the-fly decompression) - - `--without-libpsl` (Public Suffix List in cookies) - - `--without-nghttp2` (HTTP/2 using nghttp2) - - `--without-ngtcp2` (HTTP/2 using ngtcp2) - - `--without-zstd` (Zstd on-the-fly decompression) - - `--without-libidn2` (internationalized domain names) - - `--without-librtmp` (RTMP) - - `--without-ssl` (SSL/TLS) - - `--without-zlib` (gzip/deflate on-the-fly decompression) +- `--disable-aws` (cryptographic authentication) +- `--disable-basic-auth` (cryptographic authentication) +- `--disable-bearer-auth` (cryptographic authentication) +- `--disable-digest-auth` (cryptographic authentication) +- `--disable-http-auth` (all HTTP authentication) +- `--disable-kerberos-auth` (cryptographic authentication) +- `--disable-negotiate-auth` (cryptographic authentication) +- `--disable-ntlm` (NTLM authentication) +- `--disable-alt-svc` (HTTP Alt-Svc) +- `--disable-ares` (the C-ARES DNS library) +- `--disable-cookies` (HTTP cookies) +- `--disable-dateparse` (date parsing for time conditionals) +- `--disable-dnsshuffle` (internal server load spreading) +- `--disable-doh` (DNS-over-HTTP) +- `--disable-form-api` (POST form API) +- `--disable-get-easy-options` (lookup easy options at runtime) +- `--disable-headers-api` (API to access headers) +- `--disable-hsts` (HTTP Strict Transport Security) +- `--disable-ipv6` (IPv6) +- `--disable-libcurl-option` (--libcurl C code generation support) +- `--disable-manual` (--manual built-in documentation) +- `--disable-mime` (MIME API) +- `--disable-netrc` (.netrc file) +- `--disable-progress-meter` (graphical progress meter in library) +- `--disable-proxy` (HTTP and SOCKS proxies) +- `--disable-socketpair` (socketpair for asynchronous name resolving) +- `--disable-threaded-resolver` (threaded name resolver) +- `--disable-tls-srp` (Secure Remote Password authentication for TLS) +- `--disable-unix-sockets` (Unix sockets) +- `--disable-verbose` (eliminates debugging strings and error code strings) +- `--disable-versioned-symbols` (versioned symbols) +- `--enable-symbol-hiding` (eliminates unneeded symbols in the shared library) +- `--without-brotli` (Brotli on-the-fly decompression) +- `--without-libpsl` (Public Suffix List in cookies) +- `--without-nghttp2` (HTTP/2 using nghttp2) +- `--without-ngtcp2` (HTTP/2 using ngtcp2) +- `--without-zstd` (Zstd on-the-fly decompression) +- `--without-libidn2` (internationalized domain names) +- `--without-ssl` (SSL/TLS) +- `--without-zlib` (gzip/deflate on-the-fly decompression) Be sure also to strip debugging symbols from your binaries after compiling using 'strip' or an option like `-s`. If space is really tight, you may be able @@ -608,10 +649,10 @@ relevant tests by specifying certain key words on the `runtests.pl` command line. Following is a list of appropriate key words for those configure options that are not automatically detected: - - `--disable-cookies` !cookies - - `--disable-dateparse` !RETRY-AFTER !`CURLOPT_TIMECONDITION` !`CURLINFO_FILETIME` !`If-Modified-Since` !`curl_getdate` !`-z` - - `--disable-libcurl-option` !`--libcurl` - - `--disable-verbose` !verbose\ logs +- `--disable-cookies` !cookies +- `--disable-dateparse` !RETRY-AFTER !`CURLOPT_TIMECONDITION` !`CURLINFO_FILETIME` !`If-Modified-Since` !`curl_getdate` !`-z` +- `--disable-libcurl-option` !`--libcurl` +- `--disable-verbose` !verbose\ logs # Ports @@ -619,22 +660,22 @@ This is a probably incomplete list of known CPU architectures and operating systems that curl has been compiled for. If you know a system curl compiles and runs on, that is not listed, please let us know. -## 104 Operating Systems +## 108 Operating Systems - AIX, AmigaOS, Android, ArcoOS, Aros, Atari FreeMiNT, BeOS, Blackberry - 10, Blackberry Tablet OS, Cell OS, CheriBSD, Chrome OS, Cisco IOS, - DG/UX, DR DOS, Dragonfly BSD, eCOS, FreeBSD, FreeDOS, FreeRTOS, Fuchsia, - Garmin OS, Genode, Haiku, HardenedBSD, HP-UX, Hurd, IBM I, illumos, - Integrity, iOS, ipadOS, IRIX, Linux, Lua RTOS, Mac OS 9, macOS, Maemo, - Mbed, Meego, Micrium, MINIX, Minoca, Moblin, MorphOS, MPE/iX, MS-DOS, - NCR MP-RAS, NetBSD, Netware, NextStep, Nintendo 3DS Nintendo Switch, - NonStop OS, NuttX, OpenBSD, OpenStep, Orbis OS, OS/2, OS21, Plan 9, - PlayStation Portable, QNX, Qubes OS, ReactOS, Redox, RISC OS, ROS, - RTEMS, Sailfish OS, SCO Unix, Serenity, SINIX-Z, SkyOS, software, - Solaris, Sortix, SunOS, Syllable OS, Symbian, Tizen, TPF, Tru64, tvOS, - ucLinux, Ultrix, UNICOS, UnixWare, VMS, vxWorks, watchOS, Wear OS, - WebOS, Wii system Wii U, Windows CE, Windows, Xbox System, Xenix, z/OS, - z/TPF, z/VM, z/VSE, Zephyr + AIX, AmigaOS, Android, ArcaOS, Aros, Atari FreeMiNT, Azure Sphere, BeOS, + Blackberry 10, Blackberry Tablet OS, Cell OS, Cesium, CheriBSD, Chrome OS, + Cisco IOS, DG/UX, DR DOS, Dragonfly BSD, eCOS, FreeBSD, FreeDOS, FreeRTOS, + Fuchsia, Garmin OS, Genode, Haiku, HardenedBSD, HP-UX, Hurd, IBM I, + illumos, Integrity, iOS, ipadOS, IRIX, KasperskyOS, Linux, Lua RTOS, + Mac OS 9, macOS, Maemo, Mbed, Meego, Micrium, MINIX, Minoca, Moblin, + MorphOS, MPE/iX, MS-DOS, NCR MP-RAS, NetBSD, Netware, NextStep, + Nintendo 3DS, Nintendo Switch, NonStop OS, NuttX, OpenBSD, OpenStep, + Orbis OS, OS/2, OS21, PikeOS, Plan 9, PlayStation Portable, QNX, Qubes OS, + ReactOS, Redox, RISC OS, ROS, RTEMS, Sailfish OS, SCO Unix, Serenity, + SINIX-Z, SkyOS, SmartOS, Solaris, Sortix, SunOS, Syllable OS, Symbian, + Tizen, TPF, Tru64, tvOS, ucLinux, Ultrix, UNICOS, UnixWare, visionOS, VMS, + vxWorks, watchOS, Wear OS, WebOS, Wii System Software, Wii U, Windows, + Xbox System, Xenix, z/OS, z/TPF, z/VM, z/VSE, Zephyr ## 28 CPU Architectures diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md index de993c3e4b..c145690a2c 100644 --- a/docs/INTERNALS.md +++ b/docs/INTERNALS.md @@ -12,53 +12,57 @@ versions of libs and build tools. ## Portability - We write curl and libcurl to compile with C89 compilers on 32-bit and up - machines. Most of libcurl assumes more or less POSIX compliance but that is - not a requirement. +We write curl and libcurl to compile with C89 compilers on 32-bit and up +machines. Most of libcurl assumes more or less POSIX compliance but that is +not a requirement. The compiler must support a 64-bit integer type as well as +supply a stdint.h header file that defines C99-style fixed-width integer types +like uint32_t. - We write libcurl to build and work with lots of third party tools, and we - want it to remain functional and buildable with these and later versions - (older versions may still work but is not what we work hard to maintain): +We write libcurl to build and work with lots of third party tools, and we +want it to remain functional and buildable with these and later versions +(older versions may still work but is not what we work hard to maintain): ## Dependencies - We aim to support these or later versions. +We aim to support these or later versions. - - OpenSSL 1.0.2a - - LibreSSL 2.9.1 - - GnuTLS 3.1.10 - - mbedTLS 3.2.0 - - zlib 1.2.5.2 - - libssh2 1.2.8 - - c-ares 1.6.0 - - libssh 0.9.0 - - libidn2 2.0.0 - - wolfSSL 3.4.6 - - OpenLDAP 2.0 - - MIT Kerberos 1.2.4 - - Heimdal ? - - nghttp2 1.15.0 - - Winsock 2.2 (on Windows 95+ and Windows CE .NET 4.1+) +- brotli 1.0.0 (2017-09-21) +- c-ares 1.16.0 (2020-03-13) +- GnuTLS 3.6.5 (2018-12-01) +- libidn2 2.0.0 (2017-03-29) +- LibreSSL 2.9.1 (2019-04-22) +- libssh 0.9.0 (2019-06-28) +- libssh2 1.9.0 (2019-06-20) +- mbedTLS 3.2.0 (2022-07-11) +- MIT Kerberos 1.3 (2003-07-31) +- nghttp2 1.15.0 (2016-09-25) +- OpenLDAP 2.0 (2000-08-01) +- OpenSSL 3.0.0 (2021-09-07) +- Windows Vista 6.0 (2006-11-08 - 2012-04-10) +- wolfSSL 5.0.0 (2021-11-01) +- zlib 1.2.5.2 (2011-12-11) +- zstd 1.0 (2016-08-31) ## Build tools - When writing code (mostly for generating stuff included in release tarballs) - we use a few "build tools" and we make sure that we remain functional with - these versions: +When writing code (mostly for generating stuff included in release tarballs) +we use a few "build tools" and we make sure that we remain functional with +these versions: - - GNU Libtool 1.4.2 - - GNU Autoconf 2.59 - - GNU Automake 1.7 - - GNU M4 1.4 - - perl 5.8 - - roffit 0.5 - - cmake 3.7 +- clang-tidy 17.0.0 (2023-09-19), recommended: 19.1.0 or later (2024-09-17) +- cmake 3.18 (2020-07-15) +- GNU autoconf 2.59 (2003-11-06) +- GNU automake 1.7 (2002-09-25) +- GNU libtool 1.4.2 (2001-09-11) +- GNU m4 1.4 (2007-09-21) +- mingw-w64 3.0 (2013-09-20) +- perl 5.8 (2002-07-19), on Windows: 5.22 (2015-06-01) +- Visual Studio 2010 10.0 (2010-04-12 - 2020-07-14) -Library Symbols -=============== +## Library Symbols - All symbols used internally in libcurl must use a `Curl_` prefix if they are - used in more than a single file. Single-file symbols must be made static. - Public ("exported") symbols must use a `curl_` prefix. Public API functions - are marked with `CURL_EXTERN` in the public header files so that all others - can be hidden on platforms where this is possible. +All symbols used internally in libcurl must use a `Curl_` prefix if they are +used in more than a single file. Single-file symbols must be made static. +Public ("exported") symbols must use a `curl_` prefix. Public API functions +are marked with `CURL_EXTERN` in the public header files so that all others +can be hidden on platforms where this is possible. diff --git a/docs/IPFS.md b/docs/IPFS.md index 82dae94399..2cf64543b9 100644 --- a/docs/IPFS.md +++ b/docs/IPFS.md @@ -5,24 +5,41 @@ SPDX-License-Identifier: curl --> # IPFS + For an overview about IPFS, visit the [IPFS project site](https://ipfs.tech/). -In IPFS there are two protocols. IPFS and IPNS (their workings are explained in detail [here](https://docs.ipfs.tech/concepts/)). The ideal way to access data on the IPFS network is through those protocols. For example to access the Big Buck Bunny video the ideal way to access it is like: `ipfs://bafybeigagd5nmnn2iys2f3doro7ydrevyr2mzarwidgadawmamiteydbzi` +In IPFS there are two protocols. IPFS and IPNS (their workings are explained +in detail [here](https://docs.ipfs.tech/concepts/)). The ideal way to access +data on the IPFS network is through those protocols. For example to access +the Big Buck Bunny video the ideal way to access it is like: +`ipfs://bafybeigagd5nmnn2iys2f3doro7ydrevyr2mzarwidgadawmamiteydbzi` ## IPFS Gateways IPFS Gateway acts as a bridge between traditional HTTP clients and IPFS. -IPFS Gateway specifications of HTTP semantics can be found [here](https://specs.ipfs.tech/http-gateways/). +IPFS Gateway specifications of HTTP semantics can be found +[here](https://specs.ipfs.tech/http-gateways/). ### Deserialized responses -By default, a gateway acts as a bridge between traditional HTTP clients and IPFS and performs necessary hash verification and deserialization. Through such gateway, users can download files, directories, and other content-addressed data stored with IPFS or IPNS as if they were stored in a traditional web server. +By default, a gateway acts as a bridge between traditional HTTP clients and +IPFS and performs necessary hash verification and deserialization. Through such +gateway, users can download files, directories, and other content-addressed +data stored with IPFS or IPNS as if they were stored in a traditional web +server. ### Verifiable responses -By explicitly requesting [application/vnd.ipld.raw](https://www.iana.org/assignments/media-types/application/vnd.ipld.raw) or [application/vnd.ipld.car](https://www.iana.org/assignments/media-types/application/vnd.ipld.car) responses, by means defined in [Trustless Gateway Specification](https://specs.ipfs.tech/http-gateways/trustless-gateway/), the user is able to fetch raw content-addressed data and [perform hash verification themselves](https://docs.ipfs.tech/reference/http/gateway/#trustless-verifiable-retrieval). +By explicitly requesting +[application/vnd.ipld.raw](https://www.iana.org/assignments/media-types/application/vnd.ipld.raw) or +[application/vnd.ipld.car](https://www.iana.org/assignments/media-types/application/vnd.ipld.car) +responses, by means defined in +[Trustless Gateway Specification](https://specs.ipfs.tech/http-gateways/trustless-gateway/), +the user is able to fetch raw content-addressed data and +[perform hash verification themselves](https://docs.ipfs.tech/reference/http/gateway/#trustless-verifiable-retrieval). -This enables users to use untrusted, public gateways without worrying they might return invalid/malicious bytes. +This enables users to use untrusted, public gateways without worrying they +might return invalid/malicious bytes. ## IPFS and IPNS protocol handling @@ -43,22 +60,22 @@ in this link: `http://127.0.0.1:8080/ipfs/bafybeigagd5nmnn2iys2f3doro7ydrevyr2mzarwidgadawmamiteydbzi` -## cURL handling of the IPFS protocols +## curl handling of the IPFS protocols -The IPFS integration in cURL hides this gateway logic for you. Instead of +The IPFS integration in curl hides this gateway logic for you. Instead of providing a full URL to a file on IPFS like this: -``` +```sh curl http://127.0.0.1:8080/ipfs/bafybeigagd5nmnn2iys2f3doro7ydrevyr2mzarwidgadawmamiteydbzi ``` You can provide it with the IPFS protocol instead: -``` +```sh curl ipfs://bafybeigagd5nmnn2iys2f3doro7ydrevyr2mzarwidgadawmamiteydbzi ``` -With the IPFS protocol way of asking a file, cURL still needs to know the -gateway. curl essentially just rewrites the IPFS based URL to a gateway URL. +With the IPFS protocol way of asking a file, curl still needs to know the +gateway. curl essentially rewrites the IPFS based URL to a gateway URL. ### IPFS_GATEWAY environment variable @@ -67,7 +84,7 @@ gateway. ### Automatic gateway detection -When you provide no additional details to cURL then it: +When you provide no additional details to curl then it: 1. First looks for the `IPFS_GATEWAY` environment variable and use that if it is set. @@ -75,12 +92,12 @@ When you provide no additional details to cURL then it: means that you have a local gateway running and that file contains the URL to your local gateway. -If cURL fails, you are presented with an error message and a link to this page +If curl fails, you are presented with an error message and a link to this page to the option most applicable to solving the issue. ### `--ipfs-gateway` argument -You can also provide a `--ipfs-gateway` argument to cURL. This overrules any +You can also provide a `--ipfs-gateway` argument to curl. This overrules any other gateway setting. curl does not fallback to the other options if the provided gateway did not work. @@ -107,21 +124,21 @@ option follows the redirect. ## Error messages and hints -Depending on the arguments, cURL could present the user with an error. +Depending on the arguments, curl could present the user with an error. ### Gateway file and environment variable -cURL tried to look for the file: `~/.ipfs/gateway` but could not find it. It +curl tried to look for the file: `~/.ipfs/gateway` but could not find it. It also tried to look for the `IPFS_GATEWAY` environment variable but could not -find that either. This happens when no extra arguments are passed to cURL and +find that either. This happens when no extra arguments are passed to curl and letting it try to figure it out [automatically](#automatic-gateway-detection). Any IPFS implementation that has gateway support should expose its URL in `~/.ipfs/gateway`. If you are already running a gateway, make sure it exposes -the file where cURL expects to find it. +the file where curl expects to find it. Alternatively you could set the `IPFS_GATEWAY` environment variable or pass -the `--ipfs-gateway` flag to the cURL command. +the `--ipfs-gateway` flag to the curl command. ### Malformed gateway URL diff --git a/docs/KNOWN_BUGS b/docs/KNOWN_BUGS deleted file mode 100644 index 170132f507..0000000000 --- a/docs/KNOWN_BUGS +++ /dev/null @@ -1,663 +0,0 @@ - _ _ ____ _ - ___| | | | _ \| | - / __| | | | |_) | | - | (__| |_| | _ <| |___ - \___|\___/|_| \_\_____| - - Known Bugs - -These are problems and bugs known to exist at the time of this release. Feel -free to join in and help us correct one or more of these. Also be sure to -check the changelog of the current development status, as one or more of these -problems may have been fixed or changed somewhat since this was written. - - 1. HTTP - - 2. TLS - 2.1 IMAPS connection fails with Rustls error - 2.5 Client cert handling with Issuer DN differs between backends - 2.7 Client cert (MTLS) issues with Schannel - 2.11 Schannel TLS 1.2 handshake bug in old Windows versions - 2.13 CURLOPT_CERTINFO results in CURLE_OUT_OF_MEMORY with Schannel - 2.14 mbedTLS and CURLE_AGAIN handling - - 3. Email protocols - 3.1 IMAP SEARCH ALL truncated response - 3.2 No disconnect command - 3.4 AUTH PLAIN for SMTP is not working on all servers - 3.5 APOP authentication fails on POP3 - 3.6 POP3 issue when reading small chunks - - 4. Command line - 4.1 -T /dev/stdin may upload with an incorrect content length - 4.2 -T - always uploads chunked - - 5. Build and portability issues - 5.1 OS400 port requires deprecated IBM library - 5.2 curl-config --libs contains private details - 5.3 LDFLAGS passed too late making libs linked incorrectly - 5.6 Cygwin: make install installs curl-config.1 twice - 5.11 configure --with-gssapi with Heimdal is ignored on macOS - 5.12 flaky CI builds - 5.13 long paths are not fully supported on Windows - 5.15 Unicode on Windows - - 6. Authentication - 6.2 MIT Kerberos for Windows build - 6.3 NTLM in system context uses wrong name - 6.5 NTLM does not support password with Unicode 'SECTION SIGN' character - 6.6 libcurl can fail to try alternatives with --proxy-any - 6.7 Do not clear digest for single realm - 6.8 Heimdal memory leaks - 6.9 SHA-256 digest not supported in Windows SSPI builds - 6.10 curl never completes Negotiate over HTTP - 6.11 Negotiate on Windows fails - 6.13 Negotiate against Hadoop HDFS - - 7. FTP - 7.4 FTP with ACCT - 7.12 FTPS directory listing hangs on Windows with Schannel - - 9. SFTP and SCP - 9.1 SFTP does not do CURLOPT_POSTQUOTE correct - 9.2 wolfssh: publickey auth does not work - 9.3 Remote recursive folder creation with SFTP - 9.4 libssh blocking and infinite loop problem - 9.5 Cygwin: "WARNING: UNPROTECTED PRIVATE KEY FILE!" - 9.6 wolfssh: all tests fail - - 10. Connection - 10.1 --interface with link-scoped IPv6 address - 10.2 Does not acknowledge getaddrinfo sorting policy - - 11. Internals - 11.1 gssapi library name + version is missing in curl_version_info() - 11.2 error buffer not set if connection to multiple addresses fails - 11.4 HTTP test server 'connection-monitor' problems - 11.5 Connection information when using TCP Fast Open - 11.6 test cases sometimes timeout - 11.7 CURLOPT_CONNECT_TO does not work for HTTPS proxy - 11.8 WinIDN test failures - 11.9 setting a disabled option should return CURLE_NOT_BUILT_IN - - 12. LDAP - 12.1 OpenLDAP hangs after returning results - 12.2 LDAP on Windows does authentication wrong? - 12.3 LDAP on Windows does not work - 12.4 LDAPS requests to ActiveDirectory server hang - - 13. TCP/IP - 13.2 Trying local ports fails on Windows - - 15. CMake - 15.1 cmake outputs: no version information available - 15.6 uses -lpthread instead of Threads::Threads - 15.7 generated .pc file contains strange entries - 15.13 CMake build with MIT Kerberos does not work - - 16. aws-sigv4 - 16.2 aws-sigv4 does not handle multipart/form-data correctly - - 17. HTTP/2 - 17.1 HTTP/2 prior knowledge over proxy - 17.2 HTTP/2 frames while in the connection pool kill reuse - 17.3 ENHANCE_YOUR_CALM causes infinite retries - 17.4 HTTP/2 + TLS spends a lot of time in recv - - 18. HTTP/3 - 18.1 connection migration does not work - 18.2 quiche: QUIC connection is draining - 18.3 OpenSSL-QUIC problems on google.com - - 19. RTSP - 19.1 Some methods do not support response bodies - -============================================================================== - -1. HTTP - -2. TLS - -2.1 IMAPS connection fails with Rustls error - - https://github.com/curl/curl/issues/10457 - -2.5 Client cert handling with Issuer DN differs between backends - - When the specified client certificate does not match any of the - server-specified DNs, the OpenSSL and GnuTLS backends behave differently. - The github discussion may contain a solution. - - See https://github.com/curl/curl/issues/1411 - -2.7 Client cert (MTLS) issues with Schannel - - See https://github.com/curl/curl/issues/3145 - -2.11 Schannel TLS 1.2 handshake bug in old Windows versions - - In old versions of Windows such as 7 and 8.1 the Schannel TLS 1.2 handshake - implementation likely has a bug that can rarely cause the key exchange to - fail, resulting in error SEC_E_BUFFER_TOO_SMALL or SEC_E_MESSAGE_ALTERED. - - https://github.com/curl/curl/issues/5488 - -2.13 CURLOPT_CERTINFO results in CURLE_OUT_OF_MEMORY with Schannel - - https://github.com/curl/curl/issues/8741 - -2.14 mbedTLS and CURLE_AGAIN handling - - https://github.com/curl/curl/issues/15801 - -3. Email protocols - -3.1 IMAP SEARCH ALL truncated response - - IMAP "SEARCH ALL" truncates output on large boxes. "A quick search of the - code reveals that pingpong.c contains some truncation code, at line 408, when - it deems the server response to be too large truncating it to 40 characters" - https://curl.se/bug/view.cgi?id=1366 - -3.2 No disconnect command - - The disconnect commands (LOGOUT and QUIT) may not be sent by IMAP, POP3 and - SMTP if a failure occurs during the authentication phase of a connection. - -3.4 AUTH PLAIN for SMTP is not working on all servers - - Specifying "--login-options AUTH=PLAIN" on the command line does not seem to - work correctly. - - See https://github.com/curl/curl/issues/4080 - -3.5 APOP authentication fails on POP3 - - See https://github.com/curl/curl/issues/10073 - -3.6 POP3 issue when reading small chunks - - CURL_DBG_SOCK_RMAX=4 ./runtests.pl -v 982 - - See https://github.com/curl/curl/issues/12063 - -4. Command line - -4.1 -T /dev/stdin may upload with an incorrect content length - - -T stats the path to figure out its size in bytes to use it as Content-Length - if it is a regular file. - - The problem with that is that, on BSDs and some other UNIXes (not Linux), - open(path) may not give you a file descriptor with a 0 offset from the start - of the file. - - See https://github.com/curl/curl/issues/12177 - -4.2 -T - always uploads chunked - - When the `<` shell operator is used. curl should realise that stdin is a - regular file in this case, and that it can do a non-chunked upload, like it - would do if you used -T file. - - See https://github.com/curl/curl/issues/12171 - -5. Build and portability issues - -5.1 OS400 port requires deprecated IBM library - - curl for OS400 requires QADRT to build, which provides ASCII wrappers for - libc/POSIX functions in the ILE, but IBM no longer supports or even offers - this library to download. - - See https://github.com/curl/curl/issues/5176 - -5.2 curl-config --libs contains private details - - "curl-config --libs" include details set in LDFLAGS when configure is run - that might be needed only for building libcurl. Further, curl-config --cflags - suffers from the same effects with CFLAGS/CPPFLAGS. - -5.3 LDFLAGS passed too late making libs linked incorrectly - - Compiling latest curl on HP-UX and linking against a custom OpenSSL (which is - on the default loader/linker path), fails because the generated Makefile has - LDFLAGS passed on after LIBS. - - See https://github.com/curl/curl/issues/14893 - -5.6 Cygwin: make install installs curl-config.1 twice - - https://github.com/curl/curl/issues/8839 - -5.11 configure --with-gssapi with Heimdal is ignored on macOS - - ... unless you also pass --with-gssapi-libs - - https://github.com/curl/curl/issues/3841 - -5.12 flaky CI builds - - We run many CI builds for each commit and PR on github, and especially a - number of the Windows builds are flaky. This means that we rarely get all CI - builds go green and complete without errors. This is unfortunate as it makes - us sometimes miss actual build problems and it is surprising to newcomers to - the project who (rightfully) do not expect this. - - See https://github.com/curl/curl/issues/6972 - -5.13 long paths are not fully supported on Windows - - curl on Windows cannot access long paths (paths longer than 260 characters). - However, as a workaround, the Windows path prefix \\?\ which disables all - path interpretation may work to allow curl to access the path. For example: - \\?\c:\longpath. - - See https://github.com/curl/curl/issues/8361 - -5.15 Unicode on Windows - - Passing in a Unicode filename with -o: - - https://github.com/curl/curl/issues/11461 - - Passing in Unicode character with -d: - - https://github.com/curl/curl/issues/12231 - - Windows Unicode builds use homedir in current locale - - The Windows Unicode builds of curl use the current locale, but expect Unicode - UTF-8 encoded paths for internal use such as open, access and stat. The - user's home directory is retrieved via curl_getenv in the current locale and - not as UTF-8 encoded Unicode. - - See https://github.com/curl/curl/pull/7252 and - https://github.com/curl/curl/pull/7281 - - Cannot handle Unicode arguments in non-Unicode builds on Windows - - If a URL or filename cannot be encoded using the user's current codepage then - it can only be encoded properly in the Unicode character set. Windows uses - UTF-16 encoding for Unicode and stores it in wide characters, however curl - and libcurl are not equipped for that at the moment except when built with - _UNICODE and UNICODE defined. Except for Cygwin, Windows cannot use UTF-8 as - a locale. - - https://curl.se/bug/?i=345 - https://curl.se/bug/?i=731 - https://curl.se/bug/?i=3747 - - NTLM authentication and Unicode - - NTLM authentication involving Unicode username or password only works - properly if built with UNICODE defined together with the Schannel backend. - The original problem was mentioned in: - https://curl.se/mail/lib-2009-10/0024.html - https://curl.se/bug/view.cgi?id=896 - - The Schannel version verified to work as mentioned in - https://curl.se/mail/lib-2012-07/0073.html - -6. Authentication - -6.2 MIT Kerberos for Windows build - - libcurl fails to build with MIT Kerberos for Windows (KfW) due to KfW's - library header files exporting symbols/macros that should be kept private to - the KfW library. See ticket #5601 at https://krbdev.mit.edu/rt/ - -6.3 NTLM in system context uses wrong name - - NTLM authentication using SSPI (on Windows) when (lib)curl is running in - "system context" makes it use wrong(?) username - at least when compared to - what winhttp does. See https://curl.se/bug/view.cgi?id=535 - -6.5 NTLM does not support password with Unicode 'SECTION SIGN' character - - https://en.wikipedia.org/wiki/Section_sign - https://codepoints.net/U+00A7 SECTION SIGN - https://github.com/curl/curl/issues/2120 - -6.6 libcurl can fail to try alternatives with --proxy-any - - When connecting via a proxy using --proxy-any, a failure to establish an - authentication causes libcurl to abort trying other options if the failed - method has a higher preference than the alternatives. As an example, - --proxy-any against a proxy which advertise Negotiate and NTLM, but which - fails to set up Kerberos authentication does not proceed to try - authentication using NTLM. - - https://github.com/curl/curl/issues/876 - -6.7 Do not clear digest for single realm - - https://github.com/curl/curl/issues/3267 - -6.8 Heimdal memory leaks - - Running test 2077 and 2078 with curl built to do GSS with Heimdal causes - valgrind errors (memory leak). - - https://github.com/curl/curl/issues/14446 - -6.9 SHA-256 digest not supported in Windows SSPI builds - - Windows builds of curl that have SSPI enabled use the native Windows API calls - to create authentication strings. The call to InitializeSecurityContext fails - with SEC_E_QOP_NOT_SUPPORTED which causes curl to fail with CURLE_AUTH_ERROR. - - Microsoft does not document supported digest algorithms and that SEC_E error - code is not a documented error for InitializeSecurityContext (digest). - - https://github.com/curl/curl/issues/6302 - -6.10 curl never completes Negotiate over HTTP - - Apparently it is not working correctly...? - - See https://github.com/curl/curl/issues/5235 - -6.11 Negotiate on Windows fails - - When using --negotiate (or NTLM) with curl on Windows, SSL/TLS handshake - fails despite having a valid kerberos ticket cached. Works without any issue - in Unix/Linux. - - https://github.com/curl/curl/issues/5881 - -6.13 Negotiate authentication against Hadoop HDFS - - https://github.com/curl/curl/issues/8264 - -7. FTP - -7.4 FTP with ACCT - - When doing an operation over FTP that requires the ACCT command (but not when - logging in), the operation fails since libcurl does not detect this and thus - fails to issue the correct command: https://curl.se/bug/view.cgi?id=635 - -7.12 FTPS server compatibility on Windows with Schannel - - FTPS is not widely used with the Schannel TLS backend and so there may be - more bugs compared to other TLS backends such as OpenSSL. In the past users - have reported hanging and failed connections. It is likely some changes to - curl since then fixed the issues. None of the reported issues can be - reproduced any longer. - - If you encounter an issue connecting to your server via FTPS with the latest - curl and Schannel then please search for open issues or file a new issue. - -9. SFTP and SCP - -9.1 SFTP does not do CURLOPT_POSTQUOTE correct - - When libcurl sends CURLOPT_POSTQUOTE commands when connected to an SFTP - server using the multi interface, the commands are not being sent correctly - and instead the connection is "cancelled" (the operation is considered done) - prematurely. There is a half-baked (busy-looping) patch provided in the bug - report but it cannot be accepted as-is. See - https://curl.se/bug/view.cgi?id=748 - -9.2 wolfssh: publickey auth does not work - - When building curl to use the wolfSSH backend for SFTP, the publickey - authentication does not work. This is simply functionality not written for curl - yet, the necessary API for make this work is provided by wolfSSH. - - See https://github.com/curl/curl/issues/4820 - -9.3 Remote recursive folder creation with SFTP - - On this servers, the curl fails to create directories on the remote server - even when the CURLOPT_FTP_CREATE_MISSING_DIRS option is set. - - See https://github.com/curl/curl/issues/5204 - -9.4 libssh blocking and infinite loop problem - - In the SSH_SFTP_INIT state for libssh, the ssh session working mode is set to - blocking mode. If the network is suddenly disconnected during sftp - transmission, curl is stuck, even if curl is configured with a timeout. - - https://github.com/curl/curl/issues/8632 - -9.5 Cygwin: "WARNING: UNPROTECTED PRIVATE KEY FILE!" - - Running SCP and SFTP tests on Cygwin makes this warning message appear. - - https://github.com/curl/curl/issues/11244 - -9.6 wolfssh: all tests fail - - Something fundamental stops them all from working properly. - - https://github.com/curl/curl/issues/16794 - -10. Connection - -10.1 --interface with link-scoped IPv6 address - - When you give the `--interface` option telling curl to use a specific - interface for its outgoing traffic in combination with an IPv6 address in the - URL that uses a link-local scope, curl might pick the wrong address from the - named interface and the subsequent transfer fails. - - Example command line: - - curl --interface eth0 'http://[fe80:928d:xxff:fexx:xxxx]/' - - The fact that the given IP address is link-scoped should probably be used as - input to somehow make curl make a better choice for this. - - https://github.com/curl/curl/issues/14782 - -10.2 Does not acknowledge getaddrinfo sorting policy - - Even if a user edits /etc/gai.conf to prefer IPv4, curl still prefers and - tries IPv6 addresses first. - - https://github.com/curl/curl/issues/16718 - -11. Internals - -11.1 gssapi library name + version is missing in curl_version_info() - - The struct needs to be expanded and code added to store this info. - - See https://github.com/curl/curl/issues/13492 - -11.2 error buffer not set if connection to multiple addresses fails - - If you ask libcurl to resolve a hostname like example.com to IPv6 addresses - when you only have IPv4 connectivity. libcurl fails with - CURLE_COULDNT_CONNECT, but the error buffer set by CURLOPT_ERRORBUFFER - remains empty. Issue: https://github.com/curl/curl/issues/544 - -11.4 HTTP test server 'connection-monitor' problems - - The 'connection-monitor' feature of the sws HTTP test server does not work - properly if some tests are run in unexpected order. Like 1509 and then 1525. - - See https://github.com/curl/curl/issues/868 - -11.5 Connection information when using TCP Fast Open - - CURLINFO_LOCAL_PORT (and possibly a few other) fails when TCP Fast Open is - enabled. - - See https://github.com/curl/curl/issues/1332 and - https://github.com/curl/curl/issues/4296 - -11.6 test cases sometimes timeout - - Occasionally, one of the tests timeouts. Inexplicably. - - See https://github.com/curl/curl/issues/13350 - -11.7 CURLOPT_CONNECT_TO does not work for HTTPS proxy - - It is unclear if the same option should even cover the proxy connection or if - if requires a separate option. - - See https://github.com/curl/curl/issues/14481 - -11.8 WinIDN test failures - - Test 165 disabled when built with WinIDN. - -11.9 setting a disabled option should return CURLE_NOT_BUILT_IN - - When curl has been built with specific features or protocols disabled, - setting such options with curl_easy_setopt() should rather return - CURLE_NOT_BUILT_IN instead of CURLE_UNKNOWN_OPTION to signal the difference - to the application - - See https://github.com/curl/curl/issues/15472 - -12. LDAP - -12.1 OpenLDAP hangs after returning results - - By configuration defaults, OpenLDAP automatically chase referrals on - secondary socket descriptors. The OpenLDAP backend is asynchronous and thus - should monitor all socket descriptors involved. Currently, these secondary - descriptors are not monitored, causing OpenLDAP library to never receive - data from them. - - As a temporary workaround, disable referrals chasing by configuration. - - The fix is not easy: proper automatic referrals chasing requires a - synchronous bind callback and monitoring an arbitrary number of socket - descriptors for a single easy handle (currently limited to 5). - - Generic LDAP is synchronous: OK. - - See https://github.com/curl/curl/issues/622 and - https://curl.se/mail/lib-2016-01/0101.html - -12.2 LDAP on Windows does authentication wrong? - - https://github.com/curl/curl/issues/3116 - -12.3 LDAP on Windows does not work - - A simple curl command line getting "ldap://ldap.forumsys.com" returns an - error that says "no memory" ! - - https://github.com/curl/curl/issues/4261 - -12.4 LDAPS requests to ActiveDirectory server hang - - https://github.com/curl/curl/issues/9580 - -13. TCP/IP - -13.2 Trying local ports fails on Windows - - This makes '--local-port [range]' to not work since curl cannot properly - detect if a port is already in use, so it tries the first port, uses that and - then subsequently fails anyway if that was actually in use. - - https://github.com/curl/curl/issues/8112 - -15. CMake - -15.1 cmake outputs: no version information available - - Something in the SONAME generation seems to be wrong in the cmake build. - - https://github.com/curl/curl/issues/11158 - -15.6 uses -lpthread instead of Threads::Threads - - See https://github.com/curl/curl/issues/6166 - -15.7 generated .pc file contains strange entries - - The Libs.private field of the generated .pc file contains -lgcc -lgcc_s -lc - -lgcc -lgcc_s - - See https://github.com/curl/curl/issues/6167 - -15.13 CMake build with MIT Kerberos does not work - - Minimum CMake version was bumped in curl 7.71.0 (#5358) Since CMake 3.2 - try_compile started respecting the CMAKE_EXE_FLAGS. The code dealing with - MIT Kerberos detection sets few variables to potentially weird mix of space, - and ;-separated flags. It had to blow up at some point. All the CMake checks - that involve compilation are doomed from that point, the configured tree - cannot be built. - - https://github.com/curl/curl/issues/6904 - -16. aws-sigv4 - -16.2 aws-sigv4 does not handle multipart/form-data correctly - - https://github.com/curl/curl/issues/13351 - -17. HTTP/2 - -17.1 HTTP/2 prior knowledge over proxy - - https://github.com/curl/curl/issues/12641 - -17.2 HTTP/2 frames while in the connection pool kill reuse - - If the server sends HTTP/2 frames (like for example an HTTP/2 PING frame) to - curl while the connection is held in curl's connection pool, the socket is - found readable when considered for reuse and that makes curl think it is dead - and then it is closed and a new connection gets created instead. - - This is *best* fixed by adding monitoring to connections while they are kept - in the pool so that pings can be responded to appropriately. - -17.3 ENHANCE_YOUR_CALM causes infinite retries - - Infinite retries with 2 parallel requests on one connection receiving GOAWAY - with ENHANCE_YOUR_CALM error code. - - See https://github.com/curl/curl/issues/5119 - -17.4 HTTP/2 + TLS spends a lot of time in recv - - It has been observed that by making the speed limit less accurate we could - improve this performance. (by reverting - https://github.com/curl/curl/commit/db5c9f4f9e0779b49624752b135281a0717b277b) - Can we find a golden middle ground? - - See https://curl.se/mail/lib-2024-05/0026.html and - https://github.com/curl/curl/issues/13416 - -18. HTTP/3 - -18.1 connection migration does not work - - https://github.com/curl/curl/issues/7695 - -18.2 quiche: QUIC connection is draining - - The transfer ends with error "QUIC connection is draining". - - https://github.com/curl/curl/issues/12037 - -18.3 OpenSSL-QUIC problems on google.com - - With some specific Google servers, and seemingly timing dependent, the - OpenSSL-QUIC backend seems to not actually send off the HTTP/3 request which - makes the QUIC connection just sit idle until killed by the server. curl or - OpenSSL bug? - - https://github.com/curl/curl/issues/18336 - -19. RTSP - -19.1 Some methods do not support response bodies - - The RTSP implementation is written to assume that a number of RTSP methods - always get responses without bodies, even though there seems to be no - indication in the RFC that this is always the case. - - https://github.com/curl/curl/issues/12414 diff --git a/docs/KNOWN_BUGS.md b/docs/KNOWN_BUGS.md new file mode 100644 index 0000000000..cb51bdc54b --- /dev/null +++ b/docs/KNOWN_BUGS.md @@ -0,0 +1,567 @@ + + +# Known bugs intro + +These are problems and bugs known to exist at the time of this release. Feel +free to join in and help us correct one or more of these. Also be sure to +check the changelog of the current development status, as one or more of these +problems may have been fixed or changed somewhat since this was written. + +# TLS + +## IMAPS connection fails with Rustls error + +[curl issue 10457](https://github.com/curl/curl/issues/10457) + +## Access violation sending client cert with Schannel + +When using Schannel to do client certs, curl sets `PKCS12_NO_PERSIST_KEY` to +avoid leaking the private key into the filesystem. Unfortunately that flag +instead seems to trigger a crash. + +See [curl issue 17626](https://github.com/curl/curl/issues/17626) + +## Client cert handling with Issuer `DN` differs between backends + +When the specified client certificate does not match any of the +server-specified `DN` fields, the OpenSSL and GnuTLS backends behave +differently. The GitHub discussion may contain a solution. + +See [curl issue 1411](https://github.com/curl/curl/issues/1411) + +## Client cert (MTLS) issues with Schannel + +See [curl issue 3145](https://github.com/curl/curl/issues/3145) + +## Schannel TLS 1.2 handshake bug in old Windows versions + +In old versions of Windows such as 7 and 8.1 the Schannel TLS 1.2 handshake +implementation likely has a bug that can rarely cause the key exchange to +fail, resulting in error SEC_E_BUFFER_TOO_SMALL or SEC_E_MESSAGE_ALTERED. + +[curl issue 5488](https://github.com/curl/curl/issues/5488) + +## `CURLOPT_CERTINFO` results in `CURLE_OUT_OF_MEMORY` with Schannel + +[curl issue 8741](https://github.com/curl/curl/issues/8741) + +## mbedTLS and CURLE_AGAIN handling + +[curl issue 15801](https://github.com/curl/curl/issues/15801) + +## Native CA roots incomplete on Windows with OpenSSL (or fork) + +Certain Windows installations may be missing CA roots. + +[curl issue 20897](https://github.com/curl/curl/issues/20897) +[curl issue 12303](https://github.com/curl/curl/issues/12303) + +# Email protocols + +## IMAP `SEARCH ALL` truncated response + +IMAP `SEARCH ALL` truncates output on large boxes. "A quick search of the code +reveals that `pingpong.c` contains some truncation code, at line 408, when it +deems the server response to be too large truncating it to 40 characters" + +https://curl.se/bug/view.cgi?id=1366 + +## No disconnect command + +The disconnect commands (`LOGOUT` and `QUIT`) may not be sent by IMAP, POP3 +and SMTP if a failure occurs during the authentication phase of a connection. + +## `AUTH PLAIN` for SMTP is not working on all servers + +Specifying `--login-options AUTH=PLAIN` on the command line does not seem to +work correctly. + +See [curl issue 4080](https://github.com/curl/curl/issues/4080) + +## `APOP` authentication fails on POP3 + +See [curl issue 10073](https://github.com/curl/curl/issues/10073) + +## POP3 issue when reading small chunks + + CURL_DBG_SOCK_RMAX=4 ./runtests.pl -v 982 + +See [curl issue 12063](https://github.com/curl/curl/issues/12063) + +# Command line + +## `-T /dev/stdin` may upload with an incorrect content length + +`-T` stats the path to figure out its size in bytes to use it as +`Content-Length` if it is a regular file. + +The problem with that is that on BSD and some other UNIX systems (not Linux), +open(path) may not give you a file descriptor with a 0 offset from the start +of the file. + +See [curl issue 12177](https://github.com/curl/curl/issues/12177) + +## `-T -` always uploads chunked + +When the `<` shell operator is used. curl should realize that stdin is a +regular file in this case, and that it can do a non-chunked upload, like it +would do if you used `-T` file. + +See [curl issue 12171](https://github.com/curl/curl/issues/12171) + +# Build and portability issues + +## OS400 port requires deprecated IBM library + +curl for OS400 requires `QADRT` to build, which provides ASCII wrappers for +libc/POSIX functions in the ILE, but IBM no longer supports or even offers +this library to download. + +See [curl issue 5176](https://github.com/curl/curl/issues/5176) + +## `curl-config --libs` contains private details + +`curl-config --libs` include details set in `LDFLAGS` when configure is run +that might be needed only for building libcurl. Further, `curl-config +--cflags` suffers from the same effects with `CFLAGS`/`CPPFLAGS`. + +## `LDFLAGS` passed too late making libs linked incorrectly + +Compiling latest curl on HP-UX and linking against a custom OpenSSL (which is +on the default loader/linker path), fails because the generated Makefile has +`LDFLAGS` passed on after `LIBS`. + +See [curl issue 14893](https://github.com/curl/curl/issues/14893) + +## Cygwin: make install installs curl-config.1 twice + +[curl issue 8839](https://github.com/curl/curl/issues/8839) + +## flaky CI builds + +We run many CI builds for each commit and PR on GitHub, and especially a +number of the Windows builds are flaky. This means that we rarely get all CI +builds go green and complete without errors. This is unfortunate as it makes +us sometimes miss actual build problems and it is surprising to newcomers to +the project who (rightfully) do not expect this. + +See [curl issue 6972](https://github.com/curl/curl/issues/6972) + +## long paths are not fully supported on Windows + +curl on Windows cannot access long paths (paths longer than 260 characters). +As a workaround, the Windows path prefix `\\?\` which disables all path +interpretation may work to allow curl to access the path. For example: +`\\?\c:\longpath`. + +See [curl issue 8361](https://github.com/curl/curl/issues/8361) + +## Unicode on Windows + +Passing in a Unicode filename with -o: + +[curl issue 11461](https://github.com/curl/curl/issues/11461) + +Passing in Unicode character with -d: + +[curl issue 12231](https://github.com/curl/curl/issues/12231) + +Windows Unicode builds use the home directory in current locale. + +The Windows Unicode builds of curl use the current locale, but expect Unicode +UTF-8 encoded paths for internal use such as open, access and stat. The user's +home directory is retrieved via curl_getenv in the current locale and not as +UTF-8 encoded Unicode. + +See [curl pull request 7252](https://github.com/curl/curl/pull/7252) and [curl pull request 7281](https://github.com/curl/curl/pull/7281) + +Cannot handle Unicode arguments in non-Unicode builds on Windows + +If a URL or filename cannot be encoded using the user's current code page then +it can only be encoded properly in the Unicode character set. Windows uses +UTF-16 encoding for Unicode and stores it in wide characters, however curl and +libcurl are not equipped for that at the moment except when built with +_UNICODE and UNICODE defined. Except for Cygwin, Windows cannot use UTF-8 as a +locale. + +https://curl.se/bug/?i=345 +https://curl.se/bug/?i=731 +https://curl.se/bug/?i=3747 + +NTLM authentication and Unicode + +NTLM authentication involving Unicode username or password only works properly +if built with UNICODE defined together with the Schannel backend. The original +problem was mentioned in: https://curl.se/mail/lib-2009-10/0024.html and +https://curl.se/bug/view.cgi?id=896 + +The Schannel version verified to work as mentioned in +https://curl.se/mail/lib-2012-07/0073.html + +# Authentication + +## Digest `auth-int` for PUT/POST + +We do not support auth-int for Digest using PUT or POST + +## MIT Kerberos for Windows build + +libcurl fails to build with MIT Kerberos for Windows (`KfW`) due to its +library header files exporting symbols/macros that should be kept private to +the library. + +## NTLM in system context uses wrong name + +NTLM authentication using SSPI (on Windows) when (lib)curl is running in +"system context" makes it use wrong(?) username - at least when compared to +what `winhttp` does. See https://curl.se/bug/view.cgi?id=535 + +## NTLM does not support password with Unicode 'SECTION SIGN' character + +Code point: U+00A7 + +https://en.wikipedia.org/wiki/Section_sign +[curl issue 2120](https://github.com/curl/curl/issues/2120) + +## libcurl can fail to try alternatives with `--proxy-any` + +When connecting via a proxy using `--proxy-any`, a failure to establish an +authentication causes libcurl to abort trying other options if the failed +method has a higher preference than the alternatives. As an example, +`--proxy-any` against a proxy which advertise Negotiate and NTLM, but which +fails to set up Kerberos authentication does not proceed to try authentication +using NTLM. + +[curl issue 876](https://github.com/curl/curl/issues/876) + +## Do not clear digest for single realm + +[curl issue 3267](https://github.com/curl/curl/issues/3267) + +## SHA-256 digest not supported in Windows SSPI builds + +Windows builds of curl that have SSPI enabled use the native Windows API calls +to create authentication strings. The call to `InitializeSecurityContext` fails +with `SEC_E_QOP_NOT_SUPPORTED` which causes curl to fail with +`CURLE_AUTH_ERROR`. + +Microsoft does not document supported digest algorithms and that `SEC_E` error +code is not a documented error for `InitializeSecurityContext` (digest). + +[curl issue 6302](https://github.com/curl/curl/issues/6302) + +## curl never completes Negotiate over HTTP + +Apparently it is not working correctly...? + +See [curl issue 5235](https://github.com/curl/curl/issues/5235) + +## Negotiate on Windows fails + +When using `--negotiate` (or NTLM) with curl on Windows, SSL/TLS handshake +fails despite having a valid kerberos ticket cached. Works without any issue +in Unix/Linux. + +[curl issue 5881](https://github.com/curl/curl/issues/5881) + +## Negotiate authentication against Hadoop + +[curl issue 8264](https://github.com/curl/curl/issues/8264) + +# FTP + +## FTP with ACCT + +When doing an operation over FTP that requires the `ACCT` command (but not when +logging in), the operation fails since libcurl does not detect this and thus +fails to issue the correct command: https://curl.se/bug/view.cgi?id=635 + +## FTPS server compatibility on Windows with Schannel + +FTPS is not widely used with the Schannel TLS backend and so there may be more +bugs compared to other TLS backends such as OpenSSL. In the past users have +reported hanging and failed connections. It is likely some changes to curl +since then fixed the issues. None of the reported issues can be reproduced any +longer. + +If you encounter an issue connecting to your server via FTPS with the latest +curl and Schannel then please search for open issues or file a new issue. + +# SFTP and SCP + +## SFTP does not do `CURLOPT_POSTQUOTE` correct + +When libcurl sends `CURLOPT_POSTQUOTE` commands when connected to an SFTP +server using the multi interface, the commands are not being sent correctly +and instead the connection is canceled (the operation is considered done) +prematurely. There is a half-baked (busy-looping) patch provided in the bug +report but it cannot be accepted as-is. See +https://curl.se/bug/view.cgi?id=748 + +## Remote recursive folder creation with SFTP + +On this servers, the curl fails to create directories on the remote server +even when the `CURLOPT_FTP_CREATE_MISSING_DIRS` option is set. + +See [curl issue 5204](https://github.com/curl/curl/issues/5204) + +## libssh blocking and infinite loop problem + +In the `SSH_SFTP_INIT` state for libssh, the ssh session working mode is set +to blocking mode. If the network is suddenly disconnected during sftp +transmission, curl is stuck, even if curl is configured with a timeout. + +[curl issue 8632](https://github.com/curl/curl/issues/8632) + +## Cygwin: "WARNING: UNPROTECTED PRIVATE KEY FILE!" + +Running SCP and SFTP tests on Cygwin makes this warning message appear. + +[curl issue 11244](https://github.com/curl/curl/issues/11244) + +# Connection + +## `--interface` with link-scoped IPv6 address + +When you give the `--interface` option telling curl to use a specific +interface for its outgoing traffic in combination with an IPv6 address in the +URL that uses a link-local scope, curl might pick the wrong address from the +named interface and the subsequent transfer fails. + +Example command line: + + curl --interface eth0 'http://[fe80:928d:xxff:fexx:xxxx]/' + +The fact that the given IP address is link-scoped should probably be used as +input to somehow make curl make a better choice for this. + +[curl issue 14782](https://github.com/curl/curl/issues/14782) + +## Does not acknowledge getaddrinfo sorting policy + +Even if a user edits `/etc/gai.conf` to prefer IPv4, curl still prefers and +tries IPv6 addresses first. + +[curl issue 16718](https://github.com/curl/curl/issues/16718) + +## SOCKS-SSPI discards the security context + +After a successful SSPI/GSS-API exchange, the function queries and logs the +authenticated username and reports the supported data-protection level, but +then immediately deletes the negotiated SSPI security context and frees the +credentials before returning. The negotiated context is not stored on the +connection and is therefore never used to protect later SOCKS5 traffic. + +## cannot use absolute Unix domain filename for SOCKS on Windows + +curl supports using a Unix domain socket path for speaking SOCKS to a proxy, +by providing a filename in the URL used for `-x` (`CURLOPT_PROXY`), but that +path cannot be a proper absolute Windows path with a drive letter etc. + +A solution for this probably requires that we add and provide a +`--unix-socket` (`CURLOPT_UNIX_SOCKET_PATH`) option alternative for proxy +communication. + +See [curl issue 19825](https://github.com/curl/curl/issues/19825) + +# Internals + +## GSSAPI library name + version is missing in `curl_version_info()` + +The struct needs to be expanded and code added to store this info. + +See [curl issue 13492](https://github.com/curl/curl/issues/13492) + +## error buffer not set if connection to multiple addresses fails + +If you ask libcurl to resolve a hostname like example.com to IPv6 addresses +when you only have IPv4 connectivity. libcurl fails with +`CURLE_COULDNT_CONNECT`, but the error buffer set by `CURLOPT_ERRORBUFFER` +remains empty. Issue: [curl issue 544](https://github.com/curl/curl/issues/544) + +## HTTP test server 'connection-monitor' problems + +The `connection-monitor` feature of the HTTP test server does not work +properly if some tests are run in unexpected order. Like 1509 and then 1525. + +See [curl issue 868](https://github.com/curl/curl/issues/868) + +## Connection information when using TCP Fast Open + +`CURLINFO_LOCAL_PORT` (and possibly a few other) fails when TCP Fast Open is +enabled. + +See [curl issue 1332](https://github.com/curl/curl/issues/1332) and +[curl issue 4296](https://github.com/curl/curl/issues/4296) + +## test cases sometimes timeout + +Occasionally, one of the tests timeouts. Inexplicably. + +See [curl issue 13350](https://github.com/curl/curl/issues/13350) + +## `CURLOPT_CONNECT_TO` does not work for HTTPS proxy + +It is unclear if the same option should even cover the proxy connection or if +if requires a separate option. + +See [curl issue 14481](https://github.com/curl/curl/issues/14481) + +## WinIDN test failures + +Test 165 disabled when built with WinIDN. + +## setting a disabled option should return `CURLE_NOT_BUILT_IN` + +When curl has been built with specific features or protocols disabled, setting +such options with `curl_easy_setopt()` should rather return +`CURLE_NOT_BUILT_IN` instead of `CURLE_UNKNOWN_OPTION` to signal the +difference to the application + +See [curl issue 15472](https://github.com/curl/curl/issues/15472) + +# LDAP + +## OpenLDAP hangs after returning results + +By configuration defaults, OpenLDAP automatically chase referrals on secondary +socket descriptors. The OpenLDAP backend is asynchronous and thus should +monitor all socket descriptors involved. Currently, these secondary +descriptors are not monitored, causing OpenLDAP library to never receive data +from them. + +As a temporary workaround, disable referrals chasing by configuration. + +The fix is not easy: proper automatic referrals chasing requires a synchronous +bind callback and monitoring an arbitrary number of socket descriptors for a +single easy handle (currently limited to 5). + +Generic LDAP is synchronous: OK. + +See [curl issue 622](https://github.com/curl/curl/issues/622) and +https://curl.se/mail/lib-2016-01/0101.html + +## LDAP on Windows does authentication wrong? + +[curl issue 3116](https://github.com/curl/curl/issues/3116) + +## LDAP on Windows does not work + +A simple curl command line getting `ldap://ldap.forumsys.com` returns an error +that says `no memory` ! + +[curl issue 4261](https://github.com/curl/curl/issues/4261) + +## LDAPS requests to Active Directory server hang + +[curl issue 9580](https://github.com/curl/curl/issues/9580) + +# TCP/IP + +## telnet code does not handle partial writes properly + +It probably does not happen too easily because of how slow and infrequent +sends are normally performed. + +## Trying local ports fails on Windows + +This makes `--local-port [range]` to not work since curl cannot properly +detect if a port is already in use, so it tries the first port, uses that and +then subsequently fails anyway if that was actually in use. + +[curl issue 8112](https://github.com/curl/curl/issues/8112) + +# CMake + +## cmake outputs: no version information available + +Something in the SONAME generation seems to be wrong in the cmake build. + +[curl issue 11158](https://github.com/curl/curl/issues/11158) + +## uses `-lpthread` instead of `Threads::Threads` + +See [curl issue 6166](https://github.com/curl/curl/issues/6166) + +## generated `.pc` file contains strange entries + +The `Libs.private` field of the generated `.pc` file contains `-lgcc -lgcc_s +-lc -lgcc -lgcc_s`. + +See [curl issue 6167](https://github.com/curl/curl/issues/6167) + +## CMake build with MIT Kerberos does not work + +Minimum CMake version was bumped in curl 7.71.0 (#5358) Since CMake 3.2 +try_compile started respecting the `CMAKE_EXE_FLAGS`. The code dealing with +MIT Kerberos detection sets few variables to potentially weird mix of space, +and ;-separated flags. It had to blow up at some point. All the CMake checks +that involve compilation are doomed from that point, the configured tree +cannot be built. + +[curl issue 6904](https://github.com/curl/curl/issues/6904) + +# Authentication + +## `--aws-sigv4` does not handle multipart/form-data correctly + +[curl issue 13351](https://github.com/curl/curl/issues/13351) + +# HTTP/2 + +## HTTP/2 prior knowledge over proxy + +[curl issue 12641](https://github.com/curl/curl/issues/12641) + +## HTTP/2 frames while in the connection pool kill reuse + +If the server sends HTTP/2 frames (like for example an HTTP/2 PING frame) to +curl while the connection is held in curl's connection pool, the socket is +found readable when considered for reuse and that makes curl think it is dead +and then it is closed and a new connection gets created instead. + +This is *best* fixed by adding monitoring to connections while they are kept +in the pool so that pings can be responded to appropriately. + +## `ENHANCE_YOUR_CALM` causes infinite retries + +Infinite retries with 2 parallel requests on one connection receiving `GOAWAY` +with `ENHANCE_YOUR_CALM` error code. + +See [curl issue 5119](https://github.com/curl/curl/issues/5119) + +## HTTP/2 + TLS spends a lot of time in recv + +It has been observed that by making the speed limit less accurate we could +improve this performance. (by reverting +[db5c9f4f9e0779](https://github.com/curl/curl/commit/db5c9f4f9e0779b49624752b135281a0717b277b)) +Can we find a golden middle ground? + +See https://curl.se/mail/lib-2024-05/0026.html and +[curl issue 13416](https://github.com/curl/curl/issues/13416) + +# HTTP/3 + +## connection migration does not work + +[curl issue 7695](https://github.com/curl/curl/issues/7695) + +## quiche: QUIC connection is draining + +The transfer ends with error "QUIC connection is draining". + +[curl issue 12037](https://github.com/curl/curl/issues/12037) + +# RTSP + +## Some methods do not support response bodies + +The RTSP implementation is written to assume that a number of RTSP methods +always get responses without bodies, even though there seems to be no +indication in the RFC that this is always the case. + +[curl issue 12414](https://github.com/curl/curl/issues/12414) diff --git a/docs/KNOWN_RISKS.md b/docs/KNOWN_RISKS.md new file mode 100644 index 0000000000..82125f0c66 --- /dev/null +++ b/docs/KNOWN_RISKS.md @@ -0,0 +1,149 @@ + + +# Known Risks + +This is an incomplete list of known risks when running and using curl and +libcurl. + +# Risks + +## Insecure transfers + +When using curl to perform transfers with protocols that are insecure or the +server identity is unverified, everything that is sent and received can be +intercepted by eavesdroppers and the servers can easily be spoofed by +impostors. + +## Untrusted input + +You should **never** run curl command lines or use curl config files provided +to you from untrusted sources. + +curl can do a lot of things, and you should only ask it do things you want and +deem correct. + +Even accepting only the URL part without careful vetting might make curl do +things you do not like. Like accessing internal hosts, like connecting to +rogue servers that redirect to even weirder places, like using ports or +protocols that play tricks on you. + +## Command line misuse + +The command line tool and its options should be used and be expected to work +as documented. Relying on undocumented functions or side-effects is unreliable +as they may cause problems or get changed behavior between releases. + +For several command line options, you can confuse either curl or the involved +server endpoint by using characters or byte sequences for the option that are +not expected. For example, adding line feeds and/or carriage returns to inputs +can produce unexpected, invalid, or insecure results. + +## API misuse + +Applications using the libcurl API in a way that is not documented to work or +even documented to not work, is unsafe and might cause security problems. We +only guarantee secure and proper functionality when the APIs are used as +documented. + +## Local attackers already present + +When there is a local attacker present locally, curl cannot prevent such an +adversary to use curl's full potential. Possibly in malicious ways. + +## Remote attackers already present + +When there is a remote attacker already present in the server, curl cannot +protect its operations against mischief. For example, if an attacker manages +to insert a symlink in your remote upload directory the upload may cause +havoc. Maybe the attacker makes certain responses come back with unexpected +content. + +## Debug & Experiments + +We encourage users to test curl experiments and use debug code, but only in +controlled environments and setups - never in production. + +Using debug builds and experimental curl features in production is a security +risk. Do not do that. + +The same applies to scripts and software which are not installed by default +through the make install rule: they are not intended or made for production +use. + +## URL inconsistencies + +URL parser inconsistencies between browsers and curl are expected and are not +considered security vulnerabilities. The WHATWG URL Specification and RFC +3986+ (the plus meaning that it is an extended version) [are not completely +interoperable](https://github.com/bagder/docs/blob/master/URL-interop.md). + +You must never expect two independent URL parsers to treat every URL +identically. + +## Visible command line arguments + +The curl command blanks the contents of a number of command line arguments to +prevent them from appearing in process listings. It does not blank all +arguments, even though some that are not blanked might contain sensitive data. + +- not all systems allow the arguments to be blanked in the first place +- since curl blanks the argument itself they are readable for a short moment + no matter what +- virtually every argument can contain sensitive data, depending on use +- blanking all arguments would make it impractical for users to differentiate + curl command lines in process listings + +## HTTP headers in redirects + +It is powerful to provide a set of custom headers to curl. Beware that when +asking curl to follow HTTP redirects, it also sends those headers to the new +URL which might be a different server. That might do another redirect etc. + +curl makes some limited attempts to not leak credentials this way when set +using the standard curl options, but when you pass on custom headers curl +cannot know what headers or details in those headers are sensitive. + +## Verbose logs + +When asked to provide verbose output and trace logging, curl may output and +show details that are private and sensitive. Like for example raw credentials +or the password weakly disguised using base64 encoding. + +## Terminal output and escape sequences + +Content that is transferred from a server and gets displayed in a terminal by +curl may contain escape sequences or use other tricks to fool the user. Escape +sequences, moving cursor, changing color etc, is also frequently used for +good. To reduce the risk of getting fooled, save files and browse them after +download using a display method that minimizes risks. + +## Legacy dependencies + +Every curl build is made to use a range of third party libraries. Each third +party library also needs to be safe and secure for the entire operation to be +risk-free. + +Relying on legacy dependencies is a risk. + +## Weak algorithms + +curl supports several cryptographic algorithms that are considered weak, like +DES and MD5. These algorithms are still in use because some protocols and +transfer options require use of them. For example NTLM or legacy HTTP Digest +authentication. + +curl users should consider switching to servers and options that use modern +and secure algorithms. + +## Compression bombs + +When asking curl or libcurl to automatically decompress data on arrival, there +is a risk that the size of the output from the decompression process ends up +many times larger than the input data size. + +Since curl 8.20.0, users can mitigate this risk by setting the max filesize +option that also covers the decompressed size. diff --git a/docs/MAIL-ETIQUETTE.md b/docs/MAIL-ETIQUETTE.md index 3de77b17bf..9c2527d04d 100644 --- a/docs/MAIL-ETIQUETTE.md +++ b/docs/MAIL-ETIQUETTE.md @@ -39,8 +39,8 @@ way to read the reply, but to ask the one person the question. The one person consequently gets overloaded with mail. If you really want to contact an individual and perhaps pay for his or her -services, by all means go ahead, but if it is just another curl question, take -it to a suitable list instead. +services, by all means go ahead, but if it is another curl question, take it +to a suitable list instead. ### Subscription Required @@ -48,7 +48,7 @@ All curl mailing lists require that you are subscribed to allow a mail to go through to all the subscribers. If you post without being subscribed (or from a different mail address than -the one you are subscribed with), your mail is simply silently discarded. You +the one you are subscribed with), your mail is silently discarded. You have to subscribe first, then post. The reason for this unfortunate and strict subscription policy is of course to @@ -150,8 +150,8 @@ individuals. There is no way to undo a sent email. When sending emails to a curl mailing list, do not include sensitive information such as usernames and passwords; use fake ones, temporary ones or -just remove them completely from the mail. Note that this includes base64 -encoded HTTP Basic auth headers. +remove them completely from the mail. Note that this includes base64 encoded +HTTP Basic auth headers. This public nature of the curl mailing lists makes automatically inserted mail footers about mails being "private" or "only meant for the recipient" or @@ -167,14 +167,14 @@ the lists. Many mail programs and web archivers use information within mails to keep them together as "threads", as collections of posts that discuss a certain subject. -If you do not intend to reply on the same or similar subject, do not just hit -reply on an existing mail and change the subject, create a new mail. +If you do not intend to reply on the same or similar subject, do not hit reply +on an existing mail and change the subject, create a new mail. ### Reply to the List When replying to a message from the list, make sure that you do "group reply" -or "reply to all", and not just reply to the author of the single mail you -reply to. +or "reply to all", and not reply to the author of the single mail you reply +to. We are actively discouraging replying to the single person by setting the correct field in outgoing mails back asking for replies to get sent to the @@ -222,9 +222,8 @@ mails to your friends. We speak plain text mails. ### Quoting -Quote as little as possible. Just enough to provide the context you cannot -eave out. A lengthy description can be found -[here](https://www.netmeister.org/news/learn2quote.html). +Quote as little as possible. Enough to provide the context you cannot leave +out. ### Digest diff --git a/docs/MANUAL.md b/docs/MANUAL.md index 9ff5c097c7..e6f5123d2b 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -34,7 +34,7 @@ Get the definition of curl from a dictionary: Fetch two documents at once: - curl ftp://ftp.example.com/ http://www.example.com:8000/ + curl ftp://ftp.example.com/ https://www.example.com:8000/ Get a file off an FTPS server: @@ -71,12 +71,12 @@ Get a file from an SMB server: Get a webpage and store in a local file with a specific name: - curl -o thatpage.html http://www.example.com/ + curl -o thatpage.html https://www.example.com/ Get a webpage and store in a local file, make the local file get the name of the remote document (if no filename part is specified in the URL, this fails): - curl -O http://www.example.com/index.html + curl -O https://www.example.com/index.html Fetch two files and store them with their remote names: @@ -96,8 +96,8 @@ or specify them with the `-u` flag like ### FTPS -It is just like for FTP, but you may also want to specify and use SSL-specific -options for certificates etc. +It is like FTP, but you may also want to specify and use SSL-specific options +for certificates etc. Note that using `FTPS://` as prefix is the *implicit* way as described in the standards while the recommended *explicit* way is done by using `FTP://` and @@ -115,14 +115,14 @@ matching public key file must be specified using the `--pubkey` option. ### HTTP -curl also supports user and password in HTTP URLs, thus you can pick a file +curl also supports user and password in HTTP(S) URLs. You can download a file like: - curl http://name:passwd@http.server.example/full/path/to/file + curl https://name:passwd@http.server.example/full/path/to/file or specify user and password separately like in - curl -u name:passwd http://http.server.example/full/path/to/file + curl -u name:passwd https://http.server.example/full/path/to/file HTTP offers many different methods of authentication and curl supports several: Basic, Digest, NTLM and Negotiate (SPNEGO). Without telling which @@ -151,19 +151,19 @@ Get an ftp file using an HTTP proxy named my-proxy that uses port 888: curl -x my-proxy:888 ftp://ftp.example.com/README -Get a file from an HTTP server that requires user and password, using the +Get a file from an HTTPS server that requires user and password, using the same proxy as above: - curl -u user:passwd -x my-proxy:888 http://www.example.com/ + curl -u user:passwd -x my-proxy:888 https://www.example.com/ Some proxies require special authentication. Specify by using -U as above: - curl -U user:passwd -x my-proxy:888 http://www.example.com/ + curl -U user:passwd -x my-proxy:888 https://www.example.com/ A comma-separated list of hosts and domains which do not use the proxy can be specified as: - curl --noproxy example.com -x my-proxy:888 http://www.example.com/ + curl --noproxy example.com -x my-proxy:888 https://www.example.com/ If the proxy is specified with `--proxy1.0` instead of `--proxy` or `-x`, then curl uses HTTP/1.0 instead of HTTP/1.1 for any `CONNECT` attempts. @@ -204,11 +204,11 @@ one or more sub-parts of a specified document. curl supports this with the Get the first 100 bytes of a document: - curl -r 0-99 http://www.example.com/ + curl -r 0-99 https://www.example.com/ Get the last 500 bytes of a document: - curl -r -500 http://www.example.com/ + curl -r -500 https://www.example.com/ curl also supports simple ranges for FTP files as well. Then you can only specify start and stop position. @@ -251,9 +251,9 @@ fashion similar to: ### HTTP -Upload all data on stdin to a specified HTTP site: +Upload all data on stdin to a specified HTTPS site: - curl -T - http://www.example.com/myfile + curl -T - https://www.example.com/myfile Note that the HTTP server must have been configured to accept PUT before this can be done successfully. @@ -276,7 +276,6 @@ this: curl --trace my-trace.txt www.haxx.se - ## Detailed Information Different protocols provide different ways of getting detailed information @@ -305,12 +304,12 @@ The post data must be urlencoded. Post a simple `name` and `phone` guestbook. - curl -d "name=Rafael%20Sagula&phone=3320780" http://www.example.com/guest.cgi + curl -d "name=Rafael%20Sagula&phone=3320780" https://www.example.com/guest.cgi Or automatically [URL encode the data](https://everything.curl.dev/http/post/url-encode). curl --data-urlencode "name=Rafael Sagula&phone=3320780" - http://www.example.com/guest.cgi + https://www.example.com/guest.cgi How to post a form with curl, lesson #1: @@ -329,7 +328,7 @@ of the letter's ASCII code. Example: -(say if `http://example.com` had the following html) +(say if `https://example.com` had the following html) ```html
@@ -345,7 +344,7 @@ We want to enter user `foobar` with password `12345`. To post to this, you would enter a curl command line like: curl -d "user=foobar&pass=12345&id=blablabla&ding=submit" - http://example.com/post.cgi + https://example.com/post.cgi While `-d` uses the application/x-www-form-urlencoded mime-type, generally understood by CGI's and similar, curl also supports the more capable @@ -359,7 +358,7 @@ example, the field name `coolfiles` is used to send three files, with different content types using the following syntax: curl -F "coolfiles=@fil1.gif;type=image/gif,fil2.txt,fil3.html" - http://www.example.com/postit.cgi + https://www.example.com/postit.cgi If the content-type is not specified, curl tries to guess from the file extension (it only knows a few), or use the previously specified type (from an @@ -376,7 +375,7 @@ the names of the input fields. In our example, the input field names are curl -F "file=@cooltext.txt" -F "yourname=Daniel" -F "filedescription=Cool text file with cool text inside" - http://www.example.com/postit.cgi + https://www.example.com/postit.cgi To send two files in one post you can do it in two ways: @@ -402,7 +401,7 @@ used on the command line. It is especially useful to fool or trick stupid servers or CGI scripts that rely on that information being available or contain certain data. - curl -e www.example.org http://www.example.com/ + curl -e www.example.org https://www.example.com/ ## User Agent @@ -413,7 +412,7 @@ accept certain browsers. Example: - curl -A 'Mozilla/3.0 (Win95; I)' http://www.bank.example.com/ + curl -A 'Mozilla/3.0 (Win95; I)' https://www.bank.example.com/ Other common strings: @@ -504,18 +503,18 @@ happening. The different fields in the output have the following meaning: From left-to-right: - - `%` - percentage completed of the whole transfer - - `Total` - total size of the whole expected transfer - - `%` - percentage completed of the download - - `Received` - currently downloaded amount of bytes - - `%` - percentage completed of the upload - - `Xferd` - currently uploaded amount of bytes - - `Average Speed Dload` - the average transfer speed of the download - - `Average Speed Upload` - the average transfer speed of the upload - - `Time Total` - expected time to complete the operation - - `Time Current` - time passed since the invoke - - `Time Left` - expected time left to completion - - `Curr.Speed` - the average transfer speed the last 5 seconds (the first +- `%` - percentage completed of the whole transfer +- `Total` - total size of the whole expected transfer +- `%` - percentage completed of the download +- `Received` - currently downloaded amount of bytes +- `%` - percentage completed of the upload +- `Xferd` - currently uploaded amount of bytes +- `Average Speed Dload` - the average transfer speed of the download +- `Average Speed Upload` - the average transfer speed of the upload +- `Time Total` - expected time to complete the operation +- `Time Current` - time passed since the invoke +- `Time Left` - expected time left to completion +- `Curr.Speed` - the average transfer speed the last 5 seconds (the first 5 seconds of a transfer is based on less time of course.) The `-#` option displays a totally different progress bar that does not need @@ -595,15 +594,15 @@ line parameter, like: Force curl to get and display a local help page in case it is invoked without URL by making a config file similar to: - # default url to get - url = "http://help.with.curl.example.com/curlhelp.html" + # default URL to get + url = "https://help.with.curl.example.com/curlhelp.html" You can specify another config file to be read by using the `-K`/`--config` flag. If you set config filename to `-` it reads the config from stdin, which can be handy if you want to hide options from being visible in process tables etc: - echo "user = user:passwd" | curl -K - http://that.secret.example.com + echo "user = user:passwd" | curl -K - https://that.secret.example.com ## Extra Headers @@ -661,7 +660,7 @@ incoming connections. curl ftp.example.com If the server, for example, is behind a firewall that does not allow -connections on ports other than 21 (or if it just does not support the `PASV` +connections on ports other than 21 (or if it does not support the `PASV` command), the other way to do it is to use the `PORT` command and instruct the server to connect to the client on the given IP number and port (as parameters to the PORT command). @@ -685,11 +684,11 @@ Download with `PORT` but use 192.168.0.10 as our IP address to use: Get a webpage from a server using a specified port for the interface: - curl --interface eth0:1 http://www.example.com/ + curl --interface eth0:1 https://www.example.com/ or - curl --interface 192.168.1.10 http://www.example.com/ + curl --interface 192.168.1.10 https://www.example.com/ ## HTTPS @@ -740,7 +739,7 @@ Continue uploading a document: Continue downloading a document from a web server - curl -C - -o file http://www.example.com/ + curl -C - -o file https://www.example.com/ ## Time Conditions @@ -751,17 +750,17 @@ them with the `-z`/`--time-cond` flag. For example, you can easily make a download that only gets performed if the remote file is newer than a local copy. It would be made like: - curl -z local.html http://remote.example.com/remote.html + curl -z local.html https://remote.example.com/remote.html Or you can download a file only if the local file is newer than the remote one. Do this by prepending the date string with a `-`, as in: - curl -z -local.html http://remote.example.com/remote.html + curl -z -local.html https://remote.example.com/remote.html You can specify a plain text date as condition. Tell curl to only download the file if it was updated since January 12, 2012: - curl -z "Jan 12 2012" http://remote.example.com/remote.html + curl -z "Jan 12 2012" https://remote.example.com/remote.html curl accepts a wide range of date formats. You always make the date check the other way around by prepending it with a dash (`-`). @@ -856,8 +855,8 @@ therefore most Unix programs do not read this file unless it is only readable by yourself (curl does not care though). curl supports `.netrc` files if told to (using the `-n`/`--netrc` and -`--netrc-optional` options). This is not restricted to just FTP, so curl can -use it for all protocols where authentication is used. +`--netrc-optional` options). This is not restricted to FTP, so curl can use it +for all protocols where authentication is used. A simple `.netrc` file could look something like: @@ -908,8 +907,8 @@ tell the server we use a vt100 terminal, try something like: Other interesting options for it `-t` include: - - `XDISPLOC=` Sets the X display location. - - `NEW_ENV=` Sets an environment variable. +- `XDISPLOC=` Sets the X display location. +- `NEW_ENV=` Sets an environment variable. NOTE: The telnet protocol does not specify any way to login with a specified user and password so curl cannot do that automatically. To do that, you need to @@ -936,15 +935,15 @@ are persistent. ## Multiple Transfers With A Single Command Line As is mentioned above, you can download multiple files with one command line -by simply adding more URLs. If you want those to get saved to a local file -instead of just printed to stdout, you need to add one save option for each -URL you specify. Note that this also goes for the `-O` option (but not +by adding more URLs. If you want those to get saved to a local file +instead of printed to stdout, you need to add one save option for each URL you +specify. Note that this also goes for the `-O` option (but not `--remote-name-all`). For example: get two files and use `-O` for the first and a custom file name for the second: - curl -O http://example.com/file.txt ftp://example.com/moo.exe -o moo.jpg + curl -O https://example.com/file.txt ftp://example.com/moo.exe -o moo.jpg You can also upload multiple files in a similar fashion: @@ -957,7 +956,7 @@ and fall back to IPv4 if the connection fails. The `--ipv4` and `--ipv6` options can specify which address to use when both are available. IPv6 addresses can also be specified directly in URLs using the syntax: - http://[2001:1890:1112:1::20]/overview.html + https://[2001:1890:1112:1::20]/overview.html When this style is used, the `-g` option must be given to stop curl from interpreting the square brackets as special globbing characters. Link local diff --git a/docs/Makefile.am b/docs/Makefile.am index 554657e889..5530bcf8b7 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -41,90 +41,95 @@ if BUILD_DOCS CLEANFILES = $(MK_CA_DOCS) $(man_MANS) $(TEST_DOCS) endif -TESTDOCS = \ - tests/CI.md \ - tests/FILEFORMAT.md \ - tests/HTTP.md \ - tests/TEST-SUITE.md +TESTDOCS = \ + tests/FILEFORMAT.md \ + tests/HTTP.md \ + tests/TEST-SUITE.md -INTERNALDOCS = \ - internals/BUFQ.md \ - internals/BUFREF.md \ - internals/CHECKSRC.md \ - internals/CLIENT-READERS.md \ - internals/CLIENT-WRITERS.md \ - internals/CODE_STYLE.md \ - internals/CONNECTION-FILTERS.md \ - internals/CURLX.md \ - internals/DYNBUF.md \ - internals/HASH.md \ - internals/LLIST.md \ - internals/MID.md \ - internals/MQTT.md \ - internals/MULTI-EV.md \ - internals/NEW-PROTOCOL.md \ - internals/PORTING.md \ - internals/README.md \ - internals/SCORECARD.md \ - internals/SPLAY.md \ - internals/STRPARSE.md \ - internals/TLS-SESSIONS.md \ - internals/UINT_SETS.md \ - internals/WEBSOCKET.md +INTERNALDOCS = \ + internals/BUFQ.md \ + internals/BUFREF.md \ + internals/CHECKSRC.md \ + internals/CLIENT-READERS.md \ + internals/CLIENT-WRITERS.md \ + internals/CODE_STYLE.md \ + internals/CONNECTION-FILTERS.md \ + internals/CURLX.md \ + internals/DYNBUF.md \ + internals/HASH.md \ + internals/LLIST.md \ + internals/MID.md \ + internals/MQTT.md \ + internals/MULTI-EV.md \ + internals/NEW-PROTOCOL.md \ + internals/PORTING.md \ + internals/RATELIMITS.md \ + internals/README.md \ + internals/SCORECARD.md \ + internals/SPLAY.md \ + internals/STRPARSE.md \ + internals/THRDPOOL-AND-QUEUE.md \ + internals/TIME-KEEPING.md \ + internals/TLS-SESSIONS.md \ + internals/UINT_SETS.md \ + internals/WEBSOCKET.md -EXTRA_DIST = \ - $(CURLPAGES) \ - $(INTERNALDOCS) \ - $(TESTDOCS) \ - ALTSVC.md \ - BINDINGS.md \ - BUG-BOUNTY.md \ - BUGS.md \ - CIPHERS.md \ - CIPHERS-TLS12.md \ - CMakeLists.txt \ - CODE_OF_CONDUCT.md \ - CODE_REVIEW.md \ - CONTRIBUTE.md \ - CURL-DISABLE.md \ - CURLDOWN.md \ - DEPRECATE.md \ - DISTROS.md \ - EARLY-RELEASE.md \ - ECH.md \ - EXPERIMENTAL.md \ - FAQ \ - FEATURES.md \ - GOVERNANCE.md \ - HELP-US.md \ - HISTORY.md \ - HSTS.md \ - HTTP-COOKIES.md \ - HTTP3.md \ - HTTPSRR.md \ - INFRASTRUCTURE.md \ - INSTALL \ - INSTALL-CMAKE.md \ - INSTALL.md \ - INTERNALS.md \ - IPFS.md \ - KNOWN_BUGS \ - MAIL-ETIQUETTE.md \ - MANUAL.md \ - options-in-versions \ - README.md \ - RELEASE-PROCEDURE.md \ - RUSTLS.md \ - ROADMAP.md \ - SECURITY-ADVISORY.md \ - SPONSORS.md \ - SSL-PROBLEMS.md \ - SSLCERTS.md \ - THANKS TODO \ - TheArtOfHttpScripting.md \ - URL-SYNTAX.md \ - VERSIONS.md \ - VULN-DISCLOSURE-POLICY.md +EXTRA_DIST = \ + $(CURLPAGES) \ + $(INTERNALDOCS) \ + $(TESTDOCS) \ + ALTSVC.md \ + BINDINGS.md \ + BUG-BOUNTY.md \ + BUGS.md \ + CIPHERS.md \ + CIPHERS-TLS12.md \ + CMakeLists.txt \ + CODE_OF_CONDUCT.md \ + CODE_REVIEW.md \ + CONTRIBUTE.md \ + CURL-DISABLE.md \ + CURLDOWN.md \ + DEPRECATE.md \ + DISTROS.md \ + EARLY-RELEASE.md \ + ECH.md \ + EXPERIMENTAL.md \ + FAQ.md \ + FEATURES.md \ + GOVERNANCE.md \ + HELP-US.md \ + HISTORY.md \ + HSTS.md \ + HTTP-COOKIES.md \ + HTTP3.md \ + HTTPSRR.md \ + INFRASTRUCTURE.md \ + INSTALL \ + INSTALL-CMAKE.md \ + INSTALL.md \ + INTERNALS.md \ + IPFS.md \ + KNOWN_BUGS.md \ + KNOWN_RISKS.md \ + MAIL-ETIQUETTE.md \ + MANUAL.md \ + options-in-versions \ + README.md \ + RELEASE-PROCEDURE.md \ + RUSTLS.md \ + ROADMAP.md \ + SECURITY-ADVISORY.md \ + SPONSORS.md \ + SSL-PROBLEMS.md \ + SSLCERTS.md \ + THANKS \ + TODO.md \ + TheArtOfHttpScripting.md \ + URL-SYNTAX.md \ + VERIFY.md \ + VERSIONS.md \ + VULN-DISCLOSURE-POLICY.md CD2NROFF = $(top_srcdir)/scripts/cd2nroff $< >$@ diff --git a/docs/RELEASE-PROCEDURE.md b/docs/RELEASE-PROCEDURE.md index e366254996..36c786cbfe 100644 --- a/docs/RELEASE-PROCEDURE.md +++ b/docs/RELEASE-PROCEDURE.md @@ -4,11 +4,9 @@ Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl --> -curl release procedure - how to do a release -============================================ +# curl release procedure - how to do a release -in the source code repo ------------------------ +## in the source code repo - edit `RELEASE-NOTES` to be accurate @@ -30,8 +28,7 @@ in the source code repo - upload the 8 resulting files to the primary download directory -in the curl-www repo --------------------- +## in the curl-www repo - edit `Makefile` (version number and date), @@ -45,13 +42,11 @@ in the curl-www repo (the website then updates its contents automatically) -on GitHub ---------- +## on GitHub - edit the newly made release tag so that it is listed as the latest release -inform ------- +## inform - send an email to curl-users, curl-announce and curl-library. Insert the RELEASE-NOTES into the mail. @@ -60,16 +55,13 @@ inform file to the above lists as well as to `oss-security@lists.openwall.com` (unless the problem is unique to the non-open operating systems) -celebrate ---------- +## celebrate - suitable beverage intake is encouraged for the festivities -curl release scheduling -======================= +# curl release scheduling -Release Cycle -------------- +## Release Cycle We normally do releases every 8 weeks on Wednesdays. If important problems arise, we can insert releases outside the schedule or we can move the release @@ -94,8 +86,7 @@ of common public holidays or when the lead release manager is unavailable, the release date can be moved forwards or backwards a full week. This is then advertised well in advance. -Release Candidates ------------------- +# Release Candidates We ship release candidate tarballs on three occasions in preparation for the pending release: @@ -119,8 +110,7 @@ limited period of time. **Do not use release candidates in production**. They are work in progress. Use them for testing and verification only. Use actual releases in production. -Critical problems ------------------ +# Critical problems We can break the release cycle and do a patch release at any point if a critical enough problem is reported. There is no exact definition of how to @@ -131,16 +121,15 @@ qualify. If you think an issue qualifies, bring it to the curl-library mailing list and push for it. -Coming dates ------------- +# Coming dates Based on the description above, here are some planned future release dates: -- September 10, 2025 -- November 5, 2025 -- January 7, 2026 -- March 4, 2026 +- March 11, 2026 - April 29, 2026 - June 24, 2026 - August 19, 2026 - October 14, 2026 +- December 9, 2026 +- February 3, 2027 +- March 31, 2027 diff --git a/docs/RUSTLS.md b/docs/RUSTLS.md index e46e1d8025..4f904a97e0 100644 --- a/docs/RUSTLS.md +++ b/docs/RUSTLS.md @@ -8,7 +8,7 @@ SPDX-License-Identifier: curl [Rustls is a TLS backend written in Rust](https://docs.rs/rustls/). curl can be built to use it as an alternative to OpenSSL or other TLS backends. We use -the [rustls-ffi C bindings](https://github.com/rustls/rustls-ffi/). This +the [rustls-ffi C bindings](https://github.com/rustls/rustls-ffi). This version of curl is compatible with `rustls-ffi` v0.15.x. ## Getting rustls-ffi diff --git a/docs/SECURITY-ADVISORY.md b/docs/SECURITY-ADVISORY.md index efb0e04934..4f3e1df2c9 100644 --- a/docs/SECURITY-ADVISORY.md +++ b/docs/SECURITY-ADVISORY.md @@ -33,11 +33,11 @@ pipe character (`|`). The eleven fields for each CVE in `vuln.pm` are, in order: - HTML page name, first vulnerable version, last vulnerable version, name of - the issue, CVE Id, announce date (`YYYYMMDD`), report to the project date - (`YYYYMMDD`), CWE, awarded reward amount (USD), area (single word), C-issue - (`-` if not a C issue at all, `OVERFLOW` , `OVERREAD`, `DOUBLE_FREE`, - `USE_AFTER_FREE`, `NULL_MISTAKE`, `UNINIT`) +HTML page name, first vulnerable version, last vulnerable version, name of +the issue, CVE Id, announce date (`YYYYMMDD`), report to the project date +(`YYYYMMDD`), CWE, awarded reward amount (USD), area (single word), C-issue +(`-` if not a C issue at all, `OVERFLOW` , `OVERREAD`, `DOUBLE_FREE`, +`USE_AFTER_FREE`, `NULL_MISTAKE`, `UNINIT`) ### `Makefile` @@ -50,9 +50,9 @@ generated automatically using those files. ## Document format -The easy way is to start with a recent previously published advisory and just -blank out old texts and save it using a new name. Save the subtitles and -general layout. +The easy way is to start with a recent previously published advisory and blank +out old texts and save it using a new name. Save the subtitles and general +layout. Some details and metadata are extracted from this document so it is important to stick to the existing format. diff --git a/docs/SPONSORS.md b/docs/SPONSORS.md index e4f61db6a2..dc9d26fe6b 100644 --- a/docs/SPONSORS.md +++ b/docs/SPONSORS.md @@ -17,8 +17,8 @@ two to spend work hours on curl related tasks. We promise to use donated funds for things and activities that we believe are beneficial for the project and its development. That includes but is not -limited to bug-bounties, developer conferences, infrastructure, development, -services and hardware. +limited to developer conferences, infrastructure, development, services and +hardware. Recurring donations above a certain amount of money puts the sponsor at a named sponsor level: **Silver**, **Gold**, **Platinum** or **Top**. diff --git a/docs/SSL-PROBLEMS.md b/docs/SSL-PROBLEMS.md index 301ce7e870..4809f84a97 100644 --- a/docs/SSL-PROBLEMS.md +++ b/docs/SSL-PROBLEMS.md @@ -6,92 +6,92 @@ SPDX-License-Identifier: curl # SSL problems - First, let's establish that we often refer to TLS and SSL interchangeably as - SSL here. The current protocol is called TLS, it was called SSL a long time - ago. +First, let's establish that we often refer to TLS and SSL interchangeably as +SSL here. The current protocol is called TLS, it was called SSL a long time +ago. - There are several known reasons why a connection that involves SSL might - fail. This is a document that attempts to detail the most common ones and - how to mitigate them. +There are several known reasons why a connection that involves SSL might +fail. This is a document that attempts to detail the most common ones and +how to mitigate them. ## CA certs - CA certs are used to digitally verify the server's certificate. You need a - "ca bundle" for this. See lots of more details on this in the `SSLCERTS` - document. +CA certs are used to digitally verify the server's certificate. You need a +"ca bundle" for this. See lots of more details on this in the `SSLCERTS` +document. ## CA bundle missing intermediate certificates - When using said CA bundle to verify a server cert, you may experience - problems if your CA store does not contain the certificates for the - intermediates if the server does not provide them. +When using said CA bundle to verify a server cert, you may experience +problems if your CA store does not contain the certificates for the +intermediates if the server does not provide them. - The TLS protocol mandates that the intermediate certificates are sent in the - handshake, but as browsers have ways to survive or work around such - omissions, missing intermediates in TLS handshakes still happen that browser - users do not notice. +The TLS protocol mandates that the intermediate certificates are sent in the +handshake, but as browsers have ways to survive or work around such +omissions, missing intermediates in TLS handshakes still happen that browser +users do not notice. - Browsers work around this problem in two ways: they cache intermediate - certificates from previous transfers and some implement the TLS "AIA" - extension that lets the client explicitly download such certificates on - demand. +Browsers work around this problem in two ways: they cache intermediate +certificates from previous transfers and some implement the TLS "AIA" +extension that lets the client explicitly download such certificates on +demand. ## Protocol version - Some broken servers fail to support the protocol negotiation properly that - SSL servers are supposed to handle. This may cause the connection to fail - completely. Sometimes you may need to explicitly select an SSL version to - use when connecting to make the connection succeed. +Some broken servers fail to support the protocol negotiation properly that +SSL servers are supposed to handle. This may cause the connection to fail +completely. Sometimes you may need to explicitly select an SSL version to +use when connecting to make the connection succeed. - An additional complication can be that modern SSL libraries sometimes are - built with support for older SSL and TLS versions disabled. +An additional complication can be that modern SSL libraries sometimes are +built with support for older SSL and TLS versions disabled. - All versions of SSL and the TLS versions before 1.2 are considered insecure - and should be avoided. Use TLS 1.2 or later. +All versions of SSL and the TLS versions before 1.2 are considered insecure +and should be avoided. Use TLS 1.2 or later. ## Ciphers - Clients give servers a list of ciphers to select from. If the list does not - include any ciphers the server wants/can use, the connection handshake - fails. +Clients give servers a list of ciphers to select from. If the list does not +include any ciphers the server wants/can use, the connection handshake +fails. - curl has recently disabled the user of a whole bunch of seriously insecure - ciphers from its default set (slightly depending on SSL backend in use). +curl has recently disabled the user of a whole bunch of seriously insecure +ciphers from its default set (slightly depending on SSL backend in use). - You may have to explicitly provide an alternative list of ciphers for curl - to use to allow the server to use a weak cipher for you. +You may have to explicitly provide an alternative list of ciphers for curl +to use to allow the server to use a weak cipher for you. - Note that these weak ciphers are identified as flawed. For example, this - includes symmetric ciphers with less than 128 bit keys and RC4. +Note that these weak ciphers are identified as flawed. For example, this +includes symmetric ciphers with less than 128-bit keys and RC4. - Schannel in Windows XP is not able to connect to servers that no longer - support the legacy handshakes and algorithms used by those versions, so we - advise against building curl to use Schannel on really old Windows versions. +Schannel in Windows XP is not able to connect to servers that no longer +support the legacy handshakes and algorithms used by those versions, so we +advise against building curl to use Schannel on really old Windows versions. - Reference: [Prohibiting RC4 Cipher - Suites](https://datatracker.ietf.org/doc/html/draft-popov-tls-prohibiting-rc4-01) +Reference: [Prohibiting RC4 Cipher +Suites](https://datatracker.ietf.org/doc/html/draft-popov-tls-prohibiting-rc4-01) ## Allow BEAST - BEAST is the name of a TLS 1.0 attack that surfaced 2011. When adding means - to mitigate this attack, it turned out that some broken servers out there in - the wild did not work properly with the BEAST mitigation in place. +BEAST is the name of a TLS 1.0 attack that surfaced 2011. When adding means +to mitigate this attack, it turned out that some broken servers out there in +the wild did not work properly with the BEAST mitigation in place. - To make such broken servers work, the --ssl-allow-beast option was - introduced. Exactly as it sounds, it re-introduces the BEAST vulnerability - but on the other hand it allows curl to connect to that kind of strange - servers. +To make such broken servers work, the --ssl-allow-beast option was +introduced. Exactly as it sounds, it re-introduces the BEAST vulnerability +but on the other hand it allows curl to connect to that kind of strange +servers. ## Disabling certificate revocation checks - Some SSL backends may do certificate revocation checks (CRL, OCSP, etc) - depending on the OS or build configuration. The --ssl-no-revoke option was - introduced in 7.44.0 to disable revocation checking but currently is only - supported for Schannel (the native Windows SSL library), with an exception - in the case of Windows' Untrusted Publishers block list which it seems cannot - be bypassed. This option may have broader support to accommodate other SSL - backends in the future. +Some SSL backends may do certificate revocation checks (CRL, OCSP, etc) +depending on the OS or build configuration. The --ssl-no-revoke option was +introduced in 7.44.0 to disable revocation checking but currently is only +supported for Schannel (the native Windows SSL library), with an exception +in the case of Windows' Untrusted Publishers block list which it seems cannot +be bypassed. This option may have broader support to accommodate other SSL +backends in the future. - References: +References: - https://curl.se/docs/ssl-compared.html +https://curl.se/docs/ssl-compared.html diff --git a/docs/SSLCERTS.md b/docs/SSLCERTS.md index 4efb9cf00b..3506fbd787 100644 --- a/docs/SSLCERTS.md +++ b/docs/SSLCERTS.md @@ -8,8 +8,10 @@ SPDX-License-Identifier: curl ## Native vs file based -If curl was built with Schannel support, then curl uses the system native CA -store for verification. All other TLS libraries use a file based CA store by +If curl was built with Schannel support, then curl uses the Windows native CA +store for verification. On Apple operating systems, it is possible to use Apple's +"SecTrust" services for certain TLS backends, details below. +All other TLS libraries use a file based CA store by default. ## Verification @@ -71,8 +73,10 @@ another option to restrict search to the application's directory. ### Use the native store -In several environments, in particular on Windows, you can ask curl to use the -system's native CA store when verifying the certificate. +In several environments, in particular on Microsoft and Apple operating +systems, you can ask curl to use the system's native CA store when verifying +the certificate. Depending on how curl was built, this may already be the +default. With the curl command line tool: `--ca-native`. @@ -102,14 +106,45 @@ latest Firefox bundle. ## Native CA store -If curl was built with Schannel or was instructed to use the native CA Store, -then curl uses the certificates that are built into the OS. These are the same -certificates that appear in the Internet Options control panel (under Windows) -or Keychain Access application (under macOS). Any custom security rules for -certificates are honored. +### Windows + Schannel + +If curl was built with Schannel, then curl uses the certificates that are +built into the OS. These are the same certificates that appear in the +Internet Options control panel (under Windows). +Any custom security rules for certificates are honored. Schannel runs CRL checks on certificates unless peer verification is disabled. +### Apple + OpenSSL/GnuTLS + +When curl is built with Apple SecTrust enabled and uses an OpenSSL compatible +TLS backend or GnuTLS, the default verification is handled by that Apple +service. As in: + + curl https://example.com + +You may still provide your own certificates on the command line, such as: + + curl --cacert mycerts.pem https://example.com + +In this situation, Apple SecTrust is **not** used and verification is done +**only** with the trust anchors found in `mycerts.pem`. If you want **both** +Apple SecTrust and your own file to be considered, use: + + curl --ca-native --cacert mycerts.pem https://example.com + +#### Other Combinations + +How well the use of native CA stores work in all other combinations depends +on the TLS backend and the OS. Many TLS backends offer functionality to access +the native CA on a range of operating systems. Some provide this only on specific +configurations. + +Specific support in curl exists for Windows and OpenSSL compatible TLS backends. +It tries to load the certificates from the Windows "CA" and "ROOT" stores for +transfers requesting the native CA. Due to Window's delayed population of those +stores, this might not always find all certificates. + ## HTTPS proxy curl can do HTTPS to the proxy separately from the connection to the server. diff --git a/docs/THANKS b/docs/THANKS index b5a838f57d..08c50b123f 100644 --- a/docs/THANKS +++ b/docs/THANKS @@ -55,6 +55,7 @@ afrind on github Aftab Alam Ahmad Gani ahodesuka on github +aisle-research-bot ajak in #curl Ajit Dhumale Akhilesh Nema @@ -79,6 +80,8 @@ Alejandro R. Sedeño Aleksandar Milivojevic Aleksander Mazur Aleksandr Krotov +Aleksandr Sergeev +Aleksei Bavshin Aleksey Tulinov alervd on github Ales Mlakar @@ -87,7 +90,9 @@ Alessandro Ghedini Alessandro Vesely Alex aka WindEagle Alexander Bartel +Alexander Batischev Alexander Beedie +Alexander Blach Alexander Chuykov Alexander Dyagilev Alexander Elgert @@ -219,8 +224,10 @@ Andrew Kaster Andrew Kirillov Andrew Krieger Andrew Kurushin +Andrew Kvalheim Andrew Lambert Andrew Moise +Andrew Olsen Andrew Potter Andrew Robbins Andrew Wansink @@ -238,10 +245,13 @@ Andy Reitz Andy Serpa Andy Stamp Andy Tsouladze +And-yW on github Angus Mackay anio on github annalee +Anna Liberty anon00000000 on github +anonymous237 on hackerone anshnd on github Anssi Kolehmainen Antarpreet Singh @@ -282,6 +292,8 @@ Armel Asselin Arnaud Compan Arnaud Ebalard Arnaud Rebillout +Arnav Purushotam +Arnav-Purushotam-CUBoulder Arne Soete Aron Bergman Aron Rotteveel @@ -300,6 +312,7 @@ Ask Bjørn Hansen AtariDreams on github Ates Goral atjg on github +Augment code Augustus Saunders Aurélien Pierre Austin Green @@ -324,6 +337,7 @@ Balakrishnan Balasubramanian Balazs Kovacsics balikalina on github Balint Szilakszi +BANADDA baranyaib90 on github Barry Abrahamson Barry Pollard @@ -358,7 +372,7 @@ Benjamin Sergeant Ben Kohler Ben Madsen Ben Noordhuis -Benoit Neil +Benoit Neil (Sukender) Benoit Pierre Benoit Sigoure Ben Van Hof @@ -387,6 +401,7 @@ billionai on github Bill Middlecamp Bill Nagel Bill Pyne +Billy O'Neal Billyzou0741326 on github Bin Lan Bin Meng @@ -411,7 +426,9 @@ Bob Richmond Bob Schader Bodo Bergmann Bogdan Nicula +BohwaZ boilingoden +boingball Boris Kuschel Boris Okunskiy Boris Rasin @@ -472,6 +489,7 @@ Bryan Kemp bsammon on github bsergean on github bsr13 on hackerone +bttrfl on github bubbleguuum on github Bubu on github buzo-ffm on github @@ -480,6 +498,7 @@ Bylon2 on github Byrial Jensen Cajus Pollmeier Caleb Raitto +calm329 calvin2021y on github Calvin Buckley Calvin Ruocco @@ -560,6 +579,7 @@ Christian Krause Christian Kurz Christian Robottom Reis Christian Schmitz +Christian Schmitza Christian Stewart Christian Vogt Christian Weisgerber @@ -619,6 +639,7 @@ Colm Buckley Colton Willey Constantine Sapuntzakis consulion on github +cooldadpresident on github coralw on github Corinna Brandt correctmost on github @@ -636,6 +657,7 @@ Cristian Greco Cristian Morales Vega Cristian Rodríguez CueXXIII on github +curl.stunt430 Curt Bogmine Cynthia Coan Cyril B @@ -646,9 +668,12 @@ d912e3 on github daboul on github Dag Ekengren Dagfinn Ilmari Mannsåker +Dag Haavi Finstad Dagobert Michelsen +dahmono on github Daiki Ueno Dair Grant +Dalei Dambaev Alexander Damian Dixon Damien Adant @@ -665,6 +690,7 @@ Daniel Black Daniel Böhmer Daniel Carpenter Daniel Cater +Daniel Díaz Daniel Egger Daniel Engberg Daniel Faust @@ -687,6 +713,7 @@ Daniel Melani Daniel Mentz Daniel Pouzzner Daniel Romero +Daniel Santos Daniel Schauenberg Daniel Seither Daniel Shahaf @@ -694,9 +721,12 @@ Daniel Silverstone Daniel Steinberg Daniel Stenberg Daniel Szmulewicz +Daniel Terhorst-North Daniel Theron Daniel Valenzuela +Daniel Wade Daniel Woelfel +Daniil Gentili Dan Johnson Dan Kenigsberg Dan Locks @@ -752,6 +782,7 @@ David J Meyer David Kalnischkies David Kierznowski David Kimdon +David Korczynski David L. David Lang David LeBlanc @@ -777,7 +808,9 @@ David Wright David Yan David Zhuang Da-Yoon Chung +dbalsom dbrowndan on github +dEajL3kA dEajL3kA on github Deal(一线灵) defnull @@ -794,6 +827,7 @@ Denis Feklushkin Denis Goleshchikhin Denis Laxalde Denis Ollier +Deniz Parlak Deniz Sökmen Dennis Clarke Dennis Felsing @@ -804,6 +838,7 @@ Derzsi Dániel Desmond O. Chang destman on github Detlef Schmier +Devdatta Talele devgs on github Dexter Gerig dfdity on github @@ -823,6 +858,7 @@ Dimitrios Apostolou Dimitrios Siganos Dimitris Sarris Dinar +Diogo Correia Diogo Teles Sant'Anna Dion Williams Dirk Eddelbuettel @@ -948,10 +984,12 @@ Emil Engler Emiliano Ida Emilio Cobos Álvarez Emilio López +Emilio Pozuelo Monfort Emil Lerner Emil Österlund Emil Romanus Emmanuel Tychon +Emre Çalışkan Enno Boland Enrico Scholz Enrik Berkhan @@ -1036,6 +1074,7 @@ Fata Nugraha Fawad Mirza Fay Stegerman FC Stegerman +Fd929c2CE5fA on github fds242 on github Federico Bianchi Federico Pellegrin @@ -1051,15 +1090,18 @@ Felix von Leitner Felix Yan Feng Tu Fernando Muñoz +ffath-vo on github Filip Lundgren Filip Salomonsson finkjsc on github Firefox OS +Fizn-Ahmd on github fjaell on github Flameborn on github Flavio Medeiros Florian Eckert Florian Friedrich +Florian Imdahl Florian Kohnhäuser Florian Pritz Florian Schoppmann @@ -1076,6 +1118,7 @@ François Michel Francois Petitjean François Rigault Francois Rivard +Frank Buss Frank Denis Frank Gevaerts Frank Hempel @@ -1130,6 +1173,7 @@ George Liu Georg Horn Georg Huettenegger Georg Lippitsch +Georg Schulz-Allgaier Georg Wicherski Gerd v. Egidy Gergely Nagy @@ -1178,6 +1222,7 @@ Grant Erickson Grant Pannell graywolf on github Greg Hewgill +Greg Hudson Greg Morse Greg Onufer Gregor Jasny @@ -1193,6 +1238,7 @@ Griffin Downs Grigory Entin Grisha Levit Gruber Glass +gudyuu on hackerone Guenole Bescon Guido Berhoerster Guilherme Puida @@ -1200,6 +1246,7 @@ Guillaume Algis Guillaume Arluison guitared on github Gunamoi Software +Gunni on github Gunter Knauf guoxinvmware on github Gustaf Hui @@ -1217,6 +1264,7 @@ Hakan Sunay Halil Hamish Mackenzie hammlee96 on github hamstergene on github +Hamza Bensliman Hang Kin Lau Hang Su Han Han @@ -1258,6 +1306,7 @@ Henrik Storner Henry Ludemann Henry Roeland He Qin +herdiyanitdev on hackerone Hermes Zhang Herve Amblard HexTheDragon @@ -1280,10 +1329,13 @@ Howard Chu hsiao yi HsiehYuho on github htasta on github +huanghuihui0904 Hubert Kario Hugh Macdonald Hugo van Kemenade humbleacolyte +Hunt Darlener +Huseyin Tintas Huzaifa Sidhpurwala huzunhao on github hydra3333 on github @@ -1303,6 +1355,7 @@ IcedCoffeee on github iconoclasthero icy17 on github Ignacio Vazquez-Abrams +Ignat Loskutov Igor Franchuk Igor Khristophorov Igor Makarov @@ -1338,6 +1391,7 @@ Isaac Boukris Isaiah Norton Ishan SinghLevett İsmail Dönmez +Itay Bookstein Ithubg on github Ivan Ivan Avdeev @@ -1347,6 +1401,7 @@ IvanoG on github Ivan Tsybulin Ivo Bellin Salarin iz8mbw on github +Jacek Migacz Jackarain on github JackBoosY on github Jack Boos Yu @@ -1367,6 +1422,7 @@ jakirkham on github Jakob Hirsch Jakub Bochenski Jakub Jelen +Jakub Stasiak Jakub Wilk Jakub Zakrzewski James Abbatiello @@ -1499,10 +1555,12 @@ Jesse Tan Jess Lowe Jesus Malo Poyatos jethrogb on github +jhauga jhoyla on github Jiacai Liu Jiang Wenjian Jiawen Geng +Jicea Jie He Jiehong on github Jilayne Lovejoy @@ -1523,7 +1581,9 @@ Jiri Stary Jishan Shaikh Jiwoo Park Jixinqi +Jiyong Yang jkamp-aws on github +jmaggard10 on github jmdavitt on github jnbr on github Jocelyn Jaubert @@ -1583,6 +1643,7 @@ John McGowan Johnny Luong John P. McCaskey John Porter +John Rodriguez John Schroeder John Sherrill John Simpson @@ -1635,6 +1696,7 @@ Jose Alf Josef Wolf José Joaquín Atria Jose Kahan +Joseph Birr-Pixton Joseph Chen Joseph Tharayil Josh Bialkowski @@ -1645,13 +1707,16 @@ Josh Kapell Josh McCullough Josh Soref Joshua Kwan +Joshua Rogers Joshua Root Joshua Swink +Joshua Vandaële Josie Huddleston Josip Medved Josue Andrade Gomes Jozef Kralik Juan Barreto +Juan Belon Juan Cruz Viotti Juan F. Codagnone Juan Ignacio Hervás @@ -1709,6 +1774,7 @@ Kane York Kang-Jin Lee Kang Lin Kantanat Wannapaka +kapsiR on github Kareem Kari Pahula Karl Chen @@ -1800,6 +1866,7 @@ Kristoffer Gleditsch kriztalz K. R. Walker Kuan-Wei Chiu +kuchara on github Kunal Chandarana Kunal Ekawde kupavcevdenis on github @@ -1948,6 +2015,7 @@ Maciej Puzio Maciej W. Rozycki MacKenzie madblobfish on github +madoe on github MaeIsBad on github magisterquis on hackerone Mahmoud Samir Fayed @@ -2082,6 +2150,7 @@ Massimo Callegari MasterInQuestion on github Master Inspire Mateusz Loskot +Mathesh V Mathew Benson Mathias Axelsson Mathias Fuchs @@ -2142,6 +2211,7 @@ Max Zettlmeißl mbeifuss on github mccormickt12 on github Median Median Stride +Megamouse on github mehatzri on github Mehmet Bozkurt Mekonikum @@ -2241,6 +2311,7 @@ Mingtao Yang Miroslav Franc Miroslav Spousta Mischa Salle +Mitchell Blank Jr Mitz Wark mkzero on github modbw on github @@ -2269,6 +2340,7 @@ MrdUkk on github MrSorcus on github M.R.T on github mschroeder-fzj on github +Muhamad Arga Reksapati Muhammad Herdiansyah Muhammad Hussein Ammari Muhammed Yavuz Nuzumlalı @@ -2279,6 +2351,7 @@ Myk Taylor n0name321 on github Nach M. S. Nagai H +nait-furry naost3rn on github Nao Yonashiro Natanael Copa @@ -2288,12 +2361,14 @@ Nathan Coulter Nathaniel J. Smith Nathaniel R. Lewis Nathaniel Waisbrot +Nathan-M-code on github Nathan Moinvaziri Nathan O'Sullivan Natris on github na-trium-144 on github Naveen Chandran Naveen Noel +ncaklovic on github Neal McBurnett Neal Poole nedres on github @@ -2325,6 +2400,7 @@ Nick Coghlan Nick Draffen Nick Gimbrone Nick Humfrey +Nick Korepanov Nicklas Avén Nick Miyake nick-telia on github @@ -2363,6 +2439,7 @@ Ning Dong NINIKA Niracler Li Niranjan Hasabnis +Nir Azkiel Nir Soffer Nis Jorgensen nk @@ -2379,6 +2456,7 @@ norbertmm on github Norbert Novotny nosajsnikta on github NTMan on github +Nuno Goncalves Nuru on github Octavio Schroeder odek86 on github @@ -2408,6 +2486,7 @@ Olivier Bonaventure Olivier Brunel Omar Ramadan omau on github +Omdahake on github Ondřej Hlavatý Ondřej Koláček opensignature on github @@ -2489,6 +2568,7 @@ Pavel Kropachev Pavel Löbl Pavel Mayorov Pavel Orehov +Pavel P Pavel Pavlov Pavel Raiskup Pavel Rochnyak @@ -2504,9 +2584,11 @@ Pedro Henrique Pedro Larroy Pedro Monreal Pedro Neves +pelioro on hackerone pendrek at hackerone Peng Li Peng-Yu Chen +pennae on github Per Jensen Per Lundberg Per Malmberg @@ -2586,7 +2668,10 @@ Piotr Dobrogost Piotr Komborski Piotr Nakraszewicz PleaseJustDont +plv1313 on github Po-Chuan Hsieh +Pocs Norbert +pojomi Pontakorn Prasertsuk Pontus Lundkvist Pooyan McSporran @@ -2598,6 +2683,7 @@ Prithvi MK privetryan on github Priyanka Shah ProceduralMan on github +programmerlexi on github promptfuzz_ on hackerone Pronyushkin Petr prpr19xx on github @@ -2738,6 +2824,7 @@ Rider Linden Rikard Falkeborn rilysh Rinku Das +rinsuki on github rl1987 on github rmg-x on github rm-rmonaghan on github @@ -2769,6 +2856,7 @@ Robert Simpson Robert Southee Robert Weaver Robert Wruck +Robert W. Van Kirk Robin A. Meade Robin Cornelius Robin Douine @@ -2843,6 +2931,7 @@ rzrymiak on github s0urc3_ on hackerone saimen Sai Ram Kunala +Sakthi SK Salah-Eddin Shaban Saleem Abdulrasool SaltyMilk @@ -2853,6 +2942,7 @@ Sam Deane Sam Hurst Sam James Sam Jessup +sammydono on github Sampo Kellomaki Sam Roth Sam Schanken @@ -2876,6 +2966,7 @@ Saqib Ali Sara Golemon Sarah Gooding Saran Neti +Sascha Frinken Sascha Swiercy Sascha Zengler Satadru Pramanik @@ -2924,6 +3015,7 @@ Sergey Sergey Alirzaev Sergey Bronnikov Sergey Fionov +Sergey Katsubo Sergey Markelov Sergey Ogryzkov Sergey Ryabinin @@ -2998,6 +3090,8 @@ Sonia Subramanian Sören Tempel southernedge on github Spacen Jasset +spectreglobalsec on hackerone +Spenser Black Spezifant on github Spiridonoff A.V Spoon Man @@ -3005,8 +3099,10 @@ Spork Schivago ssdbest on github sspiri on github sstruchtrup on github +st751228051 on github Stadler Stephan Stan Hu +Stanislav Fort Stanislav Ivochkin Stanislav Lange Stanislav Osipov @@ -3082,6 +3178,7 @@ Stuart Henderson Sukanya Hanumanthu SumatraPeter on github Sune Ahlgren +Sunny Sunny Bean Sunny Purushe sunriseL @@ -3116,18 +3213,23 @@ Ted Lyngmo Teemu Yli-Elsila Teh Kok How Temprimus +Tenant HellTower Terence Eden Terri Oda Terry Wu +Tetetest thanhchungbtc on github TheAssassin on github +TheBitBrine The Infinnovation team TheKnarf on github Theo +Theo Buehler Theodore A. Roth Theodore Dubois therealhirudo on github Thiago Suchorski +Thibault de Villèle thisisgk on github tholin on github Thomas @@ -3162,8 +3264,10 @@ Till Wegmüller Tim Ansell Tim Baker Tim Bartley +Tim Becker Tim Chen Tim Costello +Tim Friedrich Brüggemann Tim Harder Tim Heckman Tim Hill @@ -3208,6 +3312,7 @@ Tobias Rundström Tobias Schaefer Tobias Stoeckmann Tobias Wendorff +Tobias Zimmermann Toby Peterson Todd A Ouska Todd Gamblin @@ -3218,6 +3323,7 @@ Todd Vierling Tomas Berger Tomas Hoger Tomas Jakobsson +Tomáš Malý Tomas Mlcoch Tomas Mraz Tomas Pospisek @@ -3238,6 +3344,7 @@ Tommie Gannert tommink[at]post.pl Tom Moers Tom Mueller +tommy Tommy Chiang Tommy Odom Tommy Petty @@ -3245,6 +3352,7 @@ Tommy Tam Tom Regner Tom Seddon Tom Sparrow +Tom St Denis Tom van der Woerdt Tom Wright tomy2105 on github @@ -3275,6 +3383,7 @@ Troels Walsted Hansen Troy Engel trrui-huawei Trumeet on github +trxvorr Trzik on github Tseng Jun Tuomas Siipola @@ -3295,10 +3404,12 @@ UrsusArctos on github User Sg ustcqidi on github Vadim Grinshpun +Vaibhav Kumar Valentin David Valentín Gutiérrez Valentin Richter Valentyn Korniienko +Valerie Snyder Valerii Zapodovnikov vanillajonathan on github Varnavas Papaioannou @@ -3317,6 +3428,7 @@ VictorVG on github Victor Vieux Vijay Panghal Vikram Saxena +Viktor Dukhovni Viktor Petersson Viktor Szakats Vilhelm Prytz @@ -3361,6 +3473,7 @@ w0x42 on hackerone Waldek Kozba Waldemar Kornewald Walter J. Mack +WangDaLei on github wangzhikun Ward Willats Warren Menzer @@ -3375,6 +3488,7 @@ Wes Hinsley wesinator on github Wesley Laxton Wesley Miaw +Wesley Moore Wez Furlong Wham Bang Wilfredo Sanchez @@ -3400,6 +3514,7 @@ Wu Zheng wxiaoguang on github Wyatt O'Day Wyatt OʼDay +Wyuer on github x1sc0 on github x2018 on github Xavier Bouchoux @@ -3417,6 +3532,7 @@ Xiaoyin Liu Xì Gà Xi Ruoyao XmiliaH on github +xmoezzz on github xnynx on github xtonik on github xwxbug on github @@ -3449,6 +3565,7 @@ Yoshimasa Ohno Younes El-karama youngchopin on github Yousuke Kimoto +Yuhao Jiang Yukihiro Kawada Yun SangHo Yurii Rashkovskii @@ -3480,6 +3597,7 @@ Zhaoyang Wu Zhao Yisha zhengqwe on github Zhibiao Wu +Zhicheng Chen zhihaoy on github Zhouyihai Ding ZimCodes on github @@ -3491,12 +3609,14 @@ zopsicle on github Zvi Har'El zzq1015 on github Борис Верховский +Йоте Коваленко Анатолий Викторович наб Никита Дорохин ウさん 不确定 加藤郁之 +包布丁 南宫雪珊 左潇峰 李四 diff --git a/docs/THANKS-filter b/docs/THANKS-filter index fd8722da8b..cc964a49b2 100644 --- a/docs/THANKS-filter +++ b/docs/THANKS-filter @@ -160,3 +160,5 @@ s/jethrogb$/jethrogb on github/ s/on github/on github/i s/Maksim Sciepanienka/Maksim Ściepanienka/ s/Qriist.*/Qriist on github/ +s/Viktor Szakatas/Viktor Szakats/ +s/Val S\./Valerie Snyder/ diff --git a/docs/TODO b/docs/TODO deleted file mode 100644 index 1e22814f38..0000000000 --- a/docs/TODO +++ /dev/null @@ -1,1345 +0,0 @@ - _ _ ____ _ - ___| | | | _ \| | - / __| | | | |_) | | - | (__| |_| | _ <| |___ - \___|\___/|_| \_\_____| - - Things that could be nice to do in the future - - Things to do in project curl. Please tell us what you think, contribute and - send us patches that improve things. - - Be aware that these are things that we could do, or have once been considered - things we could do. If you want to work on any of these areas, please - consider bringing it up for discussions first on the mailing list so that we - all agree it is still a good idea for the project. - - All bugs documented in the KNOWN_BUGS document are subject for fixing. - - 1. libcurl - 1.1 TFO support on Windows - 1.2 Consult %APPDATA% also for .netrc - 1.3 struct lifreq - 1.4 alt-svc sharing - 1.5 get rid of PATH_MAX - 1.6 thread-safe sharing - 1.8 CURLOPT_RESOLVE for any port number - 1.10 auto-detect proxy - 1.11 minimize dependencies with dynamically loaded modules - 1.12 updated DNS server while running - 1.13 c-ares and CURLOPT_OPENSOCKETFUNCTION - 1.14 connect to multiple IPs in parallel - 1.15 Monitor connections in the connection pool - 1.16 Try to URL encode given URL - 1.17 Add support for IRIs - 1.18 try next proxy if one does not work - 1.19 provide timing info for each redirect - 1.20 SRV and URI DNS records - 1.21 netrc caching and sharing - 1.22 CURLINFO_PAUSE_STATE - 1.23 Offer API to flush the connection pool - 1.25 Expose tried IP addresses that failed - 1.28 FD_CLOEXEC - 1.29 WebSocket read callback - 1.30 config file parsing - 1.31 erase secrets from heap/stack after use - 1.32 add asynch getaddrinfo support - 1.33 make DoH inherit more transfer properties - - 2. libcurl - multi interface - 2.1 More non-blocking - 2.2 Better support for same name resolves - 2.3 Non-blocking curl_multi_remove_handle() - 2.4 Split connect and authentication process - 2.5 Edge-triggered sockets should work - 2.6 multi upkeep - 2.7 Virtual external sockets - 2.8 dynamically decide to use socketpair - - 3. Documentation - 3.1 Improve documentation about fork safety - - 4. FTP - 4.1 HOST - 4.6 GSSAPI via Windows SSPI - 4.7 STAT for LIST without data connection - 4.8 Passive transfer could try other IP addresses - - 5. HTTP - 5.1 Provide the error body from a CONNECT response - 5.2 Obey Retry-After in redirects - 5.3 Rearrange request header order - 5.4 Allow SAN names in HTTP/2 server push - 5.5 auth= in URLs - 5.6 alt-svc should fallback if alt-svc does not work - 5.7 Require HTTP version X or higher - - 6. TELNET - 6.1 ditch stdin - 6.2 ditch telnet-specific select - 6.3 feature negotiation debug data - 6.4 exit immediately upon connection if stdin is /dev/null - - 7. SMTP - 7.1 Passing NOTIFY option to CURLOPT_MAIL_RCPT - 7.2 Enhanced capability support - 7.3 Add CURLOPT_MAIL_CLIENT option - - 8. POP3 - 8.2 Enhanced capability support - - 9. IMAP - 9.1 Enhanced capability support - - 10. LDAP - 10.1 SASL based authentication mechanisms - 10.2 CURLOPT_SSL_CTX_FUNCTION for LDAPS - 10.3 Paged searches on LDAP server - 10.4 Certificate-Based Authentication - - 11. SMB - 11.1 File listing support - 11.2 Honor file timestamps - 11.3 Use NTLMv2 - 11.4 Create remote directories - - 12. FILE - 12.1 Directory listing on non-POSIX - - 13. TLS - 13.1 TLS-PSK with OpenSSL - 13.2 TLS channel binding - 13.3 Defeat TLS fingerprinting - 13.4 Consider OCSP stapling by default - 13.6 Provide callback for cert verification - 13.7 Less memory massaging with Schannel - 13.8 Support DANE - 13.9 TLS record padding - 13.10 Support Authority Information Access certificate extension (AIA) - 13.11 Some TLS options are not offered for HTTPS proxies - 13.13 Make sure we forbid TLS 1.3 post-handshake authentication - 13.14 Support the clienthello extension - 13.16 Share the CA cache - 13.17 Add missing features to TLS backends - - 14. Proxy - 14.1 Retry SOCKS handshake on address type not supported - - 15. Schannel - 15.1 Extend support for client certificate authentication - 15.2 Extend support for the --ciphers option - 15.4 Add option to allow abrupt server closure - - 16. SASL - 16.1 Other authentication mechanisms - 16.2 Add QOP support to GSSAPI authentication - - 17. SSH protocols - 17.1 Multiplexing - 17.2 Handle growing SFTP files - 17.3 Read keys from ~/.ssh/id_ecdsa, id_ed25519 - 17.4 Support CURLOPT_PREQUOTE - 17.5 SSH over HTTPS proxy with more backends - 17.6 SFTP with SCP:// - - 18. Command line tool - 18.1 sync - 18.2 glob posts - 18.4 --proxycommand - 18.5 UTF-8 filenames in Content-Disposition - 18.6 Option to make -Z merge lined based outputs on stdout - 18.7 specify which response codes that make -f/--fail return error - 18.9 Choose the name of file in braces for complex URLs - 18.10 improve how curl works in a Windows console window - 18.11 Windows: set attribute 'archive' for completed downloads - 18.12 keep running, read instructions from pipe/socket - 18.13 Acknowledge Ratelimit headers - 18.14 --dry-run - 18.15 --retry should resume - 18.17 consider filename from the redirected URL with -O ? - 18.18 retry on network is unreachable - 18.20 hostname sections in config files - 18.21 retry on the redirected-to URL - 18.23 Set the modification date on an uploaded file - 18.24 Use multiple parallel transfers for a single download - 18.25 Prevent terminal injection when writing to terminal - 18.26 Custom progress meter update interval - 18.27 -J and -O with %-encoded filenames - 18.28 -J with -C - - 18.29 --retry and transfer timeouts - - 19. Build - 19.2 Enable PIE and RELRO by default - 19.3 Do not use GNU libtool on OpenBSD - 19.4 Package curl for Windows in a signed installer - 19.5 make configure use --cache-file more and better - - 20. Test suite - 20.1 SSL tunnel - 20.2 more protocols supported - 20.3 more platforms supported - 20.4 write an SMB test server to replace impacket - 20.5 Use the RFC 6265 test suite - 20.6 Run web-platform-tests URL tests - - 21. MQTT - 21.1 Support rate-limiting - 21.2 Support MQTTS - 21.3 Handle network blocks - - 22. TFTP - 22.1 TFTP does not convert LF to CRLF for mode=netascii - - 23. Gopher - 23.1 Handle network blocks - -============================================================================== - -1. libcurl - -1.1 TFO support on Windows - - libcurl supports the CURLOPT_TCP_FASTOPEN option since 7.49.0 for Linux and - macOS. Windows supports TCP Fast Open starting with Windows 10, version 1607 - and we should add support for it. - - TCP Fast Open is supported on several platforms but not on Windows. Work on - this was once started but never finished. - - See https://github.com/curl/curl/pull/3378 - -1.2 Consult %APPDATA% also for .netrc - - %APPDATA%\.netrc is not considered when running on Windows. should not it? - - See https://github.com/curl/curl/issues/4016 - -1.3 struct lifreq - - Use 'struct lifreq' and SIOCGLIFADDR instead of 'struct ifreq' and - SIOCGIFADDR on newer Solaris versions as they claim the latter is obsolete. - To support IPv6 interface addresses for network interfaces properly. - -1.4 alt-svc sharing - - The share interface could benefit from allowing the alt-svc cache to be - possible to share between easy handles. - - See https://github.com/curl/curl/issues/4476 - - The share interface offers CURL_LOCK_DATA_CONNECT to have multiple easy - handle share a connection cache, but due to how connections are used they are - still not thread-safe when used shared. - - See https://github.com/curl/curl/issues/4915 and lib1541.c - - The share interface offers CURL_LOCK_DATA_HSTS to have multiple easy handle - share an HSTS cache, but this is not thread-safe. - -1.5 get rid of PATH_MAX - - Having code use and rely on PATH_MAX is not nice: - https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html - - Currently the libssh2 SSH based code uses it, but to remove PATH_MAX from - there we need libssh2 to properly tell us when we pass in a too small buffer - and its current API (as of libssh2 1.2.7) does not. - -1.6 thread-safe sharing - - Using the share interface users can share some data between easy handles but - several of the sharing options are documented as not safe and supported to - share between multiple concurrent threads. Fixing this would enable more - users to share data in more powerful ways. - -1.8 CURLOPT_RESOLVE for any port number - - This option allows applications to set a replacement IP address for a given - host + port pair. Consider making support for providing a replacement address - for the hostname on all port numbers. - - See https://github.com/curl/curl/issues/1264 - -1.10 auto-detect proxy - - libcurl could be made to detect the system proxy setup automatically and use - that. On Windows, macOS and Linux desktops for example. - - The pull-request to use libproxy for this was deferred due to doubts on the - reliability of the dependency and how to use it: - https://github.com/curl/curl/pull/977 - - libdetectproxy is a (C++) library for detecting the proxy on Windows - https://github.com/paulharris/libdetectproxy - -1.11 minimize dependencies with dynamically loaded modules - - We can create a system with loadable modules/plug-ins, where these modules - would be the ones that link to 3rd party libs. That would allow us to avoid - having to load ALL dependencies since only the necessary ones for this - app/invoke/used protocols would be necessary to load. See - https://github.com/curl/curl/issues/349 - -1.12 updated DNS server while running - - If /etc/resolv.conf gets updated while a program using libcurl is running, it - is may cause name resolves to fail unless res_init() is called. We should - consider calling res_init() + retry once unconditionally on all name resolve - failures to mitigate against this. Firefox works like that. Note that Windows - does not have res_init() or an alternative. - - https://github.com/curl/curl/issues/2251 - -1.13 c-ares and CURLOPT_OPENSOCKETFUNCTION - - curl creates most sockets via the CURLOPT_OPENSOCKETFUNCTION callback and - close them with the CURLOPT_CLOSESOCKETFUNCTION callback. However, c-ares - does not use those functions and instead opens and closes the sockets itself. - This means that when curl passes the c-ares socket to the - CURLMOPT_SOCKETFUNCTION it is not owned by the application like other - sockets. - - See https://github.com/curl/curl/issues/2734 - -1.14 connect to multiple IPs in parallel - - curl currently implements the happy eyeball algorithm for connecting to the - IPv4 and IPv6 alternatives for a host in parallel, sticking with the - connection that "wins". We could implement a similar algorithm per individual - IP family as well when there are multiple available addresses: start with the - first address, then start a second attempt N milliseconds after and then a - third another N milliseconds later. That way there would be less waiting when - the first IP has problems. It also improves the connection timeout value - handling for multiple address situations. - -1.15 Monitor connections in the connection pool - - libcurl's connection cache or pool holds a number of open connections for the - purpose of possible subsequent connection reuse. It may contain a few up to a - significant amount of connections. Currently, libcurl leaves all connections - as they are and first when a connection is iterated over for matching or - reuse purpose it is verified that it is still alive. - - Those connections may get closed by the server side for idleness or they may - get an HTTP/2 ping from the peer to verify that they are still alive. By - adding monitoring of the connections while in the pool, libcurl can detect - dead connections (and close them) better and earlier, and it can handle - HTTP/2 pings to keep such ones alive even when not actively doing transfers - on them. - -1.16 Try to URL encode given URL - - Given a URL that for example contains spaces, libcurl could have an option - that would try somewhat harder than it does now and convert spaces to %20 and - perhaps URL encoded byte values over 128 etc (basically do what the redirect - following code already does). - - https://github.com/curl/curl/issues/514 - -1.17 Add support for IRIs - - IRIs (RFC 3987) allow localized, non-ASCII, names in the URL. To properly - support this, curl/libcurl would need to translate/encode the given input - from the input string encoding into percent encoded output "over the wire". - - To make that work smoothly for curl users even on Windows, curl would - probably need to be able to convert from several input encodings. - -1.18 try next proxy if one does not work - - Allow an application to specify a list of proxies to try, and failing to - connect to the first go on and try the next instead until the list is - exhausted. Browsers support this feature at least when they specify proxies - using PACs. - - https://github.com/curl/curl/issues/896 - -1.19 provide timing info for each redirect - - curl and libcurl provide timing information via a set of different - time-stamps (CURLINFO_*_TIME). When curl is following redirects, those - returned time value are the accumulated sums. An improvement could be to - offer separate timings for each redirect. - - https://github.com/curl/curl/issues/6743 - -1.20 SRV and URI DNS records - - Offer support for resolving SRV and URI DNS records for libcurl to know which - server to connect to for various protocols (including HTTP). - -1.21 netrc caching and sharing - - The netrc file is read and parsed each time a connection is setup, which - means that if a transfer needs multiple connections for authentication or - redirects, the file might be reread (and parsed) multiple times. This makes - it impossible to provide the file as a pipe. - -1.22 CURLINFO_PAUSE_STATE - - Return information about the transfer's current pause state, in both - directions. https://github.com/curl/curl/issues/2588 - -1.23 Offer API to flush the connection pool - - Sometimes applications want to flush all the existing connections kept alive. - An API could allow a forced flush or just a forced loop that would properly - close all connections that have been closed by the server already. - -1.25 Expose tried IP addresses that failed - - When libcurl fails to connect to a host, it could offer the application the - addresses that were used in the attempt. Source + dest IP, source + dest port - and protocol (UDP or TCP) for each failure. Possibly as a callback. Perhaps - also provide "reason". - - https://github.com/curl/curl/issues/2126 - -1.28 FD_CLOEXEC - - It sets the close-on-exec flag for the file descriptor, which causes the file - descriptor to be automatically (and atomically) closed when any of the - exec-family functions succeed. Should probably be set by default? - - https://github.com/curl/curl/issues/2252 - -1.29 WebSocket read callback - - Call the read callback once the connection is established to allow sending - the first message in the connection. - - https://github.com/curl/curl/issues/11402 - -1.30 config file parsing - - Consider providing an API, possibly in a separate companion library, for - parsing a config file like curl's -K/--config option to allow applications to - get the same ability to read curl options from files. - - See https://github.com/curl/curl/issues/3698 - -1.31 erase secrets from heap/stack after use - - Introducing a concept and system to erase secrets from memory after use, it - could help mitigate and lessen the impact of (future) security problems etc. - However: most secrets are passed to libcurl as clear text from the - application and then clearing them within the library adds nothing... - - https://github.com/curl/curl/issues/7268 - -1.32 add asynch getaddrinfo support - - Use getaddrinfo_a() to provide an asynch name resolver backend to libcurl - that does not use threads and does not depend on c-ares. The getaddrinfo_a - function is (probably?) glibc specific but that is a widely used libc among - our users. - - https://github.com/curl/curl/pull/6746 - -1.33 make DoH inherit more transfer properties - - Some options are not inherited because they are not relevant for the DoH SSL - connections, or inheriting the option may result in unexpected behavior. For - example the user's debug function callback is not inherited because it would - be unexpected for internal handles (ie DoH handles) to be passed to that - callback. - - If an option is not inherited then it is not possible to set it separately - for DoH without a DoH-specific option. For example: - CURLOPT_DOH_SSL_VERIFYHOST, CURLOPT_DOH_SSL_VERIFYPEER and - CURLOPT_DOH_SSL_VERIFYSTATUS. - - See https://github.com/curl/curl/issues/6605 - -2. libcurl - multi interface - -2.1 More non-blocking - - Make sure we do not ever loop because of non-blocking sockets returning - EWOULDBLOCK or similar. Blocking cases include: - - - Name resolves on non-Windows unless c-ares or the threaded resolver is used. - - - The threaded resolver may block on cleanup: - https://github.com/curl/curl/issues/4852 - - - file:// transfers - - - TELNET transfers - - - GSSAPI authentication for FTP transfers - - - The "DONE" operation (post transfer protocol-specific actions) for the - protocols SFTP, SMTP, FTP. Fixing multi_done() for this is a worthy task. - - - curl_multi_remove_handle for any of the above. See section 2.3. - - - Calling curl_ws_send() from a callback - -2.2 Better support for same name resolves - - If a name resolve has been initiated for name NN and a second easy handle - wants to resolve that name as well, make it wait for the first resolve to end - up in the cache instead of doing a second separate resolve. This is - especially needed when adding many simultaneous handles using the same host - name when the DNS resolver can get flooded. - -2.3 Non-blocking curl_multi_remove_handle() - - The multi interface has a few API calls that assume a blocking behavior, like - add_handle() and remove_handle() which limits what we can do internally. The - multi API need to be moved even more into a single function that "drives" - everything in a non-blocking manner and signals when something is done. A - remove or add would then only ask for the action to get started and then - multi_perform() etc still be called until the add/remove is completed. - -2.4 Split connect and authentication process - - The multi interface treats the authentication process as part of the connect - phase. As such any failures during authentication does not trigger the - relevant QUIT or LOGOFF for protocols such as IMAP, POP3 and SMTP. - -2.5 Edge-triggered sockets should work - - The multi_socket API should work with edge-triggered socket events. One of - the internal actions that need to be improved for this to work perfectly is - the 'maxloops' handling in transfer.c:readwrite_data(). - -2.6 multi upkeep - - In libcurl 7.62.0 we introduced curl_easy_upkeep. It unfortunately only works - on easy handles. We should introduces a version of that for the multi handle, - and also consider doing "upkeep" automatically on connections in the - connection pool when the multi handle is in used. - - See https://github.com/curl/curl/issues/3199 - -2.7 Virtual external sockets - - libcurl performs operations on the given file descriptor that presumes it is - a socket and an application cannot replace them at the moment. Allowing an - application to fully replace those would allow a larger degree of freedom and - flexibility. - - See https://github.com/curl/curl/issues/5835 - -2.8 dynamically decide to use socketpair - - For users who do not use curl_multi_wait() or do not care for - curl_multi_wakeup(), we could introduce a way to make libcurl NOT - create a socketpair in the multi handle. - - See https://github.com/curl/curl/issues/4829 - -3. Documentation - -3.1 Improve documentation about fork safety - - See https://github.com/curl/curl/issues/6968 - -4. FTP - -4.1 HOST - - HOST is a command for a client to tell which hostname to use, to offer FTP - servers named-based virtual hosting: - - https://datatracker.ietf.org/doc/html/rfc7151 - -4.6 GSSAPI via Windows SSPI - - In addition to currently supporting the SASL GSSAPI mechanism (Kerberos V5) - via third-party GSS-API libraries, such as Heimdal or MIT Kerberos, also add - support for GSSAPI authentication via Windows SSPI. - -4.7 STAT for LIST without data connection - - Some FTP servers allow STAT for listing directories instead of using LIST, - and the response is then sent over the control connection instead of as the - otherwise usedw data connection: https://www.nsftools.com/tips/RawFTP.htm#STAT - - This is not detailed in any FTP specification. - -4.8 Passive transfer could try other IP addresses - - When doing FTP operations through a proxy at localhost, the reported spotted - that curl only tried to connect once to the proxy, while it had multiple - addresses and a failed connect on one address should make it try the next. - - After switching to passive mode (EPSV), curl could try all IP addresses for - "localhost". Currently it tries ::1, but it should also try 127.0.0.1. - - See https://github.com/curl/curl/issues/1508 - -5. HTTP - -5.1 Provide the error body from a CONNECT response - - When curl receives a body response from a CONNECT request to a proxy, it - always just reads and ignores it. It would make some users happy if curl - instead optionally would be able to make that responsible available. Via a - new callback? Through some other means? - - See https://github.com/curl/curl/issues/9513 - -5.2 Obey Retry-After in redirects - - The Retry-After is said to dicate "the minimum time that the user agent is - asked to wait before issuing the redirected request" and libcurl does not - obey this. - - See https://github.com/curl/curl/issues/11447 - -5.3 Rearrange request header order - - Server implementers often make an effort to detect browser and to reject - clients it can detect to not match. One of the last details we cannot yet - control in libcurl's HTTP requests, which also can be exploited to detect - that libcurl is in fact used even when it tries to impersonate a browser, is - the order of the request headers. I propose that we introduce a new option in - which you give headers a value, and then when the HTTP request is built it - sorts the headers based on that number. We could then have internally created - headers use a default value so only headers that need to be moved have to be - specified. - -5.4 Allow SAN names in HTTP/2 server push - - curl only allows HTTP/2 push promise if the provided :authority header value - exactly matches the hostname given in the URL. It could be extended to allow - any name that would match the Subject Alternative Names in the server's TLS - certificate. - - See https://github.com/curl/curl/pull/3581 - -5.5 auth= in URLs - - Add the ability to specify the preferred authentication mechanism to use by - using ;auth= in the login part of the URL. - - For example: - - http://test:pass;auth=NTLM@example.com would be equivalent to specifying - --user test:pass;auth=NTLM or --user test:pass --ntlm from the command line. - - Additionally this should be implemented for proxy base URLs as well. - -5.6 alt-svc should fallback if alt-svc does not work - - The alt-svc: header provides a set of alternative services for curl to use - instead of the original. If the first attempted one fails, it should try the - next etc and if all alternatives fail go back to the original. - - See https://github.com/curl/curl/issues/4908 - -5.7 Require HTTP version X or higher - - curl and libcurl provide options for trying higher HTTP versions (for example - HTTP/2) but then still allows the server to pick version 1.1. We could - consider adding a way to require a minimum version. - - See https://github.com/curl/curl/issues/7980 - -6. TELNET - -6.1 ditch stdin - - Reading input (to send to the remote server) on stdin is a crappy solution - for library purposes. We need to invent a good way for the application to be - able to provide the data to send. - -6.2 ditch telnet-specific select - - Move the telnet support's network select() loop go away and merge the code - into the main transfer loop. Until this is done, the multi interface does not - work for telnet. - -6.3 feature negotiation debug data - - Add telnet feature negotiation data to the debug callback as header data. - -6.4 exit immediately upon connection if stdin is /dev/null - - If it did, curl could be used to probe if there is an server there listening - on a specific port. That is, the following command would exit immediately - after the connection is established with exit code 0: - - curl -s --connect-timeout 2 telnet://example.com:80 NOTIFY=SUCCESS,FAILURE" ); - - https://github.com/curl/curl/issues/8232 - -7.2 Enhanced capability support - - Add the ability, for an application that uses libcurl, to obtain the list of - capabilities returned from the EHLO command. - -7.3 Add CURLOPT_MAIL_CLIENT option - - Rather than use the URL to specify the mail client string to present in the - HELO and EHLO commands, libcurl should support a new CURLOPT specifically for - specifying this data as the URL is non-standard and to be honest a bit of a - hack ;-) - - Please see the following thread for more information: - https://curl.se/mail/lib-2012-05/0178.html - - -8. POP3 - -8.2 Enhanced capability support - - Add the ability, for an application that uses libcurl, to obtain the list of - capabilities returned from the CAPA command. - -9. IMAP - -9.1 Enhanced capability support - - Add the ability, for an application that uses libcurl, to obtain the list of - capabilities returned from the CAPABILITY command. - -10. LDAP - -10.1 SASL based authentication mechanisms - - Currently the LDAP module only supports ldap_simple_bind_s() in order to bind - to an LDAP server. However, this function sends username and password details - using the simple authentication mechanism (as clear text). However, it should - be possible to use ldap_bind_s() instead specifying the security context - information ourselves. - -10.2 CURLOPT_SSL_CTX_FUNCTION for LDAPS - - CURLOPT_SSL_CTX_FUNCTION works perfectly for HTTPS and email protocols, but - it has no effect for LDAPS connections. - - https://github.com/curl/curl/issues/4108 - -10.3 Paged searches on LDAP server - - https://github.com/curl/curl/issues/4452 - -10.4 Certificate-Based Authentication - - LDAPS not possible with macOS and Windows with Certificate-Based Authentication - - https://github.com/curl/curl/issues/9641 - -11. SMB - -11.1 File listing support - - Add support for listing the contents of an SMB share. The output should - probably be the same as/similar to FTP. - -11.2 Honor file timestamps - - The timestamp of the transferred file should reflect that of the original - file. - -11.3 Use NTLMv2 - - Currently the SMB authentication uses NTLMv1. - -11.4 Create remote directories - - Support for creating remote directories when uploading a file to a directory - that does not exist on the server, just like --ftp-create-dirs. - - -12. FILE - -12.1 Directory listing on non-POSIX - - Listing the contents of a directory accessed with FILE only works on - platforms with opendir. Support could be added for more systems, like - Windows. - -13. TLS - -13.1 TLS-PSK with OpenSSL - - Transport Layer Security pre-shared key ciphersuites (TLS-PSK) is a set of - cryptographic protocols that provide secure communication based on pre-shared - keys (PSKs). These pre-shared keys are symmetric keys shared in advance among - the communicating parties. - - https://github.com/curl/curl/issues/5081 - -13.2 TLS channel binding - - TLS 1.2 and 1.3 provide the ability to extract some secret data from the TLS - connection and use it in the client request (usually in some sort of - authentication) to ensure that the data sent is bound to the specific TLS - connection and cannot be successfully intercepted by a proxy. This - functionality can be used in a standard authentication mechanism such as - GSS-API or SCRAM, or in custom approaches like custom HTTP Authentication - headers. - - For TLS 1.2, the binding type is usually tls-unique, and for TLS 1.3 it is - tls-exporter. - - https://datatracker.ietf.org/doc/html/rfc5929 - https://datatracker.ietf.org/doc/html/rfc9266 - https://github.com/curl/curl/issues/9226 - -13.3 Defeat TLS fingerprinting - - By changing the order of TLS extensions provided in the TLS handshake, it is - sometimes possible to circumvent TLS fingerprinting by servers. The TLS - extension order is of course not the only way to fingerprint a client. - -13.4 Consider OCSP stapling by default - - Treat a negative response a reason for aborting the connection. Since OCSP - stapling is presumed to get used much less in the future when Let's Encrypt - drops the OCSP support, the benefit of this might however be limited. - - https://github.com/curl/curl/issues/15483 - -13.6 Provide callback for cert verification - - OpenSSL supports a callback for customised verification of the peer - certificate, but this does not seem to be exposed in the libcurl APIs. Could - it be? There is so much that could be done if it were. - -13.7 Less memory massaging with Schannel - - The Schannel backend does a lot of custom memory management we would rather - avoid: the repeated alloc + free in sends and the custom memory + realloc - system for encrypted and decrypted data. That should be avoided and reduced - for 1) efficiency and 2) safety. - -13.8 Support DANE - - DNS-Based Authentication of Named Entities (DANE) is a way to provide SSL - keys and certs over DNS using DNSSEC as an alternative to the CA model. - https://www.rfc-editor.org/rfc/rfc6698.txt - - An initial patch was posted by Suresh Krishnaswamy on March 7th 2013 - (https://curl.se/mail/lib-2013-03/0075.html) but it was a too simple - approach. See Daniel's comments: - https://curl.se/mail/lib-2013-03/0103.html . libunbound may be the - correct library to base this development on. - - Björn Stenberg wrote a separate initial take on DANE that was never - completed. - -13.9 TLS record padding - - TLS (1.3) offers optional record padding and OpenSSL provides an API for it. - I could make sense for libcurl to offer this ability to applications to make - traffic patterns harder to figure out by network traffic observers. - - See https://github.com/curl/curl/issues/5398 - -13.10 Support Authority Information Access certificate extension (AIA) - - AIA can provide various things like CRLs but more importantly information - about intermediate CA certificates that can allow validation path to be - fulfilled when the HTTPS server does not itself provide them. - - Since AIA is about downloading certs on demand to complete a TLS handshake, - it is probably a bit tricky to get done right. - - See https://github.com/curl/curl/issues/2793 - -13.11 Some TLS options are not offered for HTTPS proxies - - Some TLS related options to the command line tool and libcurl are only - provided for the server and not for HTTPS proxies. --proxy-tls-max, - --proxy-tlsv1.3, --proxy-curves and a few more. - For more Documentation on this see: - https://curl.se/libcurl/c/tls-options.html - - https://github.com/curl/curl/issues/12286 - -13.13 Make sure we forbid TLS 1.3 post-handshake authentication - - RFC 8740 explains how using HTTP/2 must forbid the use of TLS 1.3 - post-handshake authentication. We should make sure to live up to that. - - See https://github.com/curl/curl/issues/5396 - -13.14 Support the clienthello extension - - Certain stupid networks and middle boxes have a problem with SSL handshake - packets that are within a certain size range because how that sets some bits - that previously (in older TLS version) were not set. The clienthello - extension adds padding to avoid that size range. - - https://datatracker.ietf.org/doc/html/rfc7685 - https://github.com/curl/curl/issues/2299 - -13.16 Share the CA cache - - For TLS backends that supports CA caching, it makes sense to allow the share - object to be used to store the CA cache as well via the share API. Would - allow multiple easy handles to reuse the CA cache and save themselves from a - lot of extra processing overhead. - -13.17 Add missing features to TLS backends - - The feature matrix at https://curl.se/libcurl/c/tls-options.html shows which - features are supported by which TLS backends, and thus also where there are - feature gaps. - -14. Proxy - -14.1 Retry SOCKS handshake on address type not supported - - When curl resolves a hostname, it might get a mix of IPv6 and IPv4 returned. - curl might then use an IPv6 address with a SOCKS5 proxy, which - if it does - not support IPv6 - returns "Address type not supported" and curl exits with - that error. - - Perhaps it is preferred if curl would in this situation instead first retry - the SOCKS handshake again for this case and then use one of the IPv4 - addresses for the target host. - - See https://github.com/curl/curl/issues/17222 - -15. Schannel - -15.1 Extend support for client certificate authentication - - The existing support for the -E/--cert and --key options could be - extended by supplying a custom certificate and key in PEM format, see: - - Getting a Certificate for Schannel - https://msdn.microsoft.com/en-us/library/windows/desktop/aa375447.aspx - -15.2 Extend support for the --ciphers option - - The existing support for the --ciphers option could be extended - by mapping the OpenSSL/GnuTLS cipher suites to the Schannel APIs, see - - Specifying Schannel Ciphers and Cipher Strengths - https://msdn.microsoft.com/en-us/library/windows/desktop/aa380161.aspx - -15.4 Add option to allow abrupt server closure - - libcurl with Schannel errors without a known termination point from the server - (such as length of transfer, or SSL "close notify" alert) to prevent against - a truncation attack. Really old servers may neglect to send any termination - point. An option could be added to ignore such abrupt closures. - - https://github.com/curl/curl/issues/4427 - -16. SASL - -16.1 Other authentication mechanisms - - Add support for other authentication mechanisms such as OLP, - GSS-SPNEGO and others. - -16.2 Add QOP support to GSSAPI authentication - - Currently the GSSAPI authentication only supports the default QOP of auth - (Authentication), whilst Kerberos V5 supports both auth-int (Authentication - with integrity protection) and auth-conf (Authentication with integrity and - privacy protection). - - -17. SSH protocols - -17.1 Multiplexing - - SSH is a perfectly fine multiplexed protocols which would allow libcurl to do - multiple parallel transfers from the same host using the same connection, - much in the same spirit as HTTP/2 does. libcurl however does not take - advantage of that ability but does instead always create a new connection for - new transfers even if an existing connection already exists to the host. - - To fix this, libcurl would have to detect an existing connection and "attach" - the new transfer to the existing one. - -17.2 Handle growing SFTP files - - The SFTP code in libcurl checks the file size *before* a transfer starts and - then proceeds to transfer exactly that amount of data. If the remote file - grows while the transfer is in progress libcurl does not notice and does not - adapt. The OpenSSH SFTP command line tool does and libcurl could also just - attempt to download more to see if there is more to get... - - https://github.com/curl/curl/issues/4344 - -17.3 Read keys from ~/.ssh/id_ecdsa, id_ed25519 - - The libssh2 backend in curl is limited to only reading keys from id_rsa and - id_dsa, which makes it fail connecting to servers that use more modern key - types. - - https://github.com/curl/curl/issues/8586 - -17.4 Support CURLOPT_PREQUOTE - - The two other QUOTE options are supported for SFTP, but this was left out for - unknown reasons. - -17.5 SSH over HTTPS proxy with more backends - - The SSH based protocols SFTP and SCP did not work over HTTPS proxy at - all until PR https://github.com/curl/curl/pull/6021 brought the - functionality with the libssh2 backend. Presumably, this support - can/could be added for the other backends as well. - -17.6 SFTP with SCP:// - - OpenSSH 9 switched their 'scp' tool to speak SFTP under the hood. Going - forward it might be worth having curl or libcurl attempt SFTP if SCP fails to - follow suite. - -18. Command line tool - -18.1 sync - - "curl --sync http://example.com/feed[1-100].rss" or - "curl --sync http://example.net/{index,calendar,history}.html" - - Downloads a range or set of URLs using the remote name, but only if the - remote file is newer than the local file. A Last-Modified HTTP date header - should also be used to set the mod date on the downloaded file. - -18.2 glob posts - - Globbing support for -d and -F, as in 'curl -d "name=foo[0-9]" URL'. - This is easily scripted though. - -18.4 --proxycommand - - Allow the user to make curl run a command and use its stdio to make requests - and not do any network connection by itself. Example: - - curl --proxycommand 'ssh pi@raspberrypi.local -W 10.1.1.75 80' \ - http://some/otherwise/unavailable/service.php - - See https://github.com/curl/curl/issues/4941 - -18.5 UTF-8 filenames in Content-Disposition - - RFC 6266 documents how UTF-8 names can be passed to a client in the - Content-Disposition header, and curl does not support this. - - https://github.com/curl/curl/issues/1888 - -18.6 Option to make -Z merge lined based outputs on stdout - - When a user requests multiple lined based files using -Z and sends them to - stdout, curl does not "merge" and send complete lines fine but may send - partial lines from several sources. - - https://github.com/curl/curl/issues/5175 - -18.7 specify which response codes that make -f/--fail return error - - Allows a user to better specify exactly which error code(s) that are fine - and which are errors for their specific uses cases - -18.9 Choose the name of file in braces for complex URLs - - When using braces to download a list of URLs and you use complicated names - in the list of alternatives, it could be handy to allow curl to use other - names when saving. - - Consider a way to offer that. Possibly like - {partURL1:name1,partURL2:name2,partURL3:name3} where the name following the - colon is the output name. - - See https://github.com/curl/curl/issues/221 - -18.10 improve how curl works in a Windows console window - - If you pull the scrollbar when transferring with curl in a Windows console - window, the transfer is interrupted and can get disconnected. This can - probably be improved. See https://github.com/curl/curl/issues/322 - -18.11 Windows: set attribute 'archive' for completed downloads - - The archive bit (FILE_ATTRIBUTE_ARCHIVE, 0x20) separates files that shall be - backed up from those that are either not ready or have not changed. - - Downloads in progress are neither ready to be backed up, nor should they be - opened by a different process. Only after a download has been completed it is - sensible to include it in any integer snapshot or backup of the system. - - See https://github.com/curl/curl/issues/3354 - -18.12 keep running, read instructions from pipe/socket - - Provide an option that makes curl not exit after the last URL (or even work - without a given URL), and then make it read instructions passed on a pipe or - over a socket to make further instructions so that a second subsequent curl - invoke can talk to the still running instance and ask for transfers to get - done, and thus maintain its connection pool, DNS cache and more. - -18.13 Acknowledge Ratelimit headers - - Consider a command line option that can make curl do multiple serial requests - while acknowledging server specified rate limits: - https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/ - - See https://github.com/curl/curl/issues/5406 - -18.14 --dry-run - - A command line option that makes curl show exactly what it would do and send - if it would run for real. - - See https://github.com/curl/curl/issues/5426 - -18.15 --retry should resume - - When --retry is used and curl actually retries transfer, it should use the - already transferred data and do a resumed transfer for the rest (when - possible) so that it does not have to transfer the same data again that was - already transferred before the retry. - - See https://github.com/curl/curl/issues/1084 - -18.17 consider filename from the redirected URL with -O ? - - When a user gives a URL and uses -O, and curl follows a redirect to a new - URL, the filename is not extracted and used from the newly redirected-to URL - even if the new URL may have a much more sensible filename. - - This is clearly documented and helps for security since there is no surprise - to users which filename that might get overwritten, but maybe a new option - could allow for this or maybe -J should imply such a treatment as well as -J - already allows for the server to decide what filename to use so it already - provides the "may overwrite any file" risk. - - This is extra tricky if the original URL has no filename part at all since - then the current code path does error out with an error message, and we - cannot *know* already at that point if curl is redirected to a URL that has a - filename... - - See https://github.com/curl/curl/issues/1241 - -18.18 retry on network is unreachable - - The --retry option retries transfers on "transient failures". We later added - --retry-connrefused to also retry for "connection refused" errors. - - Suggestions have been brought to also allow retry on "network is unreachable" - errors and while totally reasonable, maybe we should consider a way to make - this more configurable than to add a new option for every new error people - want to retry for? - - https://github.com/curl/curl/issues/1603 - -18.20 hostname sections in config files - - config files would be more powerful if they could set different - configurations depending on used URLs, hostname or possibly origin. Then a - default .curlrc could a specific user-agent only when doing requests against - a certain site. - -18.21 retry on the redirected-to URL - - When curl is told to --retry a failed transfer and follows redirects, it - might get an HTTP 429 response from the redirected-to URL and not the - original one, which then could make curl decide to rather retry the transfer - on that URL only instead of the original operation to the original URL. - - Perhaps extra emphasized if the original transfer is a large POST that - redirects to a separate GET, and that GET is what gets the 529 - - See https://github.com/curl/curl/issues/5462 - -18.23 Set the modification date on an uploaded file - - For SFTP and possibly FTP, curl could offer an option to set the - modification time for the uploaded file. - - See https://github.com/curl/curl/issues/5768 - -18.24 Use multiple parallel transfers for a single download - - To enhance transfer speed, downloading a single URL can be split up into - multiple separate range downloads that get combined into a single final - result. - - An ideal implementation would not use a specified number of parallel - transfers, but curl could: - - First start getting the full file as transfer A - - If after N seconds have passed and the transfer is expected to continue for - M seconds or more, add a new transfer (B) that asks for the second half of - A's content (and stop A at the middle). - - If splitting up the work improves the transfer rate, it could then be done - again. Then again, etc up to a limit. - - This way, if transfer B fails (because Range: is not supported) it lets - transfer A remain the single one. N and M could be set to some sensible - defaults. - - See https://github.com/curl/curl/issues/5774 - -18.25 Prevent terminal injection when writing to terminal - - curl could offer an option to make escape sequence either non-functional or - avoid cursor moves or similar to reduce the risk of a user getting tricked by - clever tricks. - - See https://github.com/curl/curl/issues/6150 - -18.26 Custom progress meter update interval - - Users who are for example doing large downloads in CI or remote setups might - want the occasional progress meter update to see that the transfer is - progressing and has not stuck, but they may not appreciate the - many-times-a-second frequency curl can end up doing it with now. - -18.27 -J and -O with %-encoded filenames - - -J/--remote-header-name does not decode %-encoded filenames. RFC 6266 details - how it should be done. The can of worm is basically that we have no charset - handling in curl and ASCII >=128 is a challenge for us. Not to mention that - decoding also means that we need to check for nastiness that is attempted, - like "../" sequences and the like. Probably everything to the left of any - embedded slashes should be cut off. - https://curl.se/bug/view.cgi?id=1294 - - -O also does not decode %-encoded names, and while it has even less - information about the charset involved the process is similar to the -J case. - - Note that we do not decode -O without the user asking for it with some other - means, since -O has always been documented to use the name exactly as - specified in the URL. - -18.28 -J with -C - - - When using -J (with -O), automatically resumed downloading together with "-C - -" fails. Without -J the same command line works. This happens because the - resume logic is worked out before the target filename (and thus its - pre-transfer size) has been figured out. This can be improved. - - https://curl.se/bug/view.cgi?id=1169 - -18.29 --retry and transfer timeouts - - If using --retry and the transfer timeouts (possibly due to using -m or - -y/-Y) the next attempt does not resume the transfer properly from what was - downloaded in the previous attempt but truncates and restarts at the original - position where it was at before the previous failed attempt. See - https://curl.se/mail/lib-2008-01/0080.html and Mandriva bug report - https://qa.mandriva.com/show_bug.cgi?id=22565 - - -19. Build - -19.2 Enable PIE and RELRO by default - - Especially when having programs that execute curl via the command line, PIE - renders the exploitation of memory corruption vulnerabilities a lot more - difficult. This can be attributed to the additional information leaks being - required to conduct a successful attack. RELRO, on the other hand, masks - different binary sections like the GOT as read-only and thus kills a handful - of techniques that come in handy when attackers are able to arbitrarily - overwrite memory. A few tests showed that enabling these features had close - to no impact, neither on the performance nor on the general functionality of - curl. - -19.3 Do not use GNU libtool on OpenBSD - - When compiling curl on OpenBSD with "--enable-debug" it gives linking errors - when you use GNU libtool. This can be fixed by using the libtool provided by - OpenBSD itself. However for this the user always needs to invoke make with - "LIBTOOL=/usr/bin/libtool". It would be nice if the script could have some - magic to detect if this system is an OpenBSD host and then use the OpenBSD - libtool instead. - - See https://github.com/curl/curl/issues/5862 - -19.4 Package curl for Windows in a signed installer - - See https://github.com/curl/curl/issues/5424 - -19.5 make configure use --cache-file more and better - - The configure script can be improved to cache more values so that repeated - invokes run much faster. - - See https://github.com/curl/curl/issues/7753 - -20. Test suite - -20.1 SSL tunnel - - Make our own version of stunnel for simple port forwarding to enable HTTPS - and FTP-SSL tests without the stunnel dependency, and it could allow us to - provide test tools built with either OpenSSL or GnuTLS - -20.2 more protocols supported - - Extend the test suite to include more protocols. The telnet could just do FTP - or http operations (for which we have test servers). - -20.3 more platforms supported - - Make the test suite work on more platforms. OpenBSD and macOS. Remove - fork()s and it should become even more portable. - -20.4 write an SMB test server to replace impacket - - This would allow us to run SMB tests on more platforms and do better and more - covering tests. - - See https://github.com/curl/curl/issues/15697 - -20.5 Use the RFC 6265 test suite - - A test suite made for HTTP cookies (RFC 6265) by Adam Barth is available at - https://github.com/abarth/http-state/tree/master/tests - - It would be good if someone would write a script/setup that would run curl - with that test suite and detect deviances. Ideally, that would even be - incorporated into our regular test suite. - -20.6 Run web-platform-tests URL tests - - Run web-platform-tests URL tests and compare results with browsers on wpt.fyi - - It would help us find issues to fix and help us document where our parser - differs from the WHATWG URL spec parsers. - - See https://github.com/curl/curl/issues/4477 - -21. MQTT - -21.1 Support rate-limiting - - The rate-limiting logic is done in the PERFORMING state in multi.c but MQTT - is not (yet) implemented to use that. - -21.2 Support MQTTS - -21.3 Handle network blocks - - Running test suite with - `CURL_DBG_SOCK_WBLOCK=90 ./runtests.pl -a mqtt` makes several - MQTT test cases fail where they should not. - -22. TFTP - -22.1 TFTP does not convert LF to CRLF for mode=netascii - - RFC 3617 defines that an TFTP transfer can be done using "netascii" - mode. curl does not support extracting that mode from the URL nor does it treat - such transfers specifically. It should probably do LF to CRLF translations - for them. - - See https://github.com/curl/curl/issues/12655 - -23. Gopher - -23.1 Handle network blocks - - Running test suite with - `CURL_DBG_SOCK_WBLOCK=90 ./runtests.pl -a 1200 to 1300` makes several - Gopher test cases fail where they should not. diff --git a/docs/TODO.md b/docs/TODO.md new file mode 100644 index 0000000000..2be796f8c9 --- /dev/null +++ b/docs/TODO.md @@ -0,0 +1,1032 @@ + + +# TODO intro + +Things to do in project curl. Please tell us what you think, contribute and +send us patches that improve things. + +Be aware that these are things that we could do, or have once been considered +things we could do. If you want to work on any of these areas, please consider +bringing it up for discussions first on the mailing list so that we all agree +it is still a good idea for the project. + +All bugs documented in the [known_bugs +document](https://curl.se/docs/knownbugs.html) are subject for fixing. + +# libcurl + +## Consult `%APPDATA%` also for `.netrc` + +`%APPDATA%\.netrc` is not considered when running on Windows. Should it not? + +See [curl issue 4016](https://github.com/curl/curl/issues/4016) + +## `struct lifreq` + +Use `struct lifreq` and `SIOCGLIFADDR` instead of `struct ifreq` and +`SIOCGIFADDR` on newer Solaris versions as they claim the latter is obsolete. +To support IPv6 interface addresses for network interfaces properly. + +## alt-svc sharing + +The share interface could benefit from allowing the alt-svc cache to be +possible to share between easy handles. + +See [curl issue 4476](https://github.com/curl/curl/issues/4476) + +The share interface offers CURL_LOCK_DATA_CONNECT to have multiple easy +handle share a connection cache, but due to how connections are used they are +still not thread-safe when used shared. + +See [curl issue 4915](https://github.com/curl/curl/issues/4915) and lib1541.c + +The share interface offers CURL_LOCK_DATA_HSTS to have multiple easy handle +share an HSTS cache, but this is not thread-safe. + +## thread-safe sharing + +Using the share interface users can share some data between easy handles but +several of the sharing options are documented as not safe and supported to +share between multiple concurrent threads. Fixing this would enable more users +to share data in more powerful ways. + +## updated DNS server while running + +If `/etc/resolv.conf` gets updated while a program using libcurl is running, it +is may cause name resolves to fail unless `res_init()` is called. We should +consider calling `res_init()` + retry once unconditionally on all name resolve +failures to mitigate against this. Firefox works like that. Note that Windows +does not have `res_init()` or an alternative. + +[curl issue 2251](https://github.com/curl/curl/issues/2251) + +## c-ares and CURLOPT_OPENSOCKETFUNCTION + +curl creates most sockets via the CURLOPT_OPENSOCKETFUNCTION callback and +close them with the CURLOPT_CLOSESOCKETFUNCTION callback. c-ares does not use +those functions and instead opens and closes the sockets itself. This means +that when curl passes the c-ares socket to the CURLMOPT_SOCKETFUNCTION it is +not owned by the application like other sockets. + +See [curl issue 2734](https://github.com/curl/curl/issues/2734) + +## Monitor connections in the connection pool + +libcurl's connection cache or pool holds a number of open connections for the +purpose of possible subsequent connection reuse. It may contain a few up to a +significant amount of connections. Currently, libcurl leaves all connections +as they are and first when a connection is iterated over for matching or reuse +purpose it is verified that it is still alive. + +Those connections may get closed by the server side for idleness or they may +get an HTTP/2 ping from the peer to verify that they are still alive. By +adding monitoring of the connections while in the pool, libcurl can detect +dead connections (and close them) better and earlier, and it can handle HTTP/2 +pings to keep such ones alive even when not actively doing transfers on them. + +## Try to URL encode given URL + +Given a URL that for example contains spaces, libcurl could have an option +that would try somewhat harder than it does now and convert spaces to %20 and +perhaps URL encoded byte values over 128 etc (do what the redirect following +code already does). + +[curl issue 514](https://github.com/curl/curl/issues/514) + +## Add support for IRIs + +IRIs (RFC 3987) allow localized, non-ASCII, names in the URL. To properly +support this, curl/libcurl would need to translate/encode the given input +from the input string encoding into percent encoded output "over the wire". + +To make that work smoothly for curl users even on Windows, curl would probably +need to be able to convert from several input encodings. + +## try next proxy if one does not work + +Allow an application to specify a list of proxies to try, and failing to +connect to the first go on and try the next instead until the list is +exhausted. Browsers support this feature at least when they specify proxies +using `PAC`. + +[curl issue 896](https://github.com/curl/curl/issues/896) + +## provide timing info for each redirect + +curl and libcurl provide timing information via a set of different time-stamps +(CURLINFO_*_TIME). When curl is following redirects, those returned time value +are the accumulated sums. An improvement could be to offer separate timings +for each redirect. + +[curl issue 6743](https://github.com/curl/curl/issues/6743) + +## CURLINFO_PAUSE_STATE + +Return information about the transfer's current pause state, in both +directions. See [curl issue 2588](https://github.com/curl/curl/issues/2588) + +## Expose tried IP addresses that failed + +When libcurl fails to connect to a host, it could offer the application the +addresses that were used in the attempt. Source + destination IP, source + +destination port and protocol (UDP or TCP) for each failure. Possibly as a +callback. Perhaps also provide reason. + +[curl issue 2126](https://github.com/curl/curl/issues/2126) + +## erase secrets from heap/stack after use + +Introducing a concept and system to erase secrets from memory after use, it +could help mitigate and lessen the impact of (future) security problems etc. +However: most secrets are passed to libcurl as clear text from the application +and then clearing them within the library adds nothing... + +[curl issue 7268](https://github.com/curl/curl/issues/7268) + +## make DoH inherit more transfer properties + +Some options are not inherited because they are not relevant for the DoH SSL +connections, or inheriting the option may result in unexpected behavior. For +example the user's debug function callback is not inherited because it would +be unexpected for internal handles (i.e DoH handles) to be passed to that +callback. + +If an option is not inherited then it is not possible to set it separately +for DoH without a DoH-specific option. For example: +`CURLOPT_DOH_SSL_VERIFYHOST`, `CURLOPT_DOH_SSL_VERIFYPEER` and +`CURLOPT_DOH_SSL_VERIFYSTATUS`. + +See [curl issue 6605](https://github.com/curl/curl/issues/6605) + +# libcurl - multi interface + +## More non-blocking + +Make sure we do not ever loop because of non-blocking sockets returning +`EWOULDBLOCK` or similar. Blocking cases include: + +- Name resolves on non-Windows unless c-ares or the threaded resolver is used. +- The threaded resolver may block on cleanup: + [curl issue 4852](https://github.com/curl/curl/issues/4852) +- `file://` transfers +- TELNET transfers +- GSSAPI authentication for FTP transfers +- The "DONE" operation (post transfer protocol-specific actions) for the +protocols SFTP, SMTP, FTP. Fixing `multi_done()` for this is a worthy task. +- `curl_multi_remove_handle()` for any of the above. +- Calling `curl_ws_send()` from a callback + +## Better support for same name resolves + +If a name resolve has been initiated for a given name and a second easy handle +wants to resolve that same name as well, make it wait for the first resolve to +end up in the cache instead of doing a second separate resolve. This is +especially needed when adding many simultaneous handles using the same +hostname when the DNS resolver can get flooded. + +## Non-blocking `curl_multi_remove_handle()` + +The multi interface has a few API calls that assume a blocking behavior, like +`add_handle()` and `remove_handle()` which limits what we can do internally. +The multi API need to be moved even more into a single function that "drives" +everything in a non-blocking manner and signals when something is done. A +remove or add would then only ask for the action to get started and then +`multi_perform()` etc still be called until the add/remove is completed. + +## Split connect and authentication process + +The multi interface treats the authentication process as part of the connect +phase. As such any failures during authentication does not trigger the +relevant QUIT or LOGOFF for protocols such as IMAP, POP3 and SMTP. + +## Edge-triggered sockets should work + +The multi_socket API should work with edge-triggered socket events. One of the +internal actions that need to be improved for this to work perfectly is the +`maxloops` handling in `transfer.c:readwrite_data()`. + +## multi upkeep + +In libcurl 7.62.0 we introduced `curl_easy_upkeep`. It unfortunately only +works on easy handles. We should introduces a version of that for the multi +handle, and also consider doing `upkeep` automatically on connections in the +connection pool when the multi handle is in used. + +See [curl issue 3199](https://github.com/curl/curl/issues/3199) + +## Virtual external sockets + +libcurl performs operations on the given file descriptor that presumes it is a +socket and an application cannot replace them at the moment. Allowing an +application to fully replace those would allow a larger degree of freedom and +flexibility. + +See [curl issue 5835](https://github.com/curl/curl/issues/5835) + +## dynamically decide to use socketpair + +For users who do not use `curl_multi_wait()` or do not care for +`curl_multi_wakeup()`, we could introduce a way to make libcurl NOT create a +socketpair in the multi handle. + +See [curl issue 4829](https://github.com/curl/curl/issues/4829) + +# Documentation + +## Improve documentation about fork safety + +See [curl issue 6968](https://github.com/curl/curl/issues/6968) + +# FTP + +## A fixed directory listing format + +Since listing the contents of a remove directory with FTP is returning the +list in a format and style the server likes without any established or even +defacto standard existing, it would be a feature to users if curl could parse +the directory listing and output a general curl format that is fixed and the +same, independent of the server's choice. This would allow users to better and +more reliably extract information about remote content via FTP directory +listings. + +## GSSAPI via Windows SSPI + +In addition to currently supporting the SASL GSSAPI mechanism (Kerberos V5) +via third-party GSS-API libraries, such as MIT Kerberos, also add support for +GSSAPI authentication via Windows SSPI. + +## STAT for LIST without data connection + +Some FTP servers allow STAT for listing directories instead of using LIST, and +the response is then sent over the control connection instead of as the +otherwise used data connection. + +This is not detailed in any FTP specification. + +## Passive transfer could try other IP addresses + +When doing FTP operations through a proxy at localhost, the reported spotted +that curl only tried to connect once to the proxy, while it had multiple +addresses and a failed connect on one address should make it try the next. + +After switching to passive mode (EPSV), curl could try all IP addresses for +`localhost`. Currently it tries `::1`, but it should also try `127.0.0.1`. + +See [curl issue 1508](https://github.com/curl/curl/issues/1508) + +# HTTP + +## Provide the error body from a CONNECT response + +When curl receives a body response from a CONNECT request to a proxy, it +always reads and ignores it. It would make some users happy if curl instead +optionally would be able to make that responsible available. Via a new +callback? Through some other means? + +See [curl issue 9513](https://github.com/curl/curl/issues/9513) + +## Obey `Retry-After` in redirects + +The `Retry-After` response header is said to dictate "the minimum time that +the user agent is asked to wait before issuing the redirected request" and +libcurl does not obey this. + +See [curl issue 11447](https://github.com/curl/curl/issues/11447) + +## Rearrange request header order + +Server implementers often make an effort to detect browser and to reject +clients it can detect to not match. One of the last details we cannot yet +control in libcurl's HTTP requests, which also can be exploited to detect that +libcurl is in fact used even when it tries to impersonate a browser, is the +order of the request headers. I propose that we introduce a new option in +which you give headers a value, and then when the HTTP request is built it +sorts the headers based on that number. We could then have internally created +headers use a default value so only headers that need to be moved have to be +specified. + +## Allow SAN names in HTTP/2 server push + +curl only allows HTTP/2 push promise if the provided :authority header value +exactly matches the hostname given in the URL. It could be extended to allow +any name that would match the Subject Alternative Names in the server's TLS +certificate. + +See [curl pull request 3581](https://github.com/curl/curl/pull/3581) + +## `auth=` in URLs + +Add the ability to specify the preferred authentication mechanism to use by +using `;auth=` in the login part of the URL. + +For example: + +`http://test:pass;auth=NTLM@example.com` would be equivalent to specifying +`--user test:pass;auth=NTLM` or `--user test:pass --ntlm` from the command +line. + +Additionally this should be implemented for proxy base URLs as well. + +## Require HTTP version X or higher + +curl and libcurl provide options for trying higher HTTP versions (for example +HTTP/2) but then still allows the server to pick version 1.1. We could +consider adding a way to require a minimum version. + +See [curl issue 7980](https://github.com/curl/curl/issues/7980) + +# TELNET + +## ditch stdin + +Reading input (to send to the remote server) on stdin is a crappy solution for +library purposes. We need to invent a good way for the application to be able +to provide the data to send. + +## ditch telnet-specific select + +Move the telnet support's network `select()` loop go away and merge the code +into the main transfer loop. Until this is done, the multi interface does not +work for telnet. + +## feature negotiation debug data + +Add telnet feature negotiation data to the debug callback as header data. + +## exit immediately upon connection if stdin is /dev/null + +If it did, curl could be used to probe if there is an server there listening +on a specific port. That is, the following command would exit immediately +after the connection is established with exit code 0: + + curl -s --connect-timeout 2 telnet://example.com:80 NOTIFY=SUCCESS,FAILURE");`. + +[curl issue 8232](https://github.com/curl/curl/issues/8232) + +## Enhanced capability support + +Add the ability, for an application that uses libcurl, to obtain the list of +capabilities returned from the EHLO command. + +## Add `CURLOPT_MAIL_CLIENT` option + +Rather than use the URL to specify the mail client string to present in the +`HELO` and `EHLO` commands, libcurl should support a new `CURLOPT` +specifically for specifying this data as the URL is non-standard and to be +honest a bit of a hack. + +Please see the following thread for more information: +https://curl.se/mail/lib-2012-05/0178.html + +# POP3 + +## Enhanced capability support + +Add the ability, for an application that uses libcurl, to obtain the list of +capabilities returned from the CAPA command. + +# IMAP + +## Enhanced capability support + +Add the ability, for an application that uses libcurl, to obtain the list of +capabilities returned from the CAPABILITY command. + +# LDAP + +## SASL based authentication mechanisms + +Currently the LDAP module only supports `ldap_simple_bind_s()` in order to +bind to an LDAP server. This function sends username and password details +using the simple authentication mechanism (as clear text). It should be +possible to use `ldap_bind_s()` instead specifying the security context +information ourselves. + +## `CURLOPT_SSL_CTX_FUNCTION` for LDAPS + +`CURLOPT_SSL_CTX_FUNCTION` works perfectly for HTTPS and email protocols, but +it has no effect for LDAPS connections. + +[curl issue 4108](https://github.com/curl/curl/issues/4108) + +## Paged searches on LDAP server + +[curl issue 4452](https://github.com/curl/curl/issues/4452) + +## Certificate-Based Authentication + +LDAPS not possible with macOS and Windows with Certificate-Based Authentication + +[curl issue 9641](https://github.com/curl/curl/issues/9641) + +# SMB + +## Support modern versions + +curl only supports version 1, which barely anyone is using anymore. + +## File listing support + +Add support for listing the contents of an SMB share. The output should +probably be the same as/similar to FTP. + +## Honor file timestamps + +The timestamp of the transferred file should reflect that of the original +file. + +## Use NTLMv2 + +Currently the SMB authentication uses NTLMv1. + +## Create remote directories + +Support for creating remote directories when uploading a file to a directory +that does not exist on the server, like `--ftp-create-dirs`. + +# FILE + +## Directory listing on non-POSIX + +Listing the contents of a directory accessed with FILE only works on platforms +with `opendir()`. Support could be added for more systems, like Windows. + +# TLS + +## `TLS-PSK` with OpenSSL + +Transport Layer Security pre-shared key cipher suites (`TLS-PSK`) is a set of +cryptographic protocols that provide secure communication based on pre-shared +keys (`PSK`). These pre-shared keys are symmetric keys shared in advance among +the communicating parties. + +[curl issue 5081](https://github.com/curl/curl/issues/5081) + +## TLS channel binding + +TLS 1.2 and 1.3 provide the ability to extract some secret data from the TLS +connection and use it in the client request (usually in some sort of +authentication) to ensure that the data sent is bound to the specific TLS +connection and cannot be successfully intercepted by a proxy. This +functionality can be used in a standard authentication mechanism such as +GSS-API or SCRAM, or in custom approaches like custom HTTP Authentication +headers. + +For TLS 1.2, the binding type is usually `tls-unique`, and for TLS 1.3 it is +`tls-exporter`. + +- https://datatracker.ietf.org/doc/html/rfc5929 +- https://datatracker.ietf.org/doc/html/rfc9266 +- [curl issue 9226](https://github.com/curl/curl/issues/9226) + +## Defeat TLS fingerprinting + +By changing the order of TLS extensions provided in the TLS handshake, it is +sometimes possible to circumvent TLS fingerprinting by servers. The TLS +extension order is of course not the only way to fingerprint a client. + +## Consider OCSP stapling by default + +Treat a negative response a reason for aborting the connection. Since OCSP +stapling is presumed to get used much less in the future when Let's Encrypt +drops the OCSP support, the benefit of this might however be limited. + +[curl issue 15483](https://github.com/curl/curl/issues/15483) + +## Provide callback for cert verification + +OpenSSL supports a callback for customized verification of the peer +certificate, but this does not seem to be exposed in the libcurl APIs. Could +it be? There is so much that could be done if it were. + +## Less memory massaging with Schannel + +The Schannel backend does a lot of custom memory management we would rather +avoid: the repeated allocation + free in sends and the custom memory + realloc +system for encrypted and decrypted data. That should be avoided and reduced +for 1) efficiency and 2) safety. + +## Support DANE + +[DNS-Based Authentication of Named Entities +(DANE)](https://datatracker.ietf.org/doc/html/rfc6698) is a way to provide +SSL keys and certs over DNS using DNSSEC as an alternative to the CA model. + +A patch was posted on March 7 2013 +(https://curl.se/mail/lib-2013-03/0075.html) but it was a too simple approach. +See Daniel's comments: https://curl.se/mail/lib-2013-03/0103.html + +Björn Stenberg once wrote a separate initial take on DANE that was never +completed. + +## TLS record padding + +TLS (1.3) offers optional record padding and OpenSSL provides an API for it. I +could make sense for libcurl to offer this ability to applications to make +traffic patterns harder to figure out by network traffic observers. + +See [curl issue 5398](https://github.com/curl/curl/issues/5398) + +## Support Authority Information Access certificate extension (AIA) + +AIA can provide various things like certificate revocation lists but more +importantly information about intermediate CA certificates that can allow +validation path to be fulfilled when the HTTPS server does not itself provide +them. + +Since AIA is about downloading certs on demand to complete a TLS handshake, it +is probably a bit tricky to get done right and a serious privacy leak. + +See [curl issue 2793](https://github.com/curl/curl/issues/2793) + +## Some TLS options are not offered for HTTPS proxies + +Some TLS related options to the command line tool and libcurl are only +provided for the server and not for HTTPS proxies. `--proxy-tls-max`, +`--proxy-tlsv1.3`, `--proxy-curves` and a few more. For more Documentation on +this see: https://curl.se/libcurl/c/tls-options.html + +[curl issue 12286](https://github.com/curl/curl/issues/12286) + +## Make sure we forbid TLS 1.3 post-handshake authentication + +RFC 8740 explains how using HTTP/2 must forbid the use of TLS 1.3 +post-handshake authentication. We should make sure to live up to that. + +See [curl issue 5396](https://github.com/curl/curl/issues/5396) + +## Support the `clienthello` extension + +Certain stupid networks and middle boxes have a problem with SSL handshake +packets that are within a certain size range because how that sets some bits +that previously (in older TLS version) were not set. The `clienthello` +extension adds padding to avoid that size range. + +- https://datatracker.ietf.org/doc/html/rfc7685 +- [curl issue 2299](https://github.com/curl/curl/issues/2299) + +## Share the CA cache + +For TLS backends that supports CA caching, it makes sense to allow the share +object to be used to store the CA cache as well via the share API. Would allow +multiple easy handles to reuse the CA cache and save themselves from a lot of +extra processing overhead. + +## Add missing features to TLS backends + +The feature matrix at https://curl.se/libcurl/c/tls-options.html shows which +features are supported by which TLS backends, and thus also where there are +feature gaps. + +# Proxy + +## Retry SOCKS handshake on address type not supported + +When curl resolves a hostname, it might get a mix of IPv6 and IPv4 returned. +curl might then use an IPv6 address with a SOCKS5 proxy, which - if it does +not support IPv6 - returns "Address type not supported" and curl exits with +that error. + +Perhaps it is preferred if curl would in this situation instead first retry +the SOCKS handshake again for this case and then use one of the IPv4 addresses +for the target host. + +See [curl issue 17222](https://github.com/curl/curl/issues/17222) + +# Schannel + +## Extend support for client certificate authentication + +The existing support for the `-E`/`--cert` and `--key` options could be +extended by supplying a custom certificate and key in PEM format, see: +[Getting a Certificate for +Schannel](https://learn.microsoft.com/windows/win32/secauthn/getting-a-certificate-for-schannel) + +## Extend support for the `--ciphers` option + +The existing support for the `--ciphers` option could be extended by mapping +the OpenSSL/GnuTLS cipher suites to the Schannel APIs, see [Specifying +Schannel Ciphers and Cipher +Strengths](https://learn.microsoft.com/windows/win32/secauthn/specifying-schannel-ciphers-and-cipher-strengths). + +## Add option to allow abrupt server closure + +libcurl with Schannel errors without a known termination point from the server +(such as length of transfer, or SSL "close notify" alert) to prevent against a +truncation attack. Really old servers may neglect to send any termination +point. An option could be added to ignore such abrupt closures. + +[curl issue 4427](https://github.com/curl/curl/issues/4427) + +# SASL + +## Other authentication mechanisms + +Add support for other authentication mechanisms such as `OLP`, `GSS-SPNEGO` +and others. + +## Add `QOP` support to GSSAPI authentication + +Currently the GSSAPI authentication only supports the default `QOP` of auth +(Authentication), whilst Kerberos V5 supports both `auth-int` (Authentication +with integrity protection) and `auth-conf` (Authentication with integrity and +privacy protection). + +# SSH protocols + +## Multiplexing + +SSH is a perfectly fine multiplexed protocols which would allow libcurl to do +multiple parallel transfers from the same host using the same connection, much +in the same spirit as HTTP/2 does. libcurl however does not take advantage of +that ability but does instead always create a new connection for new transfers +even if an existing connection already exists to the host. + +To fix this, libcurl would have to detect an existing connection and "attach" +the new transfer to the existing one. + +## Handle growing SFTP files + +The SFTP code in libcurl checks the file size *before* a transfer starts and +then proceeds to transfer exactly that amount of data. If the remote file +grows while the transfer is in progress libcurl does not notice and does not +adapt. The OpenSSH SFTP command line tool does and libcurl could also attempt +to download more to see if there is more to get... + +[curl issue 4344](https://github.com/curl/curl/issues/4344) + +## Read keys from `~/.ssh/id_ecdsa`, `id_ed25519` + +The libssh2 backend in curl is limited to only reading keys from `id_rsa` and +`id_dsa`, which makes it fail connecting to servers that use more modern key +types. + +[curl issue 8586](https://github.com/curl/curl/issues/8586) + +## Support `CURLOPT_PREQUOTE` + +The two other `QUOTE` options are supported for SFTP, but this was left out +for unknown reasons. + +## SSH over HTTPS proxy for libssh + +The SSH based protocols SFTP and SCP did not work over HTTPS proxy at all +until [curl pull request 6021](https://github.com/curl/curl/pull/6021) brought +the functionality with the libssh2 backend. Presumably, this support can/could +be added for the libssh backend as well. + +## SFTP with `SCP://` + +OpenSSH 9 switched their `scp` tool to speak SFTP under the hood. Going +forward it might be worth having curl or libcurl attempt SFTP if SCP fails to +follow suite. + +# Command line tool + +## multi-threading + +When asked to do transfers in parallel, the curl tool could be extended to use +a number of independent worker threads. This would allow faster transfers in +situations where curl becomes CPU bound. + +Ideally, curl would (with permission) fire up new threads on demand when it +deems that it might be helpful. Perhaps, if it has more transfers to add and +the existing transfers make the CPU busy enough and there are more cores +available. + +## sync + +`curl --sync http://example.com/feed[1-100].rss` or +`curl --sync http://example.net/{index,calendar,history}.html` + +Downloads a range or set of URLs using the remote name, but only if the remote +file is newer than the local file. A `Last-Modified` HTTP date header should +also be used to set the mod date on the downloaded file. + +## glob posts + +Globbing support for `-d` and `-F`, as in `curl -d "name=foo[0-9]" URL`. This +is easily scripted though. + +## `--proxycommand` + +Allow the user to make curl run a command and use its stdio to make requests +and not do any network connection by itself. Example: + + curl --proxycommand 'ssh pi@raspberrypi.local -W 10.1.1.75 80' \ + http://some/otherwise/unavailable/service.php + +See [curl issue 4941](https://github.com/curl/curl/issues/4941) + +## UTF-8 filenames in Content-Disposition + +RFC 6266 documents how UTF-8 names can be passed to a client in the +`Content-Disposition` header, and curl does not support this. + +[curl issue 1888](https://github.com/curl/curl/issues/1888) + +## Option to make `-Z` merge lined based outputs on stdout + +When a user requests multiple lined based files using `-Z` and sends them to +stdout, curl does not *merge* and send complete lines fine but may send +partial lines from several sources. + +[curl issue 5175](https://github.com/curl/curl/issues/5175) + +## specify which response codes that make `-f`/`--fail` return error + +Allows a user to better specify exactly which error code(s) that are fine and +which are errors for their specific uses cases + +## Choose the name of file in braces for complex URLs + +When using braces to download a list of URLs and you use complicated names +in the list of alternatives, it could be handy to allow curl to use other +names when saving. + +Consider a way to offer that. Possibly like +`{partURL1:name1,partURL2:name2,partURL3:name3}` where the name following the +colon is the output name. + +See [curl issue 221](https://github.com/curl/curl/issues/221) + +## improve how curl works in a Windows console window + +If you pull the scroll bar when transferring with curl in a Windows console +window, the transfer is interrupted and can get disconnected. This can +probably be improved. See [curl issue 322](https://github.com/curl/curl/issues/322) + +## Windows: set attribute 'archive' for completed downloads + +The archive bit (`FILE_ATTRIBUTE_ARCHIVE, 0x20`) separates files that shall be +backed up from those that are either not ready or have not changed. + +Downloads in progress are neither ready to be backed up, nor should they be +opened by a different process. Only after a download has been completed it is +sensible to include it in any integer snapshot or backup of the system. + +See [curl issue 3354](https://github.com/curl/curl/issues/3354) + +## keep running, read instructions from pipe/socket + +Provide an option that makes curl not exit after the last URL (or even work +without a given URL), and then make it read instructions passed on a pipe or +over a socket to make further instructions so that a second subsequent curl +invoke can talk to the still running instance and ask for transfers to get +done, and thus maintain its connection pool, DNS cache and more. + +## Acknowledge `Ratelimit` headers + +Consider a command line option that can make curl do multiple serial requests +while acknowledging server specified [rate +limits](https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/). + +See [curl issue 5406](https://github.com/curl/curl/issues/5406) + +## `--dry-run` + +A command line option that makes curl show exactly what it would do and send +if it would run for real. + +See [curl issue 5426](https://github.com/curl/curl/issues/5426) + +## `--retry` should resume + +When `--retry` is used and curl actually retries transfer, it should use the +already transferred data and do a resumed transfer for the rest (when +possible) so that it does not have to transfer the same data again that was +already transferred before the retry. + +See [curl issue 1084](https://github.com/curl/curl/issues/1084) + +## retry on network is unreachable + +The `--retry` option retries transfers on *transient failures*. We later added +`--retry-connrefused` to also retry for *connection refused* errors. + +Suggestions have been brought to also allow retry on *network is unreachable* +errors and while totally reasonable, maybe we should consider a way to make +this more configurable than to add a new option for every new error people +want to retry for? + +[curl issue 1603](https://github.com/curl/curl/issues/1603) + +## hostname sections in config files + +config files would be more powerful if they could set different configurations +depending on used URLs, hostname or possibly origin. Then a default `.curlrc` +could a specific user-agent only when doing requests against a certain site. + +## retry on the redirected-to URL + +When curl is told to `--retry` a failed transfer and follows redirects, it +might get an HTTP 429 response from the redirected-to URL and not the original +one, which then could make curl decide to rather retry the transfer on that +URL only instead of the original operation to the original URL. + +Perhaps extra emphasized if the original transfer is a large POST that +redirects to a separate GET, and that GET is what gets the 529 + +See [curl issue 5462](https://github.com/curl/curl/issues/5462) + +## Set the modification date on an uploaded file + +For SFTP and possibly FTP, curl could offer an option to set the modification +time for the uploaded file. + +See [curl issue 5768](https://github.com/curl/curl/issues/5768) + +## Use multiple parallel transfers for a single download + +To enhance transfer speed, downloading a single URL can be split up into +multiple separate range downloads that get combined into a single final +result. + +An ideal implementation would not use a specified number of parallel +transfers, but curl could: +- First start getting the full file as transfer A +- If after N seconds have passed and the transfer is expected to continue for + M seconds or more, add a new transfer (B) that asks for the second half of + A's content (and stop A at the middle). +- If splitting up the work improves the transfer rate, it could then be done + again. Then again, etc up to a limit. + +This way, if transfer B fails (because Range: is not supported) it lets +transfer A remain the single one. N and M could be set to some sensible +defaults. + +See [curl issue 5774](https://github.com/curl/curl/issues/5774) + +## Prevent terminal injection when writing to terminal + +curl could offer an option to make escape sequence either non-functional or +avoid cursor moves or similar to reduce the risk of a user getting tricked by +clever tricks. + +See [curl issue 6150](https://github.com/curl/curl/issues/6150) + +## `-J` and `-O` with %-encoded filenames + +`-J`/`--remote-header-name` does not decode %-encoded filenames. RFC 6266 +details how it should be done. The can of worm is that we have no charset +handling in curl and ASCII >=128 is a challenge for us. Not to mention that +decoding also means that we need to check for nastiness that is attempted, +like `../` sequences and the like. Probably everything to the left of any +embedded slashes should be cut off. See https://curl.se/bug/view.cgi?id=1294 + +`-O` also does not decode %-encoded names, and while it has even less +information about the charset involved the process is similar to the `-J` +case. + +Note that we do not decode `-O` without the user asking for it with some other +means, since `-O` has always been documented to use the name exactly as +specified in the URL. + +## `-J` with `-C -` + +When using `-J` (with `-O`), automatically resumed downloading together with +`-C -` fails. Without `-J` the same command line works. This happens because +the resume logic is worked out before the target filename (and thus its +pre-transfer size) has been figured out. This can be improved. + +https://curl.se/bug/view.cgi?id=1169 + +## `--retry` and transfer timeouts + +If using `--retry` and the transfer timeouts (possibly due to using -m or +`-y`/`-Y`) the next attempt does not resume the transfer properly from what +was downloaded in the previous attempt but truncates and restarts at the +original position where it was at before the previous failed attempt. See +https://curl.se/mail/lib-2008-01/0080.html + +# Build + +## Enable `PIE` and `RELRO` by default + +Especially when having programs that execute curl via the command line, `PIE` +renders the exploitation of memory corruption vulnerabilities a lot more +difficult. This can be attributed to the additional information leaks being +required to conduct a successful attack. `RELRO`, on the other hand, masks +different binary sections like the `GOT` as read-only and thus kills a handful +of techniques that come in handy when attackers are able to arbitrarily +overwrite memory. A few tests showed that enabling these features had close to +no impact, neither on the performance nor on the general functionality of +curl. + +## Do not use GNU libtool on OpenBSD + +When compiling curl on OpenBSD with `--enable-debug` it gives linking errors +when you use GNU libtool. This can be fixed by using the libtool provided by +OpenBSD itself. However for this the user always needs to invoke make with +`LIBTOOL=/usr/bin/libtool`. It would be nice if the script could have some +logic to detect if this system is an OpenBSD host and then use the OpenBSD +libtool instead. + +See [curl issue 5862](https://github.com/curl/curl/issues/5862) + +## Package curl for Windows in a signed installer + +See [curl issue 5424](https://github.com/curl/curl/issues/5424) + +## make configure use `--cache-file` more and better + +The configure script can be improved to cache more values so that repeated +invokes run much faster. + +See [curl issue 7753](https://github.com/curl/curl/issues/7753) + +# Test suite + +## SSL tunnel + +Make our own version of stunnel for simple port forwarding to enable HTTPS and +FTP-SSL tests without the stunnel dependency, and it could allow us to provide +test tools built with either OpenSSL or GnuTLS + +## more protocols supported + +Extend the test suite to include more protocols. The telnet could do FTP or +http operations (for which we have test servers). + +## more platforms supported + +Make the test suite work on more platforms. OpenBSD and macOS. Remove fork()s +and it should become even more portable. + +## write an SMB test server to replace impacket + +This would allow us to run SMB tests on more platforms and do better and more +covering tests. + +See [curl issue 15697](https://github.com/curl/curl/issues/15697) + +## Use the RFC 6265 test suite + +A test suite made for HTTP cookies (RFC 6265) by Adam Barth [is +available](https://github.com/abarth/http-state/tree/master/tests). + +It would be good if someone would write a script/setup that would run curl +with that test suite and detect deviance. Ideally, that would even be +incorporated into our regular test suite. + +## Run web-platform-tests URL tests + +Run web-platform-tests URL tests and compare results with browsers on +`wpt.fyi`. + +It would help us find issues to fix and help us document where our parser +differs from the WHATWG URL spec parsers. + +See [curl issue 4477](https://github.com/curl/curl/issues/4477) + +# MQTT + +## Support rate-limiting + +The rate-limiting logic is done in the PERFORMING state in multi.c but MQTT is +not (yet) implemented to use that. + +## Support MQTTS + +## Handle network blocks + +Running test suite with `CURL_DBG_SOCK_WBLOCK=90 ./runtests.pl -a mqtt` makes +several MQTT test cases fail where they should not. + +## large payloads + +libcurl unnecessarily allocates heap memory to hold the entire payload to get +sent, when the data is already perfectly accessible where it is when +`CURLOPT_POSTFIELDS` is used. This is highly inefficient for larger payloads. +Additionally, libcurl does not support using the read callback for sending +MQTT which is yet another way to avoid having to hold large payload in memory. + +# TFTP + +## TFTP does not convert LF to CRLF for `mode=netascii` + +RFC 3617 defines that an TFTP transfer can be done using `netascii` mode. curl +does not support extracting that mode from the URL nor does it treat such +transfers specifically. It should probably do LF to CRLF translations for +them. + +See [curl issue 12655](https://github.com/curl/curl/issues/12655) + +# Gopher + +## Handle network blocks + +Running test suite with `CURL_DBG_SOCK_WBLOCK=90 ./runtests.pl -a 1200 to +1300` makes several Gopher test cases fail where they should not. diff --git a/docs/TheArtOfHttpScripting.md b/docs/TheArtOfHttpScripting.md index 46369d6eaf..7f300f0703 100644 --- a/docs/TheArtOfHttpScripting.md +++ b/docs/TheArtOfHttpScripting.md @@ -8,236 +8,233 @@ SPDX-License-Identifier: curl ## Background - This document assumes that you are familiar with HTML and general networking. +This document assumes that you are familiar with HTML and general networking. - The increasing amount of applications moving to the web has made "HTTP - Scripting" more frequently requested and wanted. To be able to automatically - extract information from the web, to fake users, to post or upload data to - web servers are all important tasks today. +The increasing amount of applications moving to the web has made "HTTP +Scripting" more frequently requested and wanted. To be able to automatically +extract information from the web, to fake users, to post or upload data to +web servers are all important tasks today. - curl is a command line tool for doing all sorts of URL manipulations and - transfers, but this particular document focuses on how to use it when doing - HTTP requests for fun and profit. This documents assumes that you know how to - invoke `curl --help` or `curl --manual` to get basic information about it. +curl is a command line tool for doing all sorts of URL manipulations and +transfers, but this particular document focuses on how to use it when doing +HTTP requests for fun and profit. This documents assumes that you know how to +invoke `curl --help` or `curl --manual` to get basic information about it. - curl is not written to do everything for you. It makes the requests, it gets - the data, it sends data and it retrieves the information. You probably need - to glue everything together using some kind of script language or repeated - manual invokes. +curl is not written to do everything for you. It makes the requests, it gets +the data, it sends data and it retrieves the information. You probably need +to glue everything together using some kind of script language or repeated +manual invokes. ## The HTTP Protocol - HTTP is the protocol used to fetch data from web servers. It is a simple - protocol that is built upon TCP/IP. The protocol also allows information to - get sent to the server from the client using a few different methods, as is - shown here. +HTTP is the protocol used to fetch data from web servers. It is a simple +protocol that is built upon TCP/IP. The protocol also allows information to +get sent to the server from the client using a few different methods, as is +shown here. - HTTP is plain ASCII text lines being sent by the client to a server to - request a particular action, and then the server replies a few text lines - before the actual requested content is sent to the client. +HTTP is plain ASCII text lines being sent by the client to a server to +request a particular action, and then the server replies a few text lines +before the actual requested content is sent to the client. - The client, curl, sends an HTTP request. The request contains a method (like - GET, POST, HEAD etc), a number of request headers and sometimes a request - body. The HTTP server responds with a status line (indicating if things went - well), response headers and most often also a response body. The "body" part - is the plain data you requested, like the actual HTML or the image etc. +The client, curl, sends an HTTP request. The request contains a method (like +GET, POST, HEAD etc), a number of request headers and sometimes a request +body. The HTTP server responds with a status line (indicating if things went +well), response headers and most often also a response body. The "body" part +is the plain data you requested, like the actual HTML or the image etc. ## See the Protocol - Using curl's option [`--verbose`](https://curl.se/docs/manpage.html#-v) (`-v` - as a short option) displays what kind of commands curl sends to the server, - as well as a few other informational texts. +Using curl's option [`--verbose`](https://curl.se/docs/manpage.html#-v) (`-v` +as a short option) displays what kind of commands curl sends to the server, +as well as a few other informational texts. - `--verbose` is the single most useful option when it comes to debug or even - understand the curl<->server interaction. +`--verbose` is the single most useful option when it comes to debug or even +understand the curl<->server interaction. - Sometimes even `--verbose` is not enough. Then - [`--trace`](https://curl.se/docs/manpage.html#-trace) and - [`--trace-ascii`](https://curl.se/docs/manpage.html#--trace-ascii) - offer even more details as they show **everything** curl sends and - receives. Use it like this: +Sometimes even `--verbose` is not enough. Then +[`--trace`](https://curl.se/docs/manpage.html#-trace) and +[`--trace-ascii`](https://curl.se/docs/manpage.html#--trace-ascii) +offer even more details as they show **everything** curl sends and +receives. Use it like this: - curl --trace-ascii debugdump.txt http://www.example.com/ + curl --trace-ascii debugdump.txt https://www.example.com/ ## See the Timing - Many times you may wonder what exactly is taking all the time, or you just - want to know the amount of milliseconds between two points in a transfer. For - those, and other similar situations, the - [`--trace-time`](https://curl.se/docs/manpage.html#--trace-time) option is - what you need. It prepends the time to each trace output line: +Many times you may wonder what exactly is taking all the time, or you want to +know the amount of milliseconds between two points in a transfer. For those, +and other similar situations, the +[`--trace-time`](https://curl.se/docs/manpage.html#--trace-time) option is +what you need. It prepends the time to each trace output line: - curl --trace-ascii d.txt --trace-time http://example.com/ + curl --trace-ascii d.txt --trace-time https://example.com/ ## See which Transfer - When doing parallel transfers, it is relevant to see which transfer is doing - what. When response headers are received (and logged) you need to know which - transfer these are for. - [`--trace-ids`](https://curl.se/docs/manpage.html#--trace-ids) option is what - you need. It prepends the transfer and connection identifier to each trace - output line: +When doing parallel transfers, it is relevant to see which transfer is doing +what. When response headers are received (and logged) you need to know which +transfer these are for. +[`--trace-ids`](https://curl.se/docs/manpage.html#--trace-ids) option is what +you need. It prepends the transfer and connection identifier to each trace +output line: - curl --trace-ascii d.txt --trace-ids http://example.com/ + curl --trace-ascii d.txt --trace-ids https://example.com/ ## See the Response - By default curl sends the response to stdout. You need to redirect it - somewhere to avoid that, most often that is done with `-o` or `-O`. +By default curl sends the response to stdout. You need to redirect it +somewhere to avoid that, most often that is done with `-o` or `-O`. # URL ## Spec - The Uniform Resource Locator format is how you specify the address of a - particular resource on the Internet. You know these, you have seen URLs like - https://curl.se or https://example.com a million times. RFC 3986 is the - canonical spec. The formal name is not URL, it is **URI**. +The Uniform Resource Locator format is how you specify the address of a +particular resource on the Internet. You know these, you have seen URLs like +https://curl.se/ or https://example.com/ a million times. RFC 3986 is the +canonical spec. The formal name is not URL, it is **URI**. ## Host - The hostname is usually resolved using DNS or your /etc/hosts file to an IP - address and that is what curl communicates with. Alternatively you specify - the IP address directly in the URL instead of a name. +The hostname is usually resolved using DNS or your /etc/hosts file to an IP +address and that is what curl communicates with. Alternatively you specify +the IP address directly in the URL instead of a name. - For development and other trying out situations, you can point to a different - IP address for a hostname than what would otherwise be used, by using curl's - [`--resolve`](https://curl.se/docs/manpage.html#--resolve) option: +For development and other trying out situations, you can point to a different +IP address for a hostname than what would otherwise be used, by using curl's +[`--resolve`](https://curl.se/docs/manpage.html#--resolve) option: - curl --resolve www.example.org:80:127.0.0.1 http://www.example.org/ + curl --resolve www.example.org:80:127.0.0.1 https://www.example.org/ ## Port number - Each protocol curl supports operates on a default port number, be it over TCP - or in some cases UDP. Normally you do not have to take that into - consideration, but at times you run test servers on other ports or - similar. Then you can specify the port number in the URL with a colon and a - number immediately following the hostname. Like when doing HTTP to port - 1234: +Each protocol curl supports operates on a default port number, be it over TCP +or in some cases UDP. Normally you do not have to take that into +consideration, but at times you run test servers on other ports or +similar. Then you can specify the port number in the URL with a colon and a +number immediately following the hostname. Like when doing HTTP to port +1234: - curl http://www.example.org:1234/ + curl https://www.example.org:1234/ - The port number you specify in the URL is the number that the server uses to - offer its services. Sometimes you may use a proxy, and then you may - need to specify that proxy's port number separately from what curl needs to - connect to the server. Like when using an HTTP proxy on port 4321: +The port number you specify in the URL is the number that the server uses to +offer its services. Sometimes you may use a proxy, and then you may +need to specify that proxy's port number separately from what curl needs to +connect to the server. Like when using an HTTP proxy on port 4321: - curl --proxy http://proxy.example.org:4321 http://remote.example.org/ + curl --proxy http://proxy.example.org:4321 https://remote.example.org/ ## Username and password - Some services are setup to require HTTP authentication and then you need to - provide name and password which is then transferred to the remote site in - various ways depending on the exact authentication protocol used. +Some services are setup to require HTTP authentication and then you need to +provide name and password which is then transferred to the remote site in +various ways depending on the exact authentication protocol used. - You can opt to either insert the user and password in the URL or you can - provide them separately: +You can opt to either insert the user and password in the URL or you can +provide them separately: - curl http://user:password@example.org/ + curl https://user:password@example.org/ - or +or - curl -u user:password http://example.org/ + curl -u user:password https://example.org/ - You need to pay attention that this kind of HTTP authentication is not what - is usually done and requested by user-oriented websites these days. They tend - to use forms and cookies instead. +You need to pay attention that this kind of HTTP authentication is not what +is usually done and requested by user-oriented websites these days. They tend +to use forms and cookies instead. ## Path part - The path part is just sent off to the server to request that it sends back - the associated response. The path is what is to the right side of the slash - that follows the hostname and possibly port number. +The path part is sent off to the server to request that it sends back the +associated response. The path is what is to the right side of the slash that +follows the hostname and possibly port number. # Fetch a page ## GET - The simplest and most common request/operation made using HTTP is to GET a - URL. The URL could itself refer to a webpage, an image or a file. The client - issues a GET request to the server and receives the document it asked for. - If you issue the command line +The simplest and most common request/operation made using HTTP is to GET a +URL. The URL could itself refer to a webpage, an image or a file. The client +issues a GET request to the server and receives the document it asked for. +If you issue the command line - curl https://curl.se + curl https://curl.se/ - you get a webpage returned in your terminal window. The entire HTML document - this URL identifies. +you get a webpage returned in your terminal window. The entire HTML document +this URL identifies. - All HTTP replies contain a set of response headers that are normally hidden, - use curl's [`--include`](https://curl.se/docs/manpage.html#-i) (`-i`) - option to display them as well as the rest of the document. +All HTTP replies contain a set of response headers that are normally hidden, +use curl's [`--include`](https://curl.se/docs/manpage.html#-i) (`-i`) +option to display them as well as the rest of the document. ## HEAD - You can ask the remote server for ONLY the headers by using the - [`--head`](https://curl.se/docs/manpage.html#-I) (`-I`) option which makes - curl issue a HEAD request. In some special cases servers deny the HEAD method - while others still work, which is a particular kind of annoyance. +You can ask the remote server for ONLY the headers by using the +[`--head`](https://curl.se/docs/manpage.html#-I) (`-I`) option which makes +curl issue a HEAD request. In some special cases servers deny the HEAD method +while others still work, which is a particular kind of annoyance. - The HEAD method is defined and made so that the server returns the headers - exactly the way it would do for a GET, but without a body. It means that you - may see a `Content-Length:` in the response headers, but there must not be an - actual body in the HEAD response. +The HEAD method is defined and made so that the server returns the headers +exactly the way it would do for a GET, but without a body. It means that you +may see a `Content-Length:` in the response headers, but there must not be an +actual body in the HEAD response. ## Multiple URLs in a single command line - A single curl command line may involve one or many URLs. The most common case - is probably to just use one, but you can specify any amount of URLs. Yes any. - No limits. You then get requests repeated over and over for all the given - URLs. +A single curl command line may involve one or many URLs. The most common case +is probably to use one, but you can specify any amount of URLs. Yes any. No +limits. You then get requests repeated over and over for all the given URLs. - Example, send two GET requests: +Example, send two GET requests: - curl http://url1.example.com http://url2.example.com + curl https://url1.example.com https://url2.example.com - If you use [`--data`](https://curl.se/docs/manpage.html#-d) to POST to - the URL, using multiple URLs means that you send that same POST to all the - given URLs. +If you use [`--data`](https://curl.se/docs/manpage.html#-d) to POST to +the URL, using multiple URLs means that you send that same POST to all the +given URLs. - Example, send two POSTs: - - curl --data name=curl http://url1.example.com http://url2.example.com +Example, send two POSTs: + curl --data name=curl https://url1.example.com https://url2.example.com ## Multiple HTTP methods in a single command line - Sometimes you need to operate on several URLs in a single command line and do - different HTTP methods on each. For this, you might enjoy the - [`--next`](https://curl.se/docs/manpage.html#-:) option. It is basically a - separator that separates a bunch of options from the next. All the URLs - before `--next` get the same method and get all the POST data merged into - one. +Sometimes you need to operate on several URLs in a single command line and do +different HTTP methods on each. For this, you might enjoy the +[`--next`](https://curl.se/docs/manpage.html#-:) option. It is a separator +that separates a bunch of options from the next. All the URLs before `--next` +get the same method and get all the POST data merged into one. - When curl reaches the `--next` on the command line, it resets the method and - the POST data and allow a new set. +When curl reaches the `--next` on the command line, it resets the method and +the POST data and allow a new set. - Perhaps this is best shown with a few examples. To send first a HEAD and then - a GET: +Perhaps this is best shown with a few examples. To send first a HEAD and then +a GET: - curl -I http://example.com --next http://example.com + curl -I https://example.com --next https://example.com - To first send a POST and then a GET: +To first send a POST and then a GET: - curl -d score=10 http://example.com/post.cgi --next http://example.com/results.html + curl -d score=10 https://example.com/post.cgi --next https://example.com/results.html # HTML forms ## Forms explained - Forms are the general way a website can present an HTML page with fields for - the user to enter data in, and then press some kind of 'OK' or 'Submit' - button to get that data sent to the server. The server then typically uses - the posted data to decide how to act. Like using the entered words to search - in a database, or to add the info in a bug tracking system, display the - entered address on a map or using the info as a login-prompt verifying that - the user is allowed to see what it is about to see. +Forms are the general way a website can present an HTML page with fields for +the user to enter data in, and then press some kind of 'OK' or 'Submit' +button to get that data sent to the server. The server then typically uses +the posted data to decide how to act. Like using the entered words to search +in a database, or to add the info in a bug tracking system, display the +entered address on a map or using the info as a login-prompt verifying that +the user is allowed to see what it is about to see. - Of course there has to be some kind of program on the server end to receive - the data you send. You cannot just invent something out of the air. +Of course there has to be some kind of program on the server end to receive +the data you send. You cannot invent something out of the air. ## GET - A GET-form uses the method GET, as specified in HTML like: +A GET-form uses the method GET, as specified in HTML like: ```html @@ -246,36 +243,35 @@ SPDX-License-Identifier: curl ``` - In your favorite browser, this form appears with a text box to fill in and a - press-button labeled "OK". If you fill in '1905' and press the OK button, - your browser then creates a new URL to get for you. The URL gets - `junk.cgi?birthyear=1905&press=OK` appended to the path part of the previous - URL. +In your favorite browser, this form appears with a text box to fill in and a +press-button labeled "OK". If you fill in '1905' and press the OK button, +your browser then creates a new URL to get for you. The URL gets +`junk.cgi?birthyear=1905&press=OK` appended to the path part of the previous +URL. - If the original form was seen on the page `www.example.com/when/birth.html`, - the second page you get becomes - `www.example.com/when/junk.cgi?birthyear=1905&press=OK`. +If the original form was seen on the page `www.example.com/when/birth.html`, +the second page you get becomes +`www.example.com/when/junk.cgi?birthyear=1905&press=OK`. - Most search engines work this way. +Most search engines work this way. - To make curl do the GET form post for you, just enter the expected created - URL: +To make curl do the GET form post for you, enter the expected created URL: - curl "http://www.example.com/when/junk.cgi?birthyear=1905&press=OK" + curl "https://www.example.com/when/junk.cgi?birthyear=1905&press=OK" ## POST - The GET method makes all input field names get displayed in the URL field of - your browser. That is generally a good thing when you want to be able to - bookmark that page with your given data, but it is an obvious disadvantage if - you entered secret information in one of the fields or if there are a large - amount of fields creating a long and unreadable URL. +The GET method makes all input field names get displayed in the URL field of +your browser. That is generally a good thing when you want to be able to +bookmark that page with your given data, but it is an obvious disadvantage if +you entered secret information in one of the fields or if there are a large +amount of fields creating a long and unreadable URL. - The HTTP protocol then offers the POST method. This way the client sends the - data separated from the URL and thus you do not see any of it in the URL - address field. +The HTTP protocol then offers the POST method. This way the client sends the +data separated from the URL and thus you do not see any of it in the URL +address field. - The form would look similar to the previous one: +The form would look similar to the previous one: ```html
@@ -284,56 +280,56 @@ SPDX-License-Identifier: curl
``` - And to use curl to post this form with the same data filled in as before, we - could do it like: +To use curl to post this form with the same data filled in as before, we +could do it like: - curl --data "birthyear=1905&press=%20OK%20" http://www.example.com/when/junk.cgi + curl --data "birthyear=1905&press=%20OK%20" https://www.example.com/when/junk.cgi - This kind of POST uses the Content-Type `application/x-www-form-urlencoded` - and is the most widely used POST kind. +This kind of POST uses the Content-Type `application/x-www-form-urlencoded` +and is the most widely used POST kind. - The data you send to the server MUST already be properly encoded, curl does - not do that for you. For example, if you want the data to contain a space, - you need to replace that space with `%20`, etc. Failing to comply with this - most likely causes your data to be received wrongly and messed up. +The data you send to the server MUST already be properly encoded, curl does +not do that for you. For example, if you want the data to contain a space, +you need to replace that space with `%20`, etc. Failing to comply with this +most likely causes your data to be received wrongly and messed up. - Recent curl versions can in fact url-encode POST data for you, like this: +Recent curl versions can in fact URL encode POST data for you, like this: - curl --data-urlencode "name=I am Daniel" http://www.example.com + curl --data-urlencode "name=I am Daniel" https://www.example.com - If you repeat `--data` several times on the command line, curl concatenates - all the given data pieces - and put a `&` symbol between each data segment. +If you repeat `--data` several times on the command line, curl concatenates +all the given data pieces - and put a `&` symbol between each data segment. ## File Upload POST - Back in late 1995 they defined an additional way to post data over HTTP. It - is documented in the RFC 1867, why this method sometimes is referred to as - RFC 1867-posting. +Back in late 1995 they defined an additional way to post data over HTTP. It +is documented in the RFC 1867, why this method sometimes is referred to as +RFC 1867-posting. - This method is mainly designed to better support file uploads. A form that - allows a user to upload a file could be written like this in HTML: +This method is mainly designed to better support file uploads. A form that +allows a user to upload a file could be written like this in HTML:
- This clearly shows that the Content-Type about to be sent is - `multipart/form-data`. +This clearly shows that the Content-Type about to be sent is +`multipart/form-data`. - To post to a form like this with curl, you enter a command line like: +To post to a form like this with curl, you enter a command line like: curl --form upload=@localfilename --form press=OK [URL] ## Hidden Fields - A common way for HTML based applications to pass state information between - pages is to add hidden fields to the forms. Hidden fields are already filled - in, they are not displayed to the user and they get passed along just as all - the other fields. +A common way for HTML based applications to pass state information between +pages is to add hidden fields to the forms. Hidden fields are already filled +in, they are not displayed to the user and they get passed along as all the +other fields. - A similar example form with one visible field, one hidden field and one - submit button could look like: +A similar example form with one visible field, one hidden field and one +submit button could look like: ```html
@@ -343,126 +339,126 @@ SPDX-License-Identifier: curl
``` - To POST this with curl, you do not have to think about if the fields are - hidden or not. To curl they are all the same: +To POST this with curl, you do not have to think about if the fields are +hidden or not. To curl they are all the same: curl --data "birthyear=1905&press=OK&person=daniel" [URL] ## Figure Out What A POST Looks Like - When you are about to fill in a form and send it to a server by using curl - instead of a browser, you are of course interested in sending a POST exactly - the way your browser does. +When you are about to fill in a form and send it to a server by using curl +instead of a browser, you are of course interested in sending a POST exactly +the way your browser does. - An easy way to get to see this, is to save the HTML page with the form on - your local disk, modify the 'method' to a GET, and press the submit button - (you could also change the action URL if you want to). +An easy way to get to see this, is to save the HTML page with the form on +your local disk, modify the 'method' to a GET, and press the submit button +(you could also change the action URL if you want to). - You then clearly see the data get appended to the URL, separated with a - `?`-letter as GET forms are supposed to. +You then clearly see the data get appended to the URL, separated with a +`?`-letter as GET forms are supposed to. # HTTP upload ## PUT - Perhaps the best way to upload data to an HTTP server is to use PUT. Then - again, this of course requires that someone put a program or script on the - server end that knows how to receive an HTTP PUT stream. +Perhaps the best way to upload data to an HTTP server is to use PUT. Then +again, this of course requires that someone put a program or script on the +server end that knows how to receive an HTTP PUT stream. - Put a file to an HTTP server with curl: +Put a file to an HTTP server with curl: - curl --upload-file uploadfile http://www.example.com/receive.cgi + curl --upload-file uploadfile https://www.example.com/receive.cgi # HTTP Authentication ## Basic Authentication - HTTP Authentication is the ability to tell the server your username and - password so that it can verify that you are allowed to do the request you are - doing. The Basic authentication used in HTTP (which is the type curl uses by - default) is **plain text** based, which means it sends username and password - only slightly obfuscated, but still fully readable by anyone that sniffs on - the network between you and the remote server. +HTTP Authentication is the ability to tell the server your username and +password so that it can verify that you are allowed to do the request you are +doing. The Basic authentication used in HTTP (which is the type curl uses by +default) is **plain text** based, which means it sends username and password +only slightly obfuscated, but still fully readable by anyone that sniffs on +the network between you and the remote server. - To tell curl to use a user and password for authentication: +To tell curl to use a user and password for authentication: - curl --user name:password http://www.example.com + curl --user myname:password https://www.example.com ## Other Authentication - The site might require a different authentication method (check the headers - returned by the server), and then - [`--ntlm`](https://curl.se/docs/manpage.html#--ntlm), - [`--digest`](https://curl.se/docs/manpage.html#--digest), - [`--negotiate`](https://curl.se/docs/manpage.html#--negotiate) or even - [`--anyauth`](https://curl.se/docs/manpage.html#--anyauth) might be - options that suit you. +The site might require a different authentication method (check the headers +returned by the server), and then +[`--ntlm`](https://curl.se/docs/manpage.html#--ntlm), +[`--digest`](https://curl.se/docs/manpage.html#--digest), +[`--negotiate`](https://curl.se/docs/manpage.html#--negotiate) or even +[`--anyauth`](https://curl.se/docs/manpage.html#--anyauth) might be +options that suit you. ## Proxy Authentication - Sometimes your HTTP access is only available through the use of an HTTP - proxy. This seems to be especially common at various companies. An HTTP proxy - may require its own user and password to allow the client to get through to - the Internet. To specify those with curl, run something like: +Sometimes your HTTP access is only available through the use of an HTTP +proxy. This seems to be especially common at various companies. An HTTP proxy +may require its own user and password to allow the client to get through to +the Internet. To specify those with curl, run something like: curl --proxy-user proxyuser:proxypassword curl.se - If your proxy requires the authentication to be done using the NTLM method, - use [`--proxy-ntlm`](https://curl.se/docs/manpage.html#--proxy-ntlm), if - it requires Digest use - [`--proxy-digest`](https://curl.se/docs/manpage.html#--proxy-digest). +If your proxy requires the authentication to be done using the NTLM method, +use [`--proxy-ntlm`](https://curl.se/docs/manpage.html#--proxy-ntlm), if +it requires Digest use +[`--proxy-digest`](https://curl.se/docs/manpage.html#--proxy-digest). - If you use any one of these user+password options but leave out the password - part, curl prompts for the password interactively. +If you use any one of these user+password options but leave out the password +part, curl prompts for the password interactively. ## Hiding credentials - Do note that when a program is run, its parameters might be possible to see - when listing the running processes of the system. Thus, other users may be - able to watch your passwords if you pass them as plain command line - options. There are ways to circumvent this. +Do note that when a program is run, its parameters might be possible to see +when listing the running processes of the system. Thus, other users may be +able to watch your passwords if you pass them as plain command line +options. There are ways to circumvent this. - It is worth noting that while this is how HTTP Authentication works, many - websites do not use this concept when they provide logins etc. See the Web - Login chapter further below for more details on that. +It is worth noting that while this is how HTTP Authentication works, many +websites do not use this concept when they provide logins etc. See the Web +Login chapter further below for more details on that. # More HTTP Headers ## Referer - An HTTP request may include a 'referer' field (yes it is misspelled), which - can be used to tell from which URL the client got to this particular - resource. Some programs/scripts check the referer field of requests to verify - that this was not arriving from an external site or an unknown page. While - this is a stupid way to check something so easily forged, many scripts still - do it. Using curl, you can put anything you want in the referer-field and - thus more easily be able to fool the server into serving your request. +An HTTP request may include a 'referer' field (yes it is misspelled), which +can be used to tell from which URL the client got to this particular +resource. Some programs/scripts check the referer field of requests to verify +that this was not arriving from an external site or an unknown page. While +this is a stupid way to check something so easily forged, many scripts still +do it. Using curl, you can put anything you want in the referer-field and +thus more easily be able to fool the server into serving your request. - Use curl to set the referer field with: +Use curl to set the referer field with: - curl --referer http://www.example.come http://www.example.com + curl --referer https://www.example.come https://www.example.com ## User Agent - Similar to the referer field, all HTTP requests may set the User-Agent - field. It names what user agent (client) that is being used. Many - applications use this information to decide how to display pages. Silly web - programmers try to make different pages for users of different browsers to - make them look the best possible for their particular browsers. They usually - also do different kinds of JavaScript etc. +Similar to the referer field, all HTTP requests may set the User-Agent +field. It names what user agent (client) that is being used. Many +applications use this information to decide how to display pages. Silly web +programmers try to make different pages for users of different browsers to +make them look the best possible for their particular browsers. They usually +also do different kinds of JavaScript etc. - At times, you may learn that getting a page with curl does not return the - same page that you see when getting the page with your browser. Then you know - it is time to set the User Agent field to fool the server into thinking you - are one of those browsers. +At times, you may learn that getting a page with curl does not return the +same page that you see when getting the page with your browser. Then you know +it is time to set the User Agent field to fool the server into thinking you +are one of those browsers. - By default, curl uses curl/VERSION, such as User-Agent: curl/8.11.0. +By default, curl uses curl/VERSION, such as User-Agent: curl/8.11.0. - To make curl look like Internet Explorer 5 on a Windows 2000 box: +To make curl look like Internet Explorer 5 on a Windows 2000 box: curl --user-agent "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)" [URL] - Or why not look like you are using Netscape 4.73 on an old Linux box: +Or why not look like you are using Netscape 4.73 on an old Linux box: curl --user-agent "Mozilla/4.73 [en] (X11; U; Linux 2.2.15 i686)" [URL] @@ -470,136 +466,136 @@ SPDX-License-Identifier: curl ## Location header - When a resource is requested from a server, the reply from the server may - include a hint about where the browser should go next to find this page, or a - new page keeping newly generated output. The header that tells the browser to - redirect is `Location:`. +When a resource is requested from a server, the reply from the server may +include a hint about where the browser should go next to find this page, or a +new page keeping newly generated output. The header that tells the browser to +redirect is `Location:`. - curl does not follow `Location:` headers by default, but simply displays such - pages in the same manner it displays all HTTP replies. It does however - feature an option that makes it attempt to follow the `Location:` pointers. +curl does not follow `Location:` headers by default, but displays such +pages in the same manner it displays all HTTP replies. It does however +feature an option that makes it attempt to follow the `Location:` pointers. - To tell curl to follow a Location: +To tell curl to follow a Location: - curl --location http://www.example.com + curl --location https://www.example.com - If you use curl to POST to a site that immediately redirects you to another - page, you can safely use [`--location`](https://curl.se/docs/manpage.html#-L) - (`-L`) and `--data`/`--form` together. curl only uses POST in the first - request, and then revert to GET in the following operations. +If you use curl to POST to a site that immediately redirects you to another +page, you can safely use [`--location`](https://curl.se/docs/manpage.html#-L) +(`-L`) and `--data`/`--form` together. curl only uses POST in the first +request, and then revert to GET in the following operations. ## Other redirects - Browsers typically support at least two other ways of redirects that curl - does not: first the html may contain a meta refresh tag that asks the browser - to load a specific URL after a set number of seconds, or it may use - JavaScript to do it. +Browsers typically support at least two other ways of redirects that curl +does not: first the html may contain a meta refresh tag that asks the browser +to load a specific URL after a set number of seconds, or it may use +JavaScript to do it. # Cookies ## Cookie Basics - The way the web browsers do "client side state control" is by using - cookies. Cookies are just names with associated contents. The cookies are - sent to the client by the server. The server tells the client for what path - and hostname it wants the cookie sent back, and it also sends an expiration - date and a few more properties. +The way the web browsers do "client side state control" is by using cookies. +Cookies are names with associated contents. The cookies are sent to the client +by the server. The server tells the client for what path and hostname it wants +the cookie sent back, and it also sends an expiration date and a few more +properties. - When a client communicates with a server with a name and path as previously - specified in a received cookie, the client sends back the cookies and their - contents to the server, unless of course they are expired. +When a client communicates with a server with a name and path as previously +specified in a received cookie, the client sends back the cookies and their +contents to the server, unless of course they are expired. - Many applications and servers use this method to connect a series of requests - into a single logical session. To be able to use curl in such occasions, we - must be able to record and send back cookies the way the web application - expects them. The same way browsers deal with them. +Many applications and servers use this method to connect a series of requests +into a single logical session. To be able to use curl in such occasions, we +must be able to record and send back cookies the way the web application +expects them. The same way browsers deal with them. ## Cookie options - The simplest way to send a few cookies to the server when getting a page with - curl is to add them on the command line like: +The simplest way to send a few cookies to the server when getting a page with +curl is to add them on the command line like: - curl --cookie "name=Daniel" http://www.example.com + curl --cookie "name=Daniel" https://www.example.com - Cookies are sent as common HTTP headers. This is practical as it allows curl - to record cookies simply by recording headers. Record cookies with curl by - using the [`--dump-header`](https://curl.se/docs/manpage.html#-D) (`-D`) - option like: +Cookies are sent as common HTTP headers. This is practical as it allows curl +to record cookies by recording headers. Record cookies with curl by +using the [`--dump-header`](https://curl.se/docs/manpage.html#-D) (`-D`) +option like: - curl --dump-header headers_and_cookies http://www.example.com + curl --dump-header headers_and_cookies https://www.example.com - (Take note that the - [`--cookie-jar`](https://curl.se/docs/manpage.html#-c) option described - below is a better way to store cookies.) +(Take note that the +[`--cookie-jar`](https://curl.se/docs/manpage.html#-c) option described +below is a better way to store cookies.) - curl has a full blown cookie parsing engine built-in that comes in use if you - want to reconnect to a server and use cookies that were stored from a - previous connection (or hand-crafted manually to fool the server into - believing you had a previous connection). To use previously stored cookies, - you run curl like: +curl has a full blown cookie parsing engine built-in that comes in use if you +want to reconnect to a server and use cookies that were stored from a +previous connection (or hand-crafted manually to fool the server into +believing you had a previous connection). To use previously stored cookies, +you run curl like: - curl --cookie stored_cookies_in_file http://www.example.com + curl --cookie stored_cookies_in_file https://www.example.com - curl's "cookie engine" gets enabled when you use the - [`--cookie`](https://curl.se/docs/manpage.html#-b) option. If you only - want curl to understand received cookies, use `--cookie` with a file that - does not exist. Example, if you want to let curl understand cookies from a - page and follow a location (and thus possibly send back cookies it received), - you can invoke it like: +curl's "cookie engine" gets enabled when you use the +[`--cookie`](https://curl.se/docs/manpage.html#-b) option. If you only +want curl to understand received cookies, use `--cookie` with a file that +does not exist. Example, if you want to let curl understand cookies from a +page and follow a location (and thus possibly send back cookies it received), +you can invoke it like: - curl --cookie nada --location http://www.example.com + curl --cookie nada --location https://www.example.com - curl has the ability to read and write cookie files that use the same file - format that Netscape and Mozilla once used. It is a convenient way to share - cookies between scripts or invokes. The `--cookie` (`-b`) switch - automatically detects if a given file is such a cookie file and parses it, - and by using the `--cookie-jar` (`-c`) option you make curl write a new - cookie file at the end of an operation: +curl has the ability to read and write cookie files that use the same file +format that Netscape and Mozilla once used. It is a convenient way to share +cookies between scripts or invokes. The `--cookie` (`-b`) switch +automatically detects if a given file is such a cookie file and parses it, +and by using the `--cookie-jar` (`-c`) option you make curl write a new +cookie file at the end of an operation: curl --cookie cookies.txt --cookie-jar newcookies.txt \ - http://www.example.com + https://www.example.com # HTTPS ## HTTPS is HTTP secure - There are a few ways to do secure HTTP transfers. By far the most common - protocol for doing this is what is generally known as HTTPS, HTTP over - SSL. SSL encrypts all the data that is sent and received over the network and - thus makes it harder for attackers to spy on sensitive information. +There are a few ways to do secure HTTP transfers. By far the most common +protocol for doing this is what is generally known as HTTPS, HTTP over +SSL. SSL encrypts all the data that is sent and received over the network and +thus makes it harder for attackers to spy on sensitive information. - SSL (or TLS as the current version of the standard is called) offers a set of - advanced features to do secure transfers over HTTP. +SSL (or TLS as the current version of the standard is called) offers a set of +advanced features to do secure transfers over HTTP. - curl supports encrypted fetches when built to use a TLS library and it can be - built to use one out of a fairly large set of libraries - `curl -V` shows - which one your curl was built to use (if any). To get a page from an HTTPS - server, simply run curl like: +curl supports encrypted fetches when built to use a TLS library and it can be +built to use one out of a fairly large set of libraries - `curl -V` shows +which one your curl was built to use (if any). To get a page from an HTTPS +server, run curl like: curl https://secure.example.com ## Certificates - In the HTTPS world, you use certificates to validate that you are the one you - claim to be, as an addition to normal passwords. curl supports client- side - certificates. All certificates are locked with a passphrase, which you need - to enter before the certificate can be used by curl. The passphrase can be - specified on the command line or if not, entered interactively when curl - queries for it. Use a certificate with curl on an HTTPS server like: +In the HTTPS world, you use certificates to validate that you are the one you +claim to be, as an addition to normal passwords. curl supports client- side +certificates. All certificates are locked with a passphrase, which you need +to enter before the certificate can be used by curl. The passphrase can be +specified on the command line or if not, entered interactively when curl +queries for it. Use a certificate with curl on an HTTPS server like: curl --cert mycert.pem https://secure.example.com - curl also tries to verify that the server is who it claims to be, by - verifying the server's certificate against a locally stored CA cert bundle. - Failing the verification causes curl to deny the connection. You must then - use [`--insecure`](https://curl.se/docs/manpage.html#-k) (`-k`) in case you - want to tell curl to ignore that the server cannot be verified. +curl also tries to verify that the server is who it claims to be, by +verifying the server's certificate against a locally stored CA cert bundle. +Failing the verification causes curl to deny the connection. You must then +use [`--insecure`](https://curl.se/docs/manpage.html#-k) (`-k`) in case you +want to tell curl to ignore that the server cannot be verified. - More about server certificate verification and ca cert bundles can be read in - the [`SSLCERTS` document](https://curl.se/docs/sslcerts.html). +More about server certificate verification and ca cert bundles can be read in +the [`SSLCERTS` document](https://curl.se/docs/sslcerts.html). - At times you may end up with your own CA cert store and then you can tell - curl to use that to verify the server's certificate: +At times you may end up with your own CA cert store and then you can tell +curl to use that to verify the server's certificate: curl --cacert ca-bundle.pem https://example.com/ @@ -607,106 +603,106 @@ SPDX-License-Identifier: curl ## Modify method and headers - Doing fancy stuff, you may need to add or change elements of a single curl - request. +Doing fancy stuff, you may need to add or change elements of a single curl +request. - For example, you can change the POST method to `PROPFIND` and send the data - as `Content-Type: text/xml` (instead of the default `Content-Type`) like - this: +For example, you can change the POST method to `PROPFIND` and send the data +as `Content-Type: text/xml` (instead of the default `Content-Type`) like +this: curl --data "" --header "Content-Type: text/xml" \ --request PROPFIND example.com - You can delete a default header by providing one without content. Like you - can ruin the request by chopping off the `Host:` header: +You can delete a default header by providing one without content. Like you +can ruin the request by chopping off the `Host:` header: - curl --header "Host:" http://www.example.com + curl --header "Host:" https://www.example.com - You can add headers the same way. Your server may want a `Destination:` - header, and you can add it: +You can add headers the same way. Your server may want a `Destination:` +header, and you can add it: - curl --header "Destination: http://nowhere" http://example.com + curl --header "Destination: nowhere" https://example.com ## More on changed methods - It should be noted that curl selects which methods to use on its own - depending on what action to ask for. `-d` makes a POST, `-I` makes a HEAD and - so on. If you use the [`--request`](https://curl.se/docs/manpage.html#-X) / - `-X` option you can change the method keyword curl selects, but you do not - modify curl's behavior. This means that if you for example use -d "data" to - do a POST, you can modify the method to a `PROPFIND` with `-X` and curl still - thinks it sends a POST. You can change the normal GET to a POST method by - simply adding `-X POST` in a command line like: +It should be noted that curl selects which methods to use on its own +depending on what action to ask for. `-d` makes a POST, `-I` makes a HEAD and +so on. If you use the [`--request`](https://curl.se/docs/manpage.html#-X) / +`-X` option you can change the method keyword curl selects, but you do not +modify curl's behavior. This means that if you for example use -d "data" to +do a POST, you can modify the method to a `PROPFIND` with `-X` and curl still +thinks it sends a POST. You can change the normal GET to a POST method by +adding `-X POST` in a command line like: - curl -X POST http://example.org/ + curl -X POST https://example.org/ - curl however still acts as if it sent a GET so it does not send any request - body etc. +curl however still acts as if it sent a GET so it does not send any request +body etc. # Web Login ## Some login tricks - While not strictly just HTTP related, it still causes a lot of people - problems so here's the executive run-down of how the vast majority of all - login forms work and how to login to them using curl. +While not strictly HTTP related, it still causes a lot of people problems so +here's the executive run-down of how the vast majority of all login forms work +and how to login to them using curl. - It can also be noted that to do this properly in an automated fashion, you - most certainly need to script things and do multiple curl invokes etc. +It can also be noted that to do this properly in an automated fashion, you +most certainly need to script things and do multiple curl invokes etc. - First, servers mostly use cookies to track the logged-in status of the - client, so you need to capture the cookies you receive in the responses. - Then, many sites also set a special cookie on the login page (to make sure - you got there through their login page) so you should make a habit of first - getting the login-form page to capture the cookies set there. +First, servers mostly use cookies to track the logged-in status of the +client, so you need to capture the cookies you receive in the responses. +Then, many sites also set a special cookie on the login page (to make sure +you got there through their login page) so you should make a habit of first +getting the login-form page to capture the cookies set there. - Some web-based login systems feature various amounts of JavaScript, and - sometimes they use such code to set or modify cookie contents. Possibly they - do that to prevent programmed logins, like this manual describes how to... - Anyway, if reading the code is not enough to let you repeat the behavior - manually, capturing the HTTP requests done by your browsers and analyzing the - sent cookies is usually a working method to work out how to shortcut the - JavaScript need. +Some web-based login systems feature various amounts of JavaScript, and +sometimes they use such code to set or modify cookie contents. Possibly they +do that to prevent programmed logins, like this manual describes how to... +Anyway, if reading the code is not enough to let you repeat the behavior +manually, capturing the HTTP requests done by your browsers and analyzing the +sent cookies is usually a working method to work out how to shortcut the +JavaScript need. - In the actual `
` tag for the login, lots of sites fill-in - random/session or otherwise secretly generated hidden tags and you may need - to first capture the HTML code for the login form and extract all the hidden - fields to be able to do a proper login POST. Remember that the contents need - to be URL encoded when sent in a normal POST. +In the actual `` tag for the login, lots of sites fill-in +random/session or otherwise secretly generated hidden tags and you may need +to first capture the HTML code for the login form and extract all the hidden +fields to be able to do a proper login POST. Remember that the contents need +to be URL encoded when sent in a normal POST. # Debug ## Some debug tricks - Many times when you run curl on a site, you notice that the site does not - seem to respond the same way to your curl requests as it does to your - browser's. +Many times when you run curl on a site, you notice that the site does not +seem to respond the same way to your curl requests as it does to your +browser's. - Then you need to start making your curl requests more similar to your - browser's requests: +Then you need to start making your curl requests more similar to your +browser's requests: - - Use the `--trace-ascii` option to store fully detailed logs of the requests - for easier analyzing and better understanding +- Use the `--trace-ascii` option to store fully detailed logs of the requests + for easier analyzing and better understanding - - Make sure you check for and use cookies when needed (both reading with - `--cookie` and writing with `--cookie-jar`) +- Make sure you check for and use cookies when needed (both reading with + `--cookie` and writing with `--cookie-jar`) - - Set user-agent (with [`-A`](https://curl.se/docs/manpage.html#-A)) to - one like a recent popular browser does +- Set user-agent (with [`-A`](https://curl.se/docs/manpage.html#-A)) to + one like a recent popular browser does - - Set referer (with [`-E`](https://curl.se/docs/manpage.html#-E)) like - it is set by the browser +- Set referer (with [`-E`](https://curl.se/docs/manpage.html#-E)) like + it is set by the browser - - If you use POST, make sure you send all the fields and in the same order as - the browser does it. +- If you use POST, make sure you send all the fields and in the same order as + the browser does it. ## Check what the browsers do - A good helper to make sure you do this right, is the web browsers' developers - tools that let you view all headers you send and receive (even when using - HTTPS). +A good helper to make sure you do this right, is the web browsers' developers +tools that let you view all headers you send and receive (even when using +HTTPS). - A more raw approach is to capture the HTTP traffic on the network with tools - such as Wireshark or tcpdump and check what headers that were sent and - received by the browser. (HTTPS forces you to use `SSLKEYLOGFILE` to do - that.) +A more raw approach is to capture the HTTP traffic on the network with tools +such as Wireshark or tcpdump and check what headers that were sent and +received by the browser. (HTTPS forces you to use `SSLKEYLOGFILE` to do +that.) diff --git a/docs/URL-SYNTAX.md b/docs/URL-SYNTAX.md index 6232e3f609..45b6f5ab45 100644 --- a/docs/URL-SYNTAX.md +++ b/docs/URL-SYNTAX.md @@ -11,9 +11,9 @@ SPDX-License-Identifier: curl The official "URL syntax" is primarily defined in these two different specifications: - - [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) (although URL is called - "URI" in there) - - [The WHATWG URL Specification](https://url.spec.whatwg.org/) +- [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) (although URL is called + "URI" in there) +- [The WHATWG URL Specification](https://url.spec.whatwg.org/) RFC 3986 is the earlier one, and curl has always tried to adhere to that one (since it shipped in January 2005). @@ -100,13 +100,13 @@ supported by browsers early on and has been mimicked by curl. Based on what the hostname starts with, curl "guesses" what protocol to use: - - `ftp.` means FTP - - `dict.` means DICT - - `ldap.` means LDAP - - `imap.` means IMAP - - `smtp.` means SMTP - - `pop3.` means POP3 - - all other means HTTP +- `ftp.` means FTP +- `dict.` means DICT +- `ldap.` means LDAP +- `imap.` means IMAP +- `smtp.` means SMTP +- `pop3.` means POP3 +- all other means HTTP ### Globbing letters @@ -130,7 +130,7 @@ character or string. For example, this could look like: - http://user:password@www.example.com:80/index.html?foo=bar#top + https://user:password@www.example.com:80/index.html?foo=bar#top ## Scheme @@ -141,9 +141,8 @@ curl supports the following schemes on URLs specified to transfer. They are matched case insensitively: `dict`, `file`, `ftp`, `ftps`, `gopher`, `gophers`, `http`, `https`, `imap`, -`imaps`, `ldap`, `ldaps`, `mqtt`, `pop3`, `pop3s`, `rtmp`, `rtmpe`, `rtmps`, -`rtmpt`, `rtmpte`, `rtmpts`, `rtsp`, `smb`, `smbs`, `smtp`, `smtps`, `telnet`, -`tftp` +`imaps`, `ldap`, `ldaps`, `mqtt`, `pop3`, `pop3s`, `rtsp`, `smb`, `smbs`, +`smtp`, `smtps`, `telnet`, `tftp` When the URL is specified to identify a proxy, curl recognizes the following schemes: @@ -169,13 +168,13 @@ local network name of the machine on your network or the IP address of the server or machine represented by either an IPv4 or IPv6 address (within brackets). For example: - http://www.example.com/ + https://www.example.com/ - http://hostname/ + https://hostname.example/ - http://192.168.0.1/ + https://192.168.0.1/ - http://[2001:1890:1112:1::20]/ + https://[2001:1890:1112:1::20]/ ### "localhost" @@ -209,10 +208,9 @@ only if the URL starts with a scheme. If the port number is not specified in the URL, curl uses a default port number based on the provide scheme: -DICT 2628, FTP 21, FTPS 990, GOPHER 70, GOPHERS 70, HTTP 80, HTTPS 443, -IMAP 132, IMAPS 993, LDAP 369, LDAPS 636, MQTT 1883, POP3 110, POP3S 995, -RTMP 1935, RTMPS 443, RTMPT 80, RTSP 554, SCP 22, SFTP 22, SMB 445, SMBS 445, -SMTP 25, SMTPS 465, TELNET 23, TFTP 69 +DICT 2628, FTP 21, FTPS 990, GOPHER 70, GOPHERS 70, HTTP 80, HTTPS 443, IMAP +143, IMAPS 993, LDAP 389, LDAPS 636, MQTT 1883, POP3 110, POP3S 995, RTSP 554, +SCP 22, SFTP 22, SMB 445, SMBS 445, SMTP 25, SMTPS 465, TELNET 23, TFTP 69 # Scheme specific behaviors @@ -374,22 +372,10 @@ curl supports SMB version 1 (only) The path part of an SMTP request specifies the hostname to present during communication with the mail server. If the path is omitted, then libcurl -attempts to resolve the local computer's hostname. However, this may not -return the fully qualified domain name that is required by some mail servers -and specifying this path allows you to set an alternative name, such as your +attempts to resolve the local computer's hostname. This may not return the +fully qualified domain name that is required by some mail servers and +specifying this path allows you to set an alternative name, such as your machine's fully qualified domain name, which you might have obtained from an external function such as gethostname or getaddrinfo. The default smtp port is 25. Some servers use port 587 as an alternative. - -## RTMP - -There is no official URL spec for RTMP so libcurl uses the URL syntax supported -by the underlying librtmp library. It has a syntax where it wants a -traditional URL, followed by a space and a series of space-separated -`name=value` pairs. - -While space is not typically a "legal" letter, libcurl accepts them. When a -user wants to pass in a `#` (hash) character it is treated as a fragment and -it gets cut off by libcurl if provided literally. You have to escape it by -providing it as backslash and its ASCII value in hexadecimal: `\23`. diff --git a/docs/VERIFY.md b/docs/VERIFY.md new file mode 100644 index 0000000000..88c8ef4cbd --- /dev/null +++ b/docs/VERIFY.md @@ -0,0 +1,131 @@ + + +# Verify + +Do not trust, verify! + +## Signed releases + +Every curl release is shipped as a set of tarballs. They all have the exact +same content but use different archivers, visible by the different file +extensions used. + +Each tarball is signed by the curl release manager Daniel. The digital +signatures for each tarball are always provided. The digital signatures can be +used to verify that the tarballs were produced by Daniel. + +If the curl website were breached and fake curl releases were +provided, they could be detected using these signatures. + +Daniel's public GPG key: [27ED EAF2 2F3A BCEB 50DB 9A12 5CC9 08FD B71E 12C2](https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x27edeaf22f3abceb50db9a125cc908fdb71e12c2) + +## Reproducible releases + +The curl project ships *reproducible releases*. This means that everyone is +able - and encouraged - to independently verify the contents of every curl +release. Verify that it contains exactly the bits that are supposed to be in +the release and nothing extra. + +The curl releases are generated using a Docker image to make it easy to get an +identical setup. To verify an existing curl release, we provide a convenient +script that generates a new curl release from source code and then compares +this newly generated release tarball with the tarball file you downloaded from +curl.se. + +Invoke it like this: + + ./scripts/verify-release curl-8.19.0.tar.xz + +By verifying the release tarballs, you verify that Daniel does not infect the +release on purpose or involuntarily because of anything malicious running in +his setup. + +### Verify the verify + +Of course you should not blindly trust the verification script. It is short +and simple and should be quick to verify. Or you write your own script that +you trust, to do the same job. + +## Source code + +How do you then verify that what is in git is fine to build a product from? + +In the curl project we verify the source code in multiple ways, and one way to +gain trust is to verify and review our testing procedures. + + - we have a consistent code style (invalid style causes errors) + + - we ban and avoid a number of "sensitive" and "hard-to-use" C functions (use + of such functions causes errors) + + - we have a ceiling for complexity in functions to keep them easy to follow, + read and understand (failing to do so causes errors) + + - we review all pull requests before merging, both with humans and with bots. We + link back commits to their origin pull requests in commit messages. + + - we ban use of "binary blobs" in git to not provide means for malicious + actors to bundle encrypted payloads (trying to include a blob causes errors) + + - we actively avoid base64 encoded chunks as they too could function as ways + to obfuscate malicious contents + + - we ban most uses of UTF-8 in code and documentation to avoid easily mixed + up Unicode characters that look like other characters. (adding Unicode + characters causes errors) + + - we document everything to make it clear how things are supposed to work. No + surprises. Lots of documentation is tested and verified in addition to + spellchecks and consistent wording. + + - we have thousands of tests and we add test cases for (ideally) every + functionality. Finding "white spots" and adding coverage is a top priority. + curl runs on countless operating systems, CPU architectures and you can + build curl in billions of different configuration setups: not every + combination is practically possible to test + + - we build curl and run tests in over two hundred CI jobs that are run for + every commit and every PR. We do not merge commits that have unexplained + test failures. + + - we build curl in CI with the most picky compiler options enabled and we + never allow compiler warnings to linger. We always use `-Werror` that + converts warnings to errors and fail the builds. + + - we run all tests using valgrind and several combinations of sanitizers to + find and reduce the risk for memory problems, undefined behavior and + similar + + - we run all tests as "torture tests", where each test case is rerun to have + every invoked fallible function call fail once each, to make sure curl + never leaks memory or crashes due to this. + + - we run fuzzing on curl: non-stop as part of Google's OSS-Fuzz project, but + also briefly as part of the CI setup for every commit and PR + + - we make sure that the CI jobs we have for curl never "write back" to curl. + They access the source repository read-only and even if they would be + breached, they cannot infect or taint source code. + + - we run `zizmor` and other code analyzer tools on the CI job config scripts + to reduce the risk of us running or using insecure CI jobs. + + - we are committed to always fix reported vulnerabilities in the following + release. Security problems never linger around once they have been + reported. + + - we document everything and every detail about all curl vulnerabilities ever + reported + + - our commitment to never breaking ABI or API allows all users to easily + upgrade to new releases. This enables users to run recent security-fixed + versions instead of legacy insecure versions. + + - our code has been audited several times by external security experts, and + the few issues that have been detected in those were immediately addressed + + - Two-factor authentication on GitHub is mandatory for all committers diff --git a/docs/VERSIONS.md b/docs/VERSIONS.md index 35267f8294..e211487561 100644 --- a/docs/VERSIONS.md +++ b/docs/VERSIONS.md @@ -4,63 +4,62 @@ Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl --> -Version Numbers and Releases -============================ +# Version Numbers and Releases - The command line tool curl and the library libcurl are individually - versioned, but they usually follow each other closely. +The command line tool curl and the library libcurl are individually +versioned, but they usually follow each other closely. - The version numbering is always built up using the same system: +The version numbering is always built up using the same system: - X.Y.Z + X.Y.Z - - X is main version number - - Y is release number - - Z is patch number +- X is main version number +- Y is release number +- Z is patch number ## Bumping numbers - One of these numbers get bumped in each new release. The numbers to the right - of a bumped number are reset to zero. +One of these numbers get bumped in each new release. The numbers to the right +of a bumped number are reset to zero. - The main version number is bumped when *really* big, world colliding changes - are made. The release number is bumped when changes are performed or - things/features are added. The patch number is bumped when the changes are - mere bugfixes. +The main version number is bumped when *really* big, world colliding changes +are made. The release number is bumped when changes are performed or +things/features are added. The patch number is bumped when the changes are +mere bugfixes. - It means that after release 1.2.3, we can release 2.0.0 if something really - big has been made, 1.3.0 if not that big changes were made or 1.2.4 if only - bugs were fixed. +It means that after release 1.2.3, we can release 2.0.0 if something really +big has been made, 1.3.0 if not that big changes were made or 1.2.4 if only +bugs were fixed. - Bumping, as in increasing the number with 1, is unconditionally only - affecting one of the numbers (except the ones to the right of it, that may be - set to zero). 1 becomes 2, 3 becomes 4, 9 becomes 10, 88 becomes 89 and 99 - becomes 100. So, after 1.2.9 comes 1.2.10. After 3.99.3, 3.100.0 might come. +Bumping, as in increasing the number with 1, is unconditionally only affecting +one of the numbers (except the ones to the right of it, that may be set to +zero). 1 becomes 2, 3 becomes 4, 9 becomes 10, 88 becomes 89 and 99 +becomes 100. After 1.2.9 comes 1.2.10. After 3.99.3, 3.100.0 might come. - All original curl source release archives are named according to the libcurl - version (not according to the curl client version that, as said before, might - differ). +All original curl source release archives are named according to the libcurl +version (not according to the curl client version that, as said before, might +differ). - As a service to any application that might want to support new libcurl - features while still being able to build with older versions, all releases - have the libcurl version stored in the `curl/curlver.h` file using a static - numbering scheme that can be used for comparison. The version number is - defined as: +As a service to any application that might want to support new libcurl +features while still being able to build with older versions, all releases +have the libcurl version stored in the `curl/curlver.h` file using a static +numbering scheme that can be used for comparison. The version number is +defined as: ```c #define LIBCURL_VERSION_NUM 0xXXYYZZ ``` - Where `XX`, `YY` and `ZZ` are the main version, release and patch numbers in - hexadecimal. All three number fields are always represented using two digits - (eight bits each). 1.2 would appear as "0x010200" while version 9.11.7 - appears as `0x090b07`. +Where `XX`, `YY` and `ZZ` are the main version, release and patch numbers in +hexadecimal. All three number fields are always represented using two digits +(eight bits each). 1.2 would appear as "0x010200" while version 9.11.7 +appears as `0x090b07`. - This 6-digit hexadecimal number is always a greater number in a more recent - release. It makes comparisons with greater than and less than work. +This 6-digit hexadecimal number is always a greater number in a more recent +release. It makes comparisons with greater than and less than work. - This number is also available as three separate defines: - `LIBCURL_VERSION_MAJOR`, `LIBCURL_VERSION_MINOR` and `LIBCURL_VERSION_PATCH`. +This number is also available as three separate defines: +`LIBCURL_VERSION_MAJOR`, `LIBCURL_VERSION_MINOR` and `LIBCURL_VERSION_PATCH`. ## Past releases @@ -69,7 +68,10 @@ dates. The tool was called `httpget` before 2.0, `urlget` before 4.0 then `curl` since 4.0. `libcurl` and `curl` are always released in sync, using the same version numbers. -- 8.17.0: pending +- 8.20.0: pending +- 8.19.0: March 11, 2026 +- 8.18.0: January 7, 2026 +- 8.17.0: November 5, 2025 - 8.16.0: September 10, 2025 - 8.15.0: July 16, 2025 - 8.14.1: June 4 2025 diff --git a/docs/VULN-DISCLOSURE-POLICY.md b/docs/VULN-DISCLOSURE-POLICY.md index bb2b67756c..abc7ef2c0a 100644 --- a/docs/VULN-DISCLOSURE-POLICY.md +++ b/docs/VULN-DISCLOSURE-POLICY.md @@ -9,6 +9,9 @@ SPDX-License-Identifier: curl This document describes how security vulnerabilities are handled in the curl project. +There is no bug bounty and the curl project never offers rewards for reported +vulnerabilities. + ## Publishing Information All known and public curl or libcurl related vulnerabilities are listed on @@ -76,10 +79,6 @@ announcement. repository via a normal PR - but without mentioning it being a security vulnerability. -- The monetary reward part of the bug-bounty is managed by the Internet Bug - Bounty team and the reporter is asked to request the reward from them after - the issue has been completely handled and published by curl. - - No more than seven days before release, inform [distros@openwall](https://oss-security.openwall.org/wiki/mailing-lists/distros) to prepare them about the upcoming public security vulnerability @@ -109,10 +108,10 @@ issues. Who is on this list? There are a couple of criteria you must meet, and then we might ask you to join the list or you can ask to join it. It really is not a -formal process. We basically only require that you have a long-term presence -in the curl project and you have shown an understanding for the project and -its way of working. You must have been around for a good while and you should -have no plans of vanishing in the near future. +formal process. We only require that you have a long-term presence in the curl +project and you have shown an understanding for the project and its way of +working. You must have been around for a good while and you should have no +plans of vanishing in the near future. We do not make the list of participants public mostly because it tends to vary somewhat over time and a list somewhere only risks getting outdated. @@ -144,11 +143,6 @@ has been published. *All* reports submitted to the project, valid or not, should be disclosed and made public. -## Bug Bounty - -See [BUG-BOUNTY](https://curl.se/docs/bugbounty.html) for details on the -bug bounty program. - # Severity levels The curl project's security team rates security problems using four severity @@ -210,8 +204,8 @@ This is an incomplete list of issues that are not considered vulnerabilities. We do not consider a small memory leak a security problem; even if the amount of allocated memory grows by a small amount every now and then. Long-living applications and services already need to have countermeasures and deal with -growing memory usage, be it leaks or just increased use. A small memory or -resource leak is then expected to *not* cause a security problem. +growing memory usage, be it leaks or increased use. A small memory or resource +leak is then expected to *not* cause a security problem. Of course there can be a discussion if a leak is small or not. A large leak can be considered a security problem due to the DOS risk. If leaked memory @@ -224,7 +218,8 @@ problem. There are already several benign and likely reasons for transfers to stall and never end, so applications that cannot deal with never-ending transfers already need to have counter-measures established. -If the problem avoids the regular counter-measures when it causes a never- +Well known attacks, like [Slowloris](https://en.wikipedia.org/wiki/Slowloris_(cyber_attack)), that send partial +requests are usually not considered a flaw. If the problem avoids the regular counter-measures when it causes a never- ending transfer, it might be a security problem. ## Not practically possible @@ -276,12 +271,12 @@ arguments, even though some that are not blanked might contain sensitive data. We consider this functionality a best-effort and omissions are not security vulnerabilities. - - not all systems allow the arguments to be blanked in the first place - - since curl blanks the argument itself they are readable for a short moment - no matter what - - virtually every argument can contain sensitive data, depending on use - - blanking all arguments would make it impractical for users to differentiate - curl command lines in process listings +- not all systems allow the arguments to be blanked in the first place +- since curl blanks the argument itself they are readable for a short moment + no matter what +- virtually every argument can contain sensitive data, depending on use +- blanking all arguments would make it impractical for users to differentiate + curl command lines in process listings ## Busy-loops @@ -301,9 +296,8 @@ same directory where curl is directed to save files. A creative, misleading or funny looking command line is not a security problem. The curl command line tool takes options and URLs on the command line and if an attacker can trick the user to run a specifically crafted curl -command line, all bets are off. Such an attacker can just as well have the -user run a much worse command that can do something fatal (like -`sudo rm -rf /`). +command line, all bets are off. Such an attacker can already have the user run +a much worse command that can do something fatal (like `sudo rm -rf /`). ## Terminal output and escape sequences @@ -354,6 +348,21 @@ using the protocols or options that require the use of those algorithms. When servers upgrade to use secure alternatives, curl users should use those options/protocols. +## CRLF in data + +curl makes barely any claims of *cleaning* input or rejecting invalid data. A +user that uses a curl feature can send in *creative* sequences that include +carriage-return (CR) or line-feed (LF) characters. + +Therefore, we reject the idea of *CRLF injection* as a security problem. It is +a *feature* that users can send creative byte sequences. If users do not want +to send such octets, they are in control and should avoid sending such bytes +to curl. + +For example, a user might pass in a username that looks like +`Mr[CR][LF]Smith`. It may cause some minor havoc in the protocol handling, +depending on what protocol is used. + # curl major incident response Vulnerability disclosure manages the full life cycle of a vulnerability @@ -407,9 +416,9 @@ roles: It is likely that our [BDFL](https://en.wikipedia.org/wiki/Benevolent_dictator_for_life) occupies one of these roles, though this plan does not depend on it. -A declaration may also contain more detailed information but as we honor embargoes -and vulnerability disclosure throughout this process, it may also just contain -brief notification that a **major incident** is occurring. +A declaration may also contain more detailed information but as we honor +embargoes and vulnerability disclosure throughout this process, it may also +contain a brief notification that a **major incident** is occurring. ## Major incident ongoing diff --git a/docs/cmdline-opts/CMakeLists.txt b/docs/cmdline-opts/CMakeLists.txt index 745caf047f..83949969ae 100644 --- a/docs/cmdline-opts/CMakeLists.txt +++ b/docs/cmdline-opts/CMakeLists.txt @@ -40,3 +40,19 @@ add_custom_target(generate-curl.1 ALL DEPENDS "${CURL_MANPAGE}") if(NOT CURL_DISABLE_INSTALL) install(FILES "${CURL_MANPAGE}" DESTINATION "${CMAKE_INSTALL_MANDIR}/man1") endif() + +if(PERL_EXECUTABLE) + add_custom_target(curl-listhelp + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating src/tool_listhelp.c" VERBATIM USES_TERMINAL + COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/managen" -d "${CMAKE_CURRENT_SOURCE_DIR}" listhelp ${DPAGES} + > "${PROJECT_SOURCE_DIR}/src/tool_listhelp.c" + DEPENDS "${PROJECT_SOURCE_DIR}/scripts/managen" ${DPAGES} + ) + add_custom_target(curl-listcats + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating help category constants for src/tool_help.h" VERBATIM USES_TERMINAL + COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/managen" listcats ${DPAGES} + DEPENDS "${PROJECT_SOURCE_DIR}/scripts/managen" ${DPAGES} + ) +endif() diff --git a/docs/cmdline-opts/MANPAGE.md b/docs/cmdline-opts/MANPAGE.md index 3e2e7151f6..1e4facd954 100644 --- a/docs/cmdline-opts/MANPAGE.md +++ b/docs/cmdline-opts/MANPAGE.md @@ -10,7 +10,7 @@ output from the set of sources files in this directory. The `mainpage.idx` file lists all files that are rendered in that order to -produce the output. The magic `%options` keyword inserts all command line +produce the output. The special `%options` keyword inserts all command line options documented. The `%options` documentation is created with one source file for each @@ -92,7 +92,7 @@ The `#` header can be used by non-option files and it produces a `.SH` output. If the `#` header is used for a command line option file, that header is -simply ignored in the generated output. It can still serve a purpose in the +ignored in the generated output. It can still serve a purpose in the source file as it helps the user identify what option the file is for. ### Variables @@ -101,6 +101,17 @@ There are three different "variables" that can be used when creating the output. They need to be written within backticks in the source file (to escape getting spellchecked by CI jobs): `%DATE`, `%VERSION` and `%GLOBALS`. +During rendering, the generator expands them as follows: + +- `%VERSION` -- replaced with the curl version string read from + `include/curl/curlver.h` (e.g. `8.12.0`). Can be overridden by setting + the `CURL_MAKETGZ_VERSION` environment variable. +- `%DATE` -- replaced with the current date in `YYYY-MM-DD` format, or + the date derived from `SOURCE_DATE_EPOCH` if that environment variable + is set (for reproducible builds). +- `%GLOBALS` -- replaced with a comma-separated list of all command line + options that have `Scope: global` in their meta-data. + ## Generate `managen mainpage [list of markdown option file names]` @@ -116,3 +127,40 @@ curl man page in text format, used to build `tool_hugehelp.c`. `managen listhelp` Generates a full `curl --help` output for all known command line options. + +## Generating the man page + +The `curl.1` man page is generated from the source files in this directory +using the `managen` Perl script located in `scripts/managen`. The build +system runs this automatically, but it can also be invoked manually. + +### Prerequisites + +The generator requires Perl. The version string is read from +`include/curl/curlver.h` (or from the `CURL_MAKETGZ_VERSION` environment +variable if set). The date defaults to the current date unless +`SOURCE_DATE_EPOCH` is set. + +### Manual invocation + +From the `docs/cmdline-opts` directory, run: + + cd docs/cmdline-opts + perl ../../scripts/managen -I ../../include mainpage ./*.md > curl.1 + +This produces the complete `curl.1` nroff man page. To produce a plain-text +version instead, replace `mainpage` with `ascii`: + + perl ../../scripts/managen -I ../../include ascii ./*.md > curl.txt + +The `-d` flag specifies the directory containing `mainpage.idx` and the +`.md` option files. The `-I` flag specifies the include directory root +used to locate `curl/curlver.h` for the version string. + +### How it works + +The generator reads `mainpage.idx`, which lists the documentation source +files in their intended order. Each line names one `.md` file to render. +When the generator encounters the `%options` keyword in `mainpage.idx`, +it inserts the documentation for every command line option (one `.md` file +per option), sorted alphabetically by long option name. diff --git a/docs/cmdline-opts/Makefile.am b/docs/cmdline-opts/Makefile.am index e016be286c..019dd57b8a 100644 --- a/docs/cmdline-opts/Makefile.am +++ b/docs/cmdline-opts/Makefile.am @@ -40,7 +40,7 @@ GN_ = $(GN_0) MANAGEN=$(top_srcdir)/scripts/managen MAXLINE=$(top_srcdir)/scripts/maxline -# Maximum number of columns accepted in the ASCII version of the manpage +# Maximum number of columns accepted in the ASCII version of the man page INCDIR=$(top_srcdir)/include if BUILD_DOCS diff --git a/docs/cmdline-opts/Makefile.inc b/docs/cmdline-opts/Makefile.inc index b8e88421a9..f7236af1b1 100644 --- a/docs/cmdline-opts/Makefile.inc +++ b/docs/cmdline-opts/Makefile.inc @@ -147,6 +147,7 @@ DPAGES = \ keepalive-time.md \ key-type.md \ key.md \ + knownhosts.md \ krb.md \ libcurl.md \ limit-rate.md \ diff --git a/docs/cmdline-opts/_DESCRIPTION.md b/docs/cmdline-opts/_DESCRIPTION.md index 3e06c1b38f..bb21f0ba32 100644 --- a/docs/cmdline-opts/_DESCRIPTION.md +++ b/docs/cmdline-opts/_DESCRIPTION.md @@ -4,8 +4,8 @@ **curl** is a tool for transferring data from or to a server using URLs. It supports these protocols: DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, -IMAP, IMAPS, LDAP, LDAPS, MQTT, POP3, POP3S, RTMP, RTMPS, RTSP, SCP, SFTP, -SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. +IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, +SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. curl is powered by libcurl for all transfer-related features. See *libcurl(3)* for details. diff --git a/docs/cmdline-opts/_ENVIRONMENT.md b/docs/cmdline-opts/_ENVIRONMENT.md index 53fe5c3421..1ac85fb128 100644 --- a/docs/cmdline-opts/_ENVIRONMENT.md +++ b/docs/cmdline-opts/_ENVIRONMENT.md @@ -31,12 +31,12 @@ This environment variable disables use of the proxy even when specified with the --proxy option. That is NO_PROXY=direct.example.com curl -x http://proxy.example.com - http://direct.example.com + https://direct.example.com accesses the target URL directly, and NO_PROXY=direct.example.com curl -x http://proxy.example.com - http://somewhere.example.com + https://somewhere.example.com accesses the target URL through the proxy. diff --git a/docs/cmdline-opts/_GLOBBING.md b/docs/cmdline-opts/_GLOBBING.md index 282356c3ef..37c8d43069 100644 --- a/docs/cmdline-opts/_GLOBBING.md +++ b/docs/cmdline-opts/_GLOBBING.md @@ -6,31 +6,33 @@ or ranges within brackets. We call this "globbing". Provide a list with three different names like this: - "http://site.{one,two,three}.com" + https://fun.example/{one,two,three}.jpg + + sftp://{one,two,three}.example/README Do sequences of alphanumeric series by using [] as in: - "ftp://ftp.example.com/file[1-100].txt" + ftp://ftp.example.com/file[1-100].txt With leading zeroes: - "ftp://ftp.example.com/file[001-100].txt" + ftp://ftp.example.com/file[001-100].txt With letters through the alphabet: - "ftp://ftp.example.com/file[a-z].txt" + ftp://ftp.example.com/file[a-z].txt Nested sequences are not supported, but you can use several ones next to each other: - "http://example.com/archive[1996-1999]/vol[1-4]/part{a,b,c}.html" + https://example.com/archive[1996-1999]/vol[1-4]/part{a,b,c}.html You can specify a step counter for the ranges to get every Nth number or letter: - "http://example.com/file[1-100:10].txt" + https://example.com/file[1-100:10].txt - "http://example.com/file[a-z:2].txt" + https://example.com/file[a-z:2].txt When using [] or {} sequences when invoked from a command line prompt, you probably have to put the full URL within double quotes to avoid the shell from diff --git a/docs/cmdline-opts/_OPTIONS.md b/docs/cmdline-opts/_OPTIONS.md index 2b7cb80f49..9155d857ec 100644 --- a/docs/cmdline-opts/_OPTIONS.md +++ b/docs/cmdline-opts/_OPTIONS.md @@ -17,8 +17,8 @@ options *-O*, *-L* and *-v* at once as *-OLv*. In general, all boolean options are enabled with --**option** and yet again disabled with --**no-**option. That is, you use the same option name but -prefix it with `no-`. However, in this list we mostly only list and show the ---**option** version of them. +prefix it with `no-`. In this list we mostly show the --**option** version of +them. When --next is used, it resets the parser state and you start again with a clean option state, except for the options that are global. Global options diff --git a/docs/cmdline-opts/_PROGRESS.md b/docs/cmdline-opts/_PROGRESS.md index 4cbbd8eb78..a506d041dc 100644 --- a/docs/cmdline-opts/_PROGRESS.md +++ b/docs/cmdline-opts/_PROGRESS.md @@ -4,9 +4,10 @@ curl normally displays a progress meter during operations, indicating the amount of transferred data, transfer speeds and estimated time left, etc. The -progress meter displays the transfer rate in bytes per second. The suffixes -(k, M, G, T, P) are 1024 based. For example 1k is 1024 bytes. 1M is 1048576 -bytes. +progress meter displays the transfer rate in bytes per second. The used +suffixes (`k` for kilo, `M` for mega, `G` for giga, `T` for tera, `P` for peta +and `E` for exa) are 1024 based. For example 1k is 1024 bytes. 1M is 1048576 +bytes. Strictly speaking this makes the units kibibyte and mebibyte etc. curl displays this data to the terminal by default, so if you invoke curl to do an operation and it is about to write data to the terminal, it *disables* @@ -14,8 +15,8 @@ the progress meter as otherwise it would mess up the output mixing progress meter and response data. If you want a progress meter for HTTP POST or PUT requests, you need to -redirect the response output to a file, using shell redirect (\>), --output -or similar. +redirect the response output to a file, using shell redirect (\>), --output or +similar. This does not apply to FTP upload as that operation does not spit out any response data to the terminal. diff --git a/docs/cmdline-opts/_PROTOCOLS.md b/docs/cmdline-opts/_PROTOCOLS.md index 76fa79676e..831b944d24 100644 --- a/docs/cmdline-opts/_PROTOCOLS.md +++ b/docs/cmdline-opts/_PROTOCOLS.md @@ -30,9 +30,6 @@ supported (yet). ## POP3(S) Downloading from a pop3 server means getting an email. With or without using TLS. -## RTMP(S) -The **Realtime Messaging Protocol** is primarily used to serve streaming media -and curl can download it. ## RTSP curl supports RTSP 1.0 downloads. ## SCP diff --git a/docs/cmdline-opts/_URL.md b/docs/cmdline-opts/_URL.md index 48ae02a556..288b9d0aa7 100644 --- a/docs/cmdline-opts/_URL.md +++ b/docs/cmdline-opts/_URL.md @@ -1,7 +1,7 @@ # URL -The URL syntax is protocol-dependent. You find a detailed description in +The URL syntax is protocol-dependent. You can find a detailed description in RFC 3986. If you provide a URL without a leading **protocol://** scheme, curl guesses @@ -22,7 +22,7 @@ separate curl runs. Provide an IPv6 zone id in the URL with an escaped percentage sign. Like in - "http://[fe80::3%25eth0]/" + http://[fe80::3%25eth0]/ Everything provided on the command line that is not a command line option or its argument, curl assumes is a URL and treats it as such. diff --git a/docs/cmdline-opts/_VARIABLES.md b/docs/cmdline-opts/_VARIABLES.md index 3bc86b9367..834fc5ef28 100644 --- a/docs/cmdline-opts/_VARIABLES.md +++ b/docs/cmdline-opts/_VARIABLES.md @@ -11,23 +11,22 @@ variable `name` inserted, or a blank if the name does not exist as a variable. Insert `{{` verbatim in the string by prefixing it with a backslash, like `\{{`. -You access and expand environment variables by first importing them. You -select to either require the environment variable to be set or you can provide -a default value in case it is not already set. Plain `--variable %name` -imports the variable called `name` but exits with an error if that environment -variable is not already set. To provide a default value if it is not set, use -`--variable %name=content` or `--variable %name@content`. +You can access and expand environment variables by importing them with +`--variable %name`. This imports the variable called `name` but exits with an +error if that environment variable is not already set. To provide a default +value in case it is not already set, use `--variable %name=content` or +`--variable %name@content`. -Example. Get the USER environment variable into the URL, fail if USER is not -set: +Example: get the USER environment variable and expand into the URL, fail if +USER is not set: --variable '%USER' --expand-url = "https://example.com/api/{{USER}}/method" When expanding variables, curl supports a set of functions that can make the variable contents more convenient to use. It can trim leading and trailing -white space with `trim`, it can output the contents as a JSON quoted string -with `json`, URL encode the string with `url`, base64 encode it with `b64` and +white space with `trim`, output the contents as a JSON quoted string with +`json`, URL encode the string with `url`, base64 encode it with `b64` and base64 decode it with `64dec`. To apply functions to a variable expansion, add them colon separated to the right side of the variable. Variable content holding null bytes that are not encoded when expanded causes an error. diff --git a/docs/cmdline-opts/_WWW.md b/docs/cmdline-opts/_WWW.md index 35d946697f..8656e9ee8c 100644 --- a/docs/cmdline-opts/_WWW.md +++ b/docs/cmdline-opts/_WWW.md @@ -1,4 +1,4 @@ # WWW -https://curl.se +https://curl.se/ diff --git a/docs/cmdline-opts/abstract-unix-socket.md b/docs/cmdline-opts/abstract-unix-socket.md index 7078e642fd..b1b6100e16 100644 --- a/docs/cmdline-opts/abstract-unix-socket.md +++ b/docs/cmdline-opts/abstract-unix-socket.md @@ -16,6 +16,6 @@ Example: # `--abstract-unix-socket` -Connect through an abstract Unix domain socket, instead of using the network. -Note: netstat shows the path of an abstract socket prefixed with `@`, however -the \ argument should not have this leading character. +Connect to the server through an abstract Unix domain socket, instead of using +the network. Note: netstat shows the path of an abstract socket prefixed with +`@`, however the \ argument should not have this leading character. diff --git a/docs/cmdline-opts/alt-svc.md b/docs/cmdline-opts/alt-svc.md index 257f4d5b9c..fe2e8736fa 100644 --- a/docs/cmdline-opts/alt-svc.md +++ b/docs/cmdline-opts/alt-svc.md @@ -21,8 +21,11 @@ Enable the alt-svc parser. If the filename points to an existing alt-svc cache file, that gets used. After a completed transfer, the cache is saved to the filename again if it has been modified. -Specify a "" filename (zero length) to avoid loading/saving and make curl just +Specify a "" filename (zero length) to avoid loading/saving and make curl handle the cache in memory. +You may want to restrict your umask to prevent other users on the same system +to access the created file. + If this option is used several times, curl loads contents from all the files but the last one is used for saving. diff --git a/docs/cmdline-opts/ca-native.md b/docs/cmdline-opts/ca-native.md index 7e833c9d15..4a887df558 100644 --- a/docs/cmdline-opts/ca-native.md +++ b/docs/cmdline-opts/ca-native.md @@ -25,15 +25,17 @@ This option is independent of other CA certificate locations set at run time or build time. Those locations are searched in addition to the native CA store. This option works with OpenSSL and its forks (LibreSSL, BoringSSL, etc) on -Windows. (Added in 7.71.0) +Windows (Added in 7.71.0) and on Apple OS when libcurl is built with +Apple SecTrust enabled. (Added in 8.17.0) This option works with wolfSSL on Windows, Linux (Debian, Ubuntu, Gentoo, Fedora, RHEL), macOS, Android and iOS. (Added in 8.3.0) -This option works with GnuTLS. (Added in 8.5.0) +This option works with GnuTLS (Added in 8.5.0) and also uses Apple +SecTrust when libcurl is built with it. (Added in 8.17.0) -This option works with rustls on Windows, macOS, Android and iOS. On Linux it -is equivalent to using the Mozilla CA certificate bundle. When used with rustls +This option works with Rustls on Windows, macOS, Android and iOS. On Linux it +is equivalent to using the Mozilla CA certificate bundle. When used with Rustls _only_ the native CA store is consulted, not other locations set at run time or build time. (Added in 8.13.0) diff --git a/docs/cmdline-opts/compressed-ssh.md b/docs/cmdline-opts/compressed-ssh.md index 955c59c2b7..07d3981b48 100644 --- a/docs/cmdline-opts/compressed-ssh.md +++ b/docs/cmdline-opts/compressed-ssh.md @@ -16,4 +16,5 @@ Example: # `--compressed-ssh` Enable SSH compression. This is a request, not an order; the server may or may -not do it. +not do it. This allows the data to be sent compressed over the wire, and +automatically decompressed in the receiving end, to save bandwidth. diff --git a/docs/cmdline-opts/compressed.md b/docs/cmdline-opts/compressed.md index 35bbab8139..712f93df19 100644 --- a/docs/cmdline-opts/compressed.md +++ b/docs/cmdline-opts/compressed.md @@ -25,3 +25,8 @@ content is (still) compressed; while in fact it has already been decompressed. If this option is used and the server sends an unsupported encoding, curl reports an error. This is a request, not an order; the server may or may not deliver data compressed. + +**WARNING**: when decompressing data, even tiny transfers might be expanded +and generate a huge amount of bytes. You might want to limit using this option +to only known and trusted sites using secure protocols, perhaps in combination +with --max-filesize. diff --git a/docs/cmdline-opts/config.md b/docs/cmdline-opts/config.md index 1281a3d9c9..637918df75 100644 --- a/docs/cmdline-opts/config.md +++ b/docs/cmdline-opts/config.md @@ -42,8 +42,8 @@ Specify the filename to --config as minus "-" to make curl read the file from stdin. Note that to be able to specify a URL in the config file, you need to specify -it using the --url option, and not by simply writing the URL on its own -line. So, it could look similar to this: +it using the --url option, and not by writing the URL on its own line. +It could look similar to this: url = "https://curl.se/docs/" diff --git a/docs/cmdline-opts/connect-to.md b/docs/cmdline-opts/connect-to.md index 79873ab099..c7378318e2 100644 --- a/docs/cmdline-opts/connect-to.md +++ b/docs/cmdline-opts/connect-to.md @@ -27,7 +27,7 @@ original hostname and port number. A hostname specified to this option is compared as a string, so it needs to match the name used in the request URL. It can be either numerical such as -`127.0.0.1` or the full host name such as `example.org`. +`127.0.0.1` or the full hostname such as `example.org`. Example: redirect connects from the example.com hostname to 127.0.0.1 independently of port number: diff --git a/docs/cmdline-opts/cookie-jar.md b/docs/cmdline-opts/cookie-jar.md index 103144acc3..de09fd5274 100644 --- a/docs/cmdline-opts/cookie-jar.md +++ b/docs/cmdline-opts/cookie-jar.md @@ -37,3 +37,6 @@ If the cookie jar cannot be created or written to, the whole curl operation does not fail or even report an error clearly. Using --verbose gets a warning displayed, but that is the only visible feedback you get about this possibly lethal situation. + +You may want to restrict your umask to prevent other users on the same system +to access the created file. diff --git a/docs/cmdline-opts/cookie.md b/docs/cmdline-opts/cookie.md index 50f977e70d..30288fbcba 100644 --- a/docs/cmdline-opts/cookie.md +++ b/docs/cmdline-opts/cookie.md @@ -54,7 +54,8 @@ the Netscape format. Users often want to both read cookies from a file and write updated cookies back to a file, so using both --cookie and --cookie-jar in the same command -line is common. +line is common. curl ignores filenames specified with --cookie which do not +exist or point to a directory. If curl is built with PSL (**Public Suffix List**) support, it detects and discards cookies that are specified for such suffix domains that should not be diff --git a/docs/cmdline-opts/data-ascii.md b/docs/cmdline-opts/data-ascii.md index 5763d81f19..c1d9d75bbd 100644 --- a/docs/cmdline-opts/data-ascii.md +++ b/docs/cmdline-opts/data-ascii.md @@ -18,4 +18,4 @@ Example: # `--data-ascii` -This option is just an alias for --data. +This option is an alias for --data. diff --git a/docs/cmdline-opts/data-urlencode.md b/docs/cmdline-opts/data-urlencode.md index b4680e61ac..36fdf3df2f 100644 --- a/docs/cmdline-opts/data-urlencode.md +++ b/docs/cmdline-opts/data-urlencode.md @@ -28,9 +28,9 @@ a separator and a content specification. The \ part can be passed to curl using one of the following syntaxes: ## content -URL-encode the content and pass that on. Just be careful so that the content -does not contain any `=` or `@` symbols, as that makes the syntax match one of -the other cases below. +URL-encode the content and pass that on. Be careful so that the content does +not contain any `=` or `@` symbols, as that makes the syntax match one of the +other cases below. ## =content URL-encode the content and pass that on. The preceding `=` symbol is not diff --git a/docs/cmdline-opts/disallow-username-in-url.md b/docs/cmdline-opts/disallow-username-in-url.md index 012f2d0dc8..0507f531cc 100644 --- a/docs/cmdline-opts/disallow-username-in-url.md +++ b/docs/cmdline-opts/disallow-username-in-url.md @@ -16,3 +16,6 @@ Example: Exit with error if passed a URL containing a username. Probably most useful when the URL is being provided at runtime or similar. + +Accepting and using credentials in a URL is normally considered a security +hazard as they are easily leaked that way. diff --git a/docs/cmdline-opts/doh-cert-status.md b/docs/cmdline-opts/doh-cert-status.md index 7c497cf16f..445eb3dcd0 100644 --- a/docs/cmdline-opts/doh-cert-status.md +++ b/docs/cmdline-opts/doh-cert-status.md @@ -5,6 +5,7 @@ Long: doh-cert-status Help: Verify DoH server cert status OCSP-staple Added: 7.76.0 Category: dns tls +Protocols: DNS Multi: boolean See-also: - doh-insecure diff --git a/docs/cmdline-opts/doh-insecure.md b/docs/cmdline-opts/doh-insecure.md index 72f3cb7725..ee1602a242 100644 --- a/docs/cmdline-opts/doh-insecure.md +++ b/docs/cmdline-opts/doh-insecure.md @@ -5,6 +5,7 @@ Long: doh-insecure Help: Allow insecure DoH server connections Added: 7.76.0 Category: dns tls +Protocols: DNS Multi: boolean See-also: - doh-url diff --git a/docs/cmdline-opts/doh-url.md b/docs/cmdline-opts/doh-url.md index dcc6e52f8a..3d146a789c 100644 --- a/docs/cmdline-opts/doh-url.md +++ b/docs/cmdline-opts/doh-url.md @@ -6,6 +6,7 @@ Arg: Help: Resolve hostnames over DoH Added: 7.62.0 Category: dns +Protocols: DNS Multi: single See-also: - doh-insecure @@ -20,9 +21,9 @@ Specify which DNS-over-HTTPS (DoH) server to use to resolve hostnames, instead of using the default name resolver mechanism. The URL must be HTTPS. Some SSL options that you set for your transfer also apply to DoH since the -name lookups take place over SSL. However, the certificate verification -settings are not inherited but are controlled separately via --doh-insecure -and --doh-cert-status. +name lookups take place over SSL. The certificate verification settings are +not inherited but are controlled separately via --doh-insecure and +--doh-cert-status. By default, DoH is bypassed when initially looking up DNS records of the DoH server. You can specify the IP address(es) of the DoH server with --resolve to avoid this. diff --git a/docs/cmdline-opts/engine.md b/docs/cmdline-opts/engine.md index 511190023e..cde6949b86 100644 --- a/docs/cmdline-opts/engine.md +++ b/docs/cmdline-opts/engine.md @@ -17,6 +17,9 @@ Example: # `--engine` -Select the OpenSSL crypto engine to use for cipher operations. Use --engine -list to print a list of build-time supported engines. Note that not all (and +Select the OpenSSL crypto engine to use for cipher operations. Use `--engine +list` to print a list of build-time supported engines. Note that not all (and possibly none) of the engines may be available at runtime. + +The OpenSSL concept "engines" has been superseded by "providers" in OpenSSL 3, +and this option should work fine to specify such as well. diff --git a/docs/cmdline-opts/follow.md b/docs/cmdline-opts/follow.md index 9d4225ab35..096324f165 100644 --- a/docs/cmdline-opts/follow.md +++ b/docs/cmdline-opts/follow.md @@ -4,11 +4,14 @@ SPDX-License-Identifier: curl Long: follow Help: Follow redirects per spec Category: http +Protocols: HTTP Added: 8.16.0 Multi: boolean See-also: - request - location + - proto-redir + - max-redirs Example: - -X POST --follow $URL --- @@ -23,3 +26,8 @@ status codes 307 or 308, but may be reset to GET for 301, 302 and 303. This is subtly different than --location, as that option always set the custom method in all subsequent requests independent of response code. + +Restrict which protocols a redirect is accepted to follow with --proto-redir. + +When --netrc is used in combination with this option, credentials for the +followed-to hosts may also be selected from that file. diff --git a/docs/cmdline-opts/form-escape.md b/docs/cmdline-opts/form-escape.md index 0f93fde7eb..7cf1cb7403 100644 --- a/docs/cmdline-opts/form-escape.md +++ b/docs/cmdline-opts/form-escape.md @@ -3,7 +3,7 @@ c: Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl Long: form-escape Help: Escape form fields using backslash -Protocols: HTTP imap smtp +Protocols: HTTP IMAP SMTP Added: 7.81.0 Category: http upload post Multi: single diff --git a/docs/cmdline-opts/form.md b/docs/cmdline-opts/form.md index 9633b25119..87b019604f 100644 --- a/docs/cmdline-opts/form.md +++ b/docs/cmdline-opts/form.md @@ -28,11 +28,11 @@ For SMTP and IMAP protocols, this composes a multipart mail message to transmit. This enables uploading of binary files etc. To force the 'content' part to be -a file, prefix the filename with an @ sign. To just get the content part from -a file, prefix the filename with the symbol \<. The difference between @ and -\< is then that @ makes a file get attached in the post as a file upload, -while the \< makes a text field and just gets the contents for that text field -from a file. +a file, prefix the filename with an @ sign. To get the content part from a +file, prefix the filename with the symbol \<. The difference between @ and \< +is then that @ makes a file get attached in the post as a file upload, while +the \< makes a text field and gets the contents for that text field from a +file. Read content from stdin instead of a file by using a single "-" as filename. This goes for both @ and \< constructs. When stdin is used, the contents is @@ -127,7 +127,7 @@ text file: curl -F '=(;type=multipart/alternative' \ -F '=plain text message' \ -F '= HTML message;type=text/html' \ - -F '=)' -F '=@textfile.txt' ... smtp://example.com + -F '=)' -F '=@textfile.txt' ... smtp://example.com Data can be encoded for transfer using encoder=. Available encodings are *binary* and *8bit* that do nothing else than adding the corresponding @@ -141,5 +141,3 @@ base64 attached file: curl -F '=text message;encoder=quoted-printable' \ -F '=@localfile;encoder=base64' ... smtp://example.com - -See further examples and details in the MANUAL. diff --git a/docs/cmdline-opts/globoff.md b/docs/cmdline-opts/globoff.md index 3c8c341439..5ef4b2ae88 100644 --- a/docs/cmdline-opts/globoff.md +++ b/docs/cmdline-opts/globoff.md @@ -20,3 +20,6 @@ Switch off the URL globbing function. When you set this option, you can specify URLs that contain the letters {}[] without having curl itself interpret them. Note that these letters are not normal legal URL contents but they should be encoded according to the URI standard. + +curl detects numerical IPv6 addresses when used in URLs and excludes them from +the treatment, so they can still be used without having to disable globbing. diff --git a/docs/cmdline-opts/head.md b/docs/cmdline-opts/head.md index 353ef9a011..7e005c7254 100644 --- a/docs/cmdline-opts/head.md +++ b/docs/cmdline-opts/head.md @@ -5,7 +5,7 @@ Long: head Short: I Help: Show document info only Protocols: HTTP FTP FILE -Category: http ftp file +Category: important http ftp file Added: 4.0 Multi: boolean See-also: diff --git a/docs/cmdline-opts/header.md b/docs/cmdline-opts/header.md index 66816aac53..9e1f2029e0 100644 --- a/docs/cmdline-opts/header.md +++ b/docs/cmdline-opts/header.md @@ -6,12 +6,13 @@ Short: H Arg:
Help: Pass custom header(s) to server Protocols: HTTP IMAP SMTP -Category: http imap smtp +Category: important http imap smtp Added: 5.0 Multi: append See-also: - user-agent - referer + - proxy-header Example: - -H "X-First-Name: Joe" $URL - -H "User-Agent: yes-please/2000" $URL diff --git a/docs/cmdline-opts/hsts.md b/docs/cmdline-opts/hsts.md index ca6b07e66d..0f6673cc4e 100644 --- a/docs/cmdline-opts/hsts.md +++ b/docs/cmdline-opts/hsts.md @@ -25,8 +25,14 @@ in the HSTS cache, it upgrades the transfer to use HTTPS. Each HSTS cache entry has an individual lifetime after which the upgrade is no longer performed. -Specify a "" filename (zero length) to avoid loading/saving and make curl just +Specify a "" filename (zero length) to avoid loading/saving and make curl handle HSTS in memory. +You may want to restrict your umask to prevent other users on the same system +to access the created file. + If this option is used several times, curl loads contents from all the files but the last one is used for saving. + +Since curl 8.20.0, curl keeps no more than the most recently added 10,000 +unique HSTS hostnames. diff --git a/docs/cmdline-opts/http0.9.md b/docs/cmdline-opts/http0.9.md index 41b76ffd7c..65be14491c 100644 --- a/docs/cmdline-opts/http0.9.md +++ b/docs/cmdline-opts/http0.9.md @@ -21,7 +21,7 @@ Example: Accept an HTTP version 0.9 response. HTTP/0.9 is a response without headers and therefore you can also connect with -this to non-HTTP servers and still get a response since curl simply +this to non-HTTP servers and still get a response since curl transparently downgrades - if allowed. HTTP/0.9 is disabled by default (added in 7.66.0) diff --git a/docs/cmdline-opts/http2.md b/docs/cmdline-opts/http2.md index ae4d26974c..f5180be2b5 100644 --- a/docs/cmdline-opts/http2.md +++ b/docs/cmdline-opts/http2.md @@ -14,6 +14,7 @@ See-also: - http1.1 - http3 - no-alpn + - proxy-http2 Example: - --http2 $URL --- diff --git a/docs/cmdline-opts/http3.md b/docs/cmdline-opts/http3.md index a66d797829..e4dfeef075 100644 --- a/docs/cmdline-opts/http3.md +++ b/docs/cmdline-opts/http3.md @@ -33,3 +33,5 @@ still tries to proceed with an older HTTP version. The fallback performs the regular negotiation between HTTP/1 and HTTP/2. Use --http3-only for similar functionality *without* a fallback. + +curl cannot do HTTP/3 over any proxy. diff --git a/docs/cmdline-opts/ip-tos.md b/docs/cmdline-opts/ip-tos.md index 3d6473f312..f5ef589e23 100644 --- a/docs/cmdline-opts/ip-tos.md +++ b/docs/cmdline-opts/ip-tos.md @@ -6,7 +6,6 @@ Arg: Help: Set IP Type of Service or Traffic Class Added: 8.9.0 Category: connection -Protocols: All Multi: single See-also: - tcp-nodelay diff --git a/docs/cmdline-opts/ipv4.md b/docs/cmdline-opts/ipv4.md index b790cc65aa..d7d571d5bd 100644 --- a/docs/cmdline-opts/ipv4.md +++ b/docs/cmdline-opts/ipv4.md @@ -20,5 +20,5 @@ Example: # `--ipv4` -Use IPv4 addresses only when resolving hostnames, and not for example try +Request only IPv4 addresses when resolving hostnames, and not for example any IPv6. diff --git a/docs/cmdline-opts/ipv6.md b/docs/cmdline-opts/ipv6.md index 547f4917c2..c2f1398774 100644 --- a/docs/cmdline-opts/ipv6.md +++ b/docs/cmdline-opts/ipv6.md @@ -20,9 +20,9 @@ Example: # `--ipv6` -Use IPv6 addresses only when resolving hostnames, and not for example try +Request only IPv6 addresses when resolving hostnames, and not for example any IPv4. -Your resolver may respond to an IPv6-only resolve request by returning IPv6 -addresses that contain "mapped" IPv4 addresses for compatibility purposes. +Your resolver may still respond to an IPv6-only resolve request by returning +IPv6 addresses that contain "mapped" IPv4 addresses for compatibility purposes. macOS is known to do this. diff --git a/docs/cmdline-opts/junk-session-cookies.md b/docs/cmdline-opts/junk-session-cookies.md index 63971050c0..668dfce2d6 100644 --- a/docs/cmdline-opts/junk-session-cookies.md +++ b/docs/cmdline-opts/junk-session-cookies.md @@ -18,5 +18,8 @@ Example: # `--junk-session-cookies` When curl is told to read cookies from a given file, this option makes it -discard all "session cookies". This has the same effect as if a new session is +discard all session cookies. This has the same effect as if a new session is started. Typical browsers discard session cookies when they are closed down. + +Session cookies are cookies without a set expiry time. They are meant to only +last for "a session". diff --git a/docs/cmdline-opts/key.md b/docs/cmdline-opts/key.md index 967119a8b5..cc4bc73fa5 100644 --- a/docs/cmdline-opts/key.md +++ b/docs/cmdline-opts/key.md @@ -3,7 +3,7 @@ c: Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl Long: key Arg: -Protocols: TLS SSH +Protocols: TLS SCP SFTP Help: Private key filename Category: tls ssh Added: 7.9.3 diff --git a/docs/cmdline-opts/knownhosts.md b/docs/cmdline-opts/knownhosts.md new file mode 100644 index 0000000000..4b6386dd24 --- /dev/null +++ b/docs/cmdline-opts/knownhosts.md @@ -0,0 +1,31 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Long: knownhosts +Arg: +Protocols: SCP SFTP +Help: Specify knownhosts path +Category: ssh +Added: 8.17.0 +Multi: single +See-also: + - hostpubsha256 + - hostpubmd5 + - insecure + - key +Example: + - --knownhosts filename --key here $URL +--- + +# `--knownhosts` + +When doing SCP and SFTP transfers, curl automatically checks a database +containing identification for all hosts it has ever been used with to verify +that the host it connects to is the same as previously. Host keys are stored +in such a known hosts file. curl uses the ~/.ssh/known_hosts in the user's +home directory by default. + +This option lets a user specify a specific file to check the host against. + +The known hosts check can be disabled with --insecure, but that makes the +transfer insecure and is strongly discouraged. diff --git a/docs/cmdline-opts/krb.md b/docs/cmdline-opts/krb.md index c353a0c740..6d47a76d6e 100644 --- a/docs/cmdline-opts/krb.md +++ b/docs/cmdline-opts/krb.md @@ -6,7 +6,7 @@ Arg: Help: Enable Kerberos with security Protocols: FTP Requires: Kerberos -Category: ftp +Category: deprecated Added: 7.3 Multi: single See-also: @@ -18,6 +18,8 @@ Example: # `--krb` +Deprecated option (added in 8.17.0). It has no function anymore. + Enable Kerberos authentication and use. The level must be entered and should -be one of 'clear', 'safe', 'confidential', or 'private'. Should you use a -level that is not one of these, 'private' is used. +be one of `clear`, `safe`, `confidential`, or `private`. Should you use a +level that is not one of these, `private` is used. diff --git a/docs/cmdline-opts/limit-rate.md b/docs/cmdline-opts/limit-rate.md index c5e276e1b5..88f62709b0 100644 --- a/docs/cmdline-opts/limit-rate.md +++ b/docs/cmdline-opts/limit-rate.md @@ -12,9 +12,10 @@ See-also: - speed-limit - speed-time Example: - - --limit-rate 100K $URL + - --limit-rate 123.45K $URL - --limit-rate 1000 $URL - --limit-rate 10M $URL + - --limit-rate 200K --max-time 60 $URL --- # `--limit-rate` @@ -26,8 +27,8 @@ otherwise would be. The given speed is measured in bytes/second, unless a suffix is appended. Appending 'k' or 'K' counts the number as kilobytes, 'm' or 'M' makes it -megabytes, while 'g' or 'G' makes it gigabytes. The suffixes (k, M, G, T, P) -are 1024 based. For example 1k is 1024. Examples: 200K, 3m and 1G. +megabytes etc. The supported suffixes (k, M, G, T, P) are 1024-based. For +example 1k is 1024. Examples: 200K, 3m and 1G. The rate limiting logic works on averaging the transfer speed to no more than the set threshold over a period of multiple seconds. @@ -35,3 +36,7 @@ the set threshold over a period of multiple seconds. If you also use the --speed-limit option, that option takes precedence and might cripple the rate-limiting slightly, to help keep the speed-limit logic working. + +Starting in curl 8.19.0, the rate can be specified using a fraction as in +`2.5M` for two and a half megabytes per second. It only works with a period +(`.`) delimiter, independent of what your locale might prefer. diff --git a/docs/cmdline-opts/list-only.md b/docs/cmdline-opts/list-only.md index 2800a8f793..36d6321039 100644 --- a/docs/cmdline-opts/list-only.md +++ b/docs/cmdline-opts/list-only.md @@ -24,12 +24,12 @@ used like this, the option causes an NLST command to be sent to the server instead of LIST. Note: Some FTP servers list only files in their response to NLST; they do not -include sub-directories and symbolic links. +include subdirectories and symbolic links. When listing an SFTP directory, this switch forces a name-only view, one per line. This is especially useful if the user wants to machine-parse the contents of an SFTP directory since the normal directory view provides more -information than just filenames. +information than filenames. When retrieving a specific email from POP3, this switch forces a LIST command to be performed instead of RETR. This is particularly useful if the user wants diff --git a/docs/cmdline-opts/location.md b/docs/cmdline-opts/location.md index 86ae7e3580..4ea115a634 100644 --- a/docs/cmdline-opts/location.md +++ b/docs/cmdline-opts/location.md @@ -12,6 +12,8 @@ See-also: - resolve - alt-svc - follow + - proto-redir + - max-redirs Example: - -L $URL --- @@ -23,10 +25,12 @@ location (indicated with a Location: header and a 3XX response code), this option makes curl redo the request to the new place. If used together with --show-headers or --head, headers from all requested pages are shown. -When authentication is used, or when sending a cookie with `-H Cookie:`, curl -only sends its credentials to the initial host. If a redirect takes curl to a -different host, it does not get the credentials passed on. See ---location-trusted on how to change this. +When authentication is provided on the command line (for example --user or +--oauth2-bearer), or when sending a cookie with `-H Cookie:`, curl only sends +its credentials to the initial host. If a redirect takes curl to a different +host, it does not get the credentials passed on. See --location-trusted on how +to change this. When --netrc is used in combination with this option, +credentials for the followed-to hosts may also be selected from that file. Limit the amount of redirects to follow by using the --max-redirs option. @@ -40,3 +44,5 @@ using the dedicated options for that: --post301, --post302 and --post303. The method set with --request overrides the method curl would otherwise select to use. + +Restrict which protocols a redirect is accepted to follow with --proto-redir. diff --git a/docs/cmdline-opts/max-filesize.md b/docs/cmdline-opts/max-filesize.md index cf2ac65376..02b2293c56 100644 --- a/docs/cmdline-opts/max-filesize.md +++ b/docs/cmdline-opts/max-filesize.md @@ -12,6 +12,7 @@ See-also: - limit-rate Example: - --max-filesize 100K $URL + - --max-filesize 2.6M $URL --- # `--max-filesize` @@ -22,9 +23,9 @@ transfer does not start and curl returns with exit code 63. Setting the maximum value to zero disables the limit. -A size modifier may be used. For example, Appending 'k' or 'K' counts the -number as kilobytes, 'm' or 'M' makes it megabytes, while 'g' or 'G' makes it -gigabytes. Examples: 200K, 3m and 1G. (Added in 7.58.0) +A unit suffix letter can be used. Appending 'k' or 'K' counts the number as +kilobytes, 'm' or 'M' makes it megabytes etc. The supported suffixes (k, M, G, +T, P) are 1024-based. Examples: 200K, 3m and 1G. (Added in 7.58.0) **NOTE**: before curl 8.4.0, when the file size is not known prior to download, for such files this option has no effect even if the file transfer @@ -32,3 +33,10 @@ ends up being larger than this given limit. Starting with curl 8.4.0, this option aborts the transfer if it reaches the threshold during transfer. + +Starting in curl 8.19.0, the maximum size can be specified using a fraction as +in `2.5M` for two and a half megabytes. It only works with a period (`.`) +delimiter, independent of what your locale might prefer. + +Since 8.20.0, this option also stops ongoing transfers that would reach this +threshold due to automatic decompression using --compressed. diff --git a/docs/cmdline-opts/max-redirs.md b/docs/cmdline-opts/max-redirs.md index 7c9f193d2c..02bdfaa7fb 100644 --- a/docs/cmdline-opts/max-redirs.md +++ b/docs/cmdline-opts/max-redirs.md @@ -10,12 +10,14 @@ Added: 7.5 Multi: single See-also: - location + - follow Example: - --max-redirs 3 --location $URL --- # `--max-redirs` -Set the maximum number of redirections to follow. When --location is used, to -prevent curl from following too many redirects, by default, the limit is -set to 50 redirects. Set this option to -1 to make it unlimited. +Set the maximum number of redirections to follow. When --location or --follow +are used, this option prevents curl from following too many redirects. By +default the limit is set to 50 redirects. Set this option to -1 to make it +unlimited. diff --git a/docs/cmdline-opts/netrc.md b/docs/cmdline-opts/netrc.md index 72b5ff935b..03a7ed6e5d 100644 --- a/docs/cmdline-opts/netrc.md +++ b/docs/cmdline-opts/netrc.md @@ -35,10 +35,18 @@ On Windows two filenames in the home directory are checked: *.netrc* and *_netrc*, preferring the former. Older versions on Windows checked for *_netrc* only. -A quick and simple example of how to setup a *.netrc* to allow curl to FTP to -the machine host.example.com with username 'myself' and password 'secret' +A quick and simple example of how to setup a *.netrc* to allow curl to access +the machine host.example.com with username `myself` and password `secret` could look similar to: machine host.example.com login myself password secret + +curl also supports the `default` keyword. This is the same as machine name +except that default matches any name. There can be only one `default` token, +and it must be after all machine tokens. + +When providing a username in the URL and a *.netrc* file, curl looks for the +password for that specific user for the given host if such an entry appears in +the file before a "generic" `machine` entry without `login` specified. diff --git a/docs/cmdline-opts/noproxy.md b/docs/cmdline-opts/noproxy.md index 698549e155..10b74ecbf4 100644 --- a/docs/cmdline-opts/noproxy.md +++ b/docs/cmdline-opts/noproxy.md @@ -22,6 +22,9 @@ as either a domain which contains the hostname, or the hostname itself. For example, `local.com` would match `local.com`, `local.com:80`, and `www.local.com`, but not `www.notlocal.com`. +To use international hostnames in this list, add the punycode version of the +hostname. + This option overrides the environment variables that disable the proxy (`no_proxy` and `NO_PROXY`) (added in 7.53.0). If there is an environment variable disabling a proxy, you can set the no proxy list to "" to override diff --git a/docs/cmdline-opts/output.md b/docs/cmdline-opts/output.md index e07c0756a6..0c4f7f9fac 100644 --- a/docs/cmdline-opts/output.md +++ b/docs/cmdline-opts/output.md @@ -13,6 +13,7 @@ See-also: - remote-name - remote-name-all - remote-header-name + - compressed Example: - -o file $URL - "http://{one,two}.example.com" -o "file_#1.txt" @@ -39,7 +40,7 @@ this: curl -o aa example.com -o bb example.net -and the order of the -o options and the URLs does not matter, just that the +and the order of the -o options and the URLs does not matter, only that the first -o is for the first URL and so on, so the above command line can also be written as @@ -65,3 +66,6 @@ Specify the filename as single minus to force the output to stdout, to override curl's internal binary output in terminal prevention: curl https://example.com/jpeg -o - + +Note that the binary output may be caused by the response being compressed, in +which case you may want to use the --compressed option. diff --git a/docs/cmdline-opts/pass.md b/docs/cmdline-opts/pass.md index 0527334f2a..79c2f8738a 100644 --- a/docs/cmdline-opts/pass.md +++ b/docs/cmdline-opts/pass.md @@ -4,7 +4,7 @@ SPDX-License-Identifier: curl Long: pass Arg: Help: Passphrase for the private key -Protocols: SSH TLS +Protocols: TLS SCP SFTP Category: ssh tls auth Added: 7.9.3 Multi: single diff --git a/docs/cmdline-opts/post301.md b/docs/cmdline-opts/post301.md index d9506f7dfe..3bcded869f 100644 --- a/docs/cmdline-opts/post301.md +++ b/docs/cmdline-opts/post301.md @@ -19,6 +19,6 @@ Example: Respect RFC 7231/6.4.2 and do not convert POST requests into GET requests when following a 301 redirect. The non-RFC behavior is ubiquitous in web browsers, -so curl does the conversion by default to maintain consistency. However, a -server may require a POST to remain a POST after such a redirection. This -option is meaningful only when using --location. +so curl does the conversion by default to maintain consistency. A server may +require a POST to remain a POST after such a redirection. This option is +meaningful only when using --location. diff --git a/docs/cmdline-opts/post302.md b/docs/cmdline-opts/post302.md index 9b2c2f658d..bcb35fc5df 100644 --- a/docs/cmdline-opts/post302.md +++ b/docs/cmdline-opts/post302.md @@ -19,6 +19,6 @@ Example: Respect RFC 7231/6.4.3 and do not convert POST requests into GET requests when following a 302 redirect. The non-RFC behavior is ubiquitous in web browsers, -so curl does the conversion by default to maintain consistency. However, a -server may require a POST to remain a POST after such a redirection. This -option is meaningful only when using --location. +so curl does the conversion by default to maintain consistency. A server may +require a POST to remain a POST after such a redirection. This option is +meaningful only when using --location. diff --git a/docs/cmdline-opts/proto-redir.md b/docs/cmdline-opts/proto-redir.md index 337aa93cb6..1f75bfdfd6 100644 --- a/docs/cmdline-opts/proto-redir.md +++ b/docs/cmdline-opts/proto-redir.md @@ -9,8 +9,9 @@ Category: connection curl Multi: single See-also: - proto + - follow Example: - - --proto-redir =http,https $URL + - --proto-redir =http,https --follow $URL --- # `--proto-redir` @@ -20,7 +21,7 @@ not overridden by this option. See --proto for how protocols are represented. Example, allow only HTTP and HTTPS on redirect: - curl --proto-redir -all,http,https http://example.com + curl --proto-redir -all,http,https --follow http://example.com By default curl only allows HTTP, HTTPS, FTP and FTPS on redirects (added in 7.65.2). Specifying *all* or *+all* enables all protocols on diff --git a/docs/cmdline-opts/proxy-cacert.md b/docs/cmdline-opts/proxy-cacert.md index 682349a7e2..0b2405d1c9 100644 --- a/docs/cmdline-opts/proxy-cacert.md +++ b/docs/cmdline-opts/proxy-cacert.md @@ -14,7 +14,7 @@ See-also: - dump-ca-embed - proxy Example: - - --proxy-cacert CA-file.txt -x https://proxy $URL + - --proxy-cacert CA-file.txt -x https://proxy.example $URL --- # `--proxy-cacert` diff --git a/docs/cmdline-opts/proxy-capath.md b/docs/cmdline-opts/proxy-capath.md index 91575ae4d8..344756a43e 100644 --- a/docs/cmdline-opts/proxy-capath.md +++ b/docs/cmdline-opts/proxy-capath.md @@ -13,7 +13,7 @@ See-also: - capath - dump-ca-embed Example: - - --proxy-capath /local/directory -x https://proxy $URL + - --proxy-capath /local/directory -x https://proxy.example $URL --- # `--proxy-capath` diff --git a/docs/cmdline-opts/proxy-cert-type.md b/docs/cmdline-opts/proxy-cert-type.md index 8a1121bb36..3dcd2017c4 100644 --- a/docs/cmdline-opts/proxy-cert-type.md +++ b/docs/cmdline-opts/proxy-cert-type.md @@ -11,7 +11,7 @@ See-also: - proxy-cert - proxy-key Example: - - --proxy-cert-type PEM --proxy-cert file -x https://proxy $URL + - --proxy-cert-type PEM --proxy-cert file -x https://proxy.example $URL --- # `--proxy-cert-type` diff --git a/docs/cmdline-opts/proxy-cert.md b/docs/cmdline-opts/proxy-cert.md index 734766730c..929791e3a1 100644 --- a/docs/cmdline-opts/proxy-cert.md +++ b/docs/cmdline-opts/proxy-cert.md @@ -12,7 +12,7 @@ See-also: - proxy-key - proxy-cert-type Example: - - --proxy-cert file -x https://proxy $URL + - --proxy-cert file -x https://proxy.example $URL --- # `--proxy-cert` diff --git a/docs/cmdline-opts/proxy-ciphers.md b/docs/cmdline-opts/proxy-ciphers.md index 420e7563bd..4cb85e1e67 100644 --- a/docs/cmdline-opts/proxy-ciphers.md +++ b/docs/cmdline-opts/proxy-ciphers.md @@ -13,7 +13,7 @@ See-also: - ciphers - proxy Example: - - --proxy-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 -x https://proxy $URL + - --proxy-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 -x https://proxy.example $URL --- # `--proxy-ciphers` diff --git a/docs/cmdline-opts/proxy-crlfile.md b/docs/cmdline-opts/proxy-crlfile.md index 726e449557..9f7d3304fa 100644 --- a/docs/cmdline-opts/proxy-crlfile.md +++ b/docs/cmdline-opts/proxy-crlfile.md @@ -11,7 +11,7 @@ See-also: - crlfile - proxy Example: - - --proxy-crlfile rejects.txt -x https://proxy $URL + - --proxy-crlfile rejects.txt -x https://proxy.example $URL --- # `--proxy-crlfile` diff --git a/docs/cmdline-opts/proxy-header.md b/docs/cmdline-opts/proxy-header.md index 3edf6c9a90..459eb462f6 100644 --- a/docs/cmdline-opts/proxy-header.md +++ b/docs/cmdline-opts/proxy-header.md @@ -10,6 +10,7 @@ Category: proxy Multi: append See-also: - proxy + - header Example: - --proxy-header "X-First-Name: Joe" -x http://proxy $URL - --proxy-header "User-Agent: surprise" -x http://proxy $URL diff --git a/docs/cmdline-opts/proxy-insecure.md b/docs/cmdline-opts/proxy-insecure.md index 5796c36237..0c2d8b99bc 100644 --- a/docs/cmdline-opts/proxy-insecure.md +++ b/docs/cmdline-opts/proxy-insecure.md @@ -10,7 +10,7 @@ See-also: - proxy - insecure Example: - - --proxy-insecure -x https://proxy $URL + - --proxy-insecure -x https://proxy.example $URL --- # `--proxy-insecure` diff --git a/docs/cmdline-opts/proxy-key-type.md b/docs/cmdline-opts/proxy-key-type.md index 587c13c592..e455140016 100644 --- a/docs/cmdline-opts/proxy-key-type.md +++ b/docs/cmdline-opts/proxy-key-type.md @@ -11,7 +11,7 @@ See-also: - proxy-key - proxy Example: - - --proxy-key-type DER --proxy-key here -x https://proxy $URL + - --proxy-key-type DER --proxy-key here -x https://proxy.example $URL --- # `--proxy-key-type` diff --git a/docs/cmdline-opts/proxy-key.md b/docs/cmdline-opts/proxy-key.md index 7caa636e36..8ee78c46ea 100644 --- a/docs/cmdline-opts/proxy-key.md +++ b/docs/cmdline-opts/proxy-key.md @@ -11,7 +11,7 @@ See-also: - proxy-key-type - proxy Example: - - --proxy-key here -x https://proxy $URL + - --proxy-key here -x https://proxy.example $URL --- # `--proxy-key` diff --git a/docs/cmdline-opts/proxy-pass.md b/docs/cmdline-opts/proxy-pass.md index 88cefd54c8..1005d95d2f 100644 --- a/docs/cmdline-opts/proxy-pass.md +++ b/docs/cmdline-opts/proxy-pass.md @@ -11,7 +11,7 @@ See-also: - proxy - proxy-key Example: - - --proxy-pass secret --proxy-key here -x https://proxy $URL + - --proxy-pass secret --proxy-key here -x https://proxy.example $URL --- # `--proxy-pass` diff --git a/docs/cmdline-opts/proxy-ssl-allow-beast.md b/docs/cmdline-opts/proxy-ssl-allow-beast.md index 089038dec4..909a7f026e 100644 --- a/docs/cmdline-opts/proxy-ssl-allow-beast.md +++ b/docs/cmdline-opts/proxy-ssl-allow-beast.md @@ -10,7 +10,7 @@ See-also: - ssl-allow-beast - proxy Example: - - --proxy-ssl-allow-beast -x https://proxy $URL + - --proxy-ssl-allow-beast -x https://proxy.example $URL --- # `--proxy-ssl-allow-beast` diff --git a/docs/cmdline-opts/proxy-ssl-auto-client-cert.md b/docs/cmdline-opts/proxy-ssl-auto-client-cert.md index 578a7a6417..e041b81523 100644 --- a/docs/cmdline-opts/proxy-ssl-auto-client-cert.md +++ b/docs/cmdline-opts/proxy-ssl-auto-client-cert.md @@ -10,7 +10,7 @@ See-also: - ssl-auto-client-cert - proxy Example: - - --proxy-ssl-auto-client-cert -x https://proxy $URL + - --proxy-ssl-auto-client-cert -x https://proxy.example $URL --- # `--proxy-ssl-auto-client-cert` diff --git a/docs/cmdline-opts/proxy-tlsauthtype.md b/docs/cmdline-opts/proxy-tlsauthtype.md index 684a7d55ef..84becc149e 100644 --- a/docs/cmdline-opts/proxy-tlsauthtype.md +++ b/docs/cmdline-opts/proxy-tlsauthtype.md @@ -12,7 +12,7 @@ See-also: - proxy-tlsuser - proxy-tlspassword Example: - - --proxy-tlsauthtype SRP -x https://proxy $URL + - --proxy-tlsauthtype SRP -x https://proxy.example $URL --- # `--proxy-tlsauthtype` diff --git a/docs/cmdline-opts/proxy-tlspassword.md b/docs/cmdline-opts/proxy-tlspassword.md index fe9ae7d2e2..63c2521566 100644 --- a/docs/cmdline-opts/proxy-tlspassword.md +++ b/docs/cmdline-opts/proxy-tlspassword.md @@ -11,7 +11,7 @@ See-also: - proxy - proxy-tlsuser Example: - - --proxy-tlspassword passwd -x https://proxy $URL + - --proxy-tlspassword passwd -x https://proxy.example $URL --- # `--proxy-tlspassword` diff --git a/docs/cmdline-opts/proxy-tlsuser.md b/docs/cmdline-opts/proxy-tlsuser.md index 3517701119..610a2169b8 100644 --- a/docs/cmdline-opts/proxy-tlsuser.md +++ b/docs/cmdline-opts/proxy-tlsuser.md @@ -11,7 +11,7 @@ See-also: - proxy - proxy-tlspassword Example: - - --proxy-tlsuser smith -x https://proxy $URL + - --proxy-tlsuser smith -x https://proxy.example $URL --- # `--proxy-tlsuser` diff --git a/docs/cmdline-opts/proxy-tlsv1.md b/docs/cmdline-opts/proxy-tlsv1.md index 7b322e3a32..20643fd82b 100644 --- a/docs/cmdline-opts/proxy-tlsv1.md +++ b/docs/cmdline-opts/proxy-tlsv1.md @@ -9,7 +9,7 @@ Multi: mutex See-also: - proxy Example: - - --proxy-tlsv1 -x https://proxy $URL + - --proxy-tlsv1 -x https://proxy.example $URL --- # `--proxy-tlsv1` diff --git a/docs/cmdline-opts/proxy.md b/docs/cmdline-opts/proxy.md index 1ed503c108..6cd456169d 100644 --- a/docs/cmdline-opts/proxy.md +++ b/docs/cmdline-opts/proxy.md @@ -31,8 +31,7 @@ HTTPS proxy support works with the https:// protocol prefix for OpenSSL and GnuTLS (added in 7.52.0). It also works for mbedTLS, Rustls, Schannel and wolfSSL (added in 7.87.0). -Unrecognized and unsupported proxy protocols cause an error (added in 7.52.0). -Ancient curl versions ignored unknown schemes and used http:// instead. +Unrecognized and unsupported proxy protocol schemes cause an error. If the port number is not specified in the proxy string, it is assumed to be 1080. diff --git a/docs/cmdline-opts/quote.md b/docs/cmdline-opts/quote.md index 04a47424a0..1ac2076b74 100644 --- a/docs/cmdline-opts/quote.md +++ b/docs/cmdline-opts/quote.md @@ -18,13 +18,13 @@ Example: # `--quote` Send an arbitrary command to the remote FTP or SFTP server. Quote commands are -sent BEFORE the transfer takes place (just after the initial **PWD** command -in an FTP transfer, to be exact). To make commands take place after a +sent BEFORE the transfer takes place (immediately after the initial **PWD** +command in an FTP transfer, to be exact). To make commands take place after a successful transfer, prefix them with a dash '-'. (FTP only) To make commands be sent after curl has changed the working -directory, just before the file transfer command(s), prefix the command with a -'+'. +directory, immediately before the file transfer command(s), prefix the command +with a '+'. You may specify any number of commands. @@ -37,9 +37,12 @@ You must send syntactically correct FTP commands as RFC 959 defines to FTP servers, or one of the commands listed below to SFTP servers. SFTP is a binary protocol. Unlike for FTP, curl interprets SFTP quote commands -itself before sending them to the server. Filenames may be quoted shell-style -to embed spaces or special characters. Following is the list of all supported -SFTP quote commands: +itself before sending them to the server. Filenames must be provided within +double quotes to embed spaces, backslashes, quotes or double quotes. Within +double quotes the following escape sequences are available for that purpose: +\\, \", and \'. + +Following is the list of all supported SFTP quote commands: ## atime date file The atime command sets the last access time of the file named by the file diff --git a/docs/cmdline-opts/remote-header-name.md b/docs/cmdline-opts/remote-header-name.md index 88c2808a3f..52ae98b01c 100644 --- a/docs/cmdline-opts/remote-header-name.md +++ b/docs/cmdline-opts/remote-header-name.md @@ -34,6 +34,9 @@ this option may provide you with rather unexpected filenames. This feature uses the name from the `filename` field, it does not yet support the `filename*` field (filenames with explicit character sets). +Starting in 8.19.0, curl falls back and uses the filename extracted from the +last redirect header if no `Content-Disposition:` header provides a filename. + **WARNING**: Exercise judicious use of this option, especially on Windows. A rogue server could send you the name of a DLL or other file that could be loaded automatically by Windows or some third party software. diff --git a/docs/cmdline-opts/request.md b/docs/cmdline-opts/request.md index 86cf10deaf..2c9d7776e5 100644 --- a/docs/cmdline-opts/request.md +++ b/docs/cmdline-opts/request.md @@ -10,8 +10,9 @@ Added: 6.0 Multi: single See-also: - request-target + - follow Example: - - -X "DELETE" $URL + - --request "DELETE" $URL - -X NLST ftp://example.com/ --- @@ -37,10 +38,10 @@ This option only changes the actual word used in the HTTP request, it does not alter the way curl behaves. For example if you want to make a proper HEAD request, using -X HEAD does not suffice. You need to use the --head option. -The method string you set with --request is used for all requests, which -if you for example use --location may cause unintended side-effects when curl -does not change request method according to the HTTP 30x response codes - and -similar. +If --location is used, the method string you set with --request is used for +all requests, which may cause unintended side-effects when curl does not +change request method according to the HTTP 30x response codes - and similar. +Consider using --follow instead in combination with --request. ## FTP Specifies a custom FTP command to use instead of *LIST* when doing file lists diff --git a/docs/cmdline-opts/retry-connrefused.md b/docs/cmdline-opts/retry-connrefused.md index 22345cd881..c7f96a8fee 100644 --- a/docs/cmdline-opts/retry-connrefused.md +++ b/docs/cmdline-opts/retry-connrefused.md @@ -15,5 +15,7 @@ Example: # `--retry-connrefused` -In addition to the other conditions, consider ECONNREFUSED as a transient -error too for --retry. This option is used together with --retry. +In addition to the other conditions, also consider ECONNREFUSED as a transient +error for --retry. This option is used together with --retry. Normally, a +refused connection is not considered a transient error and therefore would not +otherwise trigger a retry. diff --git a/docs/cmdline-opts/retry.md b/docs/cmdline-opts/retry.md index 2176e8f43d..4d9e83cd5f 100644 --- a/docs/cmdline-opts/retry.md +++ b/docs/cmdline-opts/retry.md @@ -9,6 +9,8 @@ Category: curl Multi: single See-also: - retry-max-time + - retry-connrefused + - retry-delay Example: - --retry 7 $URL --- @@ -16,16 +18,17 @@ Example: # `--retry` If a transient error is returned when curl tries to perform a transfer, it -retries this number of times before giving up. Setting the number to 0 -makes curl do no retries (which is the default). Transient error means either: -a timeout, an FTP 4xx response code or an HTTP 408, 429, 500, 502, 503 or 504 -response code. +retries this number of times before giving up. Setting the number to 0 makes +curl do no retries (which is the default). Transient error means either: a +timeout, an FTP 4xx response code or an HTTP 408, 429, 500, 502, 503, 504, 522 +or 524 response code. When curl is about to retry a transfer, it first waits one second and then for all forthcoming retries it doubles the waiting time until it reaches 10 minutes, which then remains the set fixed delay time between the rest of the -retries. By using --retry-delay you disable this exponential backoff algorithm. -See also --retry-max-time to limit the total time allowed for retries. +retries. By using --retry-delay you disable this exponential backoff +algorithm. See also --retry-max-time to limit the total time allowed for +retries. curl complies with the Retry-After: response header if one was present to know when to issue the next retry (added in 7.66.0). diff --git a/docs/cmdline-opts/sasl-authzid.md b/docs/cmdline-opts/sasl-authzid.md index 4c4282d14d..4e92a20541 100644 --- a/docs/cmdline-opts/sasl-authzid.md +++ b/docs/cmdline-opts/sasl-authzid.md @@ -4,6 +4,7 @@ SPDX-License-Identifier: curl Long: sasl-authzid Arg: Help: Identity for SASL PLAIN authentication +Protocols: LDAP IMAP POP3 SMTP Added: 7.66.0 Category: auth Multi: single diff --git a/docs/cmdline-opts/sasl-ir.md b/docs/cmdline-opts/sasl-ir.md index b11137df0a..206bf29317 100644 --- a/docs/cmdline-opts/sasl-ir.md +++ b/docs/cmdline-opts/sasl-ir.md @@ -3,6 +3,7 @@ c: Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl Long: sasl-ir Help: Initial response in SASL authentication +Protocols: LDAP IMAP POP3 SMTP Added: 7.31.0 Category: auth Multi: boolean @@ -14,4 +15,6 @@ Example: # `--sasl-ir` -Enable initial response in SASL authentication. +Enable initial response in SASL authentication. Such an "initial response" is +a message sent by the client to the server after the client selects an +authentication mechanism. diff --git a/docs/cmdline-opts/silent.md b/docs/cmdline-opts/silent.md index 4e80ee7640..2498ca56f4 100644 --- a/docs/cmdline-opts/silent.md +++ b/docs/cmdline-opts/silent.md @@ -17,9 +17,9 @@ Example: # `--silent` -Silent or quiet mode. Do not show progress meter or error messages. Makes curl -mute. It still outputs the data you ask for, potentially even to the -terminal/stdout unless you redirect it. +Silent or quiet mode. Do not show progress meter, warning messages or error +messages. Makes curl mute. It still outputs the data you ask for, potentially +even to the terminal/stdout unless you redirect it. Use --show-error in addition to this option to disable progress meter but still show error messages. diff --git a/docs/cmdline-opts/skip-existing.md b/docs/cmdline-opts/skip-existing.md index cfb7c2f953..dbef2fae92 100644 --- a/docs/cmdline-opts/skip-existing.md +++ b/docs/cmdline-opts/skip-existing.md @@ -18,5 +18,5 @@ Example: If there is a local file present when a download is requested, the operation is skipped. Note that curl cannot know if the local file was previously -downloaded fine, or if it is incomplete etc, it just knows if there is a -filename present in the file system or not and it skips the transfer if it is. +downloaded fine, or if it is incomplete etc, it knows if there is a filename +present in the file system or not and it skips the transfer if it is. diff --git a/docs/cmdline-opts/socks4.md b/docs/cmdline-opts/socks4.md index 8e92d5930c..59ec172b8d 100644 --- a/docs/cmdline-opts/socks4.md +++ b/docs/cmdline-opts/socks4.md @@ -7,6 +7,7 @@ Help: SOCKS4 proxy on given host + port Added: 7.15.2 Category: proxy Multi: single +Mutexed: proxy socks4a socks5 socks5-hostname See-also: - socks4a - socks5 @@ -21,8 +22,9 @@ Use the specified SOCKS4 proxy. If the port number is not specified, it is assumed at port 1080. Using this socket type makes curl resolve the hostname and pass the address on to the proxy. -To specify proxy on a Unix domain socket, use localhost for host, e.g. -`socks4://localhost/path/to/socket.sock` +To specify the proxy on a Unix domain socket, use localhost for host and +append the absolute path to the domain socket. For example: +`socks4://localhost/path/to/socket.sock` (the scheme may be omitted). This option overrides any previous use of --proxy, as they are mutually exclusive. diff --git a/docs/cmdline-opts/socks4a.md b/docs/cmdline-opts/socks4a.md index 04d60b81b6..9e451cf7b0 100644 --- a/docs/cmdline-opts/socks4a.md +++ b/docs/cmdline-opts/socks4a.md @@ -7,6 +7,7 @@ Help: SOCKS4a proxy on given host + port Added: 7.18.0 Category: proxy Multi: single +Mutexed: proxy socks4 socks5 socks5-hostname See-also: - socks4 - socks5 @@ -20,8 +21,9 @@ Example: Use the specified SOCKS4a proxy. If the port number is not specified, it is assumed at port 1080. This asks the proxy to resolve the hostname. -To specify proxy on a Unix domain socket, use localhost for host, e.g. -`socks4a://localhost/path/to/socket.sock` +To specify the proxy on a Unix domain socket, use localhost for host and +append the absolute path to the domain socket. For example: +`socks4a://localhost/path/to/socket.sock` (the scheme may be omitted). This option overrides any previous use of --proxy, as they are mutually exclusive. diff --git a/docs/cmdline-opts/socks5-gssapi-nec.md b/docs/cmdline-opts/socks5-gssapi-nec.md index eef6b2de9d..9cd91b9615 100644 --- a/docs/cmdline-opts/socks5-gssapi-nec.md +++ b/docs/cmdline-opts/socks5-gssapi-nec.md @@ -5,6 +5,7 @@ Long: socks5-gssapi-nec Help: Compatibility with NEC SOCKS5 server Added: 7.19.4 Category: proxy auth +Protocols: GSS/kerberos Multi: boolean See-also: - socks5 diff --git a/docs/cmdline-opts/socks5-gssapi.md b/docs/cmdline-opts/socks5-gssapi.md index e17425431b..b8520b22cc 100644 --- a/docs/cmdline-opts/socks5-gssapi.md +++ b/docs/cmdline-opts/socks5-gssapi.md @@ -5,6 +5,7 @@ Long: socks5-gssapi Help: Enable GSS-API auth for SOCKS5 proxies Added: 7.55.0 Category: proxy auth +Protocols: GSS/kerberos Multi: boolean See-also: - socks5 diff --git a/docs/cmdline-opts/socks5-hostname.md b/docs/cmdline-opts/socks5-hostname.md index 0ea2ed739f..b558248a78 100644 --- a/docs/cmdline-opts/socks5-hostname.md +++ b/docs/cmdline-opts/socks5-hostname.md @@ -7,6 +7,7 @@ Help: SOCKS5 proxy, pass hostname to proxy Added: 7.18.0 Category: proxy Multi: single +Mutexed: proxy socks4 socks4a socks5 See-also: - socks5 - socks4a @@ -19,8 +20,9 @@ Example: Use the specified SOCKS5 proxy (and let the proxy resolve the hostname). If the port number is not specified, it is assumed at port 1080. -To specify proxy on a Unix domain socket, use localhost for host, e.g. -`socks5h://localhost/path/to/socket.sock` +To specify the proxy on a Unix domain socket, use localhost for host and +append the absolute path to the domain socket. For example: +`socks5h://localhost/path/to/socket.sock` (the scheme may be omitted). This option overrides any previous use of --proxy, as they are mutually exclusive. diff --git a/docs/cmdline-opts/socks5.md b/docs/cmdline-opts/socks5.md index 4ea660d62d..3aa65b33ad 100644 --- a/docs/cmdline-opts/socks5.md +++ b/docs/cmdline-opts/socks5.md @@ -10,8 +10,10 @@ Multi: single See-also: - socks5-hostname - socks4a +Mutexed: proxy socks4 socks4a socks5-hostname Example: - --socks5 proxy.example:7000 $URL + - --socks5 localhost/path/unix-domain $URL --- # `--socks5` @@ -19,8 +21,9 @@ Example: Use the specified SOCKS5 proxy - but resolve the hostname locally. If the port number is not specified, it is assumed at port 1080. -To specify proxy on a Unix domain socket, use localhost for host, e.g. -`socks5://localhost/path/to/socket.sock` +To specify the proxy on a Unix domain socket, use localhost for host and +append the absolute path to the domain socket. For example: +`socks5://localhost/path/to/socket.sock` (the scheme may be omitted). This option overrides any previous use of --proxy, as they are mutually exclusive. diff --git a/docs/cmdline-opts/ssl-sessions.md b/docs/cmdline-opts/ssl-sessions.md index bb1e251e73..33ef984a3e 100644 --- a/docs/cmdline-opts/ssl-sessions.md +++ b/docs/cmdline-opts/ssl-sessions.md @@ -8,6 +8,7 @@ Help: Load/save SSL session tickets from/to this file Added: 8.12.0 Category: tls Multi: single +Experimental: yes See-also: - tls-earlydata Example: diff --git a/docs/cmdline-opts/ssl.md b/docs/cmdline-opts/ssl.md index 0c0f28172a..5951d01991 100644 --- a/docs/cmdline-opts/ssl.md +++ b/docs/cmdline-opts/ssl.md @@ -28,8 +28,7 @@ different levels of encryption required. This option is handled in LDAP (added in 7.81.0). It is fully supported by the OpenLDAP backend and ignored by the generic ldap backend. -Please note that a server may close the connection if the negotiation does -not succeed. +Please note that a server may close the connection if the negotiation fails. This option was formerly known as --ftp-ssl (added in 7.11.0). That option name can still be used but might be removed in a future version. diff --git a/docs/cmdline-opts/tcp-nodelay.md b/docs/cmdline-opts/tcp-nodelay.md index 6944d70142..605667181a 100644 --- a/docs/cmdline-opts/tcp-nodelay.md +++ b/docs/cmdline-opts/tcp-nodelay.md @@ -14,8 +14,17 @@ Example: # `--tcp-nodelay` -Turn on the TCP_NODELAY option. See the *curl_easy_setopt(3)* man page for -details about this option. +Turn on the TCP_NODELAY option. + +This option disables the Nagle algorithm on TCP connections. The purpose of +this algorithm is to minimize the number of small packets on the network +(where "small packets" means TCP segments less than the Maximum Segment Size +for the network). + +Maximizing the amount of data sent per TCP segment is good because it +amortizes the overhead of the send. In some cases small segments may need to +be sent without delay. This is less efficient than sending larger amounts of +data at a time, and can contribute to congestion on the network if overdone. curl sets this option by default and you need to explicitly switch it off if you do not want it on (added in 7.50.2). diff --git a/docs/cmdline-opts/telnet-option.md b/docs/cmdline-opts/telnet-option.md index a332b1a5cd..ca82a4ceb8 100644 --- a/docs/cmdline-opts/telnet-option.md +++ b/docs/cmdline-opts/telnet-option.md @@ -6,6 +6,7 @@ Short: t Arg: Help: Set telnet option Category: telnet +Protocols: TELNET Added: 7.7 Multi: append See-also: diff --git a/docs/cmdline-opts/time-cond.md b/docs/cmdline-opts/time-cond.md index 44cb166349..d902dd49d1 100644 --- a/docs/cmdline-opts/time-cond.md +++ b/docs/cmdline-opts/time-cond.md @@ -24,7 +24,7 @@ Request a file that has been modified later than the given time and date, or one that has been modified before that time. The date expression can be all sorts of date strings or if it does not match any internal ones, it is treated as a filename and curl tries to get the modification date (mtime) from that -file instead. See the *curl_getdate(3)* man pages for date expression details. +file instead. See the *curl_getdate(3)* man page for date expression details. Start the date expression with a dash (-) to make it request for a document that is older than the given date/time, default is a document that is newer diff --git a/docs/cmdline-opts/tls-earlydata.md b/docs/cmdline-opts/tls-earlydata.md index 6428977983..8e344758be 100644 --- a/docs/cmdline-opts/tls-earlydata.md +++ b/docs/cmdline-opts/tls-earlydata.md @@ -21,7 +21,7 @@ Enable the use of TLSv1.3 early data, also known as '0RTT' where possible. This has security implications for the requests sent that way. This option can be used when curl is built to use GnuTLS, wolfSSL, quictls and -OpenSSL as a TLS provider (but not BoringSSL, AWS-LC, or rustls). +OpenSSL as a TLS provider (but not BoringSSL, AWS-LC, or Rustls). If a server supports this TLSv1.3 feature, and to what extent, is announced as part of the TLS "session" sent back to curl. Until curl has seen such diff --git a/docs/cmdline-opts/tr-encoding.md b/docs/cmdline-opts/tr-encoding.md index cdd8f02d67..c364c07d4b 100644 --- a/docs/cmdline-opts/tr-encoding.md +++ b/docs/cmdline-opts/tr-encoding.md @@ -17,3 +17,8 @@ Example: Request a compressed Transfer-Encoding response using one of the algorithms curl supports, and uncompress the data while receiving it. + +This method was once intended to be the way to do automatic data compression +for HTTP but for all practical purposes using Content-Encoding as done with +--compressed has superseded transfer encoding. The --tr-encoding option is +therefore often not be one you want. diff --git a/docs/cmdline-opts/trace-ids.md b/docs/cmdline-opts/trace-ids.md index b9a6222600..302631d844 100644 --- a/docs/cmdline-opts/trace-ids.md +++ b/docs/cmdline-opts/trace-ids.md @@ -18,3 +18,7 @@ Example: Prepend the transfer and connection identifiers to each trace or verbose line that curl displays. + +The identifiers are unique numbers assigned to each connection and transfer to +allow a user to better understand which transfer and connection each verbose +output line refers to. diff --git a/docs/cmdline-opts/unix-socket.md b/docs/cmdline-opts/unix-socket.md index 582f32dc50..34cc714f79 100644 --- a/docs/cmdline-opts/unix-socket.md +++ b/docs/cmdline-opts/unix-socket.md @@ -16,4 +16,7 @@ Example: # `--unix-socket` -Connect through this Unix domain socket, instead of using the network. +Connect to the server through this Unix domain socket, instead of using the +network. + +To connect to a proxy over Unix domain socket, see --proxy. diff --git a/docs/cmdline-opts/upload-flags.md b/docs/cmdline-opts/upload-flags.md index e30fb3dbdb..6c014d0895 100644 --- a/docs/cmdline-opts/upload-flags.md +++ b/docs/cmdline-opts/upload-flags.md @@ -4,6 +4,7 @@ SPDX-License-Identifier: curl Long: upload-flags Arg: Help: IMAP upload behavior +Protocols: IMAP Category: curl output Added: 8.13.0 Multi: single @@ -20,5 +21,5 @@ specified as either a single flag value or a comma-separated list of flag values. These values are case-sensitive and may be negated by prepending them with a '-' character. Currently the following flag values are accepted: answered, deleted, draft, flagged, and -seen. The currently-accepted flag values are used to set flags on +seen. The currently accepted flag values are used to set flags on IMAP uploads. diff --git a/docs/cmdline-opts/url-query.md b/docs/cmdline-opts/url-query.md index 43bf43d932..3953eda4c7 100644 --- a/docs/cmdline-opts/url-query.md +++ b/docs/cmdline-opts/url-query.md @@ -4,7 +4,6 @@ SPDX-License-Identifier: curl Long: url-query Arg: Help: Add a URL query part -Protocols: all Added: 7.87.0 Category: http post upload Multi: append diff --git a/docs/cmdline-opts/use-ascii.md b/docs/cmdline-opts/use-ascii.md index 30cc860b0a..4dba446ffe 100644 --- a/docs/cmdline-opts/use-ascii.md +++ b/docs/cmdline-opts/use-ascii.md @@ -4,8 +4,8 @@ SPDX-License-Identifier: curl Short: B Long: use-ascii Help: Use ASCII/text transfer -Protocols: FTP LDAP -Category: ftp output ldap +Protocols: FTP LDAP TFTP +Category: ftp output ldap tftp Added: 5.0 Multi: boolean See-also: @@ -18,5 +18,6 @@ Example: # `--use-ascii` Enable ASCII transfer mode. For FTP, this can also be enforced by using a URL -that ends with `;type=A`. This option causes data sent to stdout to be in text -mode for Win32 systems. +that ends with `;type=A`. For TFTP, this can also be enforced by using a URL +that ends with `;mode=netascii`. This option causes data sent to stdout to be +in text mode for Win32 systems. diff --git a/docs/cmdline-opts/user.md b/docs/cmdline-opts/user.md index 9e7392f5a6..d7600e0310 100644 --- a/docs/cmdline-opts/user.md +++ b/docs/cmdline-opts/user.md @@ -20,7 +20,7 @@ Example: Specify the username and password to use for server authentication. Overrides --netrc and --netrc-optional. -If you simply specify the username, curl prompts for a password. +If you specify only the username, curl prompts for a password. The username and passwords are split up on the first colon, which makes it impossible to use a colon in the username with this option. The password can, @@ -37,8 +37,8 @@ Windows domain name in the username, in order for the server to successfully obtain a Kerberos Ticket. If you do not, then the initial authentication handshake may fail. -When using NTLM, the username can be specified simply as the username, without -the domain, if there is a single domain and forest in your setup for example. +When using NTLM, the username can be specified without the domain, if +there is a single domain and forest in your setup for example. To specify the domain name use either Down-Level Logon Name or UPN (User Principal Name) formats. For example, EXAMPLE\user and user@example.com diff --git a/docs/cmdline-opts/verbose.md b/docs/cmdline-opts/verbose.md index 819899ce24..b5d6b57d64 100644 --- a/docs/cmdline-opts/verbose.md +++ b/docs/cmdline-opts/verbose.md @@ -21,20 +21,40 @@ Example: # `--verbose` Make curl output verbose information during the operation. Useful for -debugging and seeing what's going on under the hood. A line starting with \> -means header data sent by curl, \< means header data received by curl that is -hidden in normal cases, and a line starting with * means additional info -provided by curl. +debugging and seeing what's going on under the hood. Verbose output lines are +prefixed with letters: + +## > + +header sent by curl + +## < + +header received by curl + +## } + +data sent by curl + +## { + +data received by curl + +## * + +additional info provided by curl. Text that adds explanations what goes on and +about choices curl does. + +## If you only want HTTP headers in the output, --show-headers or --dump-header might be more suitable options. Since curl 8.10, mentioning this option several times in the same argument -increases the level of the trace output. However, as before, a single ---verbose or --no-verbose reverts any additions by previous `-vv` again. This -means that `-vv -v` is equivalent to a single -v. This avoids unwanted -verbosity when the option is mentioned in the command line *and* curl config -files. +increases the level of the trace output. As before, a single --verbose or +--no-verbose reverts any additions by previous `-vv` again. This means that +`-vv -v` is equivalent to a single -v. This avoids unwanted verbosity when the +option is mentioned in the command line *and* curl config files. Using it twice, e.g. `-vv`, outputs time (--trace-time) and transfer ids (--trace-ids), as well as enabling tracing for all protocols (--trace-config diff --git a/docs/cmdline-opts/version.md b/docs/cmdline-opts/version.md index a6c83ba5fd..35e9cf237a 100644 --- a/docs/cmdline-opts/version.md +++ b/docs/cmdline-opts/version.md @@ -124,9 +124,6 @@ SSPI is supported. ## `TLS-SRP` SRP (Secure Remote Password) authentication is supported for TLS. -## `TrackMemory` -Debug memory tracking is supported. - ## `Unicode` Unicode support on Windows. diff --git a/docs/cmdline-opts/vlan-priority.md b/docs/cmdline-opts/vlan-priority.md index 34dc8ce066..c49c659e5c 100644 --- a/docs/cmdline-opts/vlan-priority.md +++ b/docs/cmdline-opts/vlan-priority.md @@ -6,7 +6,6 @@ Arg: Help: Set VLAN priority Added: 8.9.0 Category: connection -Protocols: All Multi: single See-also: - ip-tos diff --git a/docs/cmdline-opts/write-out.md b/docs/cmdline-opts/write-out.md index b365eeb22d..53e4df18fe 100644 --- a/docs/cmdline-opts/write-out.md +++ b/docs/cmdline-opts/write-out.md @@ -25,9 +25,8 @@ from stdin you write "@-". The variables present in the output format are substituted by the value or text that curl thinks fit, as described below. All variables are specified as -%{variable_name} and to output a normal % you just write them as %%. You can -output a newline by using \n, a carriage return with \r and a tab space with -\t. +%{variable_name} and to output a normal % you write them as %%. You can output +a newline by using \n, a carriage return with \r and a tab space with \t. The output is by default written to standard output, but can be changed with %{stderr} and %output{}. @@ -93,6 +92,14 @@ The value of header `name` from the transfer's most recent server response. Unlike other variables, the variable name `header` is not in braces. For example `%header{date}`. Refer to --write-out remarks. (Added in 7.84.0) +Starting with 8.17.0, output the contents of *all* header fields using a +specific name - even for a whole redirect "chain" by appending +`:all:[separator]` to the header name. The `[separator]` string (if not blank) +is output between the headers if there are more than one. When more than one +header is shown, they are output in the chronological order of appearance over +the wire. To include a close brace (`}`) in the separator, escape it with a +backslash: `\}`. + ## `header_json` A JSON object with all HTTP response headers from the recent transfer. Values are provided as arrays, since in the case of multiple headers there can be @@ -185,6 +192,11 @@ known as "http_code"). (Added in 7.18.2) ## `scheme` The URL scheme (sometimes called protocol) that was effectively used. (Added in 7.52.0) +## `size_delivered` +The total amount of data that were saved or written to stdout. When +--compressed is used, this is likely different than `size_download`. Includes +the headers in the count if --include is used. + ## `size_download` The total amount of bytes that were downloaded. This is the size of the body/data that was transferred, excluding headers. @@ -237,13 +249,13 @@ The time, in seconds, it took from the start until the name resolving was completed. ## `time_posttransfer` -The time it took from the start until the last byte is sent by libcurl. -In microseconds. (Added in 8.10.0) +The time, in seconds, it took from the start until the last byte is sent +by libcurl. (Added in 8.10.0) ## `time_pretransfer` -The time, in seconds, it took from the start until the file transfer was just -about to begin. This includes all pre-transfer commands and negotiations that -are specific to the particular protocol(s) involved. +The time, in seconds, it took from the start until immediately before the file +transfer was about to begin. This includes all pre-transfer commands and +negotiations that are specific to the particular protocol(s) involved. ## `time_queue` The time, in seconds, the transfer was queued during its run. This adds diff --git a/docs/examples/.checksrc b/docs/examples/.checksrc new file mode 100644 index 0000000000..6fbc5afcdc --- /dev/null +++ b/docs/examples/.checksrc @@ -0,0 +1,36 @@ +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + +allowfunc atoi +allowfunc atol +allowfunc calloc +allowfunc close +allowfunc fclose +allowfunc fdopen +allowfunc fopen +allowfunc fprintf +allowfunc free +allowfunc fstat +allowfunc gmtime +allowfunc localtime +allowfunc malloc +allowfunc open +allowfunc printf +allowfunc realloc +allowfunc snprintf +allowfunc socket +allowfunc sscanf +allowfunc strdup +allowfunc strerror +allowfunc vsnprintf + +# Use of curl printf functions is discouraged +banfunc curl_maprintf +banfunc curl_mfprintf +banfunc curl_mprintf +banfunc curl_msnprintf +banfunc curl_mvaprintf +banfunc curl_mvfprintf +banfunc curl_mvprintf +banfunc curl_mvsnprintf diff --git a/docs/examples/.gitignore b/docs/examples/.gitignore index 4b78bd78dd..c96ad91272 100644 --- a/docs/examples/.gitignore +++ b/docs/examples/.gitignore @@ -35,7 +35,6 @@ getreferrer ghiper headerapi hiperfifo -href_extractor hsts-preload htmltidy http-options @@ -69,6 +68,7 @@ interface ipv6 keepalive localport +log_failed_transfers maxconnects multi-app multi-debugcallback @@ -79,7 +79,6 @@ multi-legacy multi-post multi-single multi-uv -multithread netrc parseurl persistent @@ -123,7 +122,7 @@ smtp-tls smtp-vrfy sslbackend synctime -threaded-ssl +threaded unixsocket url2file urlapi diff --git a/docs/examples/10-at-a-time.c b/docs/examples/10-at-a-time.c index 38a0f24ac6..f04702f59b 100644 --- a/docs/examples/10-at-a-time.c +++ b/docs/examples/10-at-a-time.c @@ -25,128 +25,134 @@ * Download many files in parallel, in the same thread. * */ - #include #include + #include static const char *urls[] = { - "https://www.microsoft.com", - "https://opensource.org", - "https://www.google.com", - "https://www.yahoo.com", - "https://www.ibm.com", - "https://www.mysql.com", - "https://www.oracle.com", - "https://www.ripe.net", - "https://www.iana.org", - "https://www.amazon.com", - "https://www.netcraft.com", - "https://www.heise.de", - "https://www.chip.de", - "https://www.ca.com", - "https://www.cnet.com", - "https://www.mozilla.org", - "https://www.cnn.com", - "https://www.wikipedia.org", - "https://www.dell.com", - "https://www.hp.com", - "https://www.cert.org", - "https://www.mit.edu", - "https://www.nist.gov", - "https://www.ebay.com", - "https://www.playstation.com", - "https://www.uefa.com", - "https://www.ieee.org", - "https://www.apple.com", - "https://www.symantec.com", - "https://www.zdnet.com", - "https://www.fujitsu.com/global/", - "https://www.supermicro.com", - "https://www.hotmail.com", - "https://www.ietf.org", - "https://www.bbc.co.uk", - "https://news.google.com", - "https://www.foxnews.com", - "https://www.msn.com", - "https://www.wired.com", - "https://www.sky.com", - "https://www.usatoday.com", - "https://www.cbs.com", - "https://www.nbc.com/", - "https://slashdot.org", - "https://www.informationweek.com", - "https://apache.org", - "https://www.un.org", + "https://01.example/", + "https://02.example/", + "https://03.example/", + "https://04.example/", + "https://05.example/", + "https://06.example/", + "https://07.example/", + "https://08.example/", + "https://09.example/", + "https://10.example/", + "https://11.example/", + "https://12.example/", + "https://13.example/", + "https://14.example/", + "https://15.example/", + "https://16.example/", + "https://17.example/", + "https://18.example/", + "https://19.example/", + "https://20.example/", + "https://21.example/", + "https://22.example/", + "https://23.example/", + "https://24.example/", + "https://25.example/", + "https://26.example/", + "https://27.example/", + "https://28.example/", + "https://29.example/", + "https://30.example/", + "https://31.example/", + "https://32.example/", + "https://33.example/", + "https://34.example/", + "https://35.example/", + "https://36.example/", + "https://37.example/", + "https://38.example/", + "https://39.example/", + "https://40.example/", + "https://41.example/", + "https://42.example/", + "https://43.example/", + "https://44.example/", + "https://45.example/", + "https://46.example/", }; -#define MAX_PARALLEL 10 /* number of simultaneous transfers */ -#define NUM_URLS sizeof(urls)/sizeof(char *) +#define MAX_PARALLEL 10 /* number of simultaneous transfers */ +#define NUM_URLS (sizeof(urls) / sizeof(char *)) static size_t write_cb(char *data, size_t n, size_t l, void *userp) { /* take care of the data here, ignored in this example */ (void)data; (void)userp; - return n*l; + return n * l; } -static void add_transfer(CURLM *cm, unsigned int i, int *left) +static void add_transfer(CURLM *multi, unsigned int i, int *left) { - CURL *eh = curl_easy_init(); - curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, write_cb); - curl_easy_setopt(eh, CURLOPT_URL, urls[i]); - curl_easy_setopt(eh, CURLOPT_PRIVATE, urls[i]); - curl_multi_add_handle(cm, eh); + CURL *curl = curl_easy_init(); + if(curl) { + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_URL, urls[i]); + curl_easy_setopt(curl, CURLOPT_PRIVATE, urls[i]); + curl_multi_add_handle(multi, curl); + } (*left)++; } int main(void) { - CURLM *cm; - CURLMsg *msg; - unsigned int transfers = 0; - int msgs_left = -1; - int left = 0; + CURLM *multi; - curl_global_init(CURL_GLOBAL_ALL); - cm = curl_multi_init(); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - /* Limit the amount of simultaneous connections curl should allow: */ - curl_multi_setopt(cm, CURLMOPT_MAXCONNECTS, (long)MAX_PARALLEL); + multi = curl_multi_init(); + if(multi) { + CURLMsg *msg; + unsigned int transfers = 0; + int msgs_left = -1; + int left = 0; - for(transfers = 0; transfers < MAX_PARALLEL && transfers < NUM_URLS; - transfers++) - add_transfer(cm, transfers, &left); + /* Limit the amount of simultaneous connections curl should allow: */ + curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)MAX_PARALLEL); - do { - int still_alive = 1; - curl_multi_perform(cm, &still_alive); + for(transfers = 0; transfers < MAX_PARALLEL && transfers < NUM_URLS; + transfers++) + add_transfer(multi, transfers, &left); - /* !checksrc! disable EQUALSNULL 1 */ - while((msg = curl_multi_info_read(cm, &msgs_left)) != NULL) { - if(msg->msg == CURLMSG_DONE) { - char *url; - CURL *e = msg->easy_handle; - curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &url); - fprintf(stderr, "R: %d - %s <%s>\n", - msg->data.result, curl_easy_strerror(msg->data.result), url); - curl_multi_remove_handle(cm, e); - curl_easy_cleanup(e); - left--; + do { + int still_alive = 1; + curl_multi_perform(multi, &still_alive); + + /* !checksrc! disable EQUALSNULL 1 */ + while((msg = curl_multi_info_read(multi, &msgs_left)) != NULL) { + if(msg->msg == CURLMSG_DONE) { + const char *url; + CURL *curl = msg->easy_handle; + curl_easy_getinfo(curl, CURLINFO_PRIVATE, &url); + fprintf(stderr, "R: %d - %s <%s>\n", + msg->data.result, curl_easy_strerror(msg->data.result), url); + curl_multi_remove_handle(multi, curl); + curl_easy_cleanup(curl); + left--; + } + else { + fprintf(stderr, "E: CURLMsg (%d)\n", msg->msg); + } + if(transfers < NUM_URLS) + add_transfer(multi, transfers++, &left); } - else { - fprintf(stderr, "E: CURLMsg (%d)\n", msg->msg); - } - if(transfers < NUM_URLS) - add_transfer(cm, transfers++, &left); - } - if(left) - curl_multi_wait(cm, NULL, 0, 1000, NULL); + if(left) + curl_multi_wait(multi, NULL, 0, 1000, NULL); - } while(left); + } while(left); - curl_multi_cleanup(cm); + curl_multi_cleanup(multi); + } curl_global_cleanup(); return EXIT_SUCCESS; diff --git a/docs/examples/CMakeLists.txt b/docs/examples/CMakeLists.txt index cb1d983890..dc3313d6a2 100644 --- a/docs/examples/CMakeLists.txt +++ b/docs/examples/CMakeLists.txt @@ -21,49 +21,72 @@ # SPDX-License-Identifier: curl # ########################################################################### - add_custom_target(curl-examples) -# Get check_PROGRAMS variable +# Get check_PROGRAMS, COMPLICATED_MAY_BUILD, COMPLICATED_EXAMPLES variables curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") +set(_with_deps "") +set(_all_src "") set(_all_canary "") set(_all "all") -foreach(_target IN LISTS check_PROGRAMS _all) # keep '_all' last +foreach(_target IN LISTS COMPLICATED_MAY_BUILD check_PROGRAMS _all) # keep 'COMPLICATED_MAY_BUILD' first, and '_all' last + # Strip .c suffix from COMPLICATED_MAY_BUILD items + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) + cmake_path(GET _target STEM _target) + else() + get_filename_component(_target "${_target}" NAME_WE) + endif() + set(_more_libs "") set(_target_name "curl-example-${_target}") if(_target STREQUAL "all") - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12) - set(_examples_c "${check_PROGRAMS}") - list(TRANSFORM _examples_c APPEND ".c") - add_library(${_target_name} OBJECT EXCLUDE_FROM_ALL ${_examples_c}) - if(MSVC AND NOT CMAKE_C_COMPILER_ID STREQUAL "Clang") - # CMake generates a static library for the OBJECT target. Silence these 'lib.exe' warnings: - # warning LNK4006: main already defined in ....obj; second definition ignored - # warning LNK4221: This object file does not define any previously undefined public symbols, - # so it will not be used by any link operation that consumes this library - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - set_target_properties(${_target_name} PROPERTIES STATIC_LIBRARY_OPTIONS "-ignore:4006;-ignore:4221") - else() - set_target_properties(${_target_name} PROPERTIES STATIC_LIBRARY_FLAGS "-ignore:4006 -ignore:4221") - endif() - endif() - else() - set(_examples_c "") - foreach(_src IN LISTS check_PROGRAMS) - list(APPEND _examples_c "${_src}.c") - endforeach() - add_library(${_target_name} STATIC EXCLUDE_FROM_ALL ${_examples_c}) + add_library(${_target_name} OBJECT EXCLUDE_FROM_ALL ${_all_src}) + if(CMAKE_C_COMPILER_ID STREQUAL "MSVC") # MSVC but exclude clang-cl + # CMake generates a static library for the OBJECT target. Silence these 'lib.exe' warnings: + # warning LNK4006: main already defined in ....obj; second definition ignored + # warning LNK4221: This object file does not define any previously undefined public symbols, + # [...] not be used by any link operation that consumes this library + set_target_properties(${_target_name} PROPERTIES STATIC_LIBRARY_OPTIONS "-ignore:4006;-ignore:4221") + endif() + if(_with_deps) + set(_more_libs ${CURL_LIBS}) # If any examples required dependencies, link them endif() add_custom_target(curl-examples-build) # Special target to compile all tests quickly and build a single test to probe linkage add_dependencies(curl-examples-build ${_target_name} ${_all_canary}) # Include a full build of a single test else() + # Check if the example requires a build option. Then check if that build option is enabled. + # If it is, link all dependencies to the example. + set(_requires_regex "/\\* Requires: ([A-Z0-9_]+) \\*/") + file(STRINGS "${_target}.c" _req REGEX "${_requires_regex}") + string(REGEX REPLACE "${_requires_regex}" "\\1" _req "${_req}") + if(_req) + if(${${_req}}) + string(APPEND _with_deps " ${_target}:${_req}") + set(_more_libs ${CURL_LIBS}) + else() + continue() # Option required, but not found + endif() + endif() set(_all_canary ${_target_name}) # Save the last test for the curl-examples-build target + list(APPEND _all_src "${_target}.c") add_executable(${_target_name} EXCLUDE_FROM_ALL "${_target}.c") add_dependencies(curl-examples ${_target_name}) endif() - target_link_libraries(${_target_name} ${LIB_SELECTED} ${CURL_NETWORK_AND_TIME_LIBS}) - target_compile_definitions(${_target_name} PRIVATE "CURL_NO_OLDIES" - "$<$:WIN32_LEAN_AND_MEAN>" "$<$:_CRT_SECURE_NO_DEPRECATE>") + target_link_libraries(${_target_name} ${LIB_SELECTED} ${CURL_NETWORK_AND_TIME_LIBS} ${_more_libs}) + target_compile_definitions(${_target_name} PRIVATE "CURL_NO_OLDIES" "$<$:WIN32_LEAN_AND_MEAN>") + if(CURL_ANALYZER_CFLAGS) + set_property(TARGET ${_target_name} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS}) + endif() set_target_properties(${_target_name} PROPERTIES OUTPUT_NAME "${_target}" PROJECT_LABEL "Example ${_target}" UNITY_BUILD OFF) endforeach() + +if(_with_deps) + message(STATUS "Enabled examples with dependencies:${_with_deps}") +endif() + +if(CURL_BUILD_EVERYTHING STREQUAL "QUICK") + set_target_properties(curl-examples-build PROPERTIES EXCLUDE_FROM_ALL FALSE) +elseif(CURL_BUILD_EVERYTHING AND NOT CURL_BUILD_EVERYTHING STREQUAL "NOEXAMPLES") + set_target_properties(curl-examples PROPERTIES EXCLUDE_FROM_ALL FALSE) +endif() diff --git a/docs/examples/Makefile.am b/docs/examples/Makefile.am index 27d4ce741b..0885b925c4 100644 --- a/docs/examples/Makefile.am +++ b/docs/examples/Makefile.am @@ -24,7 +24,8 @@ AUTOMAKE_OPTIONS = foreign nostdinc -EXTRA_DIST = CMakeLists.txt README.md Makefile.example $(COMPLICATED_EXAMPLES) +EXTRA_DIST = CMakeLists.txt .checksrc README.md Makefile.example \ + $(COMPLICATED_MAY_BUILD) $(COMPLICATED_EXAMPLES) # Specify our include paths here, and do it relative to $(top_srcdir) and # $(top_builddir), to ensure that these paths which belong to the library @@ -53,7 +54,7 @@ LDADD = $(top_builddir)/lib/libcurl.la # This might hold -Werror CFLAGS += @CURL_CFLAG_EXTRAS@ -# Get check_PROGRAMS variable +# Get check_PROGRAMS, COMPLICATED_MAY_BUILD, COMPLICATED_EXAMPLES variables include Makefile.inc all: $(check_PROGRAMS) diff --git a/docs/examples/Makefile.example b/docs/examples/Makefile.example index cfb59c94ef..4031aa2e43 100644 --- a/docs/examples/Makefile.example +++ b/docs/examples/Makefile.example @@ -22,34 +22,29 @@ # ########################################################################### -# What to call the final executable -TARGET = example +SRC ?= https.c -# Which object files that the executable consists of -OBJS= ftpget.o +# What to call the final executable +TARGET ?= example # What compiler to use -CC = gcc +CC ?= gcc -# Compiler flags, -g for debug, -c to make an object file -CFLAGS = -c -g +# Compiler flags, -g for debug +CFLAGS ?= -g -# This should point to a directory that holds libcurl, if it is not -# in the system's standard lib dir -# We also set a -L to include the directory where we have the OpenSSL -# libraries -LDFLAGS = -L/home/dast/lib -L/usr/local/ssl/lib +# This should point to a directory that holds libcurl, if it is not in the +# system's standard lib dir +# We also set a -L to include the directory where we have the OpenSSL libraries +LDFLAGS ?= -L/usr/lib -L/usr/local/lib -# We need -lcurl for the curl stuff # We need -lsocket and -lnsl when on Solaris -# We need -lssl and -lcrypto when using libcurl with SSL support +# We need -lssl and -lcrypto when using libcurl with TLS support # We need -lpthread for the pthread example -LIBS = -lcurl -lsocket -lnsl -lssl -lcrypto +LIBS ?= -lssl -lcrypto +# We need -lcurl for the curl stuff +LIBS := -lcurl $(LIBS) # Link the target with all objects and libraries -$(TARGET) : $(OBJS) - $(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) $(LIBS) - -# Compile the source files into object files -ftpget.o : ftpget.c - $(CC) $(CFLAGS) $< +$(TARGET) : $(SRC) + $(CC) $< $(CFLAGS) $(LDFLAGS) $(LIBS) -o $(TARGET) diff --git a/docs/examples/Makefile.inc b/docs/examples/Makefile.inc index acec3b9385..8c85fb8382 100644 --- a/docs/examples/Makefile.inc +++ b/docs/examples/Makefile.inc @@ -84,6 +84,7 @@ check_PROGRAMS = \ ipv6 \ keepalive \ localport \ + log_failed_transfers \ maxconnects \ multi-app \ multi-debugcallback \ @@ -132,6 +133,7 @@ check_PROGRAMS = \ smtp-tls \ smtp-vrfy \ sslbackend \ + synctime \ unixsocket \ url2file \ urlapi \ @@ -139,10 +141,18 @@ check_PROGRAMS = \ websocket-cb \ websocket-updown +# These examples require external dependencies that may be available during +# the build. +COMPLICATED_MAY_BUILD = \ + cacertinmem.c \ + multi-uv.c \ + sessioninfo.c \ + threaded.c \ + usercertinmem.c + # These examples require external dependencies that may not be commonly # available on POSIX systems, so do not bother attempting to compile them here. COMPLICATED_EXAMPLES = \ - cacertinmem.c \ crawler.c \ ephiperfifo.c \ evhiperfifo.c \ @@ -151,12 +161,6 @@ COMPLICATED_EXAMPLES = \ htmltidy.c \ htmltitle.cpp \ multi-event.c \ - multi-uv.c \ - multithread.c \ - sessioninfo.c \ smooth-gtk-thread.c \ - synctime.c \ - threaded-ssl.c \ - usercertinmem.c \ version-check.pl \ xmlstream.c diff --git a/docs/examples/README.md b/docs/examples/README.md index a6a31c9388..06d07be0f3 100644 --- a/docs/examples/README.md +++ b/docs/examples/README.md @@ -16,8 +16,7 @@ them for submission in future packages and on the website. ## Building The `Makefile.example` is an example Makefile that could be used to build -these examples. Just edit the file according to your system and requirements -first. +these examples. Edit the file according to your system and requirements first. Most examples should build fine using a command line like this: diff --git a/docs/examples/adddocsref.pl b/docs/examples/adddocsref.pl index aba9abe2a0..cbc48c0407 100755 --- a/docs/examples/adddocsref.pl +++ b/docs/examples/adddocsref.pl @@ -28,6 +28,8 @@ use strict; use warnings; +use File::Copy; + my $docroot="https://curl.se/libcurl/c"; for my $f (@ARGV) { @@ -36,7 +38,7 @@ for my $f (@ARGV) { while() { my $l = $_; if($l =~ /\/* $docroot/) { - # just ignore preciously added refs + # ignore previously added refs } elsif($l =~ /^( *).*curl_easy_setopt\([^,]*, *([^ ,]*) *,/) { my ($prefix, $anchor) = ($1, $2); @@ -56,6 +58,6 @@ for my $f (@ARGV) { close(F); close(NEW); - system("mv $f $f.org"); - system("mv $f.new $f"); + move($f, "$f.org"); + move("$f.new", $f); } diff --git a/docs/examples/address-scope.c b/docs/examples/address-scope.c index 43e9cf8986..0cb9e0158d 100644 --- a/docs/examples/address-scope.c +++ b/docs/examples/address-scope.c @@ -26,6 +26,7 @@ * */ #include + #include #if !defined(_WIN32) && !defined(MSDOS) && !defined(__AMIGA__) @@ -37,7 +38,10 @@ int main(void) #if !defined(_WIN32) && !defined(MSDOS) && !defined(__AMIGA__) /* Windows/MS-DOS users need to find how to use if_nametoindex() */ CURL *curl; - CURLcode res; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -47,16 +51,19 @@ int main(void) my_scope_id = (long)if_nametoindex("eth0"); curl_easy_setopt(curl, CURLOPT_ADDRESS_SCOPE, my_scope_id); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } -#endif + curl_global_cleanup(); + return (int)result; +#else return 0; +#endif } diff --git a/docs/examples/altsvc.c b/docs/examples/altsvc.c index 8a1ae8ed3b..35fbc39a1a 100644 --- a/docs/examples/altsvc.c +++ b/docs/examples/altsvc.c @@ -26,12 +26,16 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -44,15 +48,16 @@ int main(void) curl_easy_setopt(curl, CURLOPT_ALTSVC_CTRL, CURLALTSVC_H1 | CURLALTSVC_H2 | CURLALTSVC_H3); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } - return 0; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/anyauthput.c b/docs/examples/anyauthput.c index c62250dce3..9b08bbe41b 100644 --- a/docs/examples/anyauthput.c +++ b/docs/examples/anyauthput.c @@ -26,6 +26,12 @@ * one the server supports/wants. * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen() */ +#endif +#endif + #include #include #include @@ -35,9 +41,9 @@ #ifdef _WIN32 #undef stat -#define stat _stat +#define stat _stati64 #undef fstat -#define fstat _fstat +#define fstat _fstati64 #define fileno _fileno #endif @@ -59,9 +65,9 @@ /* seek callback function */ static int my_seek(void *userp, curl_off_t offset, int origin) { - FILE *fp = (FILE *) userp; + FILE *fp = (FILE *)userp; - if(fseek(fp, (long) offset, origin) == -1) + if(fseek(fp, (long)offset, origin) == -1) /* could not seek */ return CURL_SEEKFUNC_CANTSEEK; @@ -69,7 +75,7 @@ static int my_seek(void *userp, curl_off_t offset, int origin) } /* read callback function, fread() look alike */ -static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) +static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { size_t nread; @@ -82,15 +88,15 @@ static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) return nread; } -int main(int argc, char **argv) +int main(int argc, const char **argv) { CURL *curl; - CURLcode res; + CURLcode result; FILE *fp; struct stat file_info; - char *file; - char *url; + const char *file; + const char *url; if(argc < 3) return 1; @@ -103,29 +109,32 @@ int main(int argc, char **argv) if(!fp) return 2; -#ifdef UNDER_CE - stat(file, &file_info); -#else - fstat(fileno(fp), &file_info); -#endif + if(fstat(fileno(fp), &file_info) != 0) { + fclose(fp); + return 1; /* cannot continue */ + } /* In Windows, this inits the Winsock stuff */ - curl_global_init(CURL_GLOBAL_ALL); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) { + fclose(fp); + return (int)result; + } /* get a curl handle */ curl = curl_easy_init(); if(curl) { /* we want to use our own read function */ - curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); /* which file to upload */ - curl_easy_setopt(curl, CURLOPT_READDATA, (void *) fp); + curl_easy_setopt(curl, CURLOPT_READDATA, (void *)fp); /* set the seek function */ curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, my_seek); /* pass the file descriptor to the seek callback as well */ - curl_easy_setopt(curl, CURLOPT_SEEKDATA, (void *) fp); + curl_easy_setopt(curl, CURLOPT_SEEKDATA, (void *)fp); /* enable "uploading" (which means PUT when doing HTTP) */ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); @@ -141,18 +150,18 @@ int main(int argc, char **argv) /* tell libcurl we can use "any" auth, which lets the lib pick one, but it also costs one extra round-trip and possibly sending of all the PUT - data twice!!! */ + data twice */ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - /* set user name and password for the authentication */ + /* set username and password for the authentication */ curl_easy_setopt(curl, CURLOPT_USERPWD, "user:password"); /* Now run off and do what you have been told! */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); @@ -160,5 +169,5 @@ int main(int argc, char **argv) fclose(fp); /* close the local file */ curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/block_ip.c b/docs/examples/block_ip.c index 181cd8270d..675490701b 100644 --- a/docs/examples/block_ip.c +++ b/docs/examples/block_ip.c @@ -29,25 +29,20 @@ * filter IP addresses. */ -#if defined(__AMIGA__) || defined(UNDER_CE) +#ifdef __AMIGA__ #include -int main(void) { printf("Platform not supported.\n"); return 1; } +int main(void) +{ + printf("Platform not supported.\n"); + return 1; +} #else -#ifdef _WIN32 -#ifndef _CRT_SECURE_NO_WARNINGS -#define _CRT_SECURE_NO_WARNINGS -#endif -#ifndef _CRT_NONSTDC_NO_DEPRECATE -#define _CRT_NONSTDC_NO_DEPRECATE -#endif -#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600 -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 /* Requires Windows Vista */ -#endif +#ifdef _WIN32 /* Requires Windows Vista+ */ #include #include #include +#define strdup _strdup #else #include #include @@ -111,7 +106,7 @@ static struct ip *ip_list_append(struct ip *list, const char *data) struct ip *ip, *last; char *cidr; - ip = (struct ip *)calloc(1, sizeof(*ip)); + ip = calloc(1, sizeof(*ip)); if(!ip) return NULL; @@ -132,7 +127,7 @@ static struct ip *ip_list_append(struct ip *list, const char *data) return NULL; } - /* determine the number of bits that this IP will match against */ + /* determine the number of bits that this IP matches against */ cidr = strchr(ip->str, '/'); if(cidr) { ip->maskbits = atoi(cidr + 1); @@ -196,8 +191,8 @@ static int ip_match(struct ip *ip, void *netaddr) int bytes, tailbits; const unsigned char *x, *y; - x = (unsigned char *)&ip->netaddr; - y = (unsigned char *)netaddr; + x = (const unsigned char *)&ip->netaddr; + y = (const unsigned char *)netaddr; for(bytes = ip->maskbits / 8; bytes; --bytes) { if(*x++ != *y++) @@ -219,7 +214,7 @@ static int is_ipv4_mapped_ipv6_address(int family, void *netaddr) { if(family == AF_INET6) { int i; - unsigned char *x = (unsigned char *)netaddr; + const unsigned char *x = (const unsigned char *)netaddr; for(i = 0; i < 12; ++i) { if(x[i]) break; @@ -234,8 +229,7 @@ static int is_ipv4_mapped_ipv6_address(int family, void *netaddr) } #endif /* AF_INET6 */ -static curl_socket_t opensocket(void *clientp, - curlsocktype purpose, +static curl_socket_t opensocket(void *clientp, curlsocktype purpose, struct curl_sockaddr *address) { /* filter the address */ @@ -269,7 +263,7 @@ static curl_socket_t opensocket(void *clientp, if(ip && filter->type == CONNECTION_FILTER_BLACKLIST) { if(filter->verbose) { - char buf[128] = {0}; + char buf[128] = { 0 }; inet_ntop(address->family, cinaddr, buf, sizeof(buf)); fprintf(stderr, "* Rejecting IP %s due to blacklist entry %s.\n", buf, ip->str); @@ -278,7 +272,7 @@ static curl_socket_t opensocket(void *clientp, } else if(!ip && filter->type == CONNECTION_FILTER_WHITELIST) { if(filter->verbose) { - char buf[128] = {0}; + char buf[128] = { 0 }; inet_ntop(address->family, cinaddr, buf, sizeof(buf)); fprintf(stderr, "* Rejecting IP %s due to missing whitelist entry.\n", buf); @@ -294,16 +288,17 @@ static curl_socket_t opensocket(void *clientp, int main(void) { CURL *curl; - CURLcode res; + CURLcode result; struct connection_filter *filter; - filter = (struct connection_filter *)calloc(1, sizeof(*filter)); + filter = calloc(1, sizeof(*filter)); if(!filter) return 1; - if(curl_global_init(CURL_GLOBAL_DEFAULT)) { + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) { free(filter); - return 1; + return (int)result; } curl = curl_easy_init(); @@ -336,12 +331,12 @@ int main(void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) { + if(result != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); } /* Clean up */ diff --git a/docs/examples/cacertinmem.c b/docs/examples/cacertinmem.c index ec12df58bf..8ede167c28 100644 --- a/docs/examples/cacertinmem.c +++ b/docs/examples/cacertinmem.c @@ -26,16 +26,21 @@ * */ -#include +/* Requires: USE_OPENSSL */ + #include -#include + #include -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic ignored "-Woverlength-strings" +#include + +#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +typedef size_t ossl_valsize_t; +#else +typedef int ossl_valsize_t; #endif -static size_t writefunction(void *ptr, size_t size, size_t nmemb, void *stream) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *stream) { fwrite(ptr, size, nmemb, (FILE *)stream); return nmemb * size; @@ -43,8 +48,6 @@ static size_t writefunction(void *ptr, size_t size, size_t nmemb, void *stream) static CURLcode sslctx_function(CURL *curl, void *sslctx, void *pointer) { - CURLcode rv = CURLE_ABORTED_BY_CALLBACK; - /** This example uses two (fake) certificates **/ /* replace the XXX with the actual CA certificates */ static const char mypem[] = @@ -67,23 +70,31 @@ static CURLcode sslctx_function(CURL *curl, void *sslctx, void *pointer) "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" "-----END CERTIFICATE-----\n"; - BIO *cbio = BIO_new_mem_buf(mypem, sizeof(mypem)); - X509_STORE *cts = SSL_CTX_get_cert_store((SSL_CTX *)sslctx); - int i; - STACK_OF(X509_INFO) *inf; + CURLcode result = CURLE_ABORTED_BY_CALLBACK; + BIO *cbio = NULL; + X509_STORE *cts; + ossl_valsize_t i; + STACK_OF(X509_INFO) * inf; (void)curl; (void)pointer; - if(!cts || !cbio) { - return rv; + cts = SSL_CTX_get_cert_store((SSL_CTX *)sslctx); + if(!cts) { + printf("SSL_CTX_get_cert_store() failed\n"); + goto out; + } + + cbio = BIO_new_mem_buf(mypem, sizeof(mypem) - 1); + if(!cbio) { + printf("BIO_new_mem_buf() failed\n"); + goto out; } inf = PEM_X509_INFO_read_bio(cbio, NULL, NULL, NULL); - if(!inf) { - BIO_free(cbio); - return rv; + printf("PEM_X509_INFO_read_bio() failed\n"); + goto out; } for(i = 0; i < sk_X509_INFO_num(inf); i++) { @@ -96,70 +107,86 @@ static CURLcode sslctx_function(CURL *curl, void *sslctx, void *pointer) } } +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +#endif sk_X509_INFO_pop_free(inf, X509_INFO_free); - BIO_free(cbio); +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic pop +#endif - rv = CURLE_OK; - return rv; + result = CURLE_OK; + +out: + + if(cbio) + BIO_free(cbio); + + return result; } int main(void) { - CURL *ch; - CURLcode rv; + CURL *curl; - curl_global_init(CURL_GLOBAL_ALL); - ch = curl_easy_init(); - curl_easy_setopt(ch, CURLOPT_VERBOSE, 0L); - curl_easy_setopt(ch, CURLOPT_HEADER, 0L); - curl_easy_setopt(ch, CURLOPT_NOPROGRESS, 1L); - curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1L); - curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, writefunction); - curl_easy_setopt(ch, CURLOPT_WRITEDATA, stdout); - curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, writefunction); - curl_easy_setopt(ch, CURLOPT_HEADERDATA, stderr); - curl_easy_setopt(ch, CURLOPT_SSLCERTTYPE, "PEM"); - curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, 1L); - curl_easy_setopt(ch, CURLOPT_URL, "https://www.example.com/"); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - /* Turn off the default CA locations, otherwise libcurl loads CA - * certificates from the locations that were detected/specified at - * build-time - */ - curl_easy_setopt(ch, CURLOPT_CAINFO, NULL); - curl_easy_setopt(ch, CURLOPT_CAPATH, NULL); + curl = curl_easy_init(); + if(curl) { + curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); + curl_easy_setopt(curl, CURLOPT_HEADER, 0L); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, stderr); + curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); - /* first try: retrieve page without ca certificates -> should fail - * unless libcurl was built --with-ca-fallback enabled at build-time - */ - rv = curl_easy_perform(ch); - if(rv == CURLE_OK) - printf("*** transfer succeeded ***\n"); - else - printf("*** transfer failed ***\n"); + /* Turn off the default CA locations, otherwise libcurl loads CA + * certificates from the locations that were detected/specified at + * build-time + */ + curl_easy_setopt(curl, CURLOPT_CAINFO, NULL); + curl_easy_setopt(curl, CURLOPT_CAPATH, NULL); - /* use a fresh connection (optional) this option seriously impacts - * performance of multiple transfers but it is necessary order to - * demonstrate this example. recall that the ssl ctx callback is only called - * _before_ an SSL connection is established, therefore it does not affect - * existing verified SSL connections already in the connection cache - * associated with this handle. normally you would set the ssl ctx function - * before making any transfers, and not use this option. - */ - curl_easy_setopt(ch, CURLOPT_FRESH_CONNECT, 1L); + /* first try: retrieve page without ca certificates -> should fail + * unless libcurl was built --with-ca-fallback enabled at build-time + */ + result = curl_easy_perform(curl); + if(result == CURLE_OK) + printf("*** transfer succeeded ***\n"); + else + printf("*** transfer failed ***\n"); - /* second try: retrieve page using cacerts' certificate -> succeeds to load - * the certificate by installing a function doing the necessary - * "modifications" to the SSL CONTEXT just before link init - */ - curl_easy_setopt(ch, CURLOPT_SSL_CTX_FUNCTION, sslctx_function); - rv = curl_easy_perform(ch); - if(rv == CURLE_OK) - printf("*** transfer succeeded ***\n"); - else - printf("*** transfer failed ***\n"); + /* use a fresh connection (optional) this option seriously impacts + * performance of multiple transfers but it is necessary order to + * demonstrate this example. recall that the ssl ctx callback is only + * called _before_ an SSL connection is established, therefore it does not + * affect existing verified SSL connections already in the connection + * cache associated with this handle. normally you would set the ssl ctx + * function before making any transfers, and not use this option. + */ + curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1L); - curl_easy_cleanup(ch); + /* second try: retrieve page using cacerts' certificate -> succeeds to + * load the certificate by installing a function doing the necessary + * "modifications" to the SSL CONTEXT before link init + */ + curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctx_function); + result = curl_easy_perform(curl); + if(result == CURLE_OK) + printf("*** transfer succeeded ***\n"); + else + printf("*** transfer failed ***\n"); + + curl_easy_cleanup(curl); + } curl_global_cleanup(); - return (int)rv; + return (int)result; } diff --git a/docs/examples/certinfo.c b/docs/examples/certinfo.c index 795be6c3d1..4f07dbaa8f 100644 --- a/docs/examples/certinfo.c +++ b/docs/examples/certinfo.c @@ -29,7 +29,7 @@ #include -static size_t wrfu(void *ptr, size_t size, size_t nmemb, void *stream) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *stream) { (void)stream; (void)ptr; @@ -39,15 +39,17 @@ static size_t wrfu(void *ptr, size_t size, size_t nmemb, void *stream) int main(void) { CURL *curl; - CURLcode res; + CURLcode result; - curl_global_init(CURL_GLOBAL_DEFAULT); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, wrfu); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); @@ -55,14 +57,14 @@ int main(void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { struct curl_certinfo *certinfo; - res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &certinfo); + result = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &certinfo); - if(!res && certinfo) { + if(!result && certinfo) { int i; printf("%d certs!\n", certinfo->num_of_certs); @@ -72,10 +74,8 @@ int main(void) for(slist = certinfo->certinfo[i]; slist; slist = slist->next) printf("%s\n", slist->data); - } } - } curl_easy_cleanup(curl); @@ -83,5 +83,5 @@ int main(void) curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/chkspeed.c b/docs/examples/chkspeed.c index 264c514ba4..4274eb0c90 100644 --- a/docs/examples/chkspeed.c +++ b/docs/examples/chkspeed.c @@ -34,6 +34,11 @@ * dd if=/dev/urandom of=file_1M.bin bs=1M count=1 * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for ctime() */ +#endif +#endif #include #include @@ -52,22 +57,22 @@ #define CHKSPEED_VERSION "1.0" -static size_t WriteCallback(void *ptr, size_t size, size_t nmemb, void *data) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *data) { /* we are not interested in the downloaded bytes itself, so we only return the size we would have saved ... */ (void)ptr; (void)data; - return (size_t)(size * nmemb); + return size * nmemb; } -int main(int argc, char *argv[]) +int main(int argc, const char *argv[]) { - CURL *curl_handle; - CURLcode res; + CURL *curl; + CURLcode result; int prtall = 0, prtsep = 0, prttime = 0; const char *url = URL_1M; - char *appname = argv[0]; + const char *appname = argv[0]; if(argc > 1) { /* parse input parameters */ @@ -100,7 +105,7 @@ int main(int argc, char *argv[]) case 'm': case 'M': if(argv[0][2] == '=') { - long m = strtol((*argv) + 3, NULL, 10); + int m = atoi((*argv) + 3); switch(m) { case 1: url = URL_1M; @@ -156,66 +161,77 @@ int main(int argc, char *argv[]) } /* init libcurl */ - curl_global_init(CURL_GLOBAL_ALL); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; /* init the curl session */ - curl_handle = curl_easy_init(); + curl = curl_easy_init(); + if(curl) { - /* specify URL to get */ - curl_easy_setopt(curl_handle, CURLOPT_URL, url); + /* specify URL to get */ + curl_easy_setopt(curl, CURLOPT_URL, url); - /* send all data to this function */ - curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteCallback); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); - /* some servers do not like requests that are made without a user-agent - field, so we provide one */ - curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, - "libcurl-speedchecker/" CHKSPEED_VERSION); + /* some servers do not like requests that are made without a user-agent + field, so we provide one */ + curl_easy_setopt(curl, CURLOPT_USERAGENT, + "libcurl-speedchecker/" CHKSPEED_VERSION); - /* get it! */ - res = curl_easy_perform(curl_handle); + /* get it! */ + result = curl_easy_perform(curl); - if(CURLE_OK == res) { - curl_off_t val; + if(result == CURLE_OK) { + curl_off_t val; - /* check for bytes downloaded */ - res = curl_easy_getinfo(curl_handle, CURLINFO_SIZE_DOWNLOAD_T, &val); - if((CURLE_OK == res) && (val > 0)) - printf("Data downloaded: %lu bytes.\n", (unsigned long)val); + /* check for bytes downloaded */ + result = curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD_T, &val); + if((result == CURLE_OK) && (val > 0)) + printf("Data downloaded: %" CURL_FORMAT_CURL_OFF_T " bytes.\n", val); - /* check for total download time */ - res = curl_easy_getinfo(curl_handle, CURLINFO_TOTAL_TIME_T, &val); - if((CURLE_OK == res) && (val > 0)) - printf("Total download time: %lu.%06lu sec.\n", - (unsigned long)(val / 1000000), (unsigned long)(val % 1000000)); + /* check for total download time */ + result = curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME_T, &val); + if((result == CURLE_OK) && (val > 0)) + printf("Total download time: %" CURL_FORMAT_CURL_OFF_T + ".%06" CURL_FORMAT_CURL_OFF_T " sec.\n", + val / 1000000, + val % 1000000); - /* check for average download speed */ - res = curl_easy_getinfo(curl_handle, CURLINFO_SPEED_DOWNLOAD_T, &val); - if((CURLE_OK == res) && (val > 0)) - printf("Average download speed: %lu kbyte/sec.\n", - (unsigned long)(val / 1024)); + /* check for average download speed */ + result = curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD_T, &val); + if((result == CURLE_OK) && (val > 0)) + printf("Average download speed: " + "%" CURL_FORMAT_CURL_OFF_T " kbyte/sec.\n", + val / 1024); - if(prtall) { - /* check for name resolution time */ - res = curl_easy_getinfo(curl_handle, CURLINFO_NAMELOOKUP_TIME_T, &val); - if((CURLE_OK == res) && (val > 0)) - printf("Name lookup time: %lu.%06lu sec.\n", - (unsigned long)(val / 1000000), (unsigned long)(val % 1000000)); + if(prtall) { + /* check for name resolution time */ + result = curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME_T, &val); + if((result == CURLE_OK) && (val > 0)) + printf("Name lookup time: %" CURL_FORMAT_CURL_OFF_T + ".%06" CURL_FORMAT_CURL_OFF_T " sec.\n", + val / 1000000, + val % 1000000); - /* check for connect time */ - res = curl_easy_getinfo(curl_handle, CURLINFO_CONNECT_TIME_T, &val); - if((CURLE_OK == res) && (val > 0)) - printf("Connect time: %lu.%06lu sec.\n", - (unsigned long)(val / 1000000), (unsigned long)(val % 1000000)); + /* check for connect time */ + result = curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME_T, &val); + if((result == CURLE_OK) && (val > 0)) + printf("Connect time: %" CURL_FORMAT_CURL_OFF_T + ".%06" CURL_FORMAT_CURL_OFF_T " sec.\n", + val / 1000000, + val % 1000000); + } + } + else { + fprintf(stderr, "Error while fetching '%s' : %s\n", + url, curl_easy_strerror(result)); } - } - else { - fprintf(stderr, "Error while fetching '%s' : %s\n", - url, curl_easy_strerror(res)); - } - /* cleanup curl stuff */ - curl_easy_cleanup(curl_handle); + /* cleanup curl stuff */ + curl_easy_cleanup(curl); + } /* we are done with libcurl, so clean it up */ curl_global_cleanup(); diff --git a/docs/examples/connect-to.c b/docs/examples/connect-to.c index ad1e304649..706e9882a0 100644 --- a/docs/examples/connect-to.c +++ b/docs/examples/connect-to.c @@ -26,23 +26,27 @@ * */ #include + #include int main(void) { + struct curl_slist *host; CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; /* Each single string should be written using the format HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT where HOST is the host of the - request, PORT is the port of the request, CONNECT-TO-HOST is the host name + request, PORT is the port of the request, CONNECT-TO-HOST is the hostname to connect to, and CONNECT-TO-PORT is the port to connect to. */ /* instead of curl.se:443, it resolves and uses example.com:443 but in other aspects work as if it still is curl.se */ - struct curl_slist *host = curl_slist_append(NULL, - "curl.se:443:example.com:443"); + host = curl_slist_append(NULL, "curl.se:443:example.com:443"); curl = curl_easy_init(); if(curl) { @@ -50,15 +54,15 @@ int main(void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_URL, "https://curl.se/"); - /* since this connects to the wrong host, checking the host name in the + /* since this connects to the wrong host, checking the hostname in the server certificate fails, so unless we disable the check libcurl returns CURLE_PEER_FAILED_VERIFICATION */ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); - /* Letting the wrong host name in the certificate be okay, the transfer + /* Letting the wrong hostname in the certificate be okay, the transfer goes through but (most likely) causes a 404 or similar because it sends an unknown name in the Host: header field */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* always cleanup */ curl_easy_cleanup(curl); @@ -66,5 +70,7 @@ int main(void) curl_slist_free_all(host); - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/cookie_interface.c b/docs/examples/cookie_interface.c index da40953d91..aa3ea1d013 100644 --- a/docs/examples/cookie_interface.c +++ b/docs/examples/cookie_interface.c @@ -25,6 +25,11 @@ * Import and export cookies with COOKIELIST. * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for _snprintf() */ +#endif +#endif #include #include @@ -32,20 +37,23 @@ #include #include -#include + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define snprintf _snprintf +#endif static int print_cookies(CURL *curl) { - CURLcode res; + CURLcode result; struct curl_slist *cookies; struct curl_slist *nc; int i; printf("Cookies, curl knows:\n"); - res = curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &cookies); - if(res != CURLE_OK) { - fprintf(stderr, "Curl curl_easy_getinfo failed: %s\n", - curl_easy_strerror(res)); + result = curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &cookies); + if(result != CURLE_OK) { + fprintf(stderr, "curl curl_easy_getinfo failed: %s\n", + curl_easy_strerror(result)); return 1; } nc = cookies; @@ -63,13 +71,15 @@ static int print_cookies(CURL *curl) return 0; } -int -main(void) +int main(void) { CURL *curl; - CURLcode res; + CURLcode result; + + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); if(curl) { char nline[512]; @@ -77,9 +87,9 @@ main(void) curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); /* start cookie engine */ - res = curl_easy_perform(curl); - if(res != CURLE_OK) { - fprintf(stderr, "Curl perform failed: %s\n", curl_easy_strerror(res)); + result = curl_easy_perform(curl); + if(result != CURLE_OK) { + fprintf(stderr, "curl perform failed: %s\n", curl_easy_strerror(result)); return 1; } @@ -93,14 +103,14 @@ main(void) printf("-----------------------------------------------\n" "Setting a cookie \"PREF\" via cookie interface:\n"); /* Netscape format cookie */ - curl_msnprintf(nline, sizeof(nline), "%s\t%s\t%s\t%s\t%.0f\t%s\t%s", - ".example.com", "TRUE", "/", "FALSE", - difftime(time(NULL) + 31337, (time_t)0), - "PREF", "hello example, i like you!"); - res = curl_easy_setopt(curl, CURLOPT_COOKIELIST, nline); - if(res != CURLE_OK) { - fprintf(stderr, "Curl curl_easy_setopt failed: %s\n", - curl_easy_strerror(res)); + snprintf(nline, sizeof(nline), "%s\t%s\t%s\t%s\t%.0f\t%s\t%s", + ".example.com", "TRUE", "/", "FALSE", + difftime(time(NULL) + 31337, (time_t)0), + "PREF", "hello example, I like you!"); + result = curl_easy_setopt(curl, CURLOPT_COOKIELIST, nline); + if(result != CURLE_OK) { + fprintf(stderr, "curl curl_easy_setopt failed: %s\n", + curl_easy_strerror(result)); return 1; } @@ -109,28 +119,28 @@ main(void) modified, likely not what you intended. For more information refer to the CURLOPT_COOKIELIST documentation. */ - curl_msnprintf(nline, sizeof(nline), + snprintf(nline, sizeof(nline), "Set-Cookie: OLD_PREF=3d141414bf4209321; " "expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.example.com"); - res = curl_easy_setopt(curl, CURLOPT_COOKIELIST, nline); - if(res != CURLE_OK) { - fprintf(stderr, "Curl curl_easy_setopt failed: %s\n", - curl_easy_strerror(res)); + result = curl_easy_setopt(curl, CURLOPT_COOKIELIST, nline); + if(result != CURLE_OK) { + fprintf(stderr, "curl curl_easy_setopt failed: %s\n", + curl_easy_strerror(result)); return 1; } print_cookies(curl); - res = curl_easy_perform(curl); - if(res != CURLE_OK) { - fprintf(stderr, "Curl perform failed: %s\n", curl_easy_strerror(res)); + result = curl_easy_perform(curl); + if(result != CURLE_OK) { + fprintf(stderr, "curl perform failed: %s\n", curl_easy_strerror(result)); return 1; } curl_easy_cleanup(curl); } else { - fprintf(stderr, "Curl init failed!\n"); + fprintf(stderr, "curl init failed!\n"); return 1; } diff --git a/docs/examples/crawler.c b/docs/examples/crawler.c index 6b737652d4..04d8163204 100644 --- a/docs/examples/crawler.c +++ b/docs/examples/crawler.c @@ -20,32 +20,34 @@ * * SPDX-License-Identifier: curl * - * To compile: - * gcc crawler.c $(pkg-config --cflags --libs libxml-2.0 libcurl) - * - */ + ***************************************************************************/ /* * Web crawler based on curl and libxml2 to stress-test curl with * hundreds of concurrent connections to various servers. * */ - +/* + * To compile: + * gcc crawler.c $(pkg-config --cflags --libs libxml-2.0 libcurl) + */ #include #include #include -#include + #include #include #include #include +#include + /* Parameters */ static int max_con = 200; static int max_total = 20000; static int max_requests = 500; static size_t max_link_per_page = 5; static int follow_relative_links = 0; -static const char *start_page = "https://www.reuters.com"; +static const char *start_page = "https://www.reuters.com/"; static int pending_interrupt = 0; static void sighandler(int dummy) @@ -60,10 +62,10 @@ struct memory { size_t size; }; -static size_t grow_buffer(void *contents, size_t sz, size_t nmemb, void *ctx) +static size_t write_cb(char *contents, size_t sz, size_t nmemb, void *ctx) { size_t realsize = sz * nmemb; - struct memory *mem = (struct memory*) ctx; + struct memory *mem = (struct memory *)ctx; char *ptr = realloc(mem->buf, mem->size + realsize); if(!ptr) { /* out of memory */ @@ -78,70 +80,69 @@ static size_t grow_buffer(void *contents, size_t sz, size_t nmemb, void *ctx) static CURL *make_handle(const char *url) { - CURL *handle = curl_easy_init(); + CURL *curl = curl_easy_init(); struct memory *mem; /* Important: use HTTP2 over HTTPS */ - curl_easy_setopt(handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); - curl_easy_setopt(handle, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); + curl_easy_setopt(curl, CURLOPT_URL, url); /* buffer body */ mem = malloc(sizeof(*mem)); mem->size = 0; mem->buf = malloc(1); - curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, grow_buffer); - curl_easy_setopt(handle, CURLOPT_WRITEDATA, mem); - curl_easy_setopt(handle, CURLOPT_PRIVATE, mem); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, mem); + curl_easy_setopt(curl, CURLOPT_PRIVATE, mem); /* For completeness */ - curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, ""); - curl_easy_setopt(handle, CURLOPT_TIMEOUT, 5L); - curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5L); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); /* only allow redirects to HTTP and HTTPS URLs */ - curl_easy_setopt(handle, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"); - curl_easy_setopt(handle, CURLOPT_AUTOREFERER, 1L); - curl_easy_setopt(handle, CURLOPT_MAXREDIRS, 10L); + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https"); + curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1L); + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 10L); /* each transfer needs to be done within 20 seconds! */ - curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, 20000L); + curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 20000L); /* connect fast or fail */ - curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT_MS, 2000L); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 2000L); /* skip files larger than a gigabyte */ - curl_easy_setopt(handle, CURLOPT_MAXFILESIZE_LARGE, - (curl_off_t)1024*1024*1024); - curl_easy_setopt(handle, CURLOPT_COOKIEFILE, ""); - curl_easy_setopt(handle, CURLOPT_FILETIME, 1L); - curl_easy_setopt(handle, CURLOPT_USERAGENT, "mini crawler"); - curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - curl_easy_setopt(handle, CURLOPT_UNRESTRICTED_AUTH, 1L); - curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); - curl_easy_setopt(handle, CURLOPT_EXPECT_100_TIMEOUT_MS, 0L); - return handle; + curl_easy_setopt(curl, CURLOPT_MAXFILESIZE_LARGE, + (curl_off_t)1024 * 1024 * 1024); + curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); + curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "mini crawler"); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + curl_easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1L); + curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY); + curl_easy_setopt(curl, CURLOPT_EXPECT_100_TIMEOUT_MS, 0L); + return curl; } /* HREF finder implemented in libxml2 but could be any HTML parser */ -static size_t follow_links(CURLM *multi_handle, struct memory *mem, - const char *url) +static size_t follow_links(CURLM *multi, struct memory *mem, const char *url) { - int opts = HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | \ - HTML_PARSE_NOWARNING | HTML_PARSE_NONET; + int opts = HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING | + HTML_PARSE_NONET; htmlDocPtr doc = htmlReadMemory(mem->buf, (int)mem->size, url, NULL, opts); size_t count; int i; xmlChar *xpath; xmlNodeSetPtr nodeset; xmlXPathContextPtr context; - xmlXPathObjectPtr result; + xmlXPathObjectPtr object; if(!doc) return 0; - xpath = (xmlChar*) "//a/@href"; + xpath = (xmlChar *)"//a/@href"; context = xmlXPathNewContext(doc); - result = xmlXPathEvalExpression(xpath, context); + object = xmlXPathEvalExpression(xpath, context); xmlXPathFreeContext(context); - if(!result) + if(!object) return 0; - nodeset = result->nodesetval; + nodeset = object->nodesetval; if(xmlXPathNodeSetIsEmpty(nodeset)) { - xmlXPathFreeObject(result); + xmlXPathFreeObject(object); return 0; } count = 0; @@ -153,101 +154,108 @@ static size_t follow_links(CURLM *multi_handle, struct memory *mem, char *link; if(follow_relative_links) { xmlChar *orig = href; - href = xmlBuildURI(href, (xmlChar *) url); + href = xmlBuildURI(href, (xmlChar *)url); xmlFree(orig); } - link = (char *) href; + link = (char *)href; if(!link || strlen(link) < 20) continue; if(!strncmp(link, "http://", 7) || !strncmp(link, "https://", 8)) { - curl_multi_add_handle(multi_handle, make_handle(link)); + curl_multi_add_handle(multi, make_handle(link)); if(count++ == max_link_per_page) break; } xmlFree(link); } - xmlXPathFreeObject(result); + xmlXPathFreeObject(object); return count; } -static int is_html(char *ctype) +static int is_html(const char *ctype) { return ctype != NULL && strlen(ctype) > 10 && strstr(ctype, "text/html"); } int main(void) { - CURLM *multi_handle; + CURLM *multi; int msgs_left; int pending; int complete; int still_running; + CURLcode result; + + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; signal(SIGINT, sighandler); LIBXML_TEST_VERSION - curl_global_init(CURL_GLOBAL_DEFAULT); - multi_handle = curl_multi_init(); - curl_multi_setopt(multi_handle, CURLMOPT_MAX_TOTAL_CONNECTIONS, max_con); - curl_multi_setopt(multi_handle, CURLMOPT_MAX_HOST_CONNECTIONS, 6L); + multi = curl_multi_init(); + if(multi) { + curl_multi_setopt(multi, CURLMOPT_MAX_TOTAL_CONNECTIONS, max_con); + curl_multi_setopt(multi, CURLMOPT_MAX_HOST_CONNECTIONS, 6L); - /* enables http/2 if available */ + /* enables http/2 if available */ #ifdef CURLPIPE_MULTIPLEX - curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); + curl_multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); #endif - /* sets html start page */ - curl_multi_add_handle(multi_handle, make_handle(start_page)); + /* sets html start page */ + curl_multi_add_handle(multi, make_handle(start_page)); - pending = 0; - complete = 0; - still_running = 1; - while(still_running && !pending_interrupt) { - int numfds; - CURLMsg *m; + pending = 0; + complete = 0; + still_running = 1; + while(still_running && !pending_interrupt) { + int numfds; + CURLMsg *m; - curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds); - curl_multi_perform(multi_handle, &still_running); + curl_multi_wait(multi, NULL, 0, 1000, &numfds); + curl_multi_perform(multi, &still_running); - /* See how the transfers went */ - m = NULL; - while((m = curl_multi_info_read(multi_handle, &msgs_left))) { - if(m->msg == CURLMSG_DONE) { - CURL *handle = m->easy_handle; - char *url; - struct memory *mem; - curl_easy_getinfo(handle, CURLINFO_PRIVATE, &mem); - curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url); - if(m->data.result == CURLE_OK) { - long res_status; - curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &res_status); - if(res_status == 200) { - char *ctype; - curl_easy_getinfo(handle, CURLINFO_CONTENT_TYPE, &ctype); - printf("[%d] HTTP 200 (%s): %s\n", complete, ctype, url); - if(is_html(ctype) && mem->size > 100) { - if(pending < max_requests && (complete + pending) < max_total) { - pending += follow_links(multi_handle, mem, url); - still_running = 1; + /* See how the transfers went */ + m = NULL; + while((m = curl_multi_info_read(multi, &msgs_left))) { + if(m->msg == CURLMSG_DONE) { + CURL *curl = m->easy_handle; + char *url; + struct memory *mem; + curl_easy_getinfo(curl, CURLINFO_PRIVATE, &mem); + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); + if(m->data.result == CURLE_OK) { + long res_status; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_status); + if(res_status == 200) { + char *ctype; + curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ctype); + printf("[%d] HTTP 200 (%s): %s\n", complete, ctype, url); + if(is_html(ctype) && mem->size > 100) { + if(pending < max_requests && + (complete + pending) < max_total) { + pending += follow_links(multi, mem, url); + still_running = 1; + } } } + else { + printf("[%d] HTTP %d: %s\n", complete, (int)res_status, url); + } } else { - printf("[%d] HTTP %d: %s\n", complete, (int) res_status, url); + printf("[%d] Connection failure: %s\n", complete, url); } + curl_multi_remove_handle(multi, curl); + curl_easy_cleanup(curl); + free(mem->buf); + free(mem); + complete++; + pending--; } - else { - printf("[%d] Connection failure: %s\n", complete, url); - } - curl_multi_remove_handle(multi_handle, handle); - curl_easy_cleanup(handle); - free(mem->buf); - free(mem); - complete++; - pending--; } } + curl_multi_cleanup(multi); } - curl_multi_cleanup(multi_handle); curl_global_cleanup(); return 0; } diff --git a/docs/examples/debug.c b/docs/examples/debug.c index 5303c833f5..449cd1111b 100644 --- a/docs/examples/debug.c +++ b/docs/examples/debug.c @@ -26,16 +26,15 @@ * */ #include + #include struct data { char trace_ascii; /* 1 or 0 */ }; -static -void dump(const char *text, - FILE *stream, unsigned char *ptr, size_t size, - char nohex) +static void dump(const char *text, const unsigned char *ptr, + size_t size, char nohex) { size_t i; size_t c; @@ -46,20 +45,20 @@ void dump(const char *text, /* without the hex output, we can fit more on screen */ width = 0x40; - fprintf(stream, "%s, %10.10lu bytes (0x%8.8lx)\n", + fprintf(stderr, "%s, %lu bytes (0x%lx)\n", text, (unsigned long)size, (unsigned long)size); for(i = 0; i < size; i += width) { - fprintf(stream, "%4.4lx: ", (unsigned long)i); + fprintf(stderr, "%4.4lx: ", (unsigned long)i); if(!nohex) { /* hex not disabled, show it */ for(c = 0; c < width; c++) if(i + c < size) - fprintf(stream, "%02x ", ptr[i + c]); + fprintf(stderr, "%02x ", ptr[i + c]); else - fputs(" ", stream); + fputs(" ", stderr); } for(c = 0; (c < width) && (i + c < size); c++) { @@ -69,28 +68,25 @@ void dump(const char *text, i += (c + 2 - width); break; } - fprintf(stream, "%c", + fprintf(stderr, "%c", (ptr[i + c] >= 0x20) && (ptr[i + c] < 0x80) ? ptr[i + c] : '.'); - /* check again for 0D0A, to avoid an extra \n if it's at width */ + /* check again for 0D0A, to avoid an extra \n if it is at width */ if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D && ptr[i + c + 2] == 0x0A) { i += (c + 3 - width); break; } } - fputc('\n', stream); /* newline */ + fputc('\n', stderr); /* newline */ } - fflush(stream); } -static -int my_trace(CURL *handle, curl_infotype type, - char *data, size_t size, - void *userp) +static int my_trace(CURL *curl, curl_infotype type, + char *data, size_t size, void *userp) { struct data *config = (struct data *)userp; const char *text; - (void)handle; + (void)curl; switch(type) { case CURLINFO_TEXT: @@ -118,16 +114,20 @@ int my_trace(CURL *handle, curl_infotype type, return 0; } - dump(text, stderr, (unsigned char *)data, size, config->trace_ascii); + dump(text, (const unsigned char *)data, size, config->trace_ascii); return 0; } int main(void) { CURL *curl; - CURLcode res; + CURLcode result; struct data config; + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + config.trace_ascii = 1; /* enable ASCII tracing */ curl = curl_easy_init(); @@ -142,14 +142,15 @@ int main(void) curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } - return 0; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/default-scheme.c b/docs/examples/default-scheme.c index 13e1e08fcb..198c9c8041 100644 --- a/docs/examples/default-scheme.c +++ b/docs/examples/default-scheme.c @@ -26,12 +26,17 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res; + CURLcode result; + + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -43,15 +48,16 @@ int main(void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } - return 0; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/ephiperfifo.c b/docs/examples/ephiperfifo.c index d30b944bbc..62c3b2c674 100644 --- a/docs/examples/ephiperfifo.c +++ b/docs/examples/ephiperfifo.c @@ -22,7 +22,7 @@ * ***************************************************************************/ /* - * multi socket API usage with epoll and timerfd + * multi socket interface with epoll and timerfd * */ /* Example application source code using the multi socket interface to @@ -32,33 +32,30 @@ * but this uses epoll and timerfd instead of libevent. * * Written by Jeff Pohlmeyer, converted to use epoll by Josh Bialkowski - -Requires a Linux system with epoll - -When running, the program creates the named pipe "hiper.fifo" - -Whenever there is input into the fifo, the program reads the input as a list -of URL's and creates some new easy handles to fetch each URL via the -curl_multi "hiper" API. - - -Thus, you can try a single URL: - % echo http://www.yahoo.com > hiper.fifo - -Or a whole bunch of them: - % cat my-url-list > hiper.fifo - -The fifo buffer is handled almost instantly, so you can even add more URL's -while the previous requests are still being downloaded. - -Note: - For the sake of simplicity, URL length is limited to 1023 char's ! - -This is purely a demo app, all retrieved data is simply discarded by the write -callback. - -*/ - + * + * Requires a Linux system with epoll + * + * When running, the program creates the named pipe "hiper.fifo" + * + * Whenever there is input into the fifo, the program reads the input as a list + * of URL's and creates some new easy handles to fetch each URL via the + * curl_multi "hiper" API. + * + * Thus, you can try a single URL: + * % echo http://www.yahoo.com > hiper.fifo + * + * Or a whole bunch of them: + * % cat my-url-list > hiper.fifo + * + * The fifo buffer is handled almost instantly, so you can even add more URL's + * while the previous requests are still being downloaded. + * + * Note: + * For the sake of simplicity, URL length is limited to 1023 chars. + * + * This is purely a demo app, all retrieved data is discarded by + * the write callback. + */ #include #include #include @@ -77,7 +74,6 @@ callback. #define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */ - /* Global information, common to all connections */ struct GlobalInfo { int epfd; /* epoll filedescriptor */ @@ -88,42 +84,52 @@ struct GlobalInfo { FILE *input; }; - /* Information associated with a specific easy handle */ struct ConnInfo { - CURL *easy; + CURL *curl; char *url; struct GlobalInfo *global; char error[CURL_ERROR_SIZE]; }; - /* Information associated with a specific socket */ struct SockInfo { curl_socket_t sockfd; - CURL *easy; + CURL *curl; int action; long timeout; struct GlobalInfo *global; }; -#define mycase(code) \ - case code: s = __STRING(code) - /* Die if we get a bad CURLMcode somewhere */ static void mcode_or_die(const char *where, CURLMcode code) { if(CURLM_OK != code) { const char *s; switch(code) { - mycase(CURLM_BAD_HANDLE); break; - mycase(CURLM_BAD_EASY_HANDLE); break; - mycase(CURLM_OUT_OF_MEMORY); break; - mycase(CURLM_INTERNAL_ERROR); break; - mycase(CURLM_UNKNOWN_OPTION); break; - mycase(CURLM_LAST); break; - default: s = "CURLM_unknown"; break; - mycase(CURLM_BAD_SOCKET); + case CURLM_BAD_HANDLE: + s = "CURLM_BAD_HANDLE"; + break; + case CURLM_BAD_EASY_HANDLE: + s = "CURLM_BAD_EASY_HANDLE"; + break; + case CURLM_OUT_OF_MEMORY: + s = "CURLM_OUT_OF_MEMORY"; + break; + case CURLM_INTERNAL_ERROR: + s = "CURLM_INTERNAL_ERROR"; + break; + case CURLM_UNKNOWN_OPTION: + s = "CURLM_UNKNOWN_OPTION"; + break; + case CURLM_LAST: + s = "CURLM_LAST"; + break; + default: + s = "CURLM_unknown"; + break; + case CURLM_BAD_SOCKET: + s = "CURLM_BAD_SOCKET"; fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s); /* ignore this error */ return; @@ -133,15 +139,71 @@ static void mcode_or_die(const char *where, CURLMcode code) } } -static void timer_cb(struct GlobalInfo *g, int revents); +/* Check for completed transfers, and remove their easy handles */ +static void check_multi_info(struct GlobalInfo *g) +{ + char *eff_url; + CURLMsg *msg; + int msgs_left; + struct ConnInfo *conn; -/* Update the timer after curl_multi library does its thing. Curl informs the + fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running); + while((msg = curl_multi_info_read(g->multi, &msgs_left))) { + if(msg->msg == CURLMSG_DONE) { + CURL *curl = msg->easy_handle; + CURLcode result = msg->data.result; + curl_easy_getinfo(curl, CURLINFO_PRIVATE, &conn); + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &eff_url); + fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, result, conn->error); + curl_multi_remove_handle(g->multi, curl); + free(conn->url); + curl_easy_cleanup(curl); + free(conn); + } + } +} + +/* Called by main loop when our timeout expires */ +static void timer_cb(struct GlobalInfo *g, int revents) +{ + CURLMcode mresult; + uint64_t count = 0; + ssize_t err = 0; + + (void)revents; + + err = read(g->tfd, &count, sizeof(uint64_t)); + if(err == -1) { + /* Note that we may call the timer callback even if the timerfd is not + * readable. It is possible that there are multiple events stored in the + * epoll buffer (i.e. the timer may have fired multiple times). The event + * count is cleared after the first call so future events in the epoll + * buffer fails to read from the timer. */ + if(errno == EAGAIN) { + fprintf(MSG_OUT, "EAGAIN on tfd %d\n", g->tfd); + return; + } + } + if(err != sizeof(uint64_t)) { + fprintf(stderr, "read(tfd) == %ld", err); + perror("read(tfd)"); + } + + mresult = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, + &g->still_running); + mcode_or_die("timer_cb: curl_multi_socket_action", mresult); + check_multi_info(g); +} + +/* Update the timer after curl_multi library does its thing. curl informs the * application through this callback what it wants the new timeout to be, * after it does some work. */ static int multi_timer_cb(CURLM *multi, long timeout_ms, struct GlobalInfo *g) { struct itimerspec its; + (void)multi; + fprintf(MSG_OUT, "multi_timer_cb: Setting timeout to %ld ms\n", timeout_ms); if(timeout_ms > 0) { @@ -163,48 +225,21 @@ static int multi_timer_cb(CURLM *multi, long timeout_ms, struct GlobalInfo *g) memset(&its, 0, sizeof(its)); } - timerfd_settime(g->tfd, /* flags= */0, &its, NULL); + timerfd_settime(g->tfd, /* flags= */ 0, &its, NULL); return 0; } - -/* Check for completed transfers, and remove their easy handles */ -static void check_multi_info(struct GlobalInfo *g) -{ - char *eff_url; - CURLMsg *msg; - int msgs_left; - struct ConnInfo *conn; - CURL *easy; - CURLcode res; - - fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running); - while((msg = curl_multi_info_read(g->multi, &msgs_left))) { - if(msg->msg == CURLMSG_DONE) { - easy = msg->easy_handle; - res = msg->data.result; - curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn); - curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url); - fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error); - curl_multi_remove_handle(g->multi, easy); - free(conn->url); - curl_easy_cleanup(easy); - free(conn); - } - } -} - /* Called by libevent when we get action on a multi socket filedescriptor */ static void event_cb(struct GlobalInfo *g, int fd, int revents) { - CURLMcode rc; + CURLMcode mresult; struct itimerspec its; int action = ((revents & EPOLLIN) ? CURL_CSELECT_IN : 0) | ((revents & EPOLLOUT) ? CURL_CSELECT_OUT : 0); - rc = curl_multi_socket_action(g->multi, fd, action, &g->still_running); - mcode_or_die("event_cb: curl_multi_socket_action", rc); + mresult = curl_multi_socket_action(g->multi, fd, action, &g->still_running); + mcode_or_die("event_cb: curl_multi_socket_action", mresult); check_multi_info(g); if(g->still_running <= 0) { @@ -214,37 +249,6 @@ static void event_cb(struct GlobalInfo *g, int fd, int revents) } } -/* Called by main loop when our timeout expires */ -static void timer_cb(struct GlobalInfo *g, int revents) -{ - CURLMcode rc; - uint64_t count = 0; - ssize_t err = 0; - - err = read(g->tfd, &count, sizeof(uint64_t)); - if(err == -1) { - /* Note that we may call the timer callback even if the timerfd is not - * readable. It's possible that there are multiple events stored in the - * epoll buffer (i.e. the timer may have fired multiple times). The event - * count is cleared after the first call so future events in the epoll - * buffer fails to read from the timer. */ - if(errno == EAGAIN) { - fprintf(MSG_OUT, "EAGAIN on tfd %d\n", g->tfd); - return; - } - } - if(err != sizeof(uint64_t)) { - fprintf(stderr, "read(tfd) == %ld", err); - perror("read(tfd)"); - } - - rc = curl_multi_socket_action(g->multi, - CURL_SOCKET_TIMEOUT, 0, &g->still_running); - mcode_or_die("timer_cb: curl_multi_socket_action", rc); - check_multi_info(g); -} - - /* Clean up the SockInfo structure */ static void remsock(struct SockInfo *f, struct GlobalInfo *g) { @@ -258,7 +262,6 @@ static void remsock(struct SockInfo *f, struct GlobalInfo *g) } } - /* Assign information to a SockInfo structure */ static void setsock(struct SockInfo *f, curl_socket_t s, CURL *e, int act, struct GlobalInfo *g) @@ -275,7 +278,7 @@ static void setsock(struct SockInfo *f, curl_socket_t s, CURL *e, int act, f->sockfd = s; f->action = act; - f->easy = e; + f->curl = e; ev.events = kind; ev.data.fd = s; @@ -284,27 +287,25 @@ static void setsock(struct SockInfo *f, curl_socket_t s, CURL *e, int act, s, strerror(errno)); } - /* Initialize a new SockInfo structure */ -static void addsock(curl_socket_t s, CURL *easy, int action, +static void addsock(curl_socket_t s, CURL *curl, int action, struct GlobalInfo *g) { - struct SockInfo *fdp = (struct SockInfo*)calloc(1, sizeof(struct SockInfo)); + struct SockInfo *fdp = calloc(1, sizeof(struct SockInfo)); fdp->global = g; - setsock(fdp, s, easy, action, g); + setsock(fdp, s, curl, action, g); curl_multi_assign(g->multi, s, fdp); } /* CURLMOPT_SOCKETFUNCTION */ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) { - struct GlobalInfo *g = (struct GlobalInfo*) cbp; - struct SockInfo *fdp = (struct SockInfo*) sockp; - const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" }; + struct GlobalInfo *g = (struct GlobalInfo *)cbp; + struct SockInfo *fdp = (struct SockInfo *)sockp; + const char *whatstr[] = { "none", "IN", "OUT", "INOUT", "REMOVE" }; - fprintf(MSG_OUT, - "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]); + fprintf(MSG_OUT, "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]); if(what == CURL_POLL_REMOVE) { fprintf(MSG_OUT, "\n"); remsock(fdp, g); @@ -315,8 +316,7 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) addsock(s, e, what, g); } else { - fprintf(MSG_OUT, - "Changing action from %s to %s\n", + fprintf(MSG_OUT, "Changing action from %s to %s\n", whatstr[fdp->action], whatstr[what]); setsock(fdp, s, e, what, g); } @@ -324,16 +324,14 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) return 0; } - /* CURLOPT_WRITEFUNCTION */ -static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *data) { (void)ptr; (void)data; return size * nmemb; } - /* CURLOPT_PROGRESSFUNCTION */ static int prog_cb(void *p, double dltotal, double dlnow, double ult, double uln) @@ -346,42 +344,42 @@ static int prog_cb(void *p, double dltotal, double dlnow, double ult, return 0; } - /* Create a new easy handle, and add it to the global curl_multi */ static void new_conn(const char *url, struct GlobalInfo *g) { struct ConnInfo *conn; - CURLMcode rc; + CURLMcode mresult; - conn = (struct ConnInfo*)calloc(1, sizeof(*conn)); + conn = calloc(1, sizeof(*conn)); conn->error[0] = '\0'; - conn->easy = curl_easy_init(); - if(!conn->easy) { + conn->curl = curl_easy_init(); + if(!conn->curl) { fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n"); exit(2); } conn->global = g; conn->url = strdup(url); - curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url); - curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb); - curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, conn); - curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error); - curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn); - curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0L); - curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb); - curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn); - curl_easy_setopt(conn->easy, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 3L); - curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 10L); - fprintf(MSG_OUT, - "Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url); - rc = curl_multi_add_handle(g->multi, conn->easy); - mcode_or_die("new_conn: curl_multi_add_handle", rc); + curl_easy_setopt(conn->curl, CURLOPT_URL, conn->url); + curl_easy_setopt(conn->curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(conn->curl, CURLOPT_WRITEDATA, conn); + curl_easy_setopt(conn->curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(conn->curl, CURLOPT_ERRORBUFFER, conn->error); + curl_easy_setopt(conn->curl, CURLOPT_PRIVATE, conn); + curl_easy_setopt(conn->curl, CURLOPT_NOPROGRESS, 0L); + curl_easy_setopt(conn->curl, CURLOPT_PROGRESSFUNCTION, prog_cb); + curl_easy_setopt(conn->curl, CURLOPT_PROGRESSDATA, conn); + curl_easy_setopt(conn->curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(conn->curl, CURLOPT_LOW_SPEED_TIME, 3L); + curl_easy_setopt(conn->curl, CURLOPT_LOW_SPEED_LIMIT, 10L); - /* note that the add_handle() sets a timeout to trigger soon so that the - * necessary socket_action() call gets called by this app */ + fprintf(MSG_OUT, "Adding easy %p to multi %p (%s)\n", + conn->curl, g->multi, url); + mresult = curl_multi_add_handle(g->multi, conn->curl); + mcode_or_die("new_conn: curl_multi_add_handle", mresult); + + /* note that add_handle() sets a timeout to trigger soon so that the + necessary socket_action() gets called */ } /* This gets called whenever data is received from the fifo */ @@ -391,10 +389,12 @@ static void fifo_cb(struct GlobalInfo *g, int revents) long int rv = 0; int n = 0; + (void)revents; + do { - s[0]='\0'; + s[0] = '\0'; rv = fscanf(g->input, "%1023s%n", s, &n); - s[n]='\0'; + s[n] = '\0'; if(n && s[0]) { new_conn(s, g); /* if we read a URL, go get it! */ } @@ -404,10 +404,10 @@ static void fifo_cb(struct GlobalInfo *g, int revents) } /* Create a named pipe and tell libevent to monitor it */ -static const char *fifo = "hiper.fifo"; static int init_fifo(struct GlobalInfo *g) { struct stat st; + static const char *fifo = "hiper.fifo"; curl_socket_t sockfd; struct epoll_event epev; @@ -448,7 +448,6 @@ static void clean_fifo(struct GlobalInfo *g) unlink(fifo); } - int g_should_exit_ = 0; void sigint_handler(int signo) @@ -456,14 +455,17 @@ void sigint_handler(int signo) g_should_exit_ = 1; } -int main(int argc, char **argv) +int main(void) { + CURLcode result; struct GlobalInfo g; struct itimerspec its; struct epoll_event ev; struct epoll_event events[10]; - (void)argc; - (void)argv; + + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; g_should_exit_ = 0; signal(SIGINT, sigint_handler); @@ -472,12 +474,14 @@ int main(int argc, char **argv) g.epfd = epoll_create1(EPOLL_CLOEXEC); if(g.epfd == -1) { perror("epoll_create1 failed"); + curl_global_cleanup(); return 1; } g.tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); if(g.tfd == -1) { perror("timerfd_create failed"); + curl_global_cleanup(); return 1; } @@ -490,8 +494,10 @@ int main(int argc, char **argv) ev.data.fd = g.tfd; epoll_ctl(g.epfd, EPOLL_CTL_ADD, g.tfd, &ev); - if(init_fifo(&g)) + if(init_fifo(&g)) { + curl_global_cleanup(); return 1; + } g.multi = curl_multi_init(); /* setup the generic multi interface options we want */ @@ -508,7 +514,7 @@ int main(int argc, char **argv) while(!g_should_exit_) { int idx; int err = epoll_wait(g.epfd, events, - sizeof(events)/sizeof(struct epoll_event), 10000); + sizeof(events) / sizeof(struct epoll_event), 10000); if(err == -1) { /* !checksrc! disable ERRNOVAR 1 */ if(errno == EINTR) { @@ -517,6 +523,7 @@ int main(int argc, char **argv) } else { perror("epoll_wait"); + curl_global_cleanup(); return 1; } } @@ -539,5 +546,6 @@ int main(int argc, char **argv) curl_multi_cleanup(g.multi); clean_fifo(&g); + curl_global_cleanup(); return 0; } diff --git a/docs/examples/evhiperfifo.c b/docs/examples/evhiperfifo.c index b28f057c82..e4fdf7d089 100644 --- a/docs/examples/evhiperfifo.c +++ b/docs/examples/evhiperfifo.c @@ -22,7 +22,7 @@ * ***************************************************************************/ /* - * multi socket interface together with libev + * multi socket interface with libev * */ /* Example application source code using the multi socket interface to @@ -32,52 +32,50 @@ * but this uses libev instead of libevent. * * Written by Jeff Pohlmeyer, converted to use libev by Markus Koetter - -Requires libev and a (POSIX?) system that has mkfifo(). - -This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c" -sample programs. - -When running, the program creates the named pipe "hiper.fifo" - -Whenever there is input into the fifo, the program reads the input as a list -of URL's and creates some new easy handles to fetch each URL via the -curl_multi "hiper" API. - - -Thus, you can try a single URL: - % echo http://www.yahoo.com > hiper.fifo - -Or a whole bunch of them: - % cat my-url-list > hiper.fifo - -The fifo buffer is handled almost instantly, so you can even add more URL's -while the previous requests are still being downloaded. - -Note: - For the sake of simplicity, URL length is limited to 1023 char's ! - -This is purely a demo app, all retrieved data is simply discarded by the write -callback. - -*/ - + * + * Requires libev and a (POSIX?) system that has mkfifo(). + * + * This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c" + * sample programs. + * + * When running, the program creates the named pipe "hiper.fifo" + * + * Whenever there is input into the fifo, the program reads the input as a list + * of URL's and creates some new easy handles to fetch each URL via the + * curl_multi "hiper" API. + * + * Thus, you can try a single URL: + * % echo http://www.yahoo.com > hiper.fifo + * + * Or a whole bunch of them: + * % cat my-url-list > hiper.fifo + * + * The fifo buffer is handled almost instantly, so you can even add more URL's + * while the previous requests are still being downloaded. + * + * Note: + * For the sake of simplicity, URL length is limited to 1023 chars. + * + * This is purely a demo app, all retrieved data is discarded by + * the write callback. + */ +#include +#include #include -#include #include +#include +#include +#include #include #include #include -#include -#include + #include -#include -#include -#include + +#include #define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */ - /* Global information, common to all connections */ struct GlobalInfo { struct ev_loop *loop; @@ -88,20 +86,18 @@ struct GlobalInfo { FILE *input; }; - /* Information associated with a specific easy handle */ struct ConnInfo { - CURL *easy; + CURL *curl; char *url; struct GlobalInfo *global; char error[CURL_ERROR_SIZE]; }; - /* Information associated with a specific socket */ struct SockInfo { curl_socket_t sockfd; - CURL *easy; + CURL *curl; int action; long timeout; struct ev_io ev; @@ -109,23 +105,6 @@ struct SockInfo { struct GlobalInfo *global; }; -static void timer_cb(EV_P_ struct ev_timer *w, int revents); - -/* Update the event timer after curl_multi library calls */ -static int multi_timer_cb(CURLM *multi, long timeout_ms, struct GlobalInfo *g) -{ - (void)multi; - printf("%s %li\n", __PRETTY_FUNCTION__, timeout_ms); - ev_timer_stop(g->loop, &g->timer_event); - if(timeout_ms >= 0) { - /* -1 means delete, other values are timeout times in milliseconds */ - double t = timeout_ms / 1000; - ev_timer_init(&g->timer_event, timer_cb, t, 0.); - ev_timer_start(g->loop, &g->timer_event); - } - return 0; -} - /* Die if we get a bad CURLMcode somewhere */ static void mcode_or_die(const char *where, CURLMcode code) { @@ -164,7 +143,6 @@ static void mcode_or_die(const char *where, CURLMcode code) } } - /* Check for completed transfers, and remove their easy handles */ static void check_multi_info(struct GlobalInfo *g) { @@ -172,63 +150,76 @@ static void check_multi_info(struct GlobalInfo *g) CURLMsg *msg; int msgs_left; struct ConnInfo *conn; - CURL *easy; - CURLcode res; fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running); while((msg = curl_multi_info_read(g->multi, &msgs_left))) { if(msg->msg == CURLMSG_DONE) { - easy = msg->easy_handle; - res = msg->data.result; - curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn); - curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url); - fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error); - curl_multi_remove_handle(g->multi, easy); + CURL *curl = msg->easy_handle; + CURLcode result = msg->data.result; + curl_easy_getinfo(curl, CURLINFO_PRIVATE, &conn); + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &eff_url); + fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, result, conn->error); + curl_multi_remove_handle(g->multi, curl); free(conn->url); - curl_easy_cleanup(easy); + curl_easy_cleanup(curl); free(conn); } } } - -/* Called by libevent when we get action on a multi socket */ -static void event_cb(EV_P_ struct ev_io *w, int revents) -{ - struct GlobalInfo *g; - CURLMcode rc; - int action; - - printf("%s w %p revents %i\n", __PRETTY_FUNCTION__, (void *)w, revents); - g = (struct GlobalInfo*) w->data; - - action = ((revents & EV_READ) ? CURL_POLL_IN : 0) | - ((revents & EV_WRITE) ? CURL_POLL_OUT : 0); - rc = curl_multi_socket_action(g->multi, w->fd, action, &g->still_running); - mcode_or_die("event_cb: curl_multi_socket_action", rc); - check_multi_info(g); - if(g->still_running <= 0) { - fprintf(MSG_OUT, "last transfer done, kill timeout\n"); - ev_timer_stop(g->loop, &g->timer_event); - } -} - /* Called by libevent when our timeout expires */ static void timer_cb(EV_P_ struct ev_timer *w, int revents) { + CURLMcode mresult; struct GlobalInfo *g; - CURLMcode rc; printf("%s w %p revents %i\n", __PRETTY_FUNCTION__, (void *)w, revents); g = (struct GlobalInfo *)w->data; - rc = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, - &g->still_running); - mcode_or_die("timer_cb: curl_multi_socket_action", rc); + mresult = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, + &g->still_running); + mcode_or_die("timer_cb: curl_multi_socket_action", mresult); check_multi_info(g); } +/* Update the event timer after curl_multi library calls */ +static int multi_timer_cb(CURLM *multi, long timeout_ms, struct GlobalInfo *g) +{ + (void)multi; + printf("%s %li\n", __PRETTY_FUNCTION__, timeout_ms); + ev_timer_stop(g->loop, &g->timer_event); + if(timeout_ms >= 0) { + /* -1 means delete, other values are timeout times in milliseconds */ + double t = timeout_ms / 1000; + ev_timer_init(&g->timer_event, timer_cb, t, 0.); + ev_timer_start(g->loop, &g->timer_event); + } + return 0; +} + +/* Called by libevent when we get action on a multi socket */ +static void event_cb(EV_P_ struct ev_io *w, int revents) +{ + CURLMcode mresult; + struct GlobalInfo *g; + + int action = ((revents & EV_READ) ? CURL_POLL_IN : 0) | + ((revents & EV_WRITE) ? CURL_POLL_OUT : 0); + + printf("%s w %p revents %i\n", __PRETTY_FUNCTION__, (void *)w, revents); + g = (struct GlobalInfo *)w->data; + + mresult = curl_multi_socket_action(g->multi, w->fd, action, + &g->still_running); + mcode_or_die("event_cb: curl_multi_socket_action", mresult); + check_multi_info(g); + if(g->still_running <= 0) { + fprintf(MSG_OUT, "last transfer done, kill timeout\n"); + ev_timer_stop(g->loop, &g->timer_event); + } +} + /* Clean up the SockInfo structure */ static void remsock(struct SockInfo *f, struct GlobalInfo *g) { @@ -240,7 +231,6 @@ static void remsock(struct SockInfo *f, struct GlobalInfo *g) } } - /* Assign information to a SockInfo structure */ static void setsock(struct SockInfo *f, curl_socket_t s, CURL *e, int act, struct GlobalInfo *g) @@ -252,7 +242,7 @@ static void setsock(struct SockInfo *f, curl_socket_t s, CURL *e, int act, f->sockfd = s; f->action = act; - f->easy = e; + f->curl = e; if(f->evset) ev_io_stop(g->loop, &f->ev); ev_io_init(&f->ev, event_cb, f->sockfd, kind); @@ -261,30 +251,28 @@ static void setsock(struct SockInfo *f, curl_socket_t s, CURL *e, int act, ev_io_start(g->loop, &f->ev); } - /* Initialize a new SockInfo structure */ -static void addsock(curl_socket_t s, CURL *easy, int action, +static void addsock(curl_socket_t s, CURL *curl, int action, struct GlobalInfo *g) { struct SockInfo *fdp = calloc(1, sizeof(struct SockInfo)); fdp->global = g; - setsock(fdp, s, easy, action, g); + setsock(fdp, s, curl, action, g); curl_multi_assign(g->multi, s, fdp); } /* CURLMOPT_SOCKETFUNCTION */ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) { - struct GlobalInfo *g = (struct GlobalInfo*) cbp; - struct SockInfo *fdp = (struct SockInfo*) sockp; - const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE"}; + struct GlobalInfo *g = (struct GlobalInfo *)cbp; + struct SockInfo *fdp = (struct SockInfo *)sockp; + const char *whatstr[] = { "none", "IN", "OUT", "INOUT", "REMOVE" }; printf("%s e %p s %i what %i cbp %p sockp %p\n", __PRETTY_FUNCTION__, e, s, what, cbp, sockp); - fprintf(MSG_OUT, - "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]); + fprintf(MSG_OUT, "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]); if(what == CURL_POLL_REMOVE) { fprintf(MSG_OUT, "\n"); remsock(fdp, g); @@ -295,8 +283,7 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) addsock(s, e, what, g); } else { - fprintf(MSG_OUT, - "Changing action from %s to %s\n", + fprintf(MSG_OUT, "Changing action from %s to %s\n", whatstr[fdp->action], whatstr[what]); setsock(fdp, s, e, what, g); } @@ -304,12 +291,11 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) return 0; } - /* CURLOPT_WRITEFUNCTION */ -static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *data) { size_t realsize = size * nmemb; - struct ConnInfo *conn = (struct ConnInfo*) data; + struct ConnInfo *conn = (struct ConnInfo *)data; (void)ptr; (void)conn; return realsize; @@ -323,44 +309,44 @@ static int xferinfo_cb(void *p, curl_off_t dltotal, curl_off_t dlnow, (void)ult; (void)uln; - fprintf(MSG_OUT, "Progress: %s (%" CURL_FORMAT_CURL_OFF_T - "/%" CURL_FORMAT_CURL_OFF_T ")\n", conn->url, dlnow, dltotal); + fprintf(MSG_OUT, "Progress: %s (%" CURL_FORMAT_CURL_OFF_T "/" + "%" CURL_FORMAT_CURL_OFF_T ")\n", + conn->url, dlnow, dltotal); return 0; } - /* Create a new easy handle, and add it to the global curl_multi */ static void new_conn(const char *url, struct GlobalInfo *g) { struct ConnInfo *conn; - CURLMcode rc; + CURLMcode mresult; conn = calloc(1, sizeof(*conn)); - conn->error[0]='\0'; + conn->error[0] = '\0'; - conn->easy = curl_easy_init(); - if(!conn->easy) { + conn->curl = curl_easy_init(); + if(!conn->curl) { fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n"); exit(2); } conn->global = g; conn->url = strdup(url); - curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url); - curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb); - curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, conn); - curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error); - curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn); - curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0L); - curl_easy_setopt(conn->easy, CURLOPT_XFERINFOFUNCTION, xferinfo_cb); - curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn); - curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 3L); - curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 10L); + curl_easy_setopt(conn->curl, CURLOPT_URL, conn->url); + curl_easy_setopt(conn->curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(conn->curl, CURLOPT_WRITEDATA, conn); + curl_easy_setopt(conn->curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(conn->curl, CURLOPT_ERRORBUFFER, conn->error); + curl_easy_setopt(conn->curl, CURLOPT_PRIVATE, conn); + curl_easy_setopt(conn->curl, CURLOPT_NOPROGRESS, 0L); + curl_easy_setopt(conn->curl, CURLOPT_XFERINFOFUNCTION, xferinfo_cb); + curl_easy_setopt(conn->curl, CURLOPT_PROGRESSDATA, conn); + curl_easy_setopt(conn->curl, CURLOPT_LOW_SPEED_TIME, 3L); + curl_easy_setopt(conn->curl, CURLOPT_LOW_SPEED_LIMIT, 10L); - fprintf(MSG_OUT, - "Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url); - rc = curl_multi_add_handle(g->multi, conn->easy); - mcode_or_die("new_conn: curl_multi_add_handle", rc); + fprintf(MSG_OUT, "Adding easy %p to multi %p (%s)\n", + conn->curl, g->multi, url); + mresult = curl_multi_add_handle(g->multi, conn->curl); + mcode_or_die("new_conn: curl_multi_add_handle", mresult); /* note that add_handle() sets a timeout to trigger soon so that the necessary socket_action() gets called */ @@ -377,11 +363,11 @@ static void fifo_cb(EV_P_ struct ev_io *w, int revents) (void)revents; do { - s[0]='\0'; + s[0] = '\0'; rv = fscanf(g->input, "%1023s%n", s, &n); - s[n]='\0'; + s[n] = '\0'; if(n && s[0]) { - new_conn(s, g); /* if we read a URL, go get it! */ + new_conn(s, g); /* if we read a URL, go get it! */ } else break; @@ -421,11 +407,14 @@ static int init_fifo(struct GlobalInfo *g) return 0; } -int main(int argc, char **argv) +int main(void) { + CURLcode result; struct GlobalInfo g; - (void)argc; - (void)argv; + + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; memset(&g, 0, sizeof(g)); g.loop = ev_default_loop(0); @@ -447,5 +436,6 @@ int main(int argc, char **argv) ev_loop(g.loop, 0); curl_multi_cleanup(g.multi); + curl_global_cleanup(); return 0; } diff --git a/docs/examples/externalsocket.c b/docs/examples/externalsocket.c index 84c9ba4eb2..1d74ade723 100644 --- a/docs/examples/externalsocket.c +++ b/docs/examples/externalsocket.c @@ -26,6 +26,9 @@ * */ #ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for strerror() */ +#endif #ifndef _WINSOCK_DEPRECATED_NO_WARNINGS #define _WINSOCK_DEPRECATED_NO_WARNINGS /* for inet_addr() */ #endif @@ -34,33 +37,30 @@ #include #include #include + #include #ifdef _WIN32 #define close closesocket #else -#include /* socket types */ -#include /* socket definitions */ +#include /* socket types */ +#include /* socket definitions */ #include -#include /* inet (3) functions */ -#include /* misc. Unix functions */ +#include /* inet (3) functions */ +#include /* misc. Unix functions */ #endif -#ifdef UNDER_CE -#define strerror(e) "?" -#else #include -#endif /* The IP address and port number to connect to */ -#define IPADDR "127.0.0.1" +#define IPADDR "127.0.0.1" #define PORTNUM 80 #ifndef INADDR_NONE #define INADDR_NONE 0xffffffff #endif -static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *stream) { size_t written = fwrite(ptr, size, nmemb, (FILE *)stream); return written; @@ -99,18 +99,13 @@ static int sockopt_callback(void *clientp, curl_socket_t curlfd, int main(void) { CURL *curl; - CURLcode res; - struct sockaddr_in servaddr; /* socket address structure */ + CURLcode result; + struct sockaddr_in servaddr; /* socket address structure */ curl_socket_t sockfd; -#ifdef _WIN32 - WSADATA wsaData; - int initwsa = WSAStartup(MAKEWORD(2, 2), &wsaData); - if(initwsa) { - printf("WSAStartup failed: %d\n", initwsa); - return 1; - } -#endif + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -129,7 +124,7 @@ int main(void) memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; - servaddr.sin_port = htons(PORTNUM); + servaddr.sin_port = htons(PORTNUM); servaddr.sin_addr.s_addr = inet_addr(IPADDR); if(INADDR_NONE == servaddr.sin_addr.s_addr) { @@ -137,8 +132,7 @@ int main(void) return 2; } - if(connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == - -1) { + if(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) { close(sockfd); printf("client error: connect: %s\n", strerror(errno)); return 1; @@ -147,8 +141,8 @@ int main(void) /* no progress meter please */ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); - /* send all data to this function */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); /* call this function to get a socket */ curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket); @@ -163,20 +157,19 @@ int main(void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); close(sockfd); - if(res) { - printf("libcurl error: %d\n", res); + if(result != CURLE_OK) { + printf("libcurl error: %d\n", result); return 4; } } -#ifdef _WIN32 - WSACleanup(); -#endif + curl_global_cleanup(); + return 0; } diff --git a/docs/examples/fileupload.c b/docs/examples/fileupload.c index 0860c9457b..f444da9b9e 100644 --- a/docs/examples/fileupload.c +++ b/docs/examples/fileupload.c @@ -25,38 +25,48 @@ * Upload to a file:// URL * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen() */ +#endif +#endif + #include -#include -#include #include +#include + +#include #ifdef _WIN32 #undef stat -#define stat _stat +#define stat _stati64 #undef fstat -#define fstat _fstat +#define fstat _fstati64 #define fileno _fileno #endif int main(void) { CURL *curl; - CURLcode res; + CURLcode result; struct stat file_info; curl_off_t speed_upload, total_time; FILE *fd; + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + fd = fopen("debugit", "rb"); /* open file to upload */ - if(!fd) + if(!fd) { + curl_global_cleanup(); return 1; /* cannot continue */ + } /* to get the file size */ -#ifdef UNDER_CE - if(stat("debugit", &file_info) != 0) { -#else if(fstat(fileno(fd), &file_info) != 0) { -#endif fclose(fd); + curl_global_cleanup(); return 1; /* cannot continue */ } @@ -79,25 +89,28 @@ int main(void) /* enable verbose for easier tracing */ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) { + if(result != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); } else { /* now extract transfer info */ curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD_T, &speed_upload); curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME_T, &total_time); - fprintf(stderr, "Speed: %lu bytes/sec during %lu.%06lu seconds\n", - (unsigned long)speed_upload, - (unsigned long)(total_time / 1000000), - (unsigned long)(total_time % 1000000)); + fprintf(stderr, "Speed: %" CURL_FORMAT_CURL_OFF_T " bytes/sec during " + "%" CURL_FORMAT_CURL_OFF_T + ".%06" CURL_FORMAT_CURL_OFF_T " seconds\n", + speed_upload, + total_time / 1000000, + total_time % 1000000); } /* always cleanup */ curl_easy_cleanup(curl); } fclose(fd); + curl_global_cleanup(); return 0; } diff --git a/docs/examples/ftp-delete.c b/docs/examples/ftp-delete.c index b879da48d3..5e49e5f8f7 100644 --- a/docs/examples/ftp-delete.c +++ b/docs/examples/ftp-delete.c @@ -21,30 +21,30 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include - -#include - /* * Delete a single file from an FTP server. * */ +#include -static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream) +#include + +static size_t write_cb(char *buffer, size_t size, size_t nmemb, void *stream) { (void)buffer; (void)stream; return size * nmemb; } - int main(void) { CURL *curl; - CURLcode res; + CURLcode result; struct curl_slist *headerlist = NULL; - curl_global_init(CURL_GLOBAL_DEFAULT); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -53,7 +53,7 @@ int main(void) */ curl_easy_setopt(curl, CURLOPT_URL, "ftp://ftp.example.com/"); /* Define our callback to get called when there is data to be written */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); /* Switch on full protocol/debug output */ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); @@ -64,7 +64,7 @@ int main(void) /* pass in list of FTP commands to run after the transfer */ curl_easy_setopt(curl, CURLOPT_POSTQUOTE, headerlist); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* always cleanup */ curl_easy_cleanup(curl); @@ -72,13 +72,13 @@ int main(void) /* clean up the FTP commands list */ curl_slist_free_all(headerlist); - if(CURLE_OK != res) { + if(result != CURLE_OK) { /* we failed */ - fprintf(stderr, "curl told us %d\n", res); + fprintf(stderr, "curl told us %d\n", result); } } curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/ftp-wildcard.c b/docs/examples/ftp-wildcard.c index ddc6e4aae4..abde048e9d 100644 --- a/docs/examples/ftp-wildcard.c +++ b/docs/examples/ftp-wildcard.c @@ -25,74 +25,20 @@ * FTP wildcard pattern matching * */ -#include +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen() */ +#endif +#endif + #include +#include + struct callback_data { FILE *output; }; -static long file_is_coming(struct curl_fileinfo *finfo, - void *data, - int remains); - -static long file_is_downloaded(void *data); - -static size_t write_it(char *buff, size_t size, size_t nmemb, - void *cb_data); - -int main(int argc, char **argv) -{ - /* curl easy handle */ - CURL *handle; - - /* help data */ - struct callback_data data = { 0 }; - - /* global initialization */ - CURLcode rc = curl_global_init(CURL_GLOBAL_ALL); - if(rc) - return (int)rc; - - /* initialization of easy handle */ - handle = curl_easy_init(); - if(!handle) { - curl_global_cleanup(); - return CURLE_OUT_OF_MEMORY; - } - - /* turn on wildcard matching */ - curl_easy_setopt(handle, CURLOPT_WILDCARDMATCH, 1L); - - /* callback is called before download of concrete file started */ - curl_easy_setopt(handle, CURLOPT_CHUNK_BGN_FUNCTION, file_is_coming); - - /* callback is called after data from the file have been transferred */ - curl_easy_setopt(handle, CURLOPT_CHUNK_END_FUNCTION, file_is_downloaded); - - /* this callback writes contents into files */ - curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_it); - - /* put transfer data into callbacks */ - curl_easy_setopt(handle, CURLOPT_CHUNK_DATA, &data); - curl_easy_setopt(handle, CURLOPT_WRITEDATA, &data); - - /* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L); */ - - /* set a URL containing wildcard pattern (only in the last part) */ - if(argc == 2) - curl_easy_setopt(handle, CURLOPT_URL, argv[1]); - else - curl_easy_setopt(handle, CURLOPT_URL, "ftp://example.com/test/*"); - - /* and start transfer! */ - rc = curl_easy_perform(handle); - - curl_easy_cleanup(handle); - curl_global_cleanup(); - return (int)rc; -} - static long file_is_coming(struct curl_fileinfo *finfo, void *input, int remains) { @@ -139,8 +85,7 @@ static long file_is_downloaded(void *input) return CURL_CHUNK_END_FUNC_OK; } -static size_t write_it(char *buff, size_t size, size_t nmemb, - void *cb_data) +static size_t write_cb(char *buff, size_t size, size_t nmemb, void *cb_data) { struct callback_data *data = cb_data; size_t written = 0; @@ -151,3 +96,57 @@ static size_t write_it(char *buff, size_t size, size_t nmemb, written = fwrite(buff, size, nmemb, stdout); return written; } + +int main(int argc, const char **argv) +{ + /* curl easy handle */ + CURL *curl; + + /* help data */ + struct callback_data data = { 0 }; + + /* global initialization */ + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + + /* initialization of easy handle */ + curl = curl_easy_init(); + if(!curl) { + curl_global_cleanup(); + return CURLE_OUT_OF_MEMORY; + } + + /* turn on wildcard matching */ + curl_easy_setopt(curl, CURLOPT_WILDCARDMATCH, 1L); + + /* callback is called before download of concrete file started */ + curl_easy_setopt(curl, CURLOPT_CHUNK_BGN_FUNCTION, file_is_coming); + + /* callback is called after data from the file have been transferred */ + curl_easy_setopt(curl, CURLOPT_CHUNK_END_FUNCTION, file_is_downloaded); + + /* this callback writes contents into files */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + + /* put transfer data into callbacks */ + curl_easy_setopt(curl, CURLOPT_CHUNK_DATA, &data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); + +#if 0 + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); +#endif + + /* set a URL containing wildcard pattern (only in the last part) */ + if(argc == 2) + curl_easy_setopt(curl, CURLOPT_URL, argv[1]); + else + curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/test/*"); + + /* and start transfer! */ + result = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + curl_global_cleanup(); + return (int)result; +} diff --git a/docs/examples/ftpget.c b/docs/examples/ftpget.c index 95369c1c02..973049e78b 100644 --- a/docs/examples/ftpget.c +++ b/docs/examples/ftpget.c @@ -21,21 +21,26 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include - -#include - /* * Get a single file from an FTP server. * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen() */ +#endif +#endif + +#include + +#include struct FtpFile { const char *filename; FILE *stream; }; -static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream) +static size_t write_cb(char *buffer, size_t size, size_t nmemb, void *stream) { struct FtpFile *out = (struct FtpFile *)stream; if(!out->stream) { @@ -47,17 +52,18 @@ static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream) return fwrite(buffer, size, nmemb, out->stream); } - int main(void) { CURL *curl; - CURLcode res; + CURLcode result; struct FtpFile ftpfile = { "curl.tar.gz", /* name to store the file as if successful */ NULL }; - curl_global_init(CURL_GLOBAL_DEFAULT); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -67,21 +73,21 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "ftp://ftp.example.com/curl/curl-7.9.2.tar.gz"); /* Define our callback to get called when there is data to be written */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); /* Set a pointer to our struct to pass to the callback */ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile); /* Switch on full protocol/debug output */ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* always cleanup */ curl_easy_cleanup(curl); - if(CURLE_OK != res) { + if(result != CURLE_OK) { /* we failed */ - fprintf(stderr, "curl told us %d\n", res); + fprintf(stderr, "curl told us %d\n", result); } } @@ -90,5 +96,5 @@ int main(void) curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/ftpgetinfo.c b/docs/examples/ftpgetinfo.c index 485b26bddc..b943f48d02 100644 --- a/docs/examples/ftpgetinfo.c +++ b/docs/examples/ftpgetinfo.c @@ -21,15 +21,20 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include -#include - -#include - /* * Checks a single file's size and mtime from an FTP server. * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for ctime(), fopen() */ +#endif +#endif + +#include +#include + +#include static size_t throw_away(void *ptr, size_t size, size_t nmemb, void *data) { @@ -37,19 +42,22 @@ static size_t throw_away(void *ptr, size_t size, size_t nmemb, void *data) (void)data; /* we are not interested in the headers itself, so we only return the size we would have saved ... */ - return (size_t)(size * nmemb); + return size * nmemb; } int main(void) { - char ftpurl[] = "ftp://ftp.example.com/gnu/binutils/binutils-2.19.1.tar.bz2"; + static const char ftpurl[] = + "ftp://ftp.example.com/gnu/binutils/binutils-2.19.1.tar.bz2"; CURL *curl; - CURLcode res; + CURLcode result; long filetime = -1; curl_off_t filesize = 0; const char *filename = strrchr(ftpurl, '/') + 1; - curl_global_init(CURL_GLOBAL_DEFAULT); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -61,26 +69,28 @@ int main(void) curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, throw_away); curl_easy_setopt(curl, CURLOPT_HEADER, 0L); /* Switch on full protocol/debug output */ - /* curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); */ +#if 0 + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); +#endif - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(CURLE_OK == res) { + if(result == CURLE_OK) { /* https://curl.se/libcurl/c/curl_easy_getinfo.html */ - res = curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime); - if((CURLE_OK == res) && (filetime >= 0)) { + result = curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime); + if((result == CURLE_OK) && (filetime >= 0)) { time_t file_time = (time_t)filetime; printf("filetime %s: %s", filename, ctime(&file_time)); } - res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, - &filesize); - if((CURLE_OK == res) && (filesize > 0)) + result = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, + &filesize); + if((result == CURLE_OK) && (filesize > 0)) printf("filesize %s: %" CURL_FORMAT_CURL_OFF_T " bytes\n", filename, filesize); } else { /* we failed */ - fprintf(stderr, "curl told us %d\n", res); + fprintf(stderr, "curl told us %d\n", result); } /* always cleanup */ @@ -89,5 +99,5 @@ int main(void) curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/ftpgetresp.c b/docs/examples/ftpgetresp.c index 7b40f77c94..8f888b470e 100644 --- a/docs/examples/ftpgetresp.c +++ b/docs/examples/ftpgetresp.c @@ -21,41 +21,53 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include - -#include - /* * Similar to ftpget.c but also stores the received response-lines * in a separate file using our own callback! * */ -static size_t -write_response(void *ptr, size_t size, size_t nmemb, void *data) +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen() */ +#endif +#endif + +#include + +#include + +static size_t write_response(void *ptr, size_t size, size_t nmemb, void *data) { FILE *writehere = (FILE *)data; return fwrite(ptr, size, nmemb, writehere); } -#define FTPBODY "ftp-list" +#define FTPBODY "ftp-list" #define FTPHEADERS "ftp-responses" int main(void) { CURL *curl; - CURLcode res; + CURLcode result; FILE *ftpfile; FILE *respfile; + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + /* local filename to store the file as */ ftpfile = fopen(FTPBODY, "wb"); /* b is binary, needed on Windows */ - if(!ftpfile) + if(!ftpfile) { + curl_global_cleanup(); return 1; + } /* local filename to store the FTP server's response lines in */ respfile = fopen(FTPHEADERS, "wb"); /* b is binary, needed on Windows */ if(!respfile) { fclose(ftpfile); + curl_global_cleanup(); return 1; } @@ -68,18 +80,20 @@ int main(void) CURLOPT_WRITEFUNCTION as well */ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, write_response); curl_easy_setopt(curl, CURLOPT_HEADERDATA, respfile); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } - fclose(ftpfile); /* close the local file */ + fclose(ftpfile); /* close the local file */ fclose(respfile); /* close the response file */ - return 0; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/ftpsget.c b/docs/examples/ftpsget.c index dfe80b9f8b..abe1d40fda 100644 --- a/docs/examples/ftpsget.c +++ b/docs/examples/ftpsget.c @@ -21,23 +21,26 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include - -#include - /* * Get a single file from an FTPS server. * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen() */ +#endif +#endif + +#include + +#include struct FtpFile { const char *filename; FILE *stream; }; -static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, - void *stream) +static size_t write_cb(char *buffer, size_t size, size_t nmemb, void *stream) { struct FtpFile *out = (struct FtpFile *)stream; if(!out->stream) { @@ -49,17 +52,18 @@ static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, return fwrite(buffer, size, nmemb, out->stream); } - int main(void) { CURL *curl; - CURLcode res; + CURLcode result; struct FtpFile ftpfile = { "yourfile.bin", /* name to store the file as if successful */ NULL }; - curl_global_init(CURL_GLOBAL_DEFAULT); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -71,7 +75,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "ftp://user@server/home/user/file.txt"); /* Define our callback to get called when there is data to be written */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); /* Set a pointer to our struct to pass to the callback */ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile); @@ -81,14 +85,14 @@ int main(void) /* Switch on full protocol/debug output */ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* always cleanup */ curl_easy_cleanup(curl); - if(CURLE_OK != res) { + if(result != CURLE_OK) { /* we failed */ - fprintf(stderr, "curl told us %d\n", res); + fprintf(stderr, "curl told us %d\n", result); } } @@ -97,5 +101,5 @@ int main(void) curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/ftpupload.c b/docs/examples/ftpupload.c index 6e7bece70d..415bafe68e 100644 --- a/docs/examples/ftpupload.c +++ b/docs/examples/ftpupload.c @@ -21,42 +21,46 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +/* + * Performs an FTP upload and renames the file after a successful transfer. + * + */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen(), strerror() */ +#endif +#endif + #include #include +#include +#include +#include +#include #include -#include -#include -#include -#ifdef UNDER_CE -#define strerror(e) "?" -#else -#include -#endif + #ifdef _WIN32 #include #undef stat -#define stat _stat +#define stat _stati64 +#undef fstat +#define fstat _fstati64 +#define fileno _fileno #else #include #endif -/* - * Performs an FTP upload and renames the file just after a successful - * transfer. - * - */ - -#define LOCAL_FILE "/tmp/uploadthis.txt" -#define UPLOAD_FILE_AS "while-uploading.txt" -#define REMOTE_URL "ftp://example.com/" UPLOAD_FILE_AS -#define RENAME_FILE_TO "renamed-and-fine.txt" +#define LOCAL_FILE "/tmp/uploadthis.txt" +#define UPLOAD_FILE_AS "while-uploading.txt" +#define REMOTE_URL "ftp://example.com/" UPLOAD_FILE_AS +#define RENAME_FILE_TO "renamed-and-fine.txt" /* NOTE: if you want this example to work on Windows with libcurl as a DLL, you MUST also provide a read callback with CURLOPT_READFUNCTION. Failing to do so might give you a crash since a DLL may not use the variable's memory when passed in to it from an app like this. */ -static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) +static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { unsigned long nread; /* in real-world cases, this would probably get this data differently @@ -75,31 +79,37 @@ static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) int main(void) { CURL *curl; - CURLcode res; + CURLcode result; FILE *hd_src; struct stat file_info; - unsigned long fsize; + curl_off_t fsize; struct curl_slist *headerlist = NULL; - static const char buf_1 [] = "RNFR " UPLOAD_FILE_AS; - static const char buf_2 [] = "RNTO " RENAME_FILE_TO; + static const char buf_1[] = "RNFR " UPLOAD_FILE_AS; + static const char buf_2[] = "RNTO " RENAME_FILE_TO; - /* get the file size of the local file */ - if(stat(LOCAL_FILE, &file_info)) { - printf("Couldn't open '%s': %s\n", LOCAL_FILE, strerror(errno)); - return 1; - } - fsize = (unsigned long)file_info.st_size; - - printf("Local file size: %lu bytes.\n", fsize); - - /* get a FILE * of the same file */ + /* get a FILE * of the file */ hd_src = fopen(LOCAL_FILE, "rb"); - if(!hd_src) + if(!hd_src) { + printf("Could not open '%s': %s\n", LOCAL_FILE, strerror(errno)); return 2; + } + + /* to get the file size */ + if(fstat(fileno(hd_src), &file_info) != 0) { + fclose(hd_src); + return 1; /* cannot continue */ + } + fsize = file_info.st_size; + + printf("Local file size: %" CURL_FORMAT_CURL_OFF_T " bytes.\n", fsize); /* In Windows, this inits the Winsock stuff */ - curl_global_init(CURL_GLOBAL_ALL); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) { + fclose(hd_src); + return (int)result; + } /* get a curl handle */ curl = curl_easy_init(); @@ -109,7 +119,7 @@ int main(void) headerlist = curl_slist_append(headerlist, buf_2); /* we want to use our own read function */ - curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); /* enable uploading */ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); @@ -127,15 +137,14 @@ int main(void) option you MUST make sure that the type of the passed-in argument is a curl_off_t. If you use CURLOPT_INFILESIZE (without _LARGE) you must make sure that to pass in a type 'long' argument. */ - curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, - (curl_off_t)fsize); + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, fsize); /* Now run off and do what you have been told! */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* clean up the FTP commands list */ curl_slist_free_all(headerlist); @@ -146,5 +155,5 @@ int main(void) fclose(hd_src); /* close the local file */ curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/ftpuploadfrommem.c b/docs/examples/ftpuploadfrommem.c index 3748d68a05..db06edcd9f 100644 --- a/docs/examples/ftpuploadfrommem.c +++ b/docs/examples/ftpuploadfrommem.c @@ -27,9 +27,10 @@ */ #include #include + #include -static const char data[]= +static const char data[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " "___ rhoncus odio id venenatis volutpat. Vestibulum dapibus " "bibendum ullamcorper. Maecenas finibus elit augue, vel " @@ -45,10 +46,10 @@ struct WriteThis { size_t sizeleft; }; -static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *userp) +static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *userp) { struct WriteThis *upload = (struct WriteThis *)userp; - size_t max = size*nmemb; + size_t max = size * nmemb; if(max < 1) return 0; @@ -63,13 +64,13 @@ static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *userp) return copylen; } - return 0; /* no more data left to deliver */ + return 0; /* no more data left to deliver */ } int main(void) { CURL *curl; - CURLcode res; + CURLcode result; struct WriteThis upload; @@ -77,11 +78,11 @@ int main(void) upload.sizeleft = strlen(data); /* In Windows, this inits the Winsock stuff */ - res = curl_global_init(CURL_GLOBAL_DEFAULT); + result = curl_global_init(CURL_GLOBAL_DEFAULT); /* Check for errors */ - if(res != CURLE_OK) { + if(result != CURLE_OK) { fprintf(stderr, "curl_global_init() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); return 1; } @@ -99,7 +100,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); /* we want to use our own read function */ - curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); /* pointer to pass to our read function */ curl_easy_setopt(curl, CURLOPT_READDATA, &upload); @@ -111,16 +112,16 @@ int main(void) curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)upload.sizeleft); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/ftpuploadresume.c b/docs/examples/ftpuploadresume.c index b02ad928a6..e79bc791e2 100644 --- a/docs/examples/ftpuploadresume.c +++ b/docs/examples/ftpuploadresume.c @@ -25,9 +25,15 @@ * Upload to FTP, resuming failed transfers. Active mode. * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen(), sscanf() */ +#endif +#endif #include #include + #include /* parse headers for Content-Length */ @@ -38,14 +44,14 @@ static size_t getcontentlengthfunc(void *ptr, size_t size, size_t nmemb, long len = 0; r = sscanf(ptr, "Content-Length: %ld\n", &len); - if(r) - *((long *) stream) = len; + if(r == 1) + *((long *)stream) = len; return size * nmemb; } /* discard downloaded data */ -static size_t discardfunc(void *ptr, size_t size, size_t nmemb, void *stream) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *stream) { (void)ptr; (void)stream; @@ -53,7 +59,7 @@ static size_t discardfunc(void *ptr, size_t size, size_t nmemb, void *stream) } /* read data to upload */ -static size_t readfunc(char *ptr, size_t size, size_t nmemb, void *stream) +static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { FILE *f = stream; size_t n; @@ -66,49 +72,46 @@ static size_t readfunc(char *ptr, size_t size, size_t nmemb, void *stream) return n; } - -static int upload(CURL *curlhandle, const char *remotepath, +static int upload(CURL *curl, const char *remotepath, const char *localpath, long timeout, long tries) { FILE *f; long uploaded_len = 0; - CURLcode r = CURLE_GOT_NOTHING; + CURLcode result = CURLE_GOT_NOTHING; int c; f = fopen(localpath, "rb"); if(!f) { -#ifndef UNDER_CE perror(NULL); -#endif return 0; } - curl_easy_setopt(curlhandle, CURLOPT_UPLOAD, 1L); + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); - curl_easy_setopt(curlhandle, CURLOPT_URL, remotepath); + curl_easy_setopt(curl, CURLOPT_URL, remotepath); if(timeout) - curl_easy_setopt(curlhandle, CURLOPT_SERVER_RESPONSE_TIMEOUT, timeout); + curl_easy_setopt(curl, CURLOPT_SERVER_RESPONSE_TIMEOUT, timeout); - curl_easy_setopt(curlhandle, CURLOPT_HEADERFUNCTION, getcontentlengthfunc); - curl_easy_setopt(curlhandle, CURLOPT_HEADERDATA, &uploaded_len); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, getcontentlengthfunc); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, &uploaded_len); - curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, discardfunc); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); - curl_easy_setopt(curlhandle, CURLOPT_READFUNCTION, readfunc); - curl_easy_setopt(curlhandle, CURLOPT_READDATA, f); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); + curl_easy_setopt(curl, CURLOPT_READDATA, f); /* enable active mode */ - curl_easy_setopt(curlhandle, CURLOPT_FTPPORT, "-"); + curl_easy_setopt(curl, CURLOPT_FTPPORT, "-"); /* allow the server no more than 7 seconds to connect back */ - curl_easy_setopt(curlhandle, CURLOPT_ACCEPTTIMEOUT_MS, 7000L); + curl_easy_setopt(curl, CURLOPT_ACCEPTTIMEOUT_MS, 7000L); - curl_easy_setopt(curlhandle, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L); + curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L); - curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - for(c = 0; (r != CURLE_OK) && (c < tries); c++) { + for(c = 0; (result != CURLE_OK) && (c < tries); c++) { /* are we resuming? */ if(c) { /* yes */ /* determine the length of the file already written */ @@ -116,51 +119,53 @@ static int upload(CURL *curlhandle, const char *remotepath, /* * With NOBODY and NOHEADER, libcurl issues a SIZE command, but the only * way to retrieve the result is to parse the returned Content-Length - * header. Thus, getcontentlengthfunc(). We need discardfunc() above + * header. Thus, getcontentlengthfunc(). We need write_cb() above * because HEADER dumps the headers to stdout without it. */ - curl_easy_setopt(curlhandle, CURLOPT_NOBODY, 1L); - curl_easy_setopt(curlhandle, CURLOPT_HEADER, 1L); + curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); + curl_easy_setopt(curl, CURLOPT_HEADER, 1L); - r = curl_easy_perform(curlhandle); - if(r != CURLE_OK) + result = curl_easy_perform(curl); + if(result != CURLE_OK) continue; - curl_easy_setopt(curlhandle, CURLOPT_NOBODY, 0L); - curl_easy_setopt(curlhandle, CURLOPT_HEADER, 0L); + curl_easy_setopt(curl, CURLOPT_NOBODY, 0L); + curl_easy_setopt(curl, CURLOPT_HEADER, 0L); fseek(f, uploaded_len, SEEK_SET); - curl_easy_setopt(curlhandle, CURLOPT_APPEND, 1L); + curl_easy_setopt(curl, CURLOPT_APPEND, 1L); } else { /* no */ - curl_easy_setopt(curlhandle, CURLOPT_APPEND, 0L); + curl_easy_setopt(curl, CURLOPT_APPEND, 0L); } - r = curl_easy_perform(curlhandle); + result = curl_easy_perform(curl); } fclose(f); - if(r == CURLE_OK) + if(result == CURLE_OK) return 1; else { - fprintf(stderr, "%s\n", curl_easy_strerror(r)); + fprintf(stderr, "%s\n", curl_easy_strerror(result)); return 0; } } int main(void) { - CURL *curlhandle = NULL; + CURL *curl = NULL; - curl_global_init(CURL_GLOBAL_ALL); - curlhandle = curl_easy_init(); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - upload(curlhandle, "ftp://user:pass@example.com/path/file", "C:\\file", - 0, 3); - - curl_easy_cleanup(curlhandle); + curl = curl_easy_init(); + if(curl) { + upload(curl, "ftp://user:pass@example.com/path/file", "C:\\file", 0, 3); + curl_easy_cleanup(curl); + } curl_global_cleanup(); return 0; diff --git a/docs/examples/getinfo.c b/docs/examples/getinfo.c index 9c178c2c8c..43a4eeb524 100644 --- a/docs/examples/getinfo.c +++ b/docs/examples/getinfo.c @@ -26,29 +26,34 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(CURLE_OK == res) { - char *ct; + if(result == CURLE_OK) { + const char *ct; /* ask for the content-type */ - res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct); + result = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct); - if((CURLE_OK == res) && ct) + if((result == CURLE_OK) && ct) printf("We received Content-Type: %s\n", ct); } /* always cleanup */ curl_easy_cleanup(curl); } - return 0; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/getinmemory.c b/docs/examples/getinmemory.c index 173247d915..036953b50c 100644 --- a/docs/examples/getinmemory.c +++ b/docs/examples/getinmemory.c @@ -26,7 +26,6 @@ * chunk of memory instead of storing it in a file. * */ - #include #include #include @@ -38,8 +37,7 @@ struct MemoryStruct { size_t size; }; -static size_t -WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) +static size_t write_cb(char *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct MemoryStruct *mem = (struct MemoryStruct *)userp; @@ -61,58 +59,62 @@ WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) int main(void) { - CURL *curl_handle; - CURLcode res; + CURL *curl; + CURLcode result; struct MemoryStruct chunk; - chunk.memory = malloc(1); /* grown as needed by the realloc above */ - chunk.size = 0; /* no data at this point */ + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - curl_global_init(CURL_GLOBAL_ALL); + chunk.memory = malloc(1); /* grown as needed by the realloc above */ + chunk.size = 0; /* no data at this point */ /* init the curl session */ - curl_handle = curl_easy_init(); + curl = curl_easy_init(); + if(curl) { - /* specify URL to get */ - curl_easy_setopt(curl_handle, CURLOPT_URL, "https://www.example.com/"); + /* specify URL to get */ + curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); - /* send all data to this function */ - curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); - /* we pass our 'chunk' struct to the callback function */ - curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); + /* we pass our 'chunk' struct to the callback function */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); - /* some servers do not like requests that are made without a user-agent - field, so we provide one */ - curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + /* some servers do not like requests that are made without a user-agent + field, so we provide one */ + curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); - /* get it! */ - res = curl_easy_perform(curl_handle); + /* get it! */ + result = curl_easy_perform(curl); - /* check for errors */ - if(res != CURLE_OK) { - fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + /* check for errors */ + if(result != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(result)); + } + else { + /* + * Now, our chunk.memory points to a memory block that is chunk.size + * bytes big and contains the remote file. + * + * Do something nice with it! + */ + + printf("%lu bytes retrieved\n", (unsigned long)chunk.size); + } + + /* cleanup curl stuff */ + curl_easy_cleanup(curl); } - else { - /* - * Now, our chunk.memory points to a memory block that is chunk.size - * bytes big and contains the remote file. - * - * Do something nice with it! - */ - - printf("%lu bytes retrieved\n", (unsigned long)chunk.size); - } - - /* cleanup curl stuff */ - curl_easy_cleanup(curl_handle); free(chunk.memory); /* we are done with libcurl, so clean it up */ curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/getredirect.c b/docs/examples/getredirect.c index 91c778d3c1..e144e628e4 100644 --- a/docs/examples/getredirect.c +++ b/docs/examples/getredirect.c @@ -26,14 +26,17 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res; - char *location; - long response_code; + CURLcode result; + + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -41,23 +44,24 @@ int main(void) /* example.com is redirected, figure out the redirection! */ - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); else { - res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); - if((res == CURLE_OK) && - ((response_code / 100) != 3)) { + long response_code; + result = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + if((result == CURLE_OK) && ((response_code / 100) != 3)) { /* a redirect implies a 3xx response code */ fprintf(stderr, "Not a redirect.\n"); } else { - res = curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &location); + const char *location; + result = curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &location); - if((res == CURLE_OK) && location) { + if((result == CURLE_OK) && location) { /* This is the new absolute URL that you could redirect to, even if * the Location: response header may have been a relative URL. */ printf("Redirected to: %s\n", location); @@ -68,5 +72,6 @@ int main(void) /* always cleanup */ curl_easy_cleanup(curl); } - return 0; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/getreferrer.c b/docs/examples/getreferrer.c index c46f7825a0..b2220842ec 100644 --- a/docs/examples/getreferrer.c +++ b/docs/examples/getreferrer.c @@ -26,34 +26,38 @@ * */ #include + #include int main(void) { CURL *curl; + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + curl = curl_easy_init(); if(curl) { - CURLcode res; - curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); curl_easy_setopt(curl, CURLOPT_REFERER, "https://example.org/referrer"); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); else { - char *hdr; - res = curl_easy_getinfo(curl, CURLINFO_REFERER, &hdr); - if((res == CURLE_OK) && hdr) + const char *hdr; + result = curl_easy_getinfo(curl, CURLINFO_REFERER, &hdr); + if((result == CURLE_OK) && hdr) printf("Referrer header: %s\n", hdr); } /* always cleanup */ curl_easy_cleanup(curl); } + curl_global_cleanup(); return 0; } diff --git a/docs/examples/ghiper.c b/docs/examples/ghiper.c index 7d8449cdf0..31db2bc9f9 100644 --- a/docs/examples/ghiper.c +++ b/docs/examples/ghiper.c @@ -22,52 +22,52 @@ * ***************************************************************************/ /* - * multi socket API usage together with glib2 + * multi socket interface with glib2 * */ /* Example application source code using the multi socket interface to * download many files at once. * * Written by Jeff Pohlmeyer - - Requires glib-2.x and a (POSIX?) system that has mkfifo(). - - This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c" - sample programs, adapted to use glib's g_io_channel in place of libevent. - - When running, the program creates the named pipe "hiper.fifo" - - Whenever there is input into the fifo, the program reads the input as a list - of URL's and creates some new easy handles to fetch each URL via the - curl_multi "hiper" API. - - - Thus, you can try a single URL: - % echo http://www.yahoo.com > hiper.fifo - - Or a whole bunch of them: - % cat my-url-list > hiper.fifo - - The fifo buffer is handled almost instantly, so you can even add more URL's - while the previous requests are still being downloaded. - - This is purely a demo app, all retrieved data is simply discarded by the write - callback. - -*/ - + * + * Requires glib-2.x and a (POSIX?) system that has mkfifo(). + * + * This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c" + * sample programs, adapted to use glib's g_io_channel in place of libevent. + * + * When running, the program creates the named pipe "hiper.fifo" + * + * Whenever there is input into the fifo, the program reads the input as a list + * of URL's and creates some new easy handles to fetch each URL via the + * curl_multi "hiper" API. + * + * Thus, you can try a single URL: + * % echo http://www.yahoo.com > hiper.fifo + * + * Or a whole bunch of them: + * % cat my-url-list > hiper.fifo + * + * The fifo buffer is handled almost instantly, so you can even add more URL's + * while the previous requests are still being downloaded. + * + * This is purely a demo app, all retrieved data is discarded by + * the write callback. + * + */ #include -#include + #include #include #include #include #include +#include + #include -#define MSG_OUT g_print /* Change to "g_error" to write to stderr */ -#define SHOW_VERBOSE 0L /* Set to non-zero for libcurl messages */ -#define SHOW_PROGRESS 0 /* Set to non-zero to enable progress callback */ +#define MSG_OUT g_print /* Change to "g_error" to write to stderr */ +#define SHOW_VERBOSE 0L /* Set to non-zero for libcurl messages */ +#define SHOW_PROGRESS 0 /* Set to non-zero to enable progress callback */ /* Global information, common to all connections */ struct GlobalInfo { @@ -78,7 +78,7 @@ struct GlobalInfo { /* Information associated with a specific easy handle */ struct ConnInfo { - CURL *easy; + CURL *curl; char *url; struct GlobalInfo *global; char error[CURL_ERROR_SIZE]; @@ -87,7 +87,7 @@ struct ConnInfo { /* Information associated with a specific socket */ struct SockInfo { curl_socket_t sockfd; - CURL *easy; + CURL *curl; int action; long timeout; GIOChannel *ch; @@ -101,14 +101,27 @@ static void mcode_or_die(const char *where, CURLMcode code) if(CURLM_OK != code) { const char *s; switch(code) { - case CURLM_BAD_HANDLE: s = "CURLM_BAD_HANDLE"; break; - case CURLM_BAD_EASY_HANDLE: s = "CURLM_BAD_EASY_HANDLE"; break; - case CURLM_OUT_OF_MEMORY: s = "CURLM_OUT_OF_MEMORY"; break; - case CURLM_INTERNAL_ERROR: s = "CURLM_INTERNAL_ERROR"; break; - case CURLM_BAD_SOCKET: s = "CURLM_BAD_SOCKET"; break; - case CURLM_UNKNOWN_OPTION: s = "CURLM_UNKNOWN_OPTION"; break; - case CURLM_LAST: s = "CURLM_LAST"; break; - default: s = "CURLM_unknown"; + case CURLM_BAD_HANDLE: + s = "CURLM_BAD_HANDLE"; + break; + case CURLM_BAD_EASY_HANDLE: + s = "CURLM_BAD_EASY_HANDLE"; + break; + case CURLM_OUT_OF_MEMORY: + s = "CURLM_OUT_OF_MEMORY"; + break; + case CURLM_INTERNAL_ERROR: + s = "CURLM_INTERNAL_ERROR"; + break; + case CURLM_BAD_SOCKET: + s = "CURLM_BAD_SOCKET"; + break; + case CURLM_UNKNOWN_OPTION: + s = "CURLM_UNKNOWN_OPTION"; + break; + default: + s = "CURLM_unknown"; + break; } MSG_OUT("ERROR: %s returns %s\n", where, s); exit(code); @@ -124,16 +137,16 @@ static void check_multi_info(struct GlobalInfo *g) MSG_OUT("REMAINING: %d\n", g->still_running); while((msg = curl_multi_info_read(g->multi, &msgs_left))) { if(msg->msg == CURLMSG_DONE) { - CURL *easy = msg->easy_handle; - CURLcode res = msg->data.result; + CURL *curl = msg->easy_handle; + CURLcode result = msg->data.result; char *eff_url; struct ConnInfo *conn; - curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn); - curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url); - MSG_OUT("DONE: %s => (%d) %s\n", eff_url, res, conn->error); - curl_multi_remove_handle(g->multi, easy); + curl_easy_getinfo(curl, CURLINFO_PRIVATE, &conn); + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &eff_url); + MSG_OUT("DONE: %s => (%d) %s\n", eff_url, result, conn->error); + curl_multi_remove_handle(g->multi, curl); free(conn->url); - curl_easy_cleanup(easy); + curl_easy_cleanup(curl); free(conn); } } @@ -143,11 +156,11 @@ static void check_multi_info(struct GlobalInfo *g) static gboolean timer_cb(gpointer data) { struct GlobalInfo *g = (struct GlobalInfo *)data; - CURLMcode rc; + CURLMcode mresult; - rc = curl_multi_socket_action(g->multi, - CURL_SOCKET_TIMEOUT, 0, &g->still_running); - mcode_or_die("timer_cb: curl_multi_socket_action", rc); + mresult = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, + &g->still_running); + mcode_or_die("timer_cb: curl_multi_socket_action", mresult); check_multi_info(g); return FALSE; } @@ -157,14 +170,14 @@ static int update_timeout_cb(CURLM *multi, long timeout_ms, void *userp) { struct timeval timeout; struct GlobalInfo *g = (struct GlobalInfo *)userp; - timeout.tv_sec = timeout_ms/1000; - timeout.tv_usec = (timeout_ms%1000)*1000; + timeout.tv_sec = timeout_ms / 1000; + timeout.tv_usec = (timeout_ms % 1000) * 1000; MSG_OUT("*** update_timeout_cb %ld => %ld:%ld ***\n", timeout_ms, timeout.tv_sec, timeout.tv_usec); /* - * if timeout_ms is -1, just delete the timer + * if timeout_ms is -1, delete the timer * * For other values of timeout_ms, this should set or *update* the timer to * the new value @@ -177,16 +190,15 @@ static int update_timeout_cb(CURLM *multi, long timeout_ms, void *userp) /* Called by glib when we get action on a multi socket */ static gboolean event_cb(GIOChannel *ch, GIOCondition condition, gpointer data) { - struct GlobalInfo *g = (struct GlobalInfo*) data; - CURLMcode rc; + struct GlobalInfo *g = (struct GlobalInfo *)data; + CURLMcode mresult; int fd = g_io_channel_unix_get_fd(ch); - int action = - ((condition & G_IO_IN) ? CURL_CSELECT_IN : 0) | - ((condition & G_IO_OUT) ? CURL_CSELECT_OUT : 0); + int action = ((condition & G_IO_IN) ? CURL_CSELECT_IN : 0) | + ((condition & G_IO_OUT) ? CURL_CSELECT_OUT : 0); - rc = curl_multi_socket_action(g->multi, fd, action, &g->still_running); - mcode_or_die("event_cb: curl_multi_socket_action", rc); + mresult = curl_multi_socket_action(g->multi, fd, action, &g->still_running); + mcode_or_die("event_cb: curl_multi_socket_action", mresult); check_multi_info(g); if(g->still_running) { @@ -217,13 +229,12 @@ static void remsock(struct SockInfo *f) static void setsock(struct SockInfo *f, curl_socket_t s, CURL *e, int act, struct GlobalInfo *g) { - GIOCondition kind = - ((act & CURL_POLL_IN) ? G_IO_IN : 0) | - ((act & CURL_POLL_OUT) ? G_IO_OUT : 0); + GIOCondition kind = ((act & CURL_POLL_IN) ? G_IO_IN : 0) | + ((act & CURL_POLL_OUT) ? G_IO_OUT : 0); f->sockfd = s; f->action = act; - f->easy = e; + f->curl = e; if(f->ev) { g_source_remove(f->ev); } @@ -231,23 +242,23 @@ static void setsock(struct SockInfo *f, curl_socket_t s, CURL *e, int act, } /* Initialize a new SockInfo structure */ -static void addsock(curl_socket_t s, CURL *easy, int action, +static void addsock(curl_socket_t s, CURL *curl, int action, struct GlobalInfo *g) { struct SockInfo *fdp = g_malloc0(sizeof(struct SockInfo)); fdp->global = g; fdp->ch = g_io_channel_unix_new(s); - setsock(fdp, s, easy, action, g); + setsock(fdp, s, curl, action, g); curl_multi_assign(g->multi, s, fdp); } /* CURLMOPT_SOCKETFUNCTION */ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) { - struct GlobalInfo *g = (struct GlobalInfo*) cbp; - struct SockInfo *fdp = (struct SockInfo*) sockp; - static const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" }; + struct GlobalInfo *g = (struct GlobalInfo *)cbp; + struct SockInfo *fdp = (struct SockInfo *)sockp; + static const char *whatstr[] = { "none", "IN", "OUT", "INOUT", "REMOVE" }; MSG_OUT("socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]); if(what == CURL_POLL_REMOVE) { @@ -262,8 +273,7 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) addsock(s, e, what, g); } else { - MSG_OUT( - "Changing action from %d to %d\n", fdp->action, what); + MSG_OUT("Changing action from %d to %d\n", fdp->action, what); setsock(fdp, s, e, what, g); } } @@ -271,10 +281,10 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) } /* CURLOPT_WRITEFUNCTION */ -static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *data) { size_t realsize = size * nmemb; - struct ConnInfo *conn = (struct ConnInfo*) data; + struct ConnInfo *conn = (struct ConnInfo *)data; (void)ptr; (void)conn; return realsize; @@ -288,8 +298,8 @@ static int xferinfo_cb(void *p, curl_off_t dltotal, curl_off_t dlnow, (void)ult; (void)uln; - fprintf(MSG_OUT, "Progress: %s (%" CURL_FORMAT_CURL_OFF_T - "/%" CURL_FORMAT_CURL_OFF_T ")\n", conn->url, dlnow, dltotal); + fprintf(MSG_OUT, "Progress: %s (%" CURL_FORMAT_CURL_OFF_T "/" + "%" CURL_FORMAT_CURL_OFF_T ")\n", conn->url, dlnow, dltotal); return 0; } @@ -297,34 +307,34 @@ static int xferinfo_cb(void *p, curl_off_t dltotal, curl_off_t dlnow, static void new_conn(const char *url, struct GlobalInfo *g) { struct ConnInfo *conn; - CURLMcode rc; + CURLMcode mresult; conn = g_malloc0(sizeof(*conn)); conn->error[0] = '\0'; - conn->easy = curl_easy_init(); - if(!conn->easy) { + conn->curl = curl_easy_init(); + if(!conn->curl) { MSG_OUT("curl_easy_init() failed, exiting!\n"); exit(2); } conn->global = g; conn->url = g_strdup(url); - curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url); - curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb); - curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, &conn); - curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, SHOW_VERBOSE); - curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error); - curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn); - curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, SHOW_PROGRESS ? 0L : 1L); - curl_easy_setopt(conn->easy, CURLOPT_XFERINFOFUNCTION, xferinfo_cb); - curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn); - curl_easy_setopt(conn->easy, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(conn->easy, CURLOPT_CONNECTTIMEOUT, 30L); - curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 1L); - curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 30L); + curl_easy_setopt(conn->curl, CURLOPT_URL, conn->url); + curl_easy_setopt(conn->curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(conn->curl, CURLOPT_WRITEDATA, &conn); + curl_easy_setopt(conn->curl, CURLOPT_VERBOSE, SHOW_VERBOSE); + curl_easy_setopt(conn->curl, CURLOPT_ERRORBUFFER, conn->error); + curl_easy_setopt(conn->curl, CURLOPT_PRIVATE, conn); + curl_easy_setopt(conn->curl, CURLOPT_NOPROGRESS, SHOW_PROGRESS ? 0L : 1L); + curl_easy_setopt(conn->curl, CURLOPT_XFERINFOFUNCTION, xferinfo_cb); + curl_easy_setopt(conn->curl, CURLOPT_PROGRESSDATA, conn); + curl_easy_setopt(conn->curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(conn->curl, CURLOPT_CONNECTTIMEOUT, 30L); + curl_easy_setopt(conn->curl, CURLOPT_LOW_SPEED_LIMIT, 1L); + curl_easy_setopt(conn->curl, CURLOPT_LOW_SPEED_TIME, 30L); - MSG_OUT("Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url); - rc = curl_multi_add_handle(g->multi, conn->easy); - mcode_or_die("new_conn: curl_multi_add_handle", rc); + MSG_OUT("Adding easy %p to multi %p (%s)\n", conn->curl, g->multi, url); + mresult = curl_multi_add_handle(g->multi, conn->curl); + mcode_or_die("new_conn: curl_multi_add_handle", mresult); /* note that add_handle() sets a timeout to trigger soon so that the necessary socket_action() gets called */ @@ -343,18 +353,18 @@ static gboolean fifo_cb(GIOChannel *ch, GIOCondition condition, gpointer data) rv = g_io_channel_read_line(ch, &buf, &len, &tp, &err); if(buf) { if(tp) { - buf[tp]='\0'; + buf[tp] = '\0'; } - new_conn(buf, (struct GlobalInfo*)data); + new_conn(buf, (struct GlobalInfo *)data); g_free(buf); } else { buf = g_malloc(BUF_SIZE + 1); - while(TRUE) { - buf[BUF_SIZE]='\0'; + for(;;) { + buf[BUF_SIZE] = '\0'; g_io_channel_read_chars(ch, buf, BUF_SIZE, &len, &err); if(len) { - buf[len]='\0'; + buf[len] = '\0'; if(all) { tmp = all; all = g_strdup_printf("%s%s", tmp, buf); @@ -369,7 +379,7 @@ static gboolean fifo_cb(GIOChannel *ch, GIOCondition condition, gpointer data) } } if(all) { - new_conn(all, (struct GlobalInfo*)data); + new_conn(all, (struct GlobalInfo *)data); g_free(all); } g_free(buf); @@ -417,26 +427,35 @@ int init_fifo(void) int main(void) { struct GlobalInfo *g = g_malloc0(sizeof(struct GlobalInfo)); - GMainLoop*gmain; + GMainLoop *gmain; int fd; - GIOChannel* ch; + GIOChannel *ch; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; fd = init_fifo(); - if(fd == CURL_SOCKET_BAD) + if(fd == CURL_SOCKET_BAD) { + curl_global_cleanup(); return 1; + } ch = g_io_channel_unix_new(fd); g_io_add_watch(ch, G_IO_IN, fifo_cb, g); gmain = g_main_loop_new(NULL, FALSE); g->multi = curl_multi_init(); - curl_multi_setopt(g->multi, CURLMOPT_SOCKETFUNCTION, sock_cb); - curl_multi_setopt(g->multi, CURLMOPT_SOCKETDATA, g); - curl_multi_setopt(g->multi, CURLMOPT_TIMERFUNCTION, update_timeout_cb); - curl_multi_setopt(g->multi, CURLMOPT_TIMERDATA, g); + if(g->multi) { + curl_multi_setopt(g->multi, CURLMOPT_SOCKETFUNCTION, sock_cb); + curl_multi_setopt(g->multi, CURLMOPT_SOCKETDATA, g); + curl_multi_setopt(g->multi, CURLMOPT_TIMERFUNCTION, update_timeout_cb); + curl_multi_setopt(g->multi, CURLMOPT_TIMERDATA, g); - /* we do not call any curl_multi_socket*() function yet as we have no handles - added! */ + /* we do not call any curl_multi_socket*() function yet as we have no + handles added! */ - g_main_loop_run(gmain); - curl_multi_cleanup(g->multi); + g_main_loop_run(gmain); + curl_multi_cleanup(g->multi); + } + curl_global_cleanup(); return 0; } diff --git a/docs/examples/headerapi.c b/docs/examples/headerapi.c index 95c366884a..fed3af5b7b 100644 --- a/docs/examples/headerapi.c +++ b/docs/examples/headerapi.c @@ -26,6 +26,7 @@ * */ #include + #include static size_t write_cb(char *data, size_t n, size_t l, void *userp) @@ -33,30 +34,33 @@ static size_t write_cb(char *data, size_t n, size_t l, void *userp) /* take care of the data here, ignored in this example */ (void)data; (void)userp; - return n*l; + return n * l; } int main(void) { CURL *curl; + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + curl = curl_easy_init(); if(curl) { - CURLcode res; struct curl_header *header; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - /* this example just ignores the content */ + /* this example ignores the content */ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); if(CURLHE_OK == curl_easy_header(curl, "Content-Type", 0, CURLH_HEADER, -1, &header)) @@ -69,13 +73,13 @@ int main(void) do { h = curl_easy_nextheader(curl, CURLH_HEADER, -1, prev); if(h) - printf(" %s: %s (%u)\n", h->name, h->value, (int)h->amount); + printf(" %s: %s (%u)\n", h->name, h->value, (unsigned int)h->amount); prev = h; } while(h); - } /* always cleanup */ curl_easy_cleanup(curl); } + curl_global_cleanup(); return 0; } diff --git a/docs/examples/hiperfifo.c b/docs/examples/hiperfifo.c index 35a519b63e..2b0ae0fdb9 100644 --- a/docs/examples/hiperfifo.c +++ b/docs/examples/hiperfifo.c @@ -22,61 +22,59 @@ * ***************************************************************************/ /* - * multi socket API usage with libevent 2 + * multi socket interface with libevent 2 * */ /* Example application source code using the multi socket interface to - download many files at once. - -Written by Jeff Pohlmeyer - -Requires libevent version 2 and a (POSIX?) system that has mkfifo(). - -This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c" -sample programs. - -When running, the program creates the named pipe "hiper.fifo" - -Whenever there is input into the fifo, the program reads the input as a list -of URL's and creates some new easy handles to fetch each URL via the -curl_multi "hiper" API. - - -Thus, you can try a single URL: - % echo http://www.yahoo.com > hiper.fifo - -Or a whole bunch of them: - % cat my-url-list > hiper.fifo - -The fifo buffer is handled almost instantly, so you can even add more URL's -while the previous requests are still being downloaded. - -Note: - For the sake of simplicity, URL length is limited to 1023 char's ! - -This is purely a demo app, all retrieved data is simply discarded by the write -callback. - -*/ - + * download many files at once. + * + * Written by Jeff Pohlmeyer + * + * Requires libevent version 2 and a (POSIX?) system that has mkfifo(). + * + * This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c" + * sample programs. + * + * When running, the program creates the named pipe "hiper.fifo" + * + * Whenever there is input into the fifo, the program reads the input as a list + * of URL's and creates some new easy handles to fetch each URL via the + * curl_multi "hiper" API. + * + * Thus, you can try a single URL: + * % echo http://www.yahoo.com > hiper.fifo + * + * Or a whole bunch of them: + * % cat my-url-list > hiper.fifo + * + * The fifo buffer is handled almost instantly, so you can even add more URL's + * while the previous requests are still being downloaded. + * + * Note: + * For the sake of simplicity, URL length is limited to 1023 chars. + * + * This is purely a demo app, all retrieved data is discarded by + * the write callback. + */ +#include +#include #include -#include #include +#include +#include +#include +#include #include #include #include -#include + #include + #include #include -#include -#include -#include -#include #define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */ - /* Global information, common to all connections */ struct GlobalInfo { struct event_base *evbase; @@ -88,43 +86,53 @@ struct GlobalInfo { int stopped; }; - /* Information associated with a specific easy handle */ struct ConnInfo { - CURL *easy; + CURL *curl; char *url; struct GlobalInfo *global; char error[CURL_ERROR_SIZE]; }; - /* Information associated with a specific socket */ struct SockInfo { curl_socket_t sockfd; - CURL *easy; + CURL *curl; int action; long timeout; struct event ev; struct GlobalInfo *global; }; -#define mycase(code) \ - case code: s = __STRING(code) - /* Die if we get a bad CURLMcode somewhere */ static void mcode_or_die(const char *where, CURLMcode code) { if(CURLM_OK != code) { const char *s; switch(code) { - mycase(CURLM_BAD_HANDLE); break; - mycase(CURLM_BAD_EASY_HANDLE); break; - mycase(CURLM_OUT_OF_MEMORY); break; - mycase(CURLM_INTERNAL_ERROR); break; - mycase(CURLM_UNKNOWN_OPTION); break; - mycase(CURLM_LAST); break; - default: s = "CURLM_unknown"; break; - mycase(CURLM_BAD_SOCKET); + case CURLM_BAD_HANDLE: + s = "CURLM_BAD_HANDLE"; + break; + case CURLM_BAD_EASY_HANDLE: + s = "CURLM_BAD_EASY_HANDLE"; + break; + case CURLM_OUT_OF_MEMORY: + s = "CURLM_OUT_OF_MEMORY"; + break; + case CURLM_INTERNAL_ERROR: + s = "CURLM_INTERNAL_ERROR"; + break; + case CURLM_UNKNOWN_OPTION: + s = "CURLM_UNKNOWN_OPTION"; + break; + case CURLM_LAST: + s = "CURLM_LAST"; + break; + default: + s = "CURLM_unknown"; + break; + case CURLM_BAD_SOCKET: + s = "CURLM_BAD_SOCKET"; fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s); /* ignore this error */ return; @@ -134,19 +142,18 @@ static void mcode_or_die(const char *where, CURLMcode code) } } - /* Update the event timer after curl_multi library calls */ static int multi_timer_cb(CURLM *multi, long timeout_ms, struct GlobalInfo *g) { struct timeval timeout; (void)multi; - timeout.tv_sec = timeout_ms/1000; - timeout.tv_usec = (timeout_ms%1000)*1000; + timeout.tv_sec = timeout_ms / 1000; + timeout.tv_usec = (timeout_ms % 1000) * 1000; fprintf(MSG_OUT, "multi_timer_cb: Setting timeout to %ld ms\n", timeout_ms); /* - * if timeout_ms is -1, just delete the timer + * if timeout_ms is -1, delete the timer * * For all other values of timeout_ms, this should set or *update* the timer * to the new value @@ -158,7 +165,6 @@ static int multi_timer_cb(CURLM *multi, long timeout_ms, struct GlobalInfo *g) return 0; } - /* Check for completed transfers, and remove their easy handles */ static void check_multi_info(struct GlobalInfo *g) { @@ -166,20 +172,18 @@ static void check_multi_info(struct GlobalInfo *g) CURLMsg *msg; int msgs_left; struct ConnInfo *conn; - CURL *easy; - CURLcode res; fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running); while((msg = curl_multi_info_read(g->multi, &msgs_left))) { if(msg->msg == CURLMSG_DONE) { - easy = msg->easy_handle; - res = msg->data.result; - curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn); - curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url); - fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error); - curl_multi_remove_handle(g->multi, easy); + CURL *curl = msg->easy_handle; + CURLcode result = msg->data.result; + curl_easy_getinfo(curl, CURLINFO_PRIVATE, &conn); + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &eff_url); + fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, result, conn->error); + curl_multi_remove_handle(g->multi, curl); free(conn->url); - curl_easy_cleanup(easy); + curl_easy_cleanup(curl); free(conn); } } @@ -187,19 +191,17 @@ static void check_multi_info(struct GlobalInfo *g) event_base_loopbreak(g->evbase); } - /* Called by libevent when we get action on a multi socket */ static void event_cb(int fd, short kind, void *userp) { - struct GlobalInfo *g = (struct GlobalInfo*) userp; - CURLMcode rc; + struct GlobalInfo *g = (struct GlobalInfo *)userp; + CURLMcode mresult; - int action = - ((kind & EV_READ) ? CURL_CSELECT_IN : 0) | - ((kind & EV_WRITE) ? CURL_CSELECT_OUT : 0); + int action = ((kind & EV_READ) ? CURL_CSELECT_IN : 0) | + ((kind & EV_WRITE) ? CURL_CSELECT_OUT : 0); - rc = curl_multi_socket_action(g->multi, fd, action, &g->still_running); - mcode_or_die("event_cb: curl_multi_socket_action", rc); + mresult = curl_multi_socket_action(g->multi, fd, action, &g->still_running); + mcode_or_die("event_cb: curl_multi_socket_action", mresult); check_multi_info(g); if(g->still_running <= 0) { @@ -210,22 +212,20 @@ static void event_cb(int fd, short kind, void *userp) } } - /* Called by libevent when our timeout expires */ static void timer_cb(int fd, short kind, void *userp) { struct GlobalInfo *g = (struct GlobalInfo *)userp; - CURLMcode rc; + CURLMcode mresult; (void)fd; (void)kind; - rc = curl_multi_socket_action(g->multi, - CURL_SOCKET_TIMEOUT, 0, &g->still_running); - mcode_or_die("timer_cb: curl_multi_socket_action", rc); + mresult = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, + &g->still_running); + mcode_or_die("timer_cb: curl_multi_socket_action", mresult); check_multi_info(g); } - /* Clean up the SockInfo structure */ static void remsock(struct SockInfo *f) { @@ -237,18 +237,16 @@ static void remsock(struct SockInfo *f) } } - /* Assign information to a SockInfo structure */ static void setsock(struct SockInfo *f, curl_socket_t s, CURL *e, int act, struct GlobalInfo *g) { - int kind = - ((act & CURL_POLL_IN) ? EV_READ : 0) | - ((act & CURL_POLL_OUT) ? EV_WRITE : 0) | EV_PERSIST; + int kind = ((act & CURL_POLL_IN) ? EV_READ : 0) | + ((act & CURL_POLL_OUT) ? EV_WRITE : 0) | EV_PERSIST; f->sockfd = s; f->action = act; - f->easy = e; + f->curl = e; if(event_initialized(&f->ev)) { event_del(&f->ev); } @@ -256,27 +254,25 @@ static void setsock(struct SockInfo *f, curl_socket_t s, CURL *e, int act, event_add(&f->ev, NULL); } - /* Initialize a new SockInfo structure */ -static void addsock(curl_socket_t s, CURL *easy, int action, +static void addsock(curl_socket_t s, CURL *curl, int action, struct GlobalInfo *g) { struct SockInfo *fdp = calloc(1, sizeof(struct SockInfo)); fdp->global = g; - setsock(fdp, s, easy, action, g); + setsock(fdp, s, curl, action, g); curl_multi_assign(g->multi, s, fdp); } /* CURLMOPT_SOCKETFUNCTION */ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) { - struct GlobalInfo *g = (struct GlobalInfo*) cbp; - struct SockInfo *fdp = (struct SockInfo*) sockp; - const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" }; + struct GlobalInfo *g = (struct GlobalInfo *)cbp; + struct SockInfo *fdp = (struct SockInfo *)sockp; + const char *whatstr[] = { "none", "IN", "OUT", "INOUT", "REMOVE" }; - fprintf(MSG_OUT, - "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]); + fprintf(MSG_OUT, "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]); if(what == CURL_POLL_REMOVE) { fprintf(MSG_OUT, "\n"); remsock(fdp); @@ -287,8 +283,7 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) addsock(s, e, what, g); } else { - fprintf(MSG_OUT, - "Changing action from %s to %s\n", + fprintf(MSG_OUT, "Changing action from %s to %s\n", whatstr[fdp->action], whatstr[what]); setsock(fdp, s, e, what, g); } @@ -296,16 +291,14 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) return 0; } - /* CURLOPT_WRITEFUNCTION */ -static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *data) { (void)ptr; (void)data; return size * nmemb; } - /* CURLOPT_PROGRESSFUNCTION */ static int xferinfo_cb(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ult, curl_off_t uln) @@ -314,42 +307,41 @@ static int xferinfo_cb(void *p, curl_off_t dltotal, curl_off_t dlnow, (void)ult; (void)uln; - fprintf(MSG_OUT, "Progress: %s (%" CURL_FORMAT_CURL_OFF_T - "/%" CURL_FORMAT_CURL_OFF_T ")\n", conn->url, dlnow, dltotal); + fprintf(MSG_OUT, "Progress: %s (%" CURL_FORMAT_CURL_OFF_T "/" + "%" CURL_FORMAT_CURL_OFF_T ")\n", conn->url, dlnow, dltotal); return 0; } - /* Create a new easy handle, and add it to the global curl_multi */ static void new_conn(const char *url, struct GlobalInfo *g) { struct ConnInfo *conn; - CURLMcode rc; + CURLMcode mresult; conn = calloc(1, sizeof(*conn)); conn->error[0] = '\0'; - conn->easy = curl_easy_init(); - if(!conn->easy) { + conn->curl = curl_easy_init(); + if(!conn->curl) { fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n"); exit(2); } conn->global = g; conn->url = strdup(url); - curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url); - curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb); - curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, conn); - curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error); - curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn); - curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0L); - curl_easy_setopt(conn->easy, CURLOPT_XFERINFOFUNCTION, xferinfo_cb); - curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn); - curl_easy_setopt(conn->easy, CURLOPT_FOLLOWLOCATION, 1L); - fprintf(MSG_OUT, - "Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url); - rc = curl_multi_add_handle(g->multi, conn->easy); - mcode_or_die("new_conn: curl_multi_add_handle", rc); + curl_easy_setopt(conn->curl, CURLOPT_URL, conn->url); + curl_easy_setopt(conn->curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(conn->curl, CURLOPT_WRITEDATA, conn); + curl_easy_setopt(conn->curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(conn->curl, CURLOPT_ERRORBUFFER, conn->error); + curl_easy_setopt(conn->curl, CURLOPT_PRIVATE, conn); + curl_easy_setopt(conn->curl, CURLOPT_NOPROGRESS, 0L); + curl_easy_setopt(conn->curl, CURLOPT_XFERINFOFUNCTION, xferinfo_cb); + curl_easy_setopt(conn->curl, CURLOPT_PROGRESSDATA, conn); + curl_easy_setopt(conn->curl, CURLOPT_FOLLOWLOCATION, 1L); + fprintf(MSG_OUT, "Adding easy %p to multi %p (%s)\n", + conn->curl, g->multi, url); + mresult = curl_multi_add_handle(g->multi, conn->curl); + mcode_or_die("new_conn: curl_multi_add_handle", mresult); /* note that the add_handle() sets a time-out to trigger soon so that the necessary socket_action() gets called */ @@ -366,9 +358,9 @@ static void fifo_cb(int fd, short event, void *arg) (void)event; do { - s[0]='\0'; + s[0] = '\0'; rv = fscanf(g->input, "%1023s%n", s, &n); - s[n]='\0'; + s[n] = '\0'; if(n && s[0]) { if(!strcmp(s, "stop")) { g->stopped = 1; @@ -376,7 +368,7 @@ static void fifo_cb(int fd, short event, void *arg) event_base_loopbreak(g->evbase); } else - new_conn(s, arg); /* if we read a URL, go get it! */ + new_conn(s, arg); /* if we read a URL, go get it! */ } else break; @@ -399,7 +391,7 @@ static int init_fifo(struct GlobalInfo *g) } } unlink(fifo); - if(mkfifo (fifo, 0600) == -1) { + if(mkfifo(fifo, 0600) == -1) { perror("mkfifo"); return 1; } @@ -411,7 +403,7 @@ static int init_fifo(struct GlobalInfo *g) g->input = fdopen(sockfd, "r"); fprintf(MSG_OUT, "Now, pipe some URL's into > %s\n", fifo); - event_assign(&g->fifo_event, g->evbase, sockfd, EV_READ|EV_PERSIST, + event_assign(&g->fifo_event, g->evbase, sockfd, EV_READ | EV_PERSIST, fifo_cb, g); event_add(&g->fifo_event, NULL); return 0; @@ -419,21 +411,26 @@ static int init_fifo(struct GlobalInfo *g) static void clean_fifo(struct GlobalInfo *g) { - event_del(&g->fifo_event); - fclose(g->input); - unlink(fifo); + event_del(&g->fifo_event); + fclose(g->input); + unlink(fifo); } -int main(int argc, char **argv) +int main(void) { + CURLcode result; struct GlobalInfo g; - (void)argc; - (void)argv; + + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; memset(&g, 0, sizeof(g)); g.evbase = event_base_new(); - if(init_fifo(&g)) + if(init_fifo(&g)) { + curl_global_cleanup(); return 1; + } g.multi = curl_multi_init(); evtimer_assign(&g.timer_event, g.evbase, timer_cb, &g); @@ -455,5 +452,6 @@ int main(int argc, char **argv) event_del(&g.timer_event); event_base_free(g.evbase); curl_multi_cleanup(g.multi); + curl_global_cleanup(); return 0; } diff --git a/docs/examples/hsts-preload.c b/docs/examples/hsts-preload.c index ba9fe87a94..877e8eb73e 100644 --- a/docs/examples/hsts-preload.c +++ b/docs/examples/hsts-preload.c @@ -27,6 +27,7 @@ */ #include #include + #include struct entry { @@ -44,22 +45,31 @@ struct state { int index; }; +static void strcopy(char *dest, size_t dsize, const char *src, size_t slen) +{ + if(slen < dsize) { + memcpy(dest, src, slen); + dest[slen] = 0; + } + else if(dsize) + dest[0] = 0; +} + /* "read" is from the point of the library, it wants data from us. One domain entry per invoke. */ -static CURLSTScode hstsread(CURL *easy, struct curl_hstsentry *e, - void *userp) +static CURLSTScode hstsread(CURL *curl, struct curl_hstsentry *e, void *userp) { const char *host; const char *expire; struct state *s = (struct state *)userp; - (void)easy; + (void)curl; host = preload_hosts[s->index].name; expire = preload_hosts[s->index++].exp; - if(host && (strlen(host) < e->namelen)) { - strcpy(e->name, host); + if(host) { + strcopy(e->name, e->namelen, host, strlen(host)); e->includeSubDomains = 0; - strcpy(e->expire, expire); + strcopy(e->expire, sizeof(e->expire), expire, strlen(expire)); fprintf(stderr, "HSTS preload '%s' until '%s'\n", host, expire); } else @@ -67,10 +77,10 @@ static CURLSTScode hstsread(CURL *easy, struct curl_hstsentry *e, return CURLSTS_OK; } -static CURLSTScode hstswrite(CURL *easy, struct curl_hstsentry *e, +static CURLSTScode hstswrite(CURL *curl, struct curl_hstsentry *e, struct curl_index *i, void *userp) { - (void)easy; + (void)curl; (void)userp; /* we have no custom input */ printf("[%u/%u] %s %s\n", (unsigned int)i->index, (unsigned int)i->total, e->name, e->expire); @@ -80,11 +90,14 @@ static CURLSTScode hstswrite(CURL *easy, struct curl_hstsentry *e, int main(void) { CURL *curl; - CURLcode res; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { - struct state st = {0}; + struct state st = { 0 }; /* enable HSTS for this handle */ curl_easy_setopt(curl, CURLOPT_HSTS_CTRL, CURLHSTS_ENABLE); @@ -104,15 +117,16 @@ int main(void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } - return 0; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/htmltidy.c b/docs/examples/htmltidy.c index 498bb85bdc..2e2def59c3 100644 --- a/docs/examples/htmltidy.c +++ b/docs/examples/htmltidy.c @@ -28,14 +28,15 @@ /* * LibTidy => https://www.html-tidy.org/ */ - #include + #include #include + #include -/* curl write callback, to fill tidy's input buffer... */ -uint write_cb(char *in, uint size, uint nmemb, TidyBuffer *out) +/* curl write callback, to fill tidy's input buffer... */ +static uint write_cb(char *in, uint size, uint nmemb, TidyBuffer *out) { uint r; r = size * nmemb; @@ -44,17 +45,17 @@ uint write_cb(char *in, uint size, uint nmemb, TidyBuffer *out) } /* Traverse the document tree */ -void dumpNode(TidyDoc doc, TidyNode tnod, int indent) +static void dumpNode(TidyDoc doc, TidyNode tnod, int indent) { TidyNode child; - for(child = tidyGetChild(tnod); child; child = tidyGetNext(child) ) { + for(child = tidyGetChild(tnod); child; child = tidyGetNext(child)) { ctmbstr name = tidyNodeGetName(child); if(name) { - /* if it has a name, then it's an HTML tag ... */ + /* if it has a name, then it is an HTML tag ... */ TidyAttr attr; printf("%*.*s%s ", indent, indent, "<", name); /* walk the attribute list */ - for(attr = tidyAttrFirst(child); attr; attr = tidyAttrNext(attr) ) { + for(attr = tidyAttrFirst(child); attr; attr = tidyAttrNext(attr)) { printf("%s", tidyAttrName(attr)); tidyAttrValue(attr) ? printf("=\"%s\" ", tidyAttrValue(attr)) : printf(" "); @@ -62,7 +63,7 @@ void dumpNode(TidyDoc doc, TidyNode tnod, int indent) printf(">\n"); } else { - /* if it does not have a name, then it's probably text, cdata, etc... */ + /* if it does not have a name, then it is probably text, cdata, etc... */ TidyBuffer buf; tidyBufInit(&buf); tidyNodeGetText(doc, child, &buf); @@ -73,39 +74,47 @@ void dumpNode(TidyDoc doc, TidyNode tnod, int indent) } } - -int main(int argc, char **argv) +int main(int argc, const char **argv) { - if(argc == 2) { - CURL *curl; - char curl_errbuf[CURL_ERROR_SIZE]; - TidyDoc tdoc; - TidyBuffer docbuf = {0}; - TidyBuffer tidy_errbuf = {0}; - int err; + CURL *curl; + char curl_errbuf[CURL_ERROR_SIZE]; + TidyDoc tdoc; + TidyBuffer docbuf = { 0 }; + TidyBuffer tidy_errbuf = { 0 }; + CURLcode result; - curl = curl_easy_init(); + if(argc != 2) { + printf("usage: %s \n", argv[0]); + return 1; + } + + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + + tdoc = tidyCreate(); + tidyOptSetBool(tdoc, TidyForceOutput, yes); /* try harder */ + tidyOptSetInt(tdoc, TidyWrapLen, 4096); + tidySetErrorBuffer(tdoc, &tidy_errbuf); + tidyBufInit(&docbuf); + + curl = curl_easy_init(); + if(curl) { curl_easy_setopt(curl, CURLOPT_URL, argv[1]); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); - tdoc = tidyCreate(); - tidyOptSetBool(tdoc, TidyForceOutput, yes); /* try harder */ - tidyOptSetInt(tdoc, TidyWrapLen, 4096); - tidySetErrorBuffer(tdoc, &tidy_errbuf); - tidyBufInit(&docbuf); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &docbuf); - err = curl_easy_perform(curl); - if(!err) { - err = tidyParseBuffer(tdoc, &docbuf); /* parse the input */ - if(err >= 0) { - err = tidyCleanAndRepair(tdoc); /* fix any problems */ - if(err >= 0) { - err = tidyRunDiagnostics(tdoc); /* load tidy error buffer */ - if(err >= 0) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = tidyParseBuffer(tdoc, &docbuf); /* parse the input */ + if(result >= 0) { + result = tidyCleanAndRepair(tdoc); /* fix any problems */ + if(result >= 0) { + result = tidyRunDiagnostics(tdoc); /* load tidy error buffer */ + if(result >= 0) { dumpNode(tdoc, tidyGetRoot(tdoc), 0); /* walk the tree */ fprintf(stderr, "%s\n", tidy_errbuf.bp); /* show errors */ } @@ -117,14 +126,13 @@ int main(int argc, char **argv) /* clean-up */ curl_easy_cleanup(curl); - tidyBufFree(&docbuf); - tidyBufFree(&tidy_errbuf); - tidyRelease(tdoc); - return err; - } - else - printf("usage: %s \n", argv[0]); - return 0; + tidyBufFree(&docbuf); + tidyBufFree(&tidy_errbuf); + tidyRelease(tdoc); + + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/htmltitle.cpp b/docs/examples/htmltitle.cpp index 8d75f588fc..7986b94640 100644 --- a/docs/examples/htmltitle.cpp +++ b/docs/examples/htmltitle.cpp @@ -22,107 +22,105 @@ * ***************************************************************************/ /* - * Get a web page, extract the title with libxml. + * Get a webpage, extract the title with libxml. * - Written by Lars Nilsson + Written by Lars Nilsson - GNU C++ compile command line suggestion (edit paths accordingly): + GNU C++ compile command line suggestion (edit paths accordingly): - g++ -Wall -I/opt/curl/include -I/opt/libxml/include/libxml2 htmltitle.cpp \ - -o htmltitle -L/opt/curl/lib -L/opt/libxml/lib -lcurl -lxml2 + g++ -Wall -I/opt/curl/include -I/opt/libxml/include/libxml2 htmltitle.cpp \ + -o htmltitle -L/opt/curl/lib -L/opt/libxml/lib -lcurl -lxml2 */ #include #include #include #include + #include + #include // -// Case-insensitive string comparison +// Case-insensitive string comparison // #ifdef _WIN32 -#define COMPARE(a, b) (!_stricmp((a), (b))) +#define COMPARE(a, b) (!_stricmp(a, b)) #else -#define COMPARE(a, b) (!strcasecmp((a), (b))) +#define COMPARE(a, b) (!strcasecmp(a, b)) #endif // -// libxml callback context structure +// libxml callback context structure // - -struct Context -{ - Context(): addTitle(false) { } +struct Context { + Context() : addTitle(false) {} bool addTitle; std::string title; }; // -// libcurl variables for error strings and returned data +// libcurl variables for error strings and returned data static char errorBuffer[CURL_ERROR_SIZE]; static std::string buffer; // -// libcurl write callback function +// libcurl write callback function // - static size_t writer(char *data, size_t size, size_t nmemb, std::string *writerData) { if(writerData == NULL) return 0; - writerData->append(data, size*nmemb); + writerData->append(data, size * nmemb); return size * nmemb; } // -// libcurl connection initialization +// libcurl connection initialization // - -static bool init(CURL *&conn, const char *url) +static bool init(CURL *&curl, const char *url) { - CURLcode code; + CURLcode result; - conn = curl_easy_init(); + curl = curl_easy_init(); - if(conn == NULL) { - fprintf(stderr, "Failed to create CURL connection\n"); + if(!curl) { + fprintf(stderr, "Failed to create CURL handle\n"); return false; } - code = curl_easy_setopt(conn, CURLOPT_ERRORBUFFER, errorBuffer); - if(code != CURLE_OK) { - fprintf(stderr, "Failed to set error buffer [%d]\n", code); + result = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer); + if(result != CURLE_OK) { + fprintf(stderr, "Failed to set error buffer [%d]\n", result); return false; } - code = curl_easy_setopt(conn, CURLOPT_URL, url); - if(code != CURLE_OK) { + result = curl_easy_setopt(curl, CURLOPT_URL, url); + if(result != CURLE_OK) { fprintf(stderr, "Failed to set URL [%s]\n", errorBuffer); return false; } - code = curl_easy_setopt(conn, CURLOPT_FOLLOWLOCATION, 1L); - if(code != CURLE_OK) { + result = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + if(result != CURLE_OK) { fprintf(stderr, "Failed to set redirect option [%s]\n", errorBuffer); return false; } - code = curl_easy_setopt(conn, CURLOPT_WRITEFUNCTION, writer); - if(code != CURLE_OK) { + result = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer); + if(result != CURLE_OK) { fprintf(stderr, "Failed to set writer [%s]\n", errorBuffer); return false; } - code = curl_easy_setopt(conn, CURLOPT_WRITEDATA, &buffer); - if(code != CURLE_OK) { + result = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); + if(result != CURLE_OK) { fprintf(stderr, "Failed to set write data [%s]\n", errorBuffer); return false; } @@ -131,9 +129,8 @@ static bool init(CURL *&conn, const char *url) } // -// libxml start element callback function +// libxml start element callback function // - static void StartElement(void *voidContext, const xmlChar *name, const xmlChar **attributes) @@ -148,9 +145,8 @@ static void StartElement(void *voidContext, } // -// libxml end element callback function +// libxml end element callback function // - static void EndElement(void *voidContext, const xmlChar *name) { @@ -161,9 +157,8 @@ static void EndElement(void *voidContext, } // -// Text handling helper function +// Text handling helper function // - static void handleCharacters(Context *context, const xmlChar *chars, int length) @@ -174,9 +169,8 @@ static void handleCharacters(Context *context, } // -// libxml PCDATA callback function +// libxml PCDATA callback function // - static void Characters(void *voidContext, const xmlChar *chars, int length) @@ -187,9 +181,8 @@ static void Characters(void *voidContext, } // -// libxml CDATA callback function +// libxml CDATA callback function // - static void cdata(void *voidContext, const xmlChar *chars, int length) @@ -200,11 +193,9 @@ static void cdata(void *voidContext, } // -// libxml SAX callback structure +// libxml SAX callback structure // - -static htmlSAXHandler saxHandler = -{ +static htmlSAXHandler saxHandler = { NULL, NULL, NULL, @@ -240,9 +231,8 @@ static htmlSAXHandler saxHandler = }; // -// Parse given (assumed to be) HTML text and return the title +// Parse given (assumed to be) HTML text and return the title // - static void parseHtml(const std::string &html, std::string &title) { @@ -260,10 +250,10 @@ static void parseHtml(const std::string &html, title = context.title; } -int main(int argc, char *argv[]) +int main(int argc, const char *argv[]) { - CURL *conn = NULL; - CURLcode code; + CURL *curl = NULL; + CURLcode result; std::string title; // Ensure one argument is given @@ -273,21 +263,24 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - curl_global_init(CURL_GLOBAL_DEFAULT); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - // Initialize CURL connection + // Initialize CURL handle - if(!init(conn, argv[1])) { - fprintf(stderr, "Connection initialization failed\n"); + if(!init(curl, argv[1])) { + fprintf(stderr, "Handle initialization failed\n"); + curl_global_cleanup(); return EXIT_FAILURE; } // Retrieve content for the URL - code = curl_easy_perform(conn); - curl_easy_cleanup(conn); + result = curl_easy_perform(curl); + curl_easy_cleanup(curl); - if(code != CURLE_OK) { + if(result != CURLE_OK) { fprintf(stderr, "Failed to get '%s' [%s]\n", argv[1], errorBuffer); return EXIT_FAILURE; } @@ -298,5 +291,5 @@ int main(int argc, char *argv[]) // Display the extracted title printf("Title: %s\n", title.c_str()); - return EXIT_SUCCESS; + return (int)result; } diff --git a/docs/examples/http-options.c b/docs/examples/http-options.c index 586b55f12a..16338441c7 100644 --- a/docs/examples/http-options.c +++ b/docs/examples/http-options.c @@ -26,12 +26,16 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -45,15 +49,16 @@ int main(void) from libcurl as this exits anyway */ curl_easy_setopt(curl, CURLOPT_QUICK_EXIT, 1L); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } + curl_global_cleanup(); return 0; } diff --git a/docs/examples/http-post.c b/docs/examples/http-post.c index 901ee1e3f0..04a755e4aa 100644 --- a/docs/examples/http-post.c +++ b/docs/examples/http-post.c @@ -26,36 +26,38 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res; + CURLcode result; /* In Windows, this inits the Winsock stuff */ - curl_global_init(CURL_GLOBAL_ALL); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; /* get a curl handle */ curl = curl_easy_init(); if(curl) { /* First set the URL that is about to receive our POST. This URL can - just as well be an https:// URL if that is what should receive the - data. */ + be an https:// URL if that is what should receive the data. */ curl_easy_setopt(curl, CURLOPT_URL, "http://postit.example.com/moo.cgi"); /* Now specify the POST data */ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "name=daniel&project=curl"); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/http2-download.c b/docs/examples/http2-download.c index 954cdcbc6d..80c3365bb2 100644 --- a/docs/examples/http2-download.c +++ b/docs/examples/http2-download.c @@ -25,18 +25,18 @@ * Multiplexed HTTP/2 downloads over a single connection * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for _snprintf(), fopen(), strerror() */ +#endif +#endif + #include #include #include -#ifdef UNDER_CE -#define strerror(e) "?" -#else #include -#endif -/* curl stuff */ #include -#include #ifndef CURLPIPE_MULTIPLEX /* This little trick makes sure that we do not enable pipelining for libcurls @@ -45,28 +45,28 @@ #define CURLPIPE_MULTIPLEX 0L #endif +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define snprintf _snprintf +#endif + struct transfer { - CURL *easy; - unsigned int num; FILE *out; + CURL *curl; + int num; }; -#define NUM_HANDLES 1000 - -static -void dump(const char *text, unsigned int num, unsigned char *ptr, size_t size, - char nohex) +static void dump(const char *text, int num, const unsigned char *ptr, + size_t size, char nohex) { size_t i; size_t c; - unsigned int width = 0x10; if(nohex) /* without the hex output, we can fit more on screen */ width = 0x40; - fprintf(stderr, "%u %s, %lu bytes (0x%lx)\n", + fprintf(stderr, "%d %s, %lu bytes (0x%lx)\n", num, text, (unsigned long)size, (unsigned long)size); for(i = 0; i < size; i += width) { @@ -91,7 +91,7 @@ void dump(const char *text, unsigned int num, unsigned char *ptr, size_t size, } fprintf(stderr, "%c", (ptr[i + c] >= 0x20) && (ptr[i + c] < 0x80) ? ptr[i + c] : '.'); - /* check again for 0D0A, to avoid an extra \n if it's at width */ + /* check again for 0D0A, to avoid an extra \n if it is at width */ if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D && ptr[i + c + 2] == 0x0A) { i += (c + 3 - width); @@ -102,19 +102,17 @@ void dump(const char *text, unsigned int num, unsigned char *ptr, size_t size, } } -static -int my_trace(CURL *handle, curl_infotype type, - char *data, size_t size, - void *userp) +static int my_trace(CURL *curl, curl_infotype type, + char *data, size_t size, void *userp) { const char *text; struct transfer *t = (struct transfer *)userp; - unsigned int num = t->num; - (void)handle; + int num = t->num; + (void)curl; switch(type) { case CURLINFO_TEXT: - fprintf(stderr, "== %u Info: %s", num, data); + fprintf(stderr, "== [%d] Info: %s", num, data); return 0; case CURLINFO_HEADER_OUT: text = "=> Send header"; @@ -138,19 +136,19 @@ int my_trace(CURL *handle, curl_infotype type, return 0; } - dump(text, num, (unsigned char *)data, size, 1); + dump(text, num, (const unsigned char *)data, size, 1); return 0; } static int setup(struct transfer *t, int num) { char filename[128]; - CURL *hnd; + CURL *curl; - hnd = t->easy = curl_easy_init(); - - curl_msnprintf(filename, 128, "dl-%d", num); + curl = t->curl = NULL; + t->num = num; + snprintf(filename, sizeof(filename), "dl-%d", num); t->out = fopen(filename, "wb"); if(!t->out) { fprintf(stderr, "error: could not open file %s for writing: %s\n", @@ -158,79 +156,109 @@ static int setup(struct transfer *t, int num) return 1; } - /* write to this file */ - curl_easy_setopt(hnd, CURLOPT_WRITEDATA, t->out); + curl = t->curl = curl_easy_init(); + if(curl) { - /* set the same URL */ - curl_easy_setopt(hnd, CURLOPT_URL, "https://localhost:8443/index.html"); + /* write to this file */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, t->out); - /* please be verbose */ - curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace); - curl_easy_setopt(hnd, CURLOPT_DEBUGDATA, t); + /* set the same URL */ + curl_easy_setopt(curl, CURLOPT_URL, "https://localhost:8443/index.html"); - /* enlarge the receive buffer for potentially higher transfer speeds */ - curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 100000L); + /* please be verbose */ + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, t); - /* HTTP/2 please */ - curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); + /* enlarge the receive buffer for potentially higher transfer speeds */ + curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 100000L); + + /* HTTP/2 please */ + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); #if (CURLPIPE_MULTIPLEX > 0) - /* wait for pipe connection to confirm */ - curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L); + /* wait for pipe connection to confirm */ + curl_easy_setopt(curl, CURLOPT_PIPEWAIT, 1L); #endif + } return 0; } /* * Download many transfers over HTTP/2, using the same connection! */ -int main(int argc, char **argv) +int main(int argc, const char **argv) { - struct transfer trans[NUM_HANDLES]; - CURLM *multi_handle; + CURLcode result; + struct transfer *trans; + CURLM *multi = NULL; int i; int still_running = 0; /* keep number of running handles */ int num_transfers; + if(argc > 1) { /* if given a number, do that many transfers */ num_transfers = atoi(argv[1]); - if((num_transfers < 1) || (num_transfers > NUM_HANDLES)) + if((num_transfers < 1) || (num_transfers > 1000)) num_transfers = 3; /* a suitable low default */ } else - num_transfers = 3; /* suitable default */ + num_transfers = 3; /* a suitable low default */ - /* init a multi stack */ - multi_handle = curl_multi_init(); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - for(i = 0; i < num_transfers; i++) { - if(setup(&trans[i], i)) - return 1; - - /* add the individual transfer */ - curl_multi_add_handle(multi_handle, trans[i].easy); + trans = calloc(num_transfers, sizeof(*trans)); + if(!trans) { + fprintf(stderr, "error allocating transfer structs\n"); + goto error; } - curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); + /* init a multi stack */ + multi = curl_multi_init(); + if(!multi) + goto error; + + for(i = 0; i < num_transfers; i++) { + if(setup(&trans[i], i)) { + goto error; + } + + /* add the individual transfer */ + curl_multi_add_handle(multi, trans[i].curl); + } + + curl_multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); do { - CURLMcode mc = curl_multi_perform(multi_handle, &still_running); + CURLMcode mresult = curl_multi_perform(multi, &still_running); if(still_running) /* wait for activity, timeout or "nothing" */ - mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); - if(mc) + if(mresult) break; + } while(still_running); - for(i = 0; i < num_transfers; i++) { - curl_multi_remove_handle(multi_handle, trans[i].easy); - curl_easy_cleanup(trans[i].easy); +error: + + if(multi) { + for(i = 0; i < num_transfers; i++) { + curl_multi_remove_handle(multi, trans[i].curl); + curl_easy_cleanup(trans[i].curl); + + if(trans[i].out) + fclose(trans[i].out); + } + curl_multi_cleanup(multi); } - curl_multi_cleanup(multi_handle); + free(trans); + + curl_global_cleanup(); return 0; } diff --git a/docs/examples/http2-pushinmemory.c b/docs/examples/http2-pushinmemory.c index 873883ca50..09a8a8c671 100644 --- a/docs/examples/http2-pushinmemory.c +++ b/docs/examples/http2-pushinmemory.c @@ -29,7 +29,6 @@ #include #include -/* curl stuff */ #include struct Memory { @@ -37,8 +36,7 @@ struct Memory { size_t size; }; -static size_t -write_cb(void *contents, size_t size, size_t nmemb, void *userp) +static size_t write_cb(char *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct Memory *mem = (struct Memory *)userp; @@ -67,35 +65,35 @@ static void init_memory(struct Memory *chunk) chunk->size = 0; /* no data at this point */ } -static void setup(CURL *hnd) +static void setup(CURL *curl) { /* set the same URL */ - curl_easy_setopt(hnd, CURLOPT_URL, "https://localhost:8443/index.html"); + curl_easy_setopt(curl, CURLOPT_URL, "https://localhost:8443/index.html"); /* HTTP/2 please */ - curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); /* we use a self-signed test server, skip verification during debugging */ - curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); - /* write data to a struct */ - curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, write_cb); + /* write data to a struct */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); init_memory(&files[0]); - curl_easy_setopt(hnd, CURLOPT_WRITEDATA, &files[0]); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &files[0]); /* wait for pipe connection to confirm */ - curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L); + curl_easy_setopt(curl, CURLOPT_PIPEWAIT, 1L); } /* called when there is an incoming push */ static int server_push_callback(CURL *parent, - CURL *easy, + CURL *curl, size_t num_headers, struct curl_pushheaders *headers, void *userp) { - char *headp; + const char *headp; int *transfers = (int *)userp; (void)parent; (void)num_headers; @@ -106,7 +104,7 @@ static int server_push_callback(CURL *parent, /* write to this buffer */ init_memory(&files[pushindex]); - curl_easy_setopt(easy, CURLOPT_WRITEDATA, &files[pushindex]); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &files[pushindex]); pushindex++; headp = curl_pushheader_byname(headers, ":path"); @@ -117,45 +115,48 @@ static int server_push_callback(CURL *parent, return CURL_PUSH_OK; } - /* * Download a file over HTTP/2, take care of server push. */ int main(void) { - CURL *easy; + CURL *curl; CURLM *multi; - int still_running; /* keep number of running handles */ int transfers = 1; /* we start with one */ int i; - struct CURLMsg *m; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; /* init a multi stack */ multi = curl_multi_init(); - easy = curl_easy_init(); + curl = curl_easy_init(); /* set options */ - setup(easy); + setup(curl); /* add the easy transfer */ - curl_multi_add_handle(multi, easy); + curl_multi_add_handle(multi, curl); curl_multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); curl_multi_setopt(multi, CURLMOPT_PUSHFUNCTION, server_push_callback); curl_multi_setopt(multi, CURLMOPT_PUSHDATA, &transfers); while(transfers) { + struct CURLMsg *m; + int still_running; /* keep number of running handles */ int rc; - CURLMcode mcode = curl_multi_perform(multi, &still_running); - if(mcode) + + CURLMcode mresult = curl_multi_perform(multi, &still_running); + if(mresult) break; - mcode = curl_multi_wait(multi, NULL, 0, 1000, &rc); - if(mcode) + mresult = curl_multi_wait(multi, NULL, 0, 1000, &rc); + if(mresult) break; - /* * When doing server push, libcurl itself created and added one or more * easy handles but *we* need to clean them up when they are done. @@ -164,17 +165,16 @@ int main(void) int msgq = 0; m = curl_multi_info_read(multi, &msgq); if(m && (m->msg == CURLMSG_DONE)) { - CURL *e = m->easy_handle; + curl = m->easy_handle; transfers--; - curl_multi_remove_handle(multi, e); - curl_easy_cleanup(e); + curl_multi_remove_handle(multi, curl); + curl_easy_cleanup(curl); } } while(m); - } - curl_multi_cleanup(multi); + curl_global_cleanup(); /* 'pushindex' is now the number of received transfers */ for(i = 0; i < pushindex; i++) { diff --git a/docs/examples/http2-serverpush.c b/docs/examples/http2-serverpush.c index df4e49ea58..e54675ceac 100644 --- a/docs/examples/http2-serverpush.c +++ b/docs/examples/http2-serverpush.c @@ -25,21 +25,30 @@ * HTTP/2 server push * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for _snprintf(), fopen() */ +#endif +#endif + #include #include #include -/* curl stuff */ #include -#include #ifndef CURLPIPE_MULTIPLEX #error "too old libcurl, cannot do HTTP/2 server push!" #endif -static -void dump(const char *text, unsigned char *ptr, size_t size, - char nohex) +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define snprintf _snprintf +#endif + +static FILE *out_download; + +static void dump(const char *text, const unsigned char *ptr, + size_t size, char nohex) { size_t i; size_t c; @@ -75,7 +84,7 @@ void dump(const char *text, unsigned char *ptr, size_t size, } fprintf(stderr, "%c", (ptr[i + c] >= 0x20) && (ptr[i + c] < 0x80) ? ptr[i + c] : '.'); - /* check again for 0D0A, to avoid an extra \n if it's at width */ + /* check again for 0D0A, to avoid an extra \n if it is at width */ if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D && ptr[i + c + 2] == 0x0A) { i += (c + 3 - width); @@ -86,13 +95,11 @@ void dump(const char *text, unsigned char *ptr, size_t size, } } -static -int my_trace(CURL *handle, curl_infotype type, - char *data, size_t size, - void *userp) +static int my_trace(CURL *curl, curl_infotype type, + char *data, size_t size, void *userp) { const char *text; - (void)handle; + (void)curl; (void)userp; switch(type) { case CURLINFO_TEXT: @@ -120,71 +127,71 @@ int my_trace(CURL *handle, curl_infotype type, return 0; } - dump(text, (unsigned char *)data, size, 1); + dump(text, (const unsigned char *)data, size, 1); return 0; } #define OUTPUTFILE "dl" -static int setup(CURL *hnd, const char *url) +static int setup(CURL *curl, const char *url) { - FILE *out = fopen(OUTPUTFILE, "wb"); - if(!out) - /* failed */ - return 1; - - /* write to this file */ - curl_easy_setopt(hnd, CURLOPT_WRITEDATA, out); + out_download = fopen(OUTPUTFILE, "wb"); + if(!out_download) + return 1; /* failed */ /* set the same URL */ - curl_easy_setopt(hnd, CURLOPT_URL, url); - - /* please be verbose */ - curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_URL, url); /* HTTP/2 please */ - curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); /* we use a self-signed test server, skip verification during debugging */ - curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); -#if (CURLPIPE_MULTIPLEX > 0) + /* write to this file */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, out_download); + + /* please be verbose */ + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + +#if CURLPIPE_MULTIPLEX > 0 /* wait for pipe connection to confirm */ - curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L); + curl_easy_setopt(curl, CURLOPT_PIPEWAIT, 1L); #endif return 0; /* all is good */ } +static FILE *out_push; + /* called when there is an incoming push */ static int server_push_callback(CURL *parent, - CURL *easy, + CURL *curl, size_t num_headers, struct curl_pushheaders *headers, void *userp) { - char *headp; + const char *headp; size_t i; int *transfers = (int *)userp; char filename[128]; - FILE *out; static unsigned int count = 0; (void)parent; - curl_msnprintf(filename, 128, "push%u", count++); + snprintf(filename, sizeof(filename), "push%u", count++); /* here's a new stream, save it in a new file for each new push */ - out = fopen(filename, "wb"); - if(!out) { + out_push = fopen(filename, "wb"); + if(!out_push) { /* if we cannot save it, deny it */ fprintf(stderr, "Failed to create output file for push\n"); return CURL_PUSH_DENY; } /* write to this file */ - curl_easy_setopt(easy, CURLOPT_WRITEDATA, out); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, out_push); fprintf(stderr, "**** push callback approves stream %u, got %lu headers!\n", count, (unsigned long)num_headers); @@ -200,51 +207,58 @@ static int server_push_callback(CURL *parent, } (*transfers)++; /* one more */ + return CURL_PUSH_OK; } - /* * Download a file over HTTP/2, take care of server push. */ -int main(int argc, char *argv[]) +int main(int argc, const char *argv[]) { - CURL *easy; - CURLM *multi_handle; + CURLcode result; + CURL *curl; + CURLM *multi; int transfers = 1; /* we start with one */ - struct CURLMsg *m; const char *url = "https://localhost:8443/index.html"; if(argc == 2) url = argv[1]; - /* init a multi stack */ - multi_handle = curl_multi_init(); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - easy = curl_easy_init(); + /* init a multi stack */ + multi = curl_multi_init(); + if(!multi) + goto error; + + curl = curl_easy_init(); /* set options */ - if(setup(easy, url)) { + if(!curl || setup(curl, url)) { fprintf(stderr, "failed\n"); - return 1; + goto error; } - /* add the easy transfer */ - curl_multi_add_handle(multi_handle, easy); + curl_multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); + curl_multi_setopt(multi, CURLMOPT_PUSHFUNCTION, server_push_callback); + curl_multi_setopt(multi, CURLMOPT_PUSHDATA, &transfers); - curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); - curl_multi_setopt(multi_handle, CURLMOPT_PUSHFUNCTION, server_push_callback); - curl_multi_setopt(multi_handle, CURLMOPT_PUSHDATA, &transfers); + /* add the easy transfer */ + curl_multi_add_handle(multi, curl); do { + struct CURLMsg *m; int still_running; /* keep number of running handles */ - CURLMcode mc = curl_multi_perform(multi_handle, &still_running); + CURLMcode mresult = curl_multi_perform(multi, &still_running); if(still_running) /* wait for activity, timeout or "nothing" */ - mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); - if(mc) + if(mresult) break; /* @@ -252,22 +266,29 @@ int main(int argc, char *argv[]) * created and added one or more easy handles but we need to clean them up * when we are done. */ - do { int msgq = 0; - m = curl_multi_info_read(multi_handle, &msgq); + m = curl_multi_info_read(multi, &msgq); if(m && (m->msg == CURLMSG_DONE)) { - CURL *e = m->easy_handle; + curl = m->easy_handle; transfers--; - curl_multi_remove_handle(multi_handle, e); - curl_easy_cleanup(e); + curl_multi_remove_handle(multi, curl); + curl_easy_cleanup(curl); } } while(m); } while(transfers); /* as long as we have transfers going */ - curl_multi_cleanup(multi_handle); +error: + if(multi) + curl_multi_cleanup(multi); + + curl_global_cleanup(); + + fclose(out_download); + if(out_push) + fclose(out_push); return 0; } diff --git a/docs/examples/http2-upload.c b/docs/examples/http2-upload.c index 482889ea18..aeac13ca2b 100644 --- a/docs/examples/http2-upload.c +++ b/docs/examples/http2-upload.c @@ -25,16 +25,19 @@ * Multiplexed HTTP/2 uploads over a single connection * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for _snprintf(), fopen(), localtime(), + strerror() */ +#endif +#endif + #include #include #include #include #include -#ifdef UNDER_CE -#define strerror(e) "?" -#else #include -#endif /* somewhat Unix-specific */ #ifndef _MSC_VER @@ -42,14 +45,7 @@ #include #endif -#ifdef _WIN32 -#undef stat -#define stat _stat -#endif - -/* curl stuff */ #include -#include #ifndef CURLPIPE_MULTIPLEX /* This little trick makes sure that we do not enable pipelining for libcurls @@ -58,24 +54,33 @@ #define CURLPIPE_MULTIPLEX 0L #endif -#define NUM_HANDLES 1000 +#ifdef _WIN32 +#undef stat +#define stat _stati64 +#undef fstat +#define fstat _fstati64 +#define fileno _fileno +#endif + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define snprintf _snprintf +#endif #ifdef _MSC_VER -#define gettimeofday(a, b) my_gettimeofday((a), (b)) -static -int my_gettimeofday(struct timeval *tp, void *tzp) +#define gettimeofday(a, b) my_gettimeofday(a, b) +static int my_gettimeofday(struct timeval *tp, void *tzp) { (void)tzp; if(tp) { - /* Offset between 1601-01-01 and 1970-01-01 in 100 nanosec units */ - #define _WIN32_FT_OFFSET (116444736000000000) +/* Offset between 1601-01-01 and 1970-01-01 in 100 nanosec units */ +#define WIN32_FT_OFFSET 116444736000000000 union { CURL_TYPEOF_CURL_OFF_T ns100; /* time since 1 Jan 1601 in 100ns units */ FILETIME ft; } _now; GetSystemTimeAsFileTime(&_now.ft); tp->tv_usec = (long)((_now.ns100 / 10) % 1000000); - tp->tv_sec = (long)((_now.ns100 - _WIN32_FT_OFFSET) / 10000000); + tp->tv_sec = (long)((_now.ns100 - WIN32_FT_OFFSET) / 10000000); } return 0; } @@ -83,14 +88,14 @@ int my_gettimeofday(struct timeval *tp, void *tzp) struct input { FILE *in; + FILE *out; size_t bytes_read; /* count up */ - CURL *hnd; + CURL *curl; int num; }; -static -void dump(const char *text, int num, unsigned char *ptr, size_t size, - char nohex) +static void dump(const char *text, int num, const unsigned char *ptr, + size_t size, char nohex) { size_t i; size_t c; @@ -125,7 +130,7 @@ void dump(const char *text, int num, unsigned char *ptr, size_t size, } fprintf(stderr, "%c", (ptr[i + c] >= 0x20) && (ptr[i + c] < 0x80) ? ptr[i + c] : '.'); - /* check again for 0D0A, to avoid an extra \n if it's at width */ + /* check again for 0D0A, to avoid an extra \n if it is at width */ if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D && ptr[i + c + 2] == 0x0A) { i += (c + 3 - width); @@ -136,10 +141,8 @@ void dump(const char *text, int num, unsigned char *ptr, size_t size, } } -static -int my_trace(CURL *handle, curl_infotype type, - char *data, size_t size, - void *userp) +static int my_trace(CURL *curl, curl_infotype type, + char *data, size_t size, void *userp) { char timebuf[60]; const char *text; @@ -150,7 +153,7 @@ int my_trace(CURL *handle, curl_infotype type, struct timeval tv; time_t secs; struct tm *now; - (void)handle; + (void)curl; gettimeofday(&tv, NULL); if(!known_offset) { @@ -158,10 +161,9 @@ int my_trace(CURL *handle, curl_infotype type, known_offset = 1; } secs = epoch_offset + tv.tv_sec; - /* !checksrc! disable BANNEDFUNC 1 */ - now = localtime(&secs); /* not thread safe but we do not care */ - curl_msnprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld", - now->tm_hour, now->tm_min, now->tm_sec, (long)tv.tv_usec); + now = localtime(&secs); /* not thread-safe but we do not care */ + snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d.%06ld", + now->tm_hour, now->tm_min, now->tm_sec, (long)tv.tv_usec); switch(type) { case CURLINFO_TEXT: @@ -189,11 +191,11 @@ int my_trace(CURL *handle, curl_infotype type, return 0; } - dump(text, num, (unsigned char *)data, size, 1); + dump(text, num, (const unsigned char *)data, size, 1); return 0; } -static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *userp) +static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *userp) { struct input *i = userp; size_t retcode = fread(ptr, size, nmemb, i->in); @@ -201,143 +203,171 @@ static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *userp) return retcode; } -static int setup(struct input *i, int num, const char *upload) +static int setup(struct input *t, int num, const char *upload) { - FILE *out; - char url[256]; + char upload_url[256]; char filename[128]; struct stat file_info; curl_off_t uploadsize; - CURL *hnd; + CURL *curl; - hnd = i->hnd = NULL; + curl = t->curl = NULL; - i->num = num; - curl_msnprintf(filename, 128, "dl-%d", num); - out = fopen(filename, "wb"); - if(!out) { + t->num = num; + snprintf(filename, sizeof(filename), "dl-%d", num); + t->out = fopen(filename, "wb"); + if(!t->out) { fprintf(stderr, "error: could not open file %s for writing: %s\n", upload, strerror(errno)); return 1; } - curl_msnprintf(url, 256, "https://localhost:8443/upload-%d", num); + snprintf(upload_url, sizeof(upload_url), "https://localhost:8443/upload-%d", + num); - /* get the file size of the local file */ - if(stat(upload, &file_info)) { + t->in = fopen(upload, "rb"); + if(!t->in) { + fprintf(stderr, "error: could not open file %s for reading: %s\n", upload, + strerror(errno)); + fclose(t->out); + t->out = NULL; + return 1; + } + + if(fstat(fileno(t->in), &file_info) != 0) { fprintf(stderr, "error: could not stat file %s: %s\n", upload, strerror(errno)); - fclose(out); + fclose(t->out); + t->out = NULL; return 1; } uploadsize = file_info.st_size; - i->in = fopen(upload, "rb"); - if(!i->in) { - fprintf(stderr, "error: could not open file %s for reading: %s\n", upload, - strerror(errno)); - fclose(out); - return 1; - } + curl = t->curl = curl_easy_init(); + if(curl) { - hnd = i->hnd = curl_easy_init(); + /* write to this file */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, t->out); - /* write to this file */ - curl_easy_setopt(hnd, CURLOPT_WRITEDATA, out); + /* we want to use our own read function */ + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); + /* read from this file */ + curl_easy_setopt(curl, CURLOPT_READDATA, t); + /* provide the size of the upload */ + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, uploadsize); - /* we want to use our own read function */ - curl_easy_setopt(hnd, CURLOPT_READFUNCTION, read_callback); - /* read from this file */ - curl_easy_setopt(hnd, CURLOPT_READDATA, i); - /* provide the size of the upload */ - curl_easy_setopt(hnd, CURLOPT_INFILESIZE_LARGE, uploadsize); + /* send in the URL to store the upload as */ + curl_easy_setopt(curl, CURLOPT_URL, upload_url); - /* send in the URL to store the upload as */ - curl_easy_setopt(hnd, CURLOPT_URL, url); + /* upload please */ + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); - /* upload please */ - curl_easy_setopt(hnd, CURLOPT_UPLOAD, 1L); + /* please be verbose */ + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, t); - /* please be verbose */ - curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, my_trace); - curl_easy_setopt(hnd, CURLOPT_DEBUGDATA, i); + /* HTTP/2 please */ + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); - /* HTTP/2 please */ - curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); - - /* we use a self-signed test server, skip verification during debugging */ - curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L); + /* we use a self-signed test server, skip verification during debugging */ + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); #if (CURLPIPE_MULTIPLEX > 0) - /* wait for pipe connection to confirm */ - curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L); + /* wait for pipe connection to confirm */ + curl_easy_setopt(curl, CURLOPT_PIPEWAIT, 1L); #endif + } return 0; } /* * Upload all files over HTTP/2, using the same physical connection! */ -int main(int argc, char **argv) +int main(int argc, const char **argv) { - struct input trans[NUM_HANDLES]; - CURLM *multi_handle; + CURLcode result; + struct input *trans; + CURLM *multi = NULL; int i; - int still_running = 0; /* keep number of running handles */ const char *filename = "index.html"; + int still_running = 0; /* keep number of running handles */ int num_transfers; if(argc > 1) { /* if given a number, do that many transfers */ num_transfers = atoi(argv[1]); - - if(!num_transfers || (num_transfers > NUM_HANDLES)) - num_transfers = 3; /* a suitable low default */ + if((num_transfers < 1) || (num_transfers > 1000)) + num_transfers = 3; /* a suitable low default */ if(argc > 2) - /* if given a file name, upload this! */ + /* if given a filename, upload this! */ filename = argv[2]; } else - num_transfers = 3; + num_transfers = 3; /* a suitable low default */ - /* init a multi stack */ - multi_handle = curl_multi_init(); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - for(i = 0; i < num_transfers; i++) { - if(setup(&trans[i], i, filename)) - return 1; - - /* add the individual transfer */ - curl_multi_add_handle(multi_handle, trans[i].hnd); + trans = calloc(num_transfers, sizeof(*trans)); + if(!trans) { + fprintf(stderr, "error allocating transfer structs\n"); + goto error; } - curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); + /* init a multi stack */ + multi = curl_multi_init(); + if(!multi) + goto error; + + for(i = 0; i < num_transfers; i++) { + if(setup(&trans[i], i, filename)) { + goto error; + } + + /* add the individual transfer */ + curl_multi_add_handle(multi, trans[i].curl); + } + + curl_multi_setopt(multi, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); /* We do HTTP/2 so let's stick to one connection per host */ - curl_multi_setopt(multi_handle, CURLMOPT_MAX_HOST_CONNECTIONS, 1L); + curl_multi_setopt(multi, CURLMOPT_MAX_HOST_CONNECTIONS, 1L); do { - CURLMcode mc = curl_multi_perform(multi_handle, &still_running); + CURLMcode mresult = curl_multi_perform(multi, &still_running); if(still_running) /* wait for activity, timeout or "nothing" */ - mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); - if(mc) + if(mresult) break; } while(still_running); - curl_multi_cleanup(multi_handle); +error: - for(i = 0; i < num_transfers; i++) { - curl_multi_remove_handle(multi_handle, trans[i].hnd); - curl_easy_cleanup(trans[i].hnd); + if(multi) { + for(i = 0; i < num_transfers; i++) { + curl_multi_remove_handle(multi, trans[i].curl); + curl_easy_cleanup(trans[i].curl); + + if(trans[i].in) + fclose(trans[i].in); + if(trans[i].out) + fclose(trans[i].out); + } + curl_multi_cleanup(multi); } + free(trans); + + curl_global_cleanup(); + return 0; } diff --git a/docs/examples/http3-present.c b/docs/examples/http3-present.c index 56ba0f5724..5de7d870f4 100644 --- a/docs/examples/http3-present.c +++ b/docs/examples/http3-present.c @@ -26,13 +26,16 @@ * */ #include + #include int main(void) { curl_version_info_data *ver; - curl_global_init(CURL_GLOBAL_ALL); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; ver = curl_version_info(CURLVERSION_NOW); if(ver->features & CURL_VERSION_HTTP2) diff --git a/docs/examples/http3.c b/docs/examples/http3.c index 573ea20d2b..7b52e43a9a 100644 --- a/docs/examples/http3.c +++ b/docs/examples/http3.c @@ -22,16 +22,20 @@ * ***************************************************************************/ /* - * Very simple HTTP/3 GET + * Simple HTTP/3 GET * */ #include + #include int main(void) { CURL *curl; - CURLcode res; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -40,15 +44,16 @@ int main(void) /* Use HTTP/3 but fallback to earlier HTTP if necessary */ curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } + curl_global_cleanup(); return 0; } diff --git a/docs/examples/httpcustomheader.c b/docs/examples/httpcustomheader.c index a3881674c1..1d500b2352 100644 --- a/docs/examples/httpcustomheader.c +++ b/docs/examples/httpcustomheader.c @@ -26,12 +26,16 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -56,11 +60,11 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "localhost"); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); @@ -68,5 +72,6 @@ int main(void) /* free the custom headers */ curl_slist_free_all(chunk); } - return 0; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/httpput-postfields.c b/docs/examples/httpput-postfields.c index 5f410837b8..caa0acfae5 100644 --- a/docs/examples/httpput-postfields.c +++ b/docs/examples/httpput-postfields.c @@ -27,11 +27,12 @@ */ #include #include + #include -static const char olivertwist[]= +static const char olivertwist[] = "Among other public buildings in a certain town, which for many reasons " - "it will be prudent to refrain from mentioning, and to which I will assign " + "it is prudent to refrain from mentioning, and to which I assign " "no fictitious name, there is one anciently common to most towns, great or " "small: to ___, a workhouse; and in this workhouse was born; on a day and " "date which I need not trouble myself to repeat, inasmuch as it can be of " @@ -46,11 +47,11 @@ static const char olivertwist[]= * CURLOPT_POSTFIELDS to the URL given as an argument. */ -int main(int argc, char **argv) +int main(int argc, const char **argv) { CURL *curl; - CURLcode res; - char *url; + CURLcode result; + const char *url; if(argc < 2) return 1; @@ -58,7 +59,9 @@ int main(int argc, char **argv) url = argv[1]; /* In Windows, this inits the Winsock stuff */ - curl_global_init(CURL_GLOBAL_ALL); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; /* get a curl handle */ curl = curl_easy_init(); @@ -86,11 +89,11 @@ int main(int argc, char **argv) curl_easy_setopt(curl, CURLOPT_URL, url); /* Now run off and do what you have been told! */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); @@ -100,5 +103,5 @@ int main(int argc, char **argv) } curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/httpput.c b/docs/examples/httpput.c index 3743e1b5b9..977d31ccaf 100644 --- a/docs/examples/httpput.c +++ b/docs/examples/httpput.c @@ -25,14 +25,24 @@ * HTTP PUT with easy interface and read callback * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen() */ +#endif +#endif + #include #include #include + #include #ifdef _WIN32 #undef stat -#define stat _stat +#define stat _stati64 +#undef fstat +#define fstat _fstati64 +#define fileno _fileno #endif /* @@ -45,7 +55,7 @@ * http://www.apacheweek.com/features/put */ -static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) +static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { size_t retcode; unsigned long nread; @@ -63,15 +73,15 @@ static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) return retcode; } -int main(int argc, char **argv) +int main(int argc, const char **argv) { CURL *curl; - CURLcode res; - FILE * hd_src; + CURLcode result; + FILE *hd_src; struct stat file_info; - char *file; - char *url; + const char *file; + const char *url; if(argc < 3) return 1; @@ -79,24 +89,30 @@ int main(int argc, char **argv) file = argv[1]; url = argv[2]; - /* get the file size of the local file */ - stat(file, &file_info); - - /* get a FILE * of the same file, could also be made with - fdopen() from the previous descriptor, but hey this is just - an example! */ + /* get a FILE * of the same file, could also be made with fdopen() from the + previous descriptor, but hey this is an example! */ hd_src = fopen(file, "rb"); if(!hd_src) return 2; + /* get the file size of the local file */ + if(fstat(fileno(hd_src), &file_info) != 0) { + fclose(hd_src); + return 1; /* cannot continue */ + } + /* In Windows, this inits the Winsock stuff */ - curl_global_init(CURL_GLOBAL_ALL); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) { + fclose(hd_src); + return (int)result; + } /* get a curl handle */ curl = curl_easy_init(); if(curl) { /* we want to use our own read function */ - curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); /* enable uploading (implies PUT over HTTP) */ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); @@ -114,11 +130,11 @@ int main(int argc, char **argv) (curl_off_t)file_info.st_size); /* Now run off and do what you have been told! */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); @@ -126,5 +142,5 @@ int main(int argc, char **argv) fclose(hd_src); /* close the local file */ curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/https.c b/docs/examples/https.c index c1cba877df..5fefb5eea3 100644 --- a/docs/examples/https.c +++ b/docs/examples/https.c @@ -26,14 +26,16 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res; - curl_global_init(CURL_GLOBAL_DEFAULT); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -55,7 +57,7 @@ int main(void) #ifdef SKIP_HOSTNAME_VERIFICATION /* - * If the site you are connecting to uses a different host name that what + * If the site you are connecting to uses a different hostname than what * they have mentioned in their server certificate's commonName (or * subjectAltName) fields, libcurl refuses to connect. You can skip this * check, but it makes the connection insecure. @@ -66,12 +68,12 @@ int main(void) /* cache the CA cert bundle in memory for a week */ curl_easy_setopt(curl, CURLOPT_CA_CACHE_TIMEOUT, 604800L); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); @@ -79,5 +81,5 @@ int main(void) curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/imap-append.c b/docs/examples/imap-append.c index 1839deac1d..77cf2bc02a 100644 --- a/docs/examples/imap-append.c +++ b/docs/examples/imap-append.c @@ -21,14 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Send email with IMAP * */ - #include #include + #include /* This is a simple example showing how to send mail using libcurl's IMAP @@ -37,9 +36,9 @@ * Note that this example requires libcurl 7.30.0 or above. */ -#define FROM "" -#define TO "" -#define CC "" +#define FROM "" +#define TO "" +#define CC "" static const char *payload_text = "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n" @@ -59,13 +58,13 @@ struct upload_status { size_t bytes_read; }; -static size_t payload_source(char *ptr, size_t size, size_t nmemb, void *userp) +static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *userp) { struct upload_status *upload_ctx = (struct upload_status *)userp; const char *data; size_t room = size * nmemb; - if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) { + if((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)) { return 0; } @@ -75,7 +74,7 @@ static size_t payload_source(char *ptr, size_t size, size_t nmemb, void *userp) size_t len = strlen(data); if(room < len) len = room; - memcpy(ptr, data, len); + memcpy(ptr, data, len); /* NOLINT(bugprone-not-null-terminated-result) */ upload_ctx->bytes_read += len; return len; @@ -87,7 +86,10 @@ static size_t payload_source(char *ptr, size_t size, size_t nmemb, void *userp) int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -103,9 +105,9 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "imap://imap.example.com/Sent"); /* In this case, we are using a callback function to specify the data. You - * could just use the CURLOPT_READDATA option to specify a FILE pointer to - * read from. */ - curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source); + * could use the CURLOPT_READDATA option to specify a FILE pointer to read + * from. */ + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); @@ -115,16 +117,18 @@ int main(void) curl_easy_setopt(curl, CURLOPT_INFILESIZE, infilesize); /* Perform the append */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/imap-authzid.c b/docs/examples/imap-authzid.c index eb615c6f3d..0130d4cf19 100644 --- a/docs/examples/imap-authzid.c +++ b/docs/examples/imap-authzid.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Retrieve emails from a shared IMAP mailbox * */ - #include + #include /* This is a simple example showing how to fetch mail using libcurl's IMAP @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -58,16 +60,18 @@ int main(void) "imap://imap.example.com/INBOX/;UID=1"); /* Perform the fetch */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/imap-copy.c b/docs/examples/imap-copy.c index a221be0ca8..e19d4b480d 100644 --- a/docs/examples/imap-copy.c +++ b/docs/examples/imap-copy.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Copy an email from one IMAP folder to another * */ - #include + #include /* This is a simple example showing how to copy a mail from one mailbox folder @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -58,16 +60,18 @@ int main(void) * imap-store.c for more information on deleting messages. */ /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/imap-create.c b/docs/examples/imap-create.c index 6a9b565345..0572171152 100644 --- a/docs/examples/imap-create.c +++ b/docs/examples/imap-create.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Create a new IMAP folder * */ - #include + #include /* This is a simple example showing how to create a new mailbox folder using @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -47,23 +49,25 @@ int main(void) curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); - /* This is just the server URL */ + /* This is the server URL */ curl_easy_setopt(curl, CURLOPT_URL, "imap://imap.example.com"); /* Set the CREATE command specifying the new folder name */ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "CREATE FOLDER"); /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/imap-delete.c b/docs/examples/imap-delete.c index e43ab2e982..16b7d53941 100644 --- a/docs/examples/imap-delete.c +++ b/docs/examples/imap-delete.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Delete an IMAP folder * */ - #include + #include /* This is a simple example showing how to delete an existing mailbox folder @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -47,23 +49,25 @@ int main(void) curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); - /* This is just the server URL */ + /* This is the server URL */ curl_easy_setopt(curl, CURLOPT_URL, "imap://imap.example.com"); /* Set the DELETE command specifying the existing folder */ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE FOLDER"); /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/imap-examine.c b/docs/examples/imap-examine.c index 34217bfa42..91903192b4 100644 --- a/docs/examples/imap-examine.c +++ b/docs/examples/imap-examine.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Obtain information about an IMAP folder * */ - #include + #include /* This is a simple example showing how to obtain information about a mailbox @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -47,23 +49,25 @@ int main(void) curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); - /* This is just the server URL */ + /* This is the server URL */ curl_easy_setopt(curl, CURLOPT_URL, "imap://imap.example.com"); /* Set the EXAMINE command specifying the mailbox folder */ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "EXAMINE OUTBOX"); /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/imap-fetch.c b/docs/examples/imap-fetch.c index 416fe88096..c3d6af75c2 100644 --- a/docs/examples/imap-fetch.c +++ b/docs/examples/imap-fetch.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Retrieve IMAP emails * */ - #include + #include /* This is a simple example showing how to fetch mail using libcurl's IMAP @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -52,16 +54,18 @@ int main(void) "imap://imap.example.com/INBOX/;UID=1"); /* Perform the fetch */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/imap-list.c b/docs/examples/imap-list.c index 0253b543e6..1c1074f85b 100644 --- a/docs/examples/imap-list.c +++ b/docs/examples/imap-list.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * List the folders within an IMAP mailbox * */ - #include + #include /* This is a simple example showing how to list the folders within an IMAP @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -53,16 +55,18 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "imap://imap.example.com"); /* Perform the list */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/imap-lsub.c b/docs/examples/imap-lsub.c index cf45a5fc2d..123d9d35c5 100644 --- a/docs/examples/imap-lsub.c +++ b/docs/examples/imap-lsub.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * List the subscribed IMAP folders * */ - #include + #include /* This is a simple example showing how to list the subscribed folders within @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -47,7 +49,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); - /* This is just the server URL */ + /* This is the server URL */ curl_easy_setopt(curl, CURLOPT_URL, "imap://imap.example.com"); /* Set the LSUB command. Note the syntax is similar to that of a LIST @@ -55,16 +57,18 @@ int main(void) curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "LSUB \"\" *"); /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/imap-multi.c b/docs/examples/imap-multi.c index 42fa7381cd..192ba15cfd 100644 --- a/docs/examples/imap-multi.c +++ b/docs/examples/imap-multi.c @@ -21,14 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Get IMAP email with the multi interface * */ - #include #include + #include /* This is a simple example showing how to fetch mail using libcurl's IMAP @@ -39,44 +38,48 @@ int main(void) { CURL *curl; - CURLM *mcurl; - int still_running = 1; - curl_global_init(CURL_GLOBAL_DEFAULT); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); - if(!curl) - return 1; + if(curl) { + CURLM *multi; - mcurl = curl_multi_init(); - if(!mcurl) - return 2; + multi = curl_multi_init(); + if(multi) { + int still_running = 1; - /* Set username and password */ - curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); - curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); + /* Set username and password */ + curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); + curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); - /* This fetches message 1 from the user's inbox */ - curl_easy_setopt(curl, CURLOPT_URL, "imap://imap.example.com/INBOX/;UID=1"); + /* This fetches message 1 from the user's inbox */ + curl_easy_setopt(curl, CURLOPT_URL, "imap://imap.example.com/" + "INBOX/;UID=1"); - /* Tell the multi stack about our easy handle */ - curl_multi_add_handle(mcurl, curl); + /* Tell the multi stack about our easy handle */ + curl_multi_add_handle(multi, curl); - do { - CURLMcode mc = curl_multi_perform(mcurl, &still_running); + do { + CURLMcode mresult = curl_multi_perform(multi, &still_running); - if(still_running) - /* wait for activity, timeout or "nothing" */ - mc = curl_multi_poll(mcurl, NULL, 0, 1000, NULL); + if(still_running) + /* wait for activity, timeout or "nothing" */ + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); - if(mc) - break; - } while(still_running); + if(mresult) + break; + } while(still_running); + + /* Always cleanup */ + curl_multi_remove_handle(multi, curl); + curl_multi_cleanup(multi); + } + curl_easy_cleanup(curl); + } - /* Always cleanup */ - curl_multi_remove_handle(mcurl, curl); - curl_multi_cleanup(mcurl); - curl_easy_cleanup(curl); curl_global_cleanup(); return 0; diff --git a/docs/examples/imap-noop.c b/docs/examples/imap-noop.c index 9e5a3da2d5..5d8153e931 100644 --- a/docs/examples/imap-noop.c +++ b/docs/examples/imap-noop.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Perform an IMAP noop * */ - #include + #include /* This is a simple example showing how to perform a noop using libcurl's IMAP @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -47,23 +49,25 @@ int main(void) curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); - /* This is just the server URL */ + /* This is the server URL */ curl_easy_setopt(curl, CURLOPT_URL, "imap://imap.example.com"); /* Set the NOOP command */ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "NOOP"); /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/imap-search.c b/docs/examples/imap-search.c index 141b06f649..ae944bb9ec 100644 --- a/docs/examples/imap-search.c +++ b/docs/examples/imap-search.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Search for new IMAP emails * */ - #include + #include /* This is a simple example showing how to search for new messages using @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -54,20 +56,22 @@ int main(void) * this can contain a message sequence set and a number of search criteria * keywords including flags such as ANSWERED, DELETED, DRAFT, FLAGGED, NEW, * RECENT and SEEN. For more information about the search criteria please - * see RFC-3501 section 6.4.4. */ + * see RFC-3501 section 6.4.4. */ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "SEARCH NEW"); /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/imap-ssl.c b/docs/examples/imap-ssl.c index e632c30e86..77a0880a32 100644 --- a/docs/examples/imap-ssl.c +++ b/docs/examples/imap-ssl.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * IMAP with implicit SSL * */ - #include + #include /* This is a simple example showing how to fetch mail using libcurl's IMAP @@ -40,7 +39,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -49,7 +51,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); /* This fetches message 1 from the user's inbox. Note the use of - * imaps:// rather than imap:// to request an SSL based connection. */ + * imaps:// rather than imap:// to request an SSL based connection. */ curl_easy_setopt(curl, CURLOPT_URL, "imaps://imap.example.com/INBOX/;UID=1"); @@ -65,7 +67,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); #endif - /* If the site you are connecting to uses a different host name that what + /* If the site you are connecting to uses a different hostname than what * they have mentioned in their server certificate's commonName (or * subjectAltName) fields, libcurl refuses to connect. You can skip this * check, but it makes the connection insecure. */ @@ -79,16 +81,18 @@ int main(void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Perform the fetch */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/imap-store.c b/docs/examples/imap-store.c index d04a6072cc..c1e9de9a91 100644 --- a/docs/examples/imap-store.c +++ b/docs/examples/imap-store.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Modify the properties of an email over IMAP * */ - #include + #include /* This is a simple example showing how to modify an existing mail using @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -56,29 +58,31 @@ int main(void) curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "STORE 1 +Flags \\Deleted"); /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); else { /* Set the EXPUNGE command, although you can use the CLOSE command if you * do not want to know the result of the STORE */ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "EXPUNGE"); /* Perform the second custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); } /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/imap-tls.c b/docs/examples/imap-tls.c index 8fbc96bb55..486bc8c467 100644 --- a/docs/examples/imap-tls.c +++ b/docs/examples/imap-tls.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * IMAP using TLS * */ - #include + #include /* This is a simple example showing how to fetch mail using libcurl's IMAP @@ -40,7 +39,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -79,16 +81,18 @@ int main(void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Perform the fetch */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/interface.c b/docs/examples/interface.c index f1a2016ced..870f94057a 100644 --- a/docs/examples/interface.c +++ b/docs/examples/interface.c @@ -26,12 +26,16 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -42,11 +46,13 @@ int main(void) curl_easy_setopt(curl, CURLOPT_INTERFACE, "enp3s0"); curl_easy_setopt(curl, CURLOPT_URL, "https://curl.se/"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/ipv6.c b/docs/examples/ipv6.c index 1b698705d0..607d9d4096 100644 --- a/docs/examples/ipv6.c +++ b/docs/examples/ipv6.c @@ -26,12 +26,16 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -39,10 +43,12 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "https://curl.se/"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/keepalive.c b/docs/examples/keepalive.c index e06d7ff37b..f6aa692897 100644 --- a/docs/examples/keepalive.c +++ b/docs/examples/keepalive.c @@ -26,12 +26,16 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -49,10 +53,12 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "https://curl.se/"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/localport.c b/docs/examples/localport.c index 7e88ce48a7..1027c4387f 100644 --- a/docs/examples/localport.c +++ b/docs/examples/localport.c @@ -26,12 +26,16 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -43,11 +47,13 @@ int main(void) curl_easy_setopt(curl, CURLOPT_LOCALPORTRANGE, 10L); curl_easy_setopt(curl, CURLOPT_URL, "https://curl.se/"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/log_failed_transfers.c b/docs/examples/log_failed_transfers.c new file mode 100644 index 0000000000..3279703176 --- /dev/null +++ b/docs/examples/log_failed_transfers.c @@ -0,0 +1,337 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 + * + ***************************************************************************/ +/* + * Save failed transfer verbose log to disk + * + */ +/* + * + * This example demonstrates per-transfer verbose logging to memory. + * The transfer's log is written to disk only if the transfer fails. + * + */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen(), strerror(), vsnprintf() */ +#endif +#endif + +#include +#include +#include +#include +#include + +#include + +#ifdef _WIN32 +#include +#define unlink _unlink +#else +#include +#include +#endif + +struct mem { + /* 'buf' points to memory contents that is always null-terminated so that it + can be treated like a string if appropriate. 'recent' points to the most + recent data written to 'buf'. */ + char *buf, *recent; + /* 'len' and 'allocsize' are the length and allocated size of 'buf' */ + size_t len, allocsize; +}; + +struct transfer { + const char *url, *bodyfile, *logfile; + struct mem log; + FILE *bodyfp; + CURL *curl; +}; + +static void mem_reset(struct mem *mem) +{ + free(mem->buf); + mem->buf = NULL; + mem->recent = NULL; + mem->len = 0; + mem->allocsize = 0; +} + +/* expand free buffer space to needed size. return -1 or 'needed'. */ +static int mem_need(struct mem *mem, size_t needed) +{ + char *newbuf; + size_t newsize; + + if(needed > (unsigned)INT_MAX) + return -1; + + if(needed <= (mem->allocsize - mem->len)) + return (int)needed; + + /* min 4k makes reallocations much less frequent when lengths are small */ + newsize = needed < 4096 ? 4096 : needed; + + newsize += mem->len; + + if(newsize < mem->len || newsize > (unsigned)INT_MAX) + return -1; + + newbuf = realloc(mem->buf, newsize); + + if(!newbuf) + return -1; + + if(mem->recent && mem->buf != newbuf) + mem->recent = newbuf + (mem->recent - mem->buf); + + mem->buf = newbuf; + mem->allocsize = newsize; + + return (int)needed; +} + +static int mem_addn(struct mem *mem, const char *buf, size_t len) +{ + if(len + 1 < len || mem_need(mem, len + 1) < 0) + return -1; + mem->recent = mem->buf + mem->len; + memcpy(mem->recent, buf, len); + mem->len += len; + mem->buf[mem->len] = '\0'; + return (int)len; +} + +static int mem_add(struct mem *mem, const char *str) +{ + return mem_addn(mem, str, strlen(str)); +} + +#if defined(__GNUC__) || defined(__clang__) +__attribute__((format(printf, 2, 3))) +#endif +static int mem_addf(struct mem *mem, const char *format, ...) +{ + int i, x; + va_list va; + + /* we need about 100 chars or less to write 95% of lines */ + x = 128; + + /* first try: there is probably enough memory to write everything. + second try: there is definitely enough memory to write everything. */ + for(i = 0; i < 2; ++i) { + if(x < 0 || mem_need(mem, (size_t)x + 1) < 0) + break; + + va_start(va, format); + x = vsnprintf(mem->buf + mem->len, mem->allocsize - mem->len, format, va); + va_end(va); + + if(x >= 0 && (size_t)x < (mem->allocsize - mem->len)) { + mem->recent = mem->buf + mem->len; + mem->len += (size_t)x; + return x; + } + +#ifdef _WIN32 + /* Not all versions of Windows CRT vsnprintf are compliant with C99. Some + return -1 if buffer too small. Try _vscprintf to get the needed size. */ + if(!i && x < 0) { + va_start(va, format); + x = _vscprintf(format, va); + va_end(va); + } +#endif + } + + if(mem->buf) + mem->buf[mem->len] = '\0'; + return -1; +} + +static int mydebug(CURL *curl, curl_infotype type, + char *data, size_t size, void *userdata) +{ + struct transfer *t = (struct transfer *)userdata; + static const char s_infotype[CURLINFO_END][3] = { + "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; + + (void)curl; + + switch(type) { + case CURLINFO_TEXT: + case CURLINFO_HEADER_OUT: + case CURLINFO_HEADER_IN: + /* mem_addn is faster than passing large data as %s to mem_addf */ + mem_addn(&t->log, s_infotype[type], 2); + mem_addn(&t->log, data, size); + if(!size || data[size - 1] != '\n') + mem_add(&t->log, "\n"); + break; + default: + break; + } + + return 0; +} + +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) +{ + struct transfer *t = (struct transfer *)userdata; + + return fwrite(ptr, size, nmemb, t->bodyfp); +} + +int main(void) +{ + CURLcode result; + unsigned i; + int total_failed = 0; + char errbuf[CURL_ERROR_SIZE] = { 0, }; + struct transfer transfer[2]; + + memset(transfer, 0, sizeof(transfer)); + + transfer[0].url = "https://httpbin.org/get"; + transfer[0].bodyfile = "200.txt"; + transfer[0].logfile = "200_transfer_log.txt"; + + transfer[1].url = "https://httpbin.org/status/400"; + transfer[1].bodyfile = "400.txt"; + transfer[1].logfile = "400_transfer_log.txt"; + + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) { + fprintf(stderr, "curl_global_init failed\n"); + return (int)result; + } + + /* You could enable global tracing for extra verbosity when verbosity is + enabled for a transfer. */ +#if 0 + curl_global_trace("all"); +#endif + + for(i = 0; i < sizeof(transfer) / sizeof(transfer[0]); ++i) { + int failed = 0; + struct transfer *t = &transfer[i]; + + t->curl = curl_easy_init(); + + if(!t->curl) { + fprintf(stderr, "curl_easy_init failed\n"); + curl_global_cleanup(); + return 1; + } + + curl_easy_setopt(t->curl, CURLOPT_URL, t->url); + + /* Enable following redirects */ + curl_easy_setopt(t->curl, CURLOPT_FOLLOWLOCATION, 1L); + + /* Enable verbose logging to memory */ + curl_easy_setopt(t->curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(t->curl, CURLOPT_DEBUGFUNCTION, mydebug); + curl_easy_setopt(t->curl, CURLOPT_DEBUGDATA, t); + + /* Enable writing the body to a file */ + curl_easy_setopt(t->curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(t->curl, CURLOPT_WRITEDATA, t); + + /* Enable immediate error on HTTP status codes >= 400 in most cases, + instead of downloading the body to a file */ + curl_easy_setopt(t->curl, CURLOPT_FAILONERROR, 1L); + + /* Enable detailed error messages */ + curl_easy_setopt(t->curl, CURLOPT_ERRORBUFFER, errbuf); + + mem_addf(&t->log, "Downloading %s to file %s\n", t->url, t->bodyfile); + printf("%s", t->log.recent); + + /* Create the body file */ + t->bodyfp = fopen(t->bodyfile, "wb"); + + if(t->bodyfp) { + /* Perform the transfer */ + result = curl_easy_perform(t->curl); + + /* Save the body file */ + fclose(t->bodyfp); + t->bodyfp = NULL; + + if(result == CURLE_OK) { + /* You could retrieve more information about the transfer here via + curl_easy_getinfo and mark the transfer as failed if needed. */ + mem_addf(&t->log, "Transfer successful.\n"); + fprintf(stderr, "%s", t->log.recent); + failed = 0; + } + else { + mem_addf(&t->log, "Transfer failed: (%d) %s\n", result, + (errbuf[0] ? errbuf : curl_easy_strerror(result))); + fprintf(stderr, "%s", t->log.recent); + failed = 1; + } + } + else { + mem_addf(&t->log, "Failed to create body output file %s: %s\n", + t->bodyfile, strerror(errno)); + fprintf(stderr, "%s", t->log.recent); + failed = 1; + } + + if(failed) { + FILE *fp = fopen(t->logfile, "wb"); + + if(fp && t->log.len == fwrite(t->log.buf, 1, t->log.len, fp)) + fprintf(stderr, "Transfer log written to %s\n", t->logfile); + else { + fprintf(stderr, "Failed to write transfer log to %s: %s\n", + t->logfile, strerror(errno)); + } + + if(fp) + fclose(fp); + + /* Depending on how the transfer failed a body file may or may not have + been written, and you may or may not want it. */ + unlink(t->bodyfile); + + ++total_failed; + } + + mem_reset(&t->log); + + curl_easy_cleanup(t->curl); + + t->curl = NULL; + + printf("\n"); + } + + curl_global_cleanup(); + + return total_failed ? 1 : 0; +} diff --git a/docs/examples/maxconnects.c b/docs/examples/maxconnects.c index 2e8e5b50a8..7ef29828ea 100644 --- a/docs/examples/maxconnects.c +++ b/docs/examples/maxconnects.c @@ -26,23 +26,28 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { - const char *urls[] = { "https://example.com", - "https://curl.se", + const char *urls[] = { + "https://example.com/", + "https://curl.se/", "https://www.example/", NULL /* end of list */ }; int i = 0; - /* Change the maximum number of persistent connection */ + /* Change the maximum number of persistent connection */ curl_easy_setopt(curl, CURLOPT_MAXCONNECTS, 1L); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); @@ -51,16 +56,19 @@ int main(void) while(urls[i]) { curl_easy_setopt(curl, CURLOPT_URL, urls[i]); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); i++; } /* always cleanup */ curl_easy_cleanup(curl); } + + curl_global_cleanup(); + return 0; } diff --git a/docs/examples/multi-app.c b/docs/examples/multi-app.c index 5bbf580f82..4a1f3b1f97 100644 --- a/docs/examples/multi-app.c +++ b/docs/examples/multi-app.c @@ -26,90 +26,100 @@ * transfers in parallel. * */ - #include #include -/* curl stuff */ #include /* * Download an HTTP file and upload an FTP file simultaneously. */ -#define HANDLECOUNT 2 /* Number of simultaneous transfers */ #define HTTP_HANDLE 0 /* Index for the HTTP transfer */ -#define FTP_HANDLE 1 /* Index for the FTP transfer */ +#define FTP_HANDLE 1 /* Index for the FTP transfer */ +#define HANDLECOUNT 2 /* Number of simultaneous transfers */ int main(void) { - CURL *handles[HANDLECOUNT]; - CURLM *multi_handle; + CURL *curl[HANDLECOUNT]; + CURLM *multi; - int still_running = 1; /* keep number of running handles */ int i; - CURLMsg *msg; /* for picking up messages with the transfer status */ - int msgs_left; /* how many messages are left */ + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; /* Allocate one curl handle per transfer */ for(i = 0; i < HANDLECOUNT; i++) - handles[i] = curl_easy_init(); + curl[i] = curl_easy_init(); /* set the options (I left out a few, you get the point anyway) */ - curl_easy_setopt(handles[HTTP_HANDLE], CURLOPT_URL, "https://example.com"); + curl_easy_setopt(curl[HTTP_HANDLE], CURLOPT_URL, "https://example.com"); - curl_easy_setopt(handles[FTP_HANDLE], CURLOPT_URL, "ftp://example.com"); - curl_easy_setopt(handles[FTP_HANDLE], CURLOPT_UPLOAD, 1L); + curl_easy_setopt(curl[FTP_HANDLE], CURLOPT_URL, "ftp://example.com"); + curl_easy_setopt(curl[FTP_HANDLE], CURLOPT_UPLOAD, 1L); /* init a multi stack */ - multi_handle = curl_multi_init(); + multi = curl_multi_init(); + if(multi) { - /* add the individual transfers */ - for(i = 0; i < HANDLECOUNT; i++) - curl_multi_add_handle(multi_handle, handles[i]); + int still_running = 1; /* keep number of running handles */ - while(still_running) { - CURLMcode mc = curl_multi_perform(multi_handle, &still_running); + CURLMsg *msg; /* for picking up messages with the transfer status */ + int msgs_left; /* how many messages are left */ - if(still_running) - /* wait for activity, timeout or "nothing" */ - mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); + /* add the individual transfers */ + for(i = 0; i < HANDLECOUNT; i++) + curl_multi_add_handle(multi, curl[i]); - if(mc) - break; - } - /* See how the transfers went */ - /* !checksrc! disable EQUALSNULL 1 */ - while((msg = curl_multi_info_read(multi_handle, &msgs_left)) != NULL) { - if(msg->msg == CURLMSG_DONE) { - int idx; + while(still_running) { + CURLMcode mresult = curl_multi_perform(multi, &still_running); - /* Find out which handle this message is about */ - for(idx = 0; idx < HANDLECOUNT; idx++) { - int found = (msg->easy_handle == handles[idx]); - if(found) + if(still_running) + /* wait for activity, timeout or "nothing" */ + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); + + if(mresult) + break; + } + + /* See how the transfers went */ + /* !checksrc! disable EQUALSNULL 1 */ + while((msg = curl_multi_info_read(multi, &msgs_left)) != NULL) { + if(msg->msg == CURLMSG_DONE) { + int idx; + + /* Find out which handle this message is about */ + for(idx = 0; idx < HANDLECOUNT; idx++) { + int found = (msg->easy_handle == curl[idx]); + if(found) + break; + } + + switch(idx) { + case HTTP_HANDLE: + printf("HTTP transfer completed with status %d\n", msg->data.result); break; - } - - switch(idx) { - case HTTP_HANDLE: - printf("HTTP transfer completed with status %d\n", msg->data.result); - break; - case FTP_HANDLE: - printf("FTP transfer completed with status %d\n", msg->data.result); - break; + case FTP_HANDLE: + printf("FTP transfer completed with status %d\n", msg->data.result); + break; + } } } + + /* remove the transfers */ + for(i = 0; i < HANDLECOUNT; i++) + curl_multi_remove_handle(multi, curl[i]); + + curl_multi_cleanup(multi); } - /* remove the transfers and cleanup the handles */ - for(i = 0; i < HANDLECOUNT; i++) { - curl_multi_remove_handle(multi_handle, handles[i]); - curl_easy_cleanup(handles[i]); - } + /* Free the curl handles */ + for(i = 0; i < HANDLECOUNT; i++) + curl_easy_cleanup(curl[i]); - curl_multi_cleanup(multi_handle); + curl_global_cleanup(); return 0; } diff --git a/docs/examples/multi-debugcallback.c b/docs/examples/multi-debugcallback.c index 738732279d..0885486ea9 100644 --- a/docs/examples/multi-debugcallback.c +++ b/docs/examples/multi-debugcallback.c @@ -25,16 +25,12 @@ * multi interface and debug callback * */ - #include #include -/* curl stuff */ #include -#define TRUE 1 - -static void dump(const char *text, FILE *stream, unsigned char *ptr, +static void dump(const char *text, const unsigned char *ptr, size_t size, char nohex) { size_t i; @@ -46,20 +42,20 @@ static void dump(const char *text, FILE *stream, unsigned char *ptr, /* without the hex output, we can fit more on screen */ width = 0x40; - fprintf(stream, "%s, %10.10lu bytes (0x%8.8lx)\n", + fprintf(stderr, "%s, %lu bytes (0x%lx)\n", text, (unsigned long)size, (unsigned long)size); for(i = 0; i < size; i += width) { - fprintf(stream, "%4.4lx: ", (unsigned long)i); + fprintf(stderr, "%4.4lx: ", (unsigned long)i); if(!nohex) { /* hex not disabled, show it */ for(c = 0; c < width; c++) if(i + c < size) - fprintf(stream, "%02x ", ptr[i + c]); + fprintf(stderr, "%02x ", ptr[i + c]); else - fputs(" ", stream); + fputs(" ", stderr); } for(c = 0; (c < width) && (i + c < size); c++) { @@ -69,29 +65,26 @@ static void dump(const char *text, FILE *stream, unsigned char *ptr, i += (c + 2 - width); break; } - fprintf(stream, "%c", + fprintf(stderr, "%c", (ptr[i + c] >= 0x20) && (ptr[i + c] < 0x80) ? ptr[i + c] : '.'); - /* check again for 0D0A, to avoid an extra \n if it's at width */ + /* check again for 0D0A, to avoid an extra \n if it is at width */ if(nohex && (i + c + 2 < size) && ptr[i + c + 1] == 0x0D && ptr[i + c + 2] == 0x0A) { i += (c + 3 - width); break; } } - fputc('\n', stream); /* newline */ + fputc('\n', stderr); /* newline */ } - fflush(stream); } -static -int my_trace(CURL *handle, curl_infotype type, - unsigned char *data, size_t size, - void *userp) +static int my_trace(CURL *curl, curl_infotype type, + char *data, size_t size, void *userp) { const char *text; (void)userp; - (void)handle; + (void)curl; switch(type) { case CURLINFO_TEXT: @@ -113,49 +106,60 @@ int my_trace(CURL *handle, curl_infotype type, return 0; } - dump(text, stderr, data, size, TRUE); + dump(text, (const unsigned char *)data, size, 1); return 0; } /* - * Simply download an HTTP file. + * Download an HTTP file. */ int main(void) { - CURL *http_handle; - CURLM *multi_handle; + CURL *curl; - int still_running = 0; /* keep number of running handles */ + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - http_handle = curl_easy_init(); + curl = curl_easy_init(); + if(curl) { - /* set the options (I left out a few, you get the point anyway) */ - curl_easy_setopt(http_handle, CURLOPT_URL, "https://www.example.com/"); + CURLM *multi; - curl_easy_setopt(http_handle, CURLOPT_DEBUGFUNCTION, my_trace); - curl_easy_setopt(http_handle, CURLOPT_VERBOSE, 1L); + /* set the options (I left out a few, you get the point anyway) */ + curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); - /* init a multi stack */ - multi_handle = curl_multi_init(); + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - /* add the individual transfers */ - curl_multi_add_handle(multi_handle, http_handle); + /* init a multi stack */ + multi = curl_multi_init(); + if(multi) { - do { - CURLMcode mc = curl_multi_perform(multi_handle, &still_running); + int still_running = 0; /* keep number of running handles */ - if(still_running) - /* wait for activity, timeout or "nothing" */ - mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); + /* add the individual transfers */ + curl_multi_add_handle(multi, curl); - if(mc) - break; + do { + CURLMcode mresult = curl_multi_perform(multi, &still_running); - } while(still_running); + if(still_running) + /* wait for activity, timeout or "nothing" */ + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); - curl_multi_cleanup(multi_handle); + if(mresult) + break; - curl_easy_cleanup(http_handle); + } while(still_running); + + curl_multi_cleanup(multi); + } + + curl_easy_cleanup(curl); + } + + curl_global_cleanup(); return 0; } diff --git a/docs/examples/multi-double.c b/docs/examples/multi-double.c index 99bd736a9c..987f1c0568 100644 --- a/docs/examples/multi-double.c +++ b/docs/examples/multi-double.c @@ -28,66 +28,78 @@ #include #include -/* curl stuff */ #include /* - * Simply download two HTTP files! + * Download two HTTP files! */ int main(void) { - CURL *http_handle; - CURL *http_handle2; - CURLM *multi_handle; + CURL *curl; + CURL *curl2; - int still_running = 1; /* keep number of running handles */ + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - http_handle = curl_easy_init(); - http_handle2 = curl_easy_init(); + curl = curl_easy_init(); + curl2 = curl_easy_init(); - /* set options */ - curl_easy_setopt(http_handle, CURLOPT_URL, "https://www.example.com/"); + if(curl && curl2) { - /* set options */ - curl_easy_setopt(http_handle2, CURLOPT_URL, "http://localhost/"); + CURLM *multi; - /* init a multi stack */ - multi_handle = curl_multi_init(); + /* set options */ + curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); - /* add the individual transfers */ - curl_multi_add_handle(multi_handle, http_handle); - curl_multi_add_handle(multi_handle, http_handle2); + /* set options */ + curl_easy_setopt(curl2, CURLOPT_URL, "http://localhost/"); - while(still_running) { - CURLMsg *msg; - int queued; - CURLMcode mc = curl_multi_perform(multi_handle, &still_running); + /* init a multi stack */ + multi = curl_multi_init(); + if(multi) { - if(still_running) - /* wait for activity, timeout or "nothing" */ - mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); + int still_running = 1; /* keep number of running handles */ - if(mc) - break; + /* add the individual transfers */ + curl_multi_add_handle(multi, curl); + curl_multi_add_handle(multi, curl2); - do { - msg = curl_multi_info_read(multi_handle, &queued); - if(msg) { - if(msg->msg == CURLMSG_DONE) { - /* a transfer ended */ - fprintf(stderr, "Transfer completed\n"); - } + while(still_running) { + CURLMsg *msg; + int queued; + + CURLMcode mresult = curl_multi_perform(multi, &still_running); + + if(still_running) + /* wait for activity, timeout or "nothing" */ + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); + + if(mresult) + break; + + do { + msg = curl_multi_info_read(multi, &queued); + if(msg) { + if(msg->msg == CURLMSG_DONE) { + /* a transfer ended */ + fprintf(stderr, "Transfer completed\n"); + } + } + } while(msg); } - } while(msg); + + curl_multi_remove_handle(multi, curl); + curl_multi_remove_handle(multi, curl2); + + curl_multi_cleanup(multi); + } } - curl_multi_remove_handle(multi_handle, http_handle); - curl_multi_remove_handle(multi_handle, http_handle2); + curl_easy_cleanup(curl); + curl_easy_cleanup(curl2); - curl_multi_cleanup(multi_handle); - - curl_easy_cleanup(http_handle); - curl_easy_cleanup(http_handle2); + curl_global_cleanup(); return 0; } diff --git a/docs/examples/multi-event.c b/docs/examples/multi-event.c index f2c3e87c2b..4c52cbe3b3 100644 --- a/docs/examples/multi-event.c +++ b/docs/examples/multi-event.c @@ -21,19 +21,19 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * multi_socket API using libevent * */ - #include #include + #include + #include static struct event_base *base; -static CURLM *curl_handle; +static CURLM *multi; static struct event *timeout; struct curl_context { @@ -41,59 +41,15 @@ struct curl_context { curl_socket_t sockfd; }; -static void curl_perform(int fd, short event, void *arg); - -static struct curl_context *create_curl_context(curl_socket_t sockfd) -{ - struct curl_context *context; - - context = (struct curl_context *) malloc(sizeof(*context)); - - context->sockfd = sockfd; - - context->event = event_new(base, sockfd, 0, curl_perform, context); - - return context; -} - -static void destroy_curl_context(struct curl_context *context) -{ - event_del(context->event); - event_free(context->event); - free(context); -} - -static void add_download(const char *url, int num) -{ - char filename[50]; - FILE *file; - CURL *handle; - - snprintf(filename, 50, "%d.download", num); - - file = fopen(filename, "wb"); - if(!file) { - fprintf(stderr, "Error opening %s\n", filename); - return; - } - - handle = curl_easy_init(); - curl_easy_setopt(handle, CURLOPT_WRITEDATA, file); - curl_easy_setopt(handle, CURLOPT_PRIVATE, file); - curl_easy_setopt(handle, CURLOPT_URL, url); - curl_multi_add_handle(curl_handle, handle); - fprintf(stderr, "Added download %s -> %s\n", url, filename); -} - static void check_multi_info(void) { char *done_url; CURLMsg *message; int pending; - CURL *easy_handle; + CURL *curl; FILE *file; - while((message = curl_multi_info_read(curl_handle, &pending))) { + while((message = curl_multi_info_read(multi, &pending))) { switch(message->msg) { case CURLMSG_DONE: /* Do not use message data after calling curl_multi_remove_handle() and @@ -101,14 +57,14 @@ static void check_multi_info(void) "WARNING: The data the returned pointer points to does not survive calling curl_multi_cleanup, curl_multi_remove_handle or curl_easy_cleanup." */ - easy_handle = message->easy_handle; + curl = message->easy_handle; - curl_easy_getinfo(easy_handle, CURLINFO_EFFECTIVE_URL, &done_url); - curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &file); + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &done_url); + curl_easy_getinfo(curl, CURLINFO_PRIVATE, &file); printf("%s DONE\n", done_url); - curl_multi_remove_handle(curl_handle, easy_handle); - curl_easy_cleanup(easy_handle); + curl_multi_remove_handle(multi, curl); + curl_easy_cleanup(curl); if(file) { fclose(file); } @@ -134,22 +90,62 @@ static void curl_perform(int fd, short event, void *arg) if(event & EV_WRITE) flags |= CURL_CSELECT_OUT; - context = (struct curl_context *) arg; + context = (struct curl_context *)arg; - curl_multi_socket_action(curl_handle, context->sockfd, flags, - &running_handles); + curl_multi_socket_action(multi, context->sockfd, flags, &running_handles); check_multi_info(); } +static struct curl_context *create_curl_context(curl_socket_t sockfd) +{ + struct curl_context *context; + + context = (struct curl_context *)malloc(sizeof(*context)); + + context->sockfd = sockfd; + + context->event = event_new(base, sockfd, 0, curl_perform, context); + + return context; +} + +static void destroy_curl_context(struct curl_context *context) +{ + event_del(context->event); + event_free(context->event); + free(context); +} + +static void add_download(const char *url, int num) +{ + char filename[50]; + FILE *file; + CURL *curl; + + snprintf(filename, sizeof(filename), "%d.download", num); + + file = fopen(filename, "wb"); + if(!file) { + fprintf(stderr, "Error opening %s\n", filename); + return; + } + + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); + curl_easy_setopt(curl, CURLOPT_PRIVATE, file); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_multi_add_handle(multi, curl); + fprintf(stderr, "Added download %s -> %s\n", url, filename); +} + static void on_timeout(evutil_socket_t fd, short events, void *arg) { int running_handles; (void)fd; (void)events; (void)arg; - curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0, - &running_handles); + curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &running_handles); check_multi_info(); } @@ -172,23 +168,23 @@ static int start_timeout(CURLM *multi, long timeout_ms, void *userp) return 0; } -static int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, +static int handle_socket(CURL *curl, curl_socket_t s, int action, void *userp, void *socketp) { struct curl_context *curl_context; int events = 0; - (void)easy; + (void)curl; (void)userp; switch(action) { case CURL_POLL_IN: case CURL_POLL_OUT: case CURL_POLL_INOUT: - curl_context = socketp ? - (struct curl_context *) socketp : create_curl_context(s); + curl_context = + socketp ? (struct curl_context *)socketp : create_curl_context(s); - curl_multi_assign(curl_handle, s, (void *) curl_context); + curl_multi_assign(multi, s, (void *)curl_context); if(action != CURL_POLL_IN) events |= EV_WRITE; @@ -199,15 +195,15 @@ static int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, event_del(curl_context->event); event_assign(curl_context->event, base, curl_context->sockfd, - (short)events, curl_perform, curl_context); + (short)events, curl_perform, curl_context); event_add(curl_context->event, NULL); break; case CURL_POLL_REMOVE: if(socketp) { - event_del(((struct curl_context*) socketp)->event); - destroy_curl_context((struct curl_context*) socketp); - curl_multi_assign(curl_handle, s, NULL); + event_del(((struct curl_context *)socketp)->event); + destroy_curl_context((struct curl_context *)socketp); + curl_multi_assign(multi, s, NULL); } break; default: @@ -217,30 +213,35 @@ static int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, return 0; } -int main(int argc, char **argv) +int main(int argc, const char **argv) { + CURLcode result; + if(argc <= 1) return 0; - if(curl_global_init(CURL_GLOBAL_ALL)) { + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) { fprintf(stderr, "Could not init curl\n"); - return 1; + return (int)result; } base = event_base_new(); timeout = evtimer_new(base, on_timeout, NULL); - curl_handle = curl_multi_init(); - curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket); - curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout); + multi = curl_multi_init(); + if(multi) { + curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, handle_socket); + curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, start_timeout); - while(argc-- > 1) { - add_download(argv[argc], argc); + while(argc-- > 1) { + add_download(argv[argc], argc); + } + + event_base_dispatch(base); + + curl_multi_cleanup(multi); } - - event_base_dispatch(base); - - curl_multi_cleanup(curl_handle); event_free(timeout); event_base_free(base); diff --git a/docs/examples/multi-formadd.c b/docs/examples/multi-formadd.c index 58c7e641c4..8816d69171 100644 --- a/docs/examples/multi-formadd.c +++ b/docs/examples/multi-formadd.c @@ -25,12 +25,10 @@ * using the multi interface to do a multipart formpost without blocking * */ - /* - * Warning: this example uses the deprecated form api. See "multi-post.c" - * for a similar example using the mime api. + * Warning: this example uses the deprecated form API. See "multi-post.c" + * for a similar example using the mime API. */ - #include #include @@ -40,17 +38,18 @@ int main(void) { CURL *curl; - CURLM *multi_handle; - int still_running = 0; - struct curl_httppost *formpost = NULL; struct curl_httppost *lastptr = NULL; struct curl_slist *headerlist = NULL; static const char buf[] = "Expect:"; + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + CURL_IGNORE_DEPRECATION( /* Fill in the file upload field. This makes libcurl load data from - the given file name when curl_easy_perform() is called. */ + the given filename when curl_easy_perform() is called. */ curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "sendfile", @@ -72,49 +71,59 @@ int main(void) CURLFORM_END); ) - curl = curl_easy_init(); - multi_handle = curl_multi_init(); - /* initialize custom header list (stating that Expect: 100-continue is not wanted */ headerlist = curl_slist_append(headerlist, buf); - if(curl && multi_handle) { - /* what URL that receives this POST */ - curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/upload.cgi"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl = curl_easy_init(); + if(curl) { + CURLM *multi; - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); - CURL_IGNORE_DEPRECATION( - curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); - ) + multi = curl_multi_init(); + if(multi) { - curl_multi_add_handle(multi_handle, curl); + int still_running = 0; - do { - CURLMcode mc = curl_multi_perform(multi_handle, &still_running); + /* what URL that receives this POST */ + curl_easy_setopt(curl, CURLOPT_URL, + "https://www.example.com/upload.cgi"); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - if(still_running) - /* wait for activity, timeout or "nothing" */ - mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); + CURL_IGNORE_DEPRECATION( + curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); + ) - if(mc) - break; + curl_multi_add_handle(multi, curl); - } while(still_running); + do { + CURLMcode mresult = curl_multi_perform(multi, &still_running); - curl_multi_cleanup(multi_handle); + if(still_running) + /* wait for activity, timeout or "nothing" */ + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); + + if(mresult) + break; + + } while(still_running); + + curl_multi_cleanup(multi); + } /* always cleanup */ curl_easy_cleanup(curl); - - CURL_IGNORE_DEPRECATION( - /* then cleanup the formpost chain */ - curl_formfree(formpost); - ) - - /* free slist */ - curl_slist_free_all(headerlist); } + + CURL_IGNORE_DEPRECATION( + /* then cleanup the formpost chain */ + curl_formfree(formpost); + ) + + /* free slist */ + curl_slist_free_all(headerlist); + + curl_global_cleanup(); + return 0; } diff --git a/docs/examples/multi-legacy.c b/docs/examples/multi-legacy.c index b0c37ea8d5..a0580c6712 100644 --- a/docs/examples/multi-legacy.c +++ b/docs/examples/multi-legacy.c @@ -26,7 +26,6 @@ * transfers in parallel without curl_multi_wait/poll. * */ - #include #include @@ -36,156 +35,165 @@ #include #endif -/* curl stuff */ #include /* * Download an HTTP file and upload an FTP file simultaneously. */ -#define HANDLECOUNT 2 /* Number of simultaneous transfers */ -#define HTTP_HANDLE 0 /* Index for the HTTP transfer */ -#define FTP_HANDLE 1 /* Index for the FTP transfer */ +#define HTTP_HANDLE 0 /* Index for the HTTP transfer */ +#define FTP_HANDLE 1 /* Index for the FTP transfer */ +#define HANDLECOUNT 2 /* Number of simultaneous transfers */ int main(void) { - CURL *handles[HANDLECOUNT]; - CURLM *multi_handle; + CURL *curl[HANDLECOUNT]; + CURLM *multi; - int still_running = 0; /* keep number of running handles */ int i; - CURLMsg *msg; /* for picking up messages with the transfer status */ - int msgs_left; /* how many messages are left */ + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; /* Allocate one curl handle per transfer */ for(i = 0; i < HANDLECOUNT; i++) - handles[i] = curl_easy_init(); + curl[i] = curl_easy_init(); /* set the options (I left out a few, you get the point anyway) */ - curl_easy_setopt(handles[HTTP_HANDLE], CURLOPT_URL, "https://example.com"); + curl_easy_setopt(curl[HTTP_HANDLE], CURLOPT_URL, "https://example.com"); - curl_easy_setopt(handles[FTP_HANDLE], CURLOPT_URL, "ftp://example.com"); - curl_easy_setopt(handles[FTP_HANDLE], CURLOPT_UPLOAD, 1L); + curl_easy_setopt(curl[FTP_HANDLE], CURLOPT_URL, "ftp://example.com"); + curl_easy_setopt(curl[FTP_HANDLE], CURLOPT_UPLOAD, 1L); /* init a multi stack */ - multi_handle = curl_multi_init(); + multi = curl_multi_init(); + if(multi) { - /* add the individual transfers */ - for(i = 0; i < HANDLECOUNT; i++) - curl_multi_add_handle(multi_handle, handles[i]); + int still_running = 0; /* keep number of running handles */ - /* we start some action by calling perform right away */ - curl_multi_perform(multi_handle, &still_running); + CURLMsg *msg; /* for picking up messages with the transfer status */ + int msgs_left; /* how many messages are left */ - while(still_running) { - struct timeval timeout; - int rc; /* select() return code */ - CURLMcode mc; /* curl_multi_fdset() return code */ + /* add the individual transfers */ + for(i = 0; i < HANDLECOUNT; i++) + curl_multi_add_handle(multi, curl[i]); - fd_set fdread; - fd_set fdwrite; - fd_set fdexcep; - int maxfd = -1; + /* we start some action by calling perform right away */ + curl_multi_perform(multi, &still_running); - long curl_timeo = -1; + while(still_running) { - FD_ZERO(&fdread); - FD_ZERO(&fdwrite); - FD_ZERO(&fdexcep); + struct timeval timeout; + int rc; /* select() return code */ + CURLMcode mresult; /* curl_multi_fdset() return code */ - /* set a suitable timeout to play around with */ - timeout.tv_sec = 1; - timeout.tv_usec = 0; + fd_set fdread; + fd_set fdwrite; + fd_set fdexcep; + int maxfd = -1; - curl_multi_timeout(multi_handle, &curl_timeo); - if(curl_timeo >= 0) { + long curl_timeo = -1; + + FD_ZERO(&fdread); + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); + + /* set a suitable timeout to play around with */ + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + curl_multi_timeout(multi, &curl_timeo); + if(curl_timeo >= 0) { #if defined(MSDOS) || defined(__AMIGA__) - timeout.tv_sec = (time_t)(curl_timeo / 1000); + timeout.tv_sec = (time_t)(curl_timeo / 1000); #else - timeout.tv_sec = curl_timeo / 1000; + timeout.tv_sec = curl_timeo / 1000; #endif - if(timeout.tv_sec > 1) - timeout.tv_sec = 1; - else + if(timeout.tv_sec > 1) + timeout.tv_sec = 1; + else #if defined(MSDOS) || defined(__AMIGA__) - timeout.tv_usec = (time_t)(curl_timeo % 1000) * 1000; + timeout.tv_usec = (time_t)(curl_timeo % 1000) * 1000; #else - timeout.tv_usec = (int)(curl_timeo % 1000) * 1000; + timeout.tv_usec = (int)(curl_timeo % 1000) * 1000; #endif - } + } - /* get file descriptors from the transfers */ - mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd); + /* get file descriptors from the transfers */ + mresult = curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd); - if(mc != CURLM_OK) { - fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc); - break; - } + if(mresult != CURLM_OK) { + fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mresult); + break; + } - /* On success the value of maxfd is guaranteed to be >= -1. We call - select(maxfd + 1, ...); specially in case of (maxfd == -1) there are - no fds ready yet so we call select(0, ...) --or Sleep() on Windows-- - to sleep 100ms, which is the minimum suggested value in the - curl_multi_fdset() doc. */ + /* On success the value of maxfd is guaranteed to be >= -1. We call + select(maxfd + 1, ...); specially in case of (maxfd == -1) there are + no fds ready yet so we call select(0, ...) --or Sleep() on Windows-- + to sleep 100ms, which is the minimum suggested value in the + curl_multi_fdset() doc. */ - if(maxfd == -1) { + if(maxfd == -1) { #ifdef _WIN32 - Sleep(100); - rc = 0; + Sleep(100); + rc = 0; #else - /* Portable sleep for platforms other than Windows. */ - struct timeval wait = {0}; - wait.tv_usec = 100 * 1000; /* 100ms */ - rc = select(0, NULL, NULL, NULL, &wait); + /* Portable sleep for platforms other than Windows. */ + struct timeval wait = { 0 }; + wait.tv_usec = 100 * 1000; /* 100ms */ + rc = select(0, NULL, NULL, NULL, &wait); #endif - } - else { - /* Note that on some platforms 'timeout' may be modified by select(). - If you need access to the original value save a copy beforehand. */ - rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); + } + else { + /* Note that on some platforms 'timeout' may be modified by select(). + If you need access to the original value save a copy beforehand. */ + rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); + } + + switch(rc) { + case -1: + /* select error */ + break; + case 0: /* timeout */ + default: /* action */ + curl_multi_perform(multi, &still_running); + break; + } } - switch(rc) { - case -1: - /* select error */ - break; - case 0: /* timeout */ - default: /* action */ - curl_multi_perform(multi_handle, &still_running); - break; - } - } + /* See how the transfers went */ + /* !checksrc! disable EQUALSNULL 1 */ + while((msg = curl_multi_info_read(multi, &msgs_left)) != NULL) { + if(msg->msg == CURLMSG_DONE) { + int idx; - /* See how the transfers went */ - /* !checksrc! disable EQUALSNULL 1 */ - while((msg = curl_multi_info_read(multi_handle, &msgs_left)) != NULL) { - if(msg->msg == CURLMSG_DONE) { - int idx; + /* Find out which handle this message is about */ + for(idx = 0; idx < HANDLECOUNT; idx++) { + int found = (msg->easy_handle == curl[idx]); + if(found) + break; + } - /* Find out which handle this message is about */ - for(idx = 0; idx < HANDLECOUNT; idx++) { - int found = (msg->easy_handle == handles[idx]); - if(found) + switch(idx) { + case HTTP_HANDLE: + printf("HTTP transfer completed with status %d\n", msg->data.result); break; - } - - switch(idx) { - case HTTP_HANDLE: - printf("HTTP transfer completed with status %d\n", msg->data.result); - break; - case FTP_HANDLE: - printf("FTP transfer completed with status %d\n", msg->data.result); - break; + case FTP_HANDLE: + printf("FTP transfer completed with status %d\n", msg->data.result); + break; + } } } - } - curl_multi_cleanup(multi_handle); + curl_multi_cleanup(multi); + } /* Free the curl handles */ for(i = 0; i < HANDLECOUNT; i++) - curl_easy_cleanup(handles[i]); + curl_easy_cleanup(curl[i]); + + curl_global_cleanup(); return 0; } diff --git a/docs/examples/multi-post.c b/docs/examples/multi-post.c index 84af48f4bf..b5d557ce8a 100644 --- a/docs/examples/multi-post.c +++ b/docs/examples/multi-post.c @@ -25,7 +25,6 @@ * using the multi interface to do a multipart formpost without blocking * */ - #include #include @@ -33,72 +32,82 @@ int main(void) { - CURL *curl; - - CURLM *multi_handle; - int still_running = 0; - curl_mime *form = NULL; curl_mimepart *field = NULL; struct curl_slist *headerlist = NULL; static const char buf[] = "Expect:"; + CURL *curl; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + curl = curl_easy_init(); - multi_handle = curl_multi_init(); + if(curl) { + CURLM *multi; - if(curl && multi_handle) { - /* Create the form */ - form = curl_mime_init(curl); + multi = curl_multi_init(); + if(multi) { + int still_running = 0; - /* Fill in the file upload field */ - field = curl_mime_addpart(form); - curl_mime_name(field, "sendfile"); - curl_mime_filedata(field, "multi-post.c"); + /* Create the form */ + form = curl_mime_init(curl); - /* Fill in the filename field */ - field = curl_mime_addpart(form); - curl_mime_name(field, "filename"); - curl_mime_data(field, "multi-post.c", CURL_ZERO_TERMINATED); + /* Fill in the file upload field */ + field = curl_mime_addpart(form); + curl_mime_name(field, "sendfile"); + curl_mime_filedata(field, "multi-post.c"); - /* Fill in the submit field too, even if this is rarely needed */ - field = curl_mime_addpart(form); - curl_mime_name(field, "submit"); - curl_mime_data(field, "send", CURL_ZERO_TERMINATED); + /* Fill in the filename field */ + field = curl_mime_addpart(form); + curl_mime_name(field, "filename"); + curl_mime_data(field, "multi-post.c", CURL_ZERO_TERMINATED); - /* initialize custom header list (stating that Expect: 100-continue is not - wanted */ - headerlist = curl_slist_append(headerlist, buf); + /* Fill in the submit field too, even if this is rarely needed */ + field = curl_mime_addpart(form); + curl_mime_name(field, "submit"); + curl_mime_data(field, "send", CURL_ZERO_TERMINATED); - /* what URL that receives this POST */ - curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/upload.cgi"); - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + /* initialize custom header list (stating that Expect: 100-continue is + not wanted */ + headerlist = curl_slist_append(headerlist, buf); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); - curl_easy_setopt(curl, CURLOPT_MIMEPOST, form); + /* what URL that receives this POST */ + curl_easy_setopt(curl, CURLOPT_URL, + "https://www.example.com/upload.cgi"); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_multi_add_handle(multi_handle, curl); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); + curl_easy_setopt(curl, CURLOPT_MIMEPOST, form); - do { - CURLMcode mc = curl_multi_perform(multi_handle, &still_running); + curl_multi_add_handle(multi, curl); - if(still_running) - /* wait for activity, timeout or "nothing" */ - mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); + do { + CURLMcode mresult = curl_multi_perform(multi, &still_running); - if(mc) - break; - } while(still_running); + if(still_running) + /* wait for activity, timeout or "nothing" */ + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); - curl_multi_cleanup(multi_handle); + if(mresult) + break; + } while(still_running); + + curl_multi_cleanup(multi); + } /* always cleanup */ curl_easy_cleanup(curl); - - /* then cleanup the form */ - curl_mime_free(form); - - /* free slist */ - curl_slist_free_all(headerlist); } + + /* then cleanup the form */ + curl_mime_free(form); + + /* free slist */ + curl_slist_free_all(headerlist); + + curl_global_cleanup(); + return 0; } diff --git a/docs/examples/multi-single.c b/docs/examples/multi-single.c index 0ead96f487..6329dd4d38 100644 --- a/docs/examples/multi-single.c +++ b/docs/examples/multi-single.c @@ -29,50 +29,57 @@ #include #include -/* curl stuff */ #include /* - * Simply download an HTTP file. + * Download an HTTP file. */ int main(void) { - CURL *http_handle; - CURLM *multi_handle; - int still_running = 1; /* keep number of running handles */ + CURL *curl; - curl_global_init(CURL_GLOBAL_DEFAULT); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - http_handle = curl_easy_init(); + curl = curl_easy_init(); + if(curl) { - /* set the options (I left out a few, you get the point anyway) */ - curl_easy_setopt(http_handle, CURLOPT_URL, "https://www.example.com/"); + CURLM *multi; + int still_running = 1; /* keep number of running handles */ - /* init a multi stack */ - multi_handle = curl_multi_init(); + /* set the options (I left out a few, you get the point anyway) */ + curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); - /* add the individual transfers */ - curl_multi_add_handle(multi_handle, http_handle); + /* init a multi stack */ + multi = curl_multi_init(); + if(multi) { - do { - CURLMcode mc = curl_multi_perform(multi_handle, &still_running); + /* add the individual transfers */ + curl_multi_add_handle(multi, curl); - if(!mc) - /* wait for activity, timeout or "nothing" */ - mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL); + do { + CURLMcode mresult = curl_multi_perform(multi, &still_running); - if(mc) { - fprintf(stderr, "curl_multi_poll() failed, code %d.\n", (int)mc); - break; + if(!mresult) + /* wait for activity, timeout or "nothing" */ + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); + + if(mresult) { + fprintf(stderr, "curl_multi_poll() failed, code %d.\n", + (int)mresult); + break; + } + + } while(still_running); + + curl_multi_remove_handle(multi, curl); + + curl_multi_cleanup(multi); } - } while(still_running); - - curl_multi_remove_handle(multi_handle, http_handle); - - curl_easy_cleanup(http_handle); - - curl_multi_cleanup(multi_handle); + curl_easy_cleanup(curl); + } curl_global_cleanup(); diff --git a/docs/examples/multi-uv.c b/docs/examples/multi-uv.c index 1a61745dfa..8d6227fc7e 100644 --- a/docs/examples/multi-uv.c +++ b/docs/examples/multi-uv.c @@ -21,22 +21,25 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * multi_socket API using libuv * */ /* Use the socket_action interface to download multiple files in parallel, - powered by libuv. + * powered by libuv. + * + * Requires libuv and (of course) libcurl. + * + * See https://docs.libuv.org/en/v1.x/index.html libuv API documentation + */ - Requires libuv and (of course) libcurl. - - See https://docs.libuv.org/en/v1.x/index.html libuv API documentation -*/ +/* Requires: USE_LIBUV */ #include #include + #include + #include /* object to pass to the callbacks */ @@ -57,7 +60,7 @@ static struct curl_context *create_curl_context(curl_socket_t sockfd, { struct curl_context *context; - context = (struct curl_context *) malloc(sizeof(*context)); + context = (struct curl_context *)malloc(sizeof(*context)); context->sockfd = sockfd; context->uv = uv; @@ -70,22 +73,22 @@ static struct curl_context *create_curl_context(curl_socket_t sockfd, static void curl_close_cb(uv_handle_t *handle) { - struct curl_context *context = (struct curl_context *) handle->data; + struct curl_context *context = (struct curl_context *)handle->data; free(context); } static void destroy_curl_context(struct curl_context *context) { - uv_close((uv_handle_t *) &context->poll_handle, curl_close_cb); + uv_close((uv_handle_t *)&context->poll_handle, curl_close_cb); } static void add_download(const char *url, int num, CURLM *multi) { char filename[50]; FILE *file; - CURL *handle; + CURL *curl; - snprintf(filename, 50, "%d.download", num); + snprintf(filename, sizeof(filename), "%d.download", num); file = fopen(filename, "wb"); if(!file) { @@ -93,23 +96,23 @@ static void add_download(const char *url, int num, CURLM *multi) return; } - handle = curl_easy_init(); - curl_easy_setopt(handle, CURLOPT_WRITEDATA, file); - curl_easy_setopt(handle, CURLOPT_PRIVATE, file); - curl_easy_setopt(handle, CURLOPT_URL, url); - curl_multi_add_handle(multi, handle); + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); + curl_easy_setopt(curl, CURLOPT_PRIVATE, file); + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_multi_add_handle(multi, curl); fprintf(stderr, "Added download %s -> %s\n", url, filename); } -static void check_multi_info(struct curl_context *context) +static void check_multi_info(struct datauv *uv) { char *done_url; CURLMsg *message; int pending; - CURL *easy_handle; + CURL *curl; FILE *file; - while((message = curl_multi_info_read(context->uv->multi, &pending))) { + while((message = curl_multi_info_read(uv->multi, &pending))) { switch(message->msg) { case CURLMSG_DONE: /* Do not use message data after calling curl_multi_remove_handle() and @@ -117,14 +120,14 @@ static void check_multi_info(struct curl_context *context) "WARNING: The data the returned pointer points to does not survive calling curl_multi_cleanup, curl_multi_remove_handle or curl_easy_cleanup." */ - easy_handle = message->easy_handle; + curl = message->easy_handle; - curl_easy_getinfo(easy_handle, CURLINFO_EFFECTIVE_URL, &done_url); - curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &file); + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &done_url); + curl_easy_getinfo(curl, CURLINFO_PRIVATE, &file); printf("%s DONE\n", done_url); - curl_multi_remove_handle(context->uv->multi, easy_handle); - curl_easy_cleanup(easy_handle); + curl_multi_remove_handle(uv->multi, curl); + curl_easy_cleanup(curl); if(file) { fclose(file); } @@ -142,7 +145,7 @@ static void on_uv_socket(uv_poll_t *req, int status, int events) { int running_handles; int flags = 0; - struct curl_context *context = (struct curl_context *) req->data; + struct curl_context *context = (struct curl_context *)req->data; (void)status; if(events & UV_READABLE) flags |= CURL_CSELECT_IN; @@ -151,25 +154,27 @@ static void on_uv_socket(uv_poll_t *req, int status, int events) curl_multi_socket_action(context->uv->multi, context->sockfd, flags, &running_handles); - check_multi_info(context); + check_multi_info(context->uv); } /* callback from libuv when timeout expires */ static void on_uv_timeout(uv_timer_t *req) { - struct curl_context *context = (struct curl_context *) req->data; - if(context) { - int running_handles; - curl_multi_socket_action(context->uv->multi, CURL_SOCKET_TIMEOUT, 0, - &running_handles); - check_multi_info(context); - } + /* get the datauv struct from the timer handle */ + struct datauv *uv = (struct datauv *)req; + int running_handles; + + curl_multi_socket_action(uv->multi, CURL_SOCKET_TIMEOUT, 0, + &running_handles); + + if(running_handles) + check_multi_info(uv); } /* callback from libcurl to update the timeout expiry */ -static int cb_timeout(CURLM *multi, long timeout_ms, - struct datauv *uv) +static int cb_timeout(CURLM *multi, long timeout_ms, void *userp) { + struct datauv *uv = (struct datauv *)userp; (void)multi; if(timeout_ms < 0) uv_timer_stop(&uv->timeout); @@ -184,22 +189,22 @@ static int cb_timeout(CURLM *multi, long timeout_ms, } /* callback from libcurl to update socket activity to wait for */ -static int cb_socket(CURL *easy, curl_socket_t s, int action, - struct datauv *uv, - void *socketp) +static int cb_socket(CURL *curl, curl_socket_t s, int action, + void *userp, void *socketp) { + struct datauv *uv = (struct datauv *)userp; struct curl_context *curl_context; int events = 0; - (void)easy; + (void)curl; switch(action) { case CURL_POLL_IN: case CURL_POLL_OUT: case CURL_POLL_INOUT: - curl_context = socketp ? - (struct curl_context *) socketp : create_curl_context(s, uv); + curl_context = + socketp ? (struct curl_context *)socketp : create_curl_context(s, uv); - curl_multi_assign(uv->multi, s, (void *) curl_context); + curl_multi_assign(uv->multi, s, (void *)curl_context); if(action != CURL_POLL_IN) events |= UV_WRITABLE; @@ -210,8 +215,8 @@ static int cb_socket(CURL *easy, curl_socket_t s, int action, break; case CURL_POLL_REMOVE: if(socketp) { - uv_poll_stop(&((struct curl_context*)socketp)->poll_handle); - destroy_curl_context((struct curl_context*) socketp); + uv_poll_stop(&((struct curl_context *)socketp)->poll_handle); + destroy_curl_context((struct curl_context *)socketp); curl_multi_assign(uv->multi, s, NULL); } break; @@ -222,33 +227,40 @@ static int cb_socket(CURL *easy, curl_socket_t s, int action, return 0; } -int main(int argc, char **argv) +int main(int argc, const char **argv) { + CURLcode result; struct datauv uv = { 0 }; int running_handles; if(argc <= 1) return 0; - curl_global_init(CURL_GLOBAL_ALL); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; uv.loop = uv_default_loop(); uv_timer_init(uv.loop, &uv.timeout); uv.multi = curl_multi_init(); - curl_multi_setopt(uv.multi, CURLMOPT_SOCKETFUNCTION, cb_socket); - curl_multi_setopt(uv.multi, CURLMOPT_SOCKETDATA, &uv); - curl_multi_setopt(uv.multi, CURLMOPT_TIMERFUNCTION, cb_timeout); - curl_multi_setopt(uv.multi, CURLMOPT_TIMERDATA, &uv); + if(uv.multi) { + curl_multi_setopt(uv.multi, CURLMOPT_SOCKETFUNCTION, cb_socket); + curl_multi_setopt(uv.multi, CURLMOPT_SOCKETDATA, &uv); + curl_multi_setopt(uv.multi, CURLMOPT_TIMERFUNCTION, cb_timeout); + curl_multi_setopt(uv.multi, CURLMOPT_TIMERDATA, &uv); - while(argc-- > 1) { - add_download(argv[argc], argc, uv.multi); + while(argc-- > 1) { + add_download(argv[argc], argc, uv.multi); + } + + /* kickstart the thing */ + curl_multi_socket_action(uv.multi, CURL_SOCKET_TIMEOUT, 0, + &running_handles); + uv_run(uv.loop, UV_RUN_DEFAULT); + curl_multi_cleanup(uv.multi); } - - /* kickstart the thing */ - curl_multi_socket_action(uv.multi, CURL_SOCKET_TIMEOUT, 0, &running_handles); - uv_run(uv.loop, UV_RUN_DEFAULT); - curl_multi_cleanup(uv.multi); + curl_global_cleanup(); return 0; } diff --git a/docs/examples/multithread.c b/docs/examples/multithread.c deleted file mode 100644 index ceee94022a..0000000000 --- a/docs/examples/multithread.c +++ /dev/null @@ -1,96 +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 - * - ***************************************************************************/ -/* - * A multi-threaded program using pthreads to fetch several files at once - * - */ - -#include -#include -#include - -#define NUMT 4 - -/* - List of URLs to fetch. - - If you intend to use an SSL-based protocol here you might need to setup TLS - library mutex callbacks as described here: - - https://curl.se/libcurl/c/threadsafe.html - -*/ -static const char * const urls[NUMT]= { - "https://curl.se/", - "ftp://example.com/", - "https://example.net/", - "www.example" -}; - -static void *pull_one_url(void *url) -{ - CURL *curl; - - curl = curl_easy_init(); - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_perform(curl); /* ignores error */ - curl_easy_cleanup(curl); - - return NULL; -} - - -/* - int pthread_create(pthread_t *new_thread_ID, - const pthread_attr_t *attr, - void * (*start_func)(void *), void *arg); -*/ - -int main(void) -{ - pthread_t tid[NUMT]; - int i; - - /* Must initialize libcurl before any threads are started */ - curl_global_init(CURL_GLOBAL_ALL); - - for(i = 0; i < NUMT; i++) { - int error = pthread_create(&tid[i], - NULL, /* default attributes please */ - pull_one_url, - (void *)urls[i]); - if(error) - fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error); - else - fprintf(stderr, "Thread %d, gets %s\n", i, urls[i]); - } - - /* now wait for all threads to terminate */ - for(i = 0; i < NUMT; i++) { - pthread_join(tid[i], NULL); - fprintf(stderr, "Thread %d terminated\n", i); - } - curl_global_cleanup(); - return 0; -} diff --git a/docs/examples/netrc.c b/docs/examples/netrc.c index 42e1b6341e..d862210445 100644 --- a/docs/examples/netrc.c +++ b/docs/examples/netrc.c @@ -26,24 +26,29 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); - curl_easy_setopt(curl, CURLOPT_NETRC_FILE, - "/home/daniel/s3cr3ts.txt"); + curl_easy_setopt(curl, CURLOPT_NETRC_FILE, "/home/daniel/s3cr3ts.txt"); curl_easy_setopt(curl, CURLOPT_URL, "https://curl.se/"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/parseurl.c b/docs/examples/parseurl.c index 8675adc623..f70df19bef 100644 --- a/docs/examples/parseurl.c +++ b/docs/examples/parseurl.c @@ -26,6 +26,7 @@ * */ #include + #include #if !CURL_AT_LEAST_VERSION(7, 62, 0) @@ -51,7 +52,7 @@ int main(void) /* extract hostname from the parsed URL */ uc = curl_url_get(h, CURLUPART_HOST, &host, 0); if(!uc) { - printf("Host name: %s\n", host); + printf("Hostname: %s\n", host); curl_free(host); } diff --git a/docs/examples/persistent.c b/docs/examples/persistent.c index be5e8c33e6..852f923dd1 100644 --- a/docs/examples/persistent.c +++ b/docs/examples/persistent.c @@ -32,9 +32,10 @@ int main(void) { CURL *curl; - CURLcode res; - curl_global_init(CURL_GLOBAL_ALL); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -44,27 +45,29 @@ int main(void) /* get the first document */ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* get another document from the same server using the same connection */ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/docs/"); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } - return 0; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/pop3-authzid.c b/docs/examples/pop3-authzid.c index 3281b322bb..3f40e78c71 100644 --- a/docs/examples/pop3-authzid.c +++ b/docs/examples/pop3-authzid.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Retrieve emails from a shared POP3 mailbox * */ - #include + #include /* This is a simple example showing how to retrieve mail using libcurl's POP3 @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -57,16 +59,18 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "pop3://pop.example.com/1"); /* Perform the retr */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/pop3-dele.c b/docs/examples/pop3-dele.c index fe3795c245..69ee1de992 100644 --- a/docs/examples/pop3-dele.c +++ b/docs/examples/pop3-dele.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Delete POP3 emails * */ - #include + #include /* This is a simple example showing how to delete an existing mail using @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -57,16 +59,18 @@ int main(void) curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/pop3-list.c b/docs/examples/pop3-list.c index 2cd44e41cd..9a2693d3b1 100644 --- a/docs/examples/pop3-list.c +++ b/docs/examples/pop3-list.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * List the contents of a POP3 mailbox * */ - #include + #include /* This is a simple example using libcurl's POP3 capabilities to list the @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -51,16 +53,18 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "pop3://pop.example.com"); /* Perform the list */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/pop3-multi.c b/docs/examples/pop3-multi.c index 54eb7ecc32..4e69172e4b 100644 --- a/docs/examples/pop3-multi.c +++ b/docs/examples/pop3-multi.c @@ -21,14 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Get POP3 email using the multi interface * */ - #include #include + #include /* This is a simple example showing how to retrieve mail using libcurl's POP3 @@ -38,46 +37,50 @@ int main(void) { + CURLcode result; CURL *curl; - CURLM *mcurl; - int still_running = 1; - curl_global_init(CURL_GLOBAL_DEFAULT); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); - if(!curl) - return 1; + if(curl) { + CURLM *multi; - mcurl = curl_multi_init(); - if(!mcurl) - return 2; + multi = curl_multi_init(); + if(multi) { + int still_running = 1; - /* Set username and password */ - curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); - curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); + /* Set username and password */ + curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); + curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); - /* This retrieves message 1 from the user's mailbox */ - curl_easy_setopt(curl, CURLOPT_URL, "pop3://pop.example.com/1"); + /* This retrieves message 1 from the user's mailbox */ + curl_easy_setopt(curl, CURLOPT_URL, "pop3://pop.example.com/1"); - /* Tell the multi stack about our easy handle */ - curl_multi_add_handle(mcurl, curl); + /* Tell the multi stack about our easy handle */ + curl_multi_add_handle(multi, curl); - do { - CURLMcode mc = curl_multi_perform(mcurl, &still_running); + do { + CURLMcode mresult = curl_multi_perform(multi, &still_running); - if(still_running) - /* wait for activity, timeout or "nothing" */ - mc = curl_multi_poll(mcurl, NULL, 0, 1000, NULL); + if(still_running) + /* wait for activity, timeout or "nothing" */ + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); - if(mc) - break; + if(mresult) + break; - } while(still_running); + } while(still_running); + + /* Always cleanup */ + curl_multi_remove_handle(multi, curl); + curl_multi_cleanup(multi); + } + curl_easy_cleanup(curl); + } - /* Always cleanup */ - curl_multi_remove_handle(mcurl, curl); - curl_multi_cleanup(mcurl); - curl_easy_cleanup(curl); curl_global_cleanup(); return 0; diff --git a/docs/examples/pop3-noop.c b/docs/examples/pop3-noop.c index 16181d2875..c8352df402 100644 --- a/docs/examples/pop3-noop.c +++ b/docs/examples/pop3-noop.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Perform a POP3 noop * */ - #include + #include /* This is a simple example showing how to perform a noop using libcurl's POP3 @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -47,7 +49,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); - /* This is just the server URL */ + /* This is the server URL */ curl_easy_setopt(curl, CURLOPT_URL, "pop3://pop.example.com"); /* Set the NOOP command */ @@ -57,16 +59,18 @@ int main(void) curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/pop3-retr.c b/docs/examples/pop3-retr.c index 8e690f972f..c0c9e92791 100644 --- a/docs/examples/pop3-retr.c +++ b/docs/examples/pop3-retr.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Retrieve POP3 email * */ - #include + #include /* This is a simple example showing how to retrieve mail using libcurl's POP3 @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -51,16 +53,18 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "pop3://pop.example.com/1"); /* Perform the retr */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/pop3-ssl.c b/docs/examples/pop3-ssl.c index 23dd959ea0..221a0ab703 100644 --- a/docs/examples/pop3-ssl.c +++ b/docs/examples/pop3-ssl.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Get POP3 email using implicit SSL * */ - #include + #include /* This is a simple example showing how to retrieve mail using libcurl's POP3 @@ -40,7 +39,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -64,7 +66,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); #endif - /* If the site you are connecting to uses a different host name that what + /* If the site you are connecting to uses a different hostname than what * they have mentioned in their server certificate's commonName (or * subjectAltName) fields, libcurl refuses to connect. You can skip this * check, but it makes the connection insecure. */ @@ -78,16 +80,18 @@ int main(void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Perform the retr */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/pop3-stat.c b/docs/examples/pop3-stat.c index 419859bfa6..6c2d3646f4 100644 --- a/docs/examples/pop3-stat.c +++ b/docs/examples/pop3-stat.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Obtain POP3 message statistics * */ - #include + #include /* This is a simple example showing how to obtain message statistics using @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -47,7 +49,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); - /* This is just the server URL */ + /* This is the server URL */ curl_easy_setopt(curl, CURLOPT_URL, "pop3://pop.example.com"); /* Set the STAT command */ @@ -57,16 +59,18 @@ int main(void) curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/pop3-tls.c b/docs/examples/pop3-tls.c index b2f504c475..ce38d044f0 100644 --- a/docs/examples/pop3-tls.c +++ b/docs/examples/pop3-tls.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * POP3 using TLS * */ - #include + #include /* This is a simple example showing how to retrieve mail using libcurl's POP3 @@ -40,7 +39,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -78,16 +80,18 @@ int main(void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Perform the retr */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/pop3-top.c b/docs/examples/pop3-top.c index 7ceba881b2..bb23eb99b9 100644 --- a/docs/examples/pop3-top.c +++ b/docs/examples/pop3-top.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * POP3 example showing how to retrieve only the headers of an email * */ - #include + #include /* This is a simple example showing how to retrieve only the headers of a mail @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -47,23 +49,25 @@ int main(void) curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); - /* This is just the server URL */ + /* This is the server URL */ curl_easy_setopt(curl, CURLOPT_URL, "pop3://pop.example.com"); /* Set the TOP command for message 1 to only include the headers */ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "TOP 1 0"); /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/pop3-uidl.c b/docs/examples/pop3-uidl.c index 496e5b08d4..fb211093d8 100644 --- a/docs/examples/pop3-uidl.c +++ b/docs/examples/pop3-uidl.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * List the contents of a POP3 mailbox by unique ID * */ - #include + #include /* This is a simple example using libcurl's POP3 capabilities to list the @@ -39,7 +38,10 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -47,23 +49,25 @@ int main(void) curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); - /* This is just the server URL */ + /* This is the server URL */ curl_easy_setopt(curl, CURLOPT_URL, "pop3://pop.example.com"); /* Set the UIDL command */ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "UIDL"); /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/post-callback.c b/docs/examples/post-callback.c index fa0c575e65..729642cbce 100644 --- a/docs/examples/post-callback.c +++ b/docs/examples/post-callback.c @@ -27,11 +27,13 @@ */ #include #include + #include /* silly test data to POST */ -static const char data[]="Lorem ipsum dolor sit amet, consectetur adipiscing " - "elit. Sed vel urna neque. Ut quis leo metus. Quisque eleifend, ex at " +static const char data[] = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Sed vel urna neque. Ut quis leo metus. Quisque eleifend, ex at " "laoreet rhoncus, odio ipsum semper metus, at tempus ante urna in mauris. " "Suspendisse ornare tempor venenatis. Ut dui neque, pellentesque a ______ " "eget, mattis vitae ligula. Fusce ut pharetra est. Ut ullamcorper mi ac " @@ -43,10 +45,10 @@ struct WriteThis { size_t sizeleft; }; -static size_t read_callback(char *dest, size_t size, size_t nmemb, void *userp) +static size_t read_cb(char *dest, size_t size, size_t nmemb, void *userp) { struct WriteThis *wt = (struct WriteThis *)userp; - size_t buffer_size = size*nmemb; + size_t buffer_size = size * nmemb; if(wt->sizeleft) { /* copy as much as possible from the source to the destination */ @@ -60,13 +62,13 @@ static size_t read_callback(char *dest, size_t size, size_t nmemb, void *userp) return copy_this_much; /* we copied this many bytes */ } - return 0; /* no more data left to deliver */ + return 0; /* no more data left to deliver */ } int main(void) { CURL *curl; - CURLcode res; + CURLcode result; struct WriteThis wt; @@ -74,12 +76,12 @@ int main(void) wt.sizeleft = strlen(data); /* In Windows, this inits the Winsock stuff */ - res = curl_global_init(CURL_GLOBAL_DEFAULT); + result = curl_global_init(CURL_GLOBAL_DEFAULT); /* Check for errors */ - if(res != CURLE_OK) { + if(result != CURLE_OK) { fprintf(stderr, "curl_global_init() failed: %s\n", - curl_easy_strerror(res)); - return 1; + curl_easy_strerror(result)); + return (int)result; } /* get a curl handle */ @@ -92,7 +94,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_POST, 1L); /* we want to use our own read function */ - curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); /* pointer to pass to our read function */ curl_easy_setopt(curl, CURLOPT_READDATA, &wt); @@ -112,7 +114,7 @@ int main(void) struct curl_slist *chunk = NULL; chunk = curl_slist_append(chunk, "Transfer-Encoding: chunked"); - res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); + result = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); /* use curl_slist_free_all() after the *perform() call to free this list again */ } @@ -135,18 +137,18 @@ int main(void) struct curl_slist *chunk = NULL; chunk = curl_slist_append(chunk, "Expect:"); - res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); + result = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); /* use curl_slist_free_all() after the *perform() call to free this list again */ } #endif - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); diff --git a/docs/examples/postinmemory.c b/docs/examples/postinmemory.c index cbdc77f75c..c4d9abcc11 100644 --- a/docs/examples/postinmemory.c +++ b/docs/examples/postinmemory.c @@ -28,6 +28,7 @@ #include #include #include + #include struct MemoryStruct { @@ -35,8 +36,7 @@ struct MemoryStruct { size_t size; }; -static size_t -WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) +static size_t write_cb(char *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct MemoryStruct *mem = (struct MemoryStruct *)userp; @@ -59,20 +59,23 @@ WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) int main(void) { CURL *curl; - CURLcode res; + CURLcode result; struct MemoryStruct chunk; static const char *postthis = "Field=1&Field=2&Field=3"; - chunk.memory = malloc(1); /* grown as needed by realloc above */ - chunk.size = 0; /* no data at this point */ + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + + chunk.memory = malloc(1); /* grown as needed by realloc above */ + chunk.size = 0; /* no data at this point */ - curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.org/"); - /* send all data to this function */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); /* we pass our 'chunk' struct to the callback function */ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); @@ -86,12 +89,12 @@ int main(void) /* if we do not provide POSTFIELDSIZE, libcurl calls strlen() by itself */ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(postthis)); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) { + if(result != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); } else { /* @@ -100,7 +103,7 @@ int main(void) * * Do something nice with it! */ - printf("%s\n",chunk.memory); + printf("%s\n", chunk.memory); } /* always cleanup */ diff --git a/docs/examples/postit2-formadd.c b/docs/examples/postit2-formadd.c index 0d9034612a..c760bcb0da 100644 --- a/docs/examples/postit2-formadd.c +++ b/docs/examples/postit2-formadd.c @@ -25,7 +25,6 @@ * HTTP Multipart formpost with file upload and two additional parts. * */ - /* * Example code that uploads a filename 'foo' to a remote script that accepts * "HTML form based" (as described in RFC 1738) uploads using HTTP POST. @@ -41,23 +40,24 @@ * * */ - #include #include #include -int main(int argc, char *argv[]) +int main(int argc, const char *argv[]) { CURL *curl; - CURLcode res; + CURLcode result; struct curl_httppost *formpost = NULL; struct curl_httppost *lastptr = NULL; struct curl_slist *headerlist = NULL; static const char buf[] = "Expect:"; - curl_global_init(CURL_GLOBAL_ALL); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; CURL_IGNORE_DEPRECATION( /* Fill in the file upload field */ @@ -74,7 +74,6 @@ int main(int argc, char *argv[]) CURLFORM_COPYCONTENTS, "postit2-formadd.c", CURLFORM_END); - /* Fill in the submit field too, even if this is rarely needed */ curl_formadd(&formpost, &lastptr, @@ -97,12 +96,12 @@ int main(int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); ) - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); @@ -115,5 +114,8 @@ int main(int argc, char *argv[]) /* free slist */ curl_slist_free_all(headerlist); } + + curl_global_cleanup(); + return 0; } diff --git a/docs/examples/postit2.c b/docs/examples/postit2.c index 0f12cd4786..0bc1e6a48f 100644 --- a/docs/examples/postit2.c +++ b/docs/examples/postit2.c @@ -37,26 +37,26 @@ * * */ - #include #include #include -int main(int argc, char *argv[]) +int main(int argc, const char *argv[]) { CURL *curl; - CURLcode res; - curl_mime *form = NULL; - curl_mimepart *field = NULL; - struct curl_slist *headerlist = NULL; - static const char buf[] = "Expect:"; - - curl_global_init(CURL_GLOBAL_ALL); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { + curl_mime *form = NULL; + curl_mimepart *field = NULL; + struct curl_slist *headerlist = NULL; + static const char buf[] = "Expect:"; + /* Create the form */ form = curl_mime_init(curl); @@ -85,12 +85,12 @@ int main(int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); curl_easy_setopt(curl, CURLOPT_MIMEPOST, form); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); @@ -100,5 +100,8 @@ int main(int argc, char *argv[]) /* free slist */ curl_slist_free_all(headerlist); } + + curl_global_cleanup(); + return 0; } diff --git a/docs/examples/progressfunc.c b/docs/examples/progressfunc.c index e164f03ca5..4138a6b63f 100644 --- a/docs/examples/progressfunc.c +++ b/docs/examples/progressfunc.c @@ -27,10 +27,11 @@ * */ #include + #include -#define MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL 3000000 -#define STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES 6000 +#define MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL 3000000 +#define STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES 6000 struct myprogress { curl_off_t lastruntime; /* type depends on version, see above */ @@ -53,14 +54,19 @@ static int xferinfo(void *p, be used */ if((curtime - myp->lastruntime) >= MINIMAL_PROGRESS_FUNCTIONALITY_INTERVAL) { myp->lastruntime = curtime; - fprintf(stderr, "TOTAL TIME: %lu.%06lu\r\n", - (unsigned long)(curtime / 1000000), - (unsigned long)(curtime % 1000000)); + fprintf(stderr, "TOTAL TIME: %" CURL_FORMAT_CURL_OFF_T + ".%06" CURL_FORMAT_CURL_OFF_T "\r\n", + curtime / 1000000, + curtime % 1000000); } - fprintf(stderr, "UP: %lu of %lu DOWN: %lu of %lu\r\n", - (unsigned long)ulnow, (unsigned long)ultotal, - (unsigned long)dlnow, (unsigned long)dltotal); + fprintf(stderr, + "UP: " + "%" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T " " + "DOWN: " + "%" CURL_FORMAT_CURL_OFF_T " of %" CURL_FORMAT_CURL_OFF_T "\r\n", + ulnow, ultotal, + dlnow, dltotal); if(dlnow > STOP_DOWNLOAD_AFTER_THIS_MANY_BYTES) return 1; @@ -70,11 +76,15 @@ static int xferinfo(void *p, int main(void) { CURL *curl; - CURLcode res = CURLE_OK; - struct myprogress prog; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { + struct myprogress prog; + prog.lastruntime = 0; prog.curl = curl; @@ -85,13 +95,14 @@ int main(void) curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &prog); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(res != CURLE_OK) - fprintf(stderr, "%s\n", curl_easy_strerror(res)); + if(result != CURLE_OK) + fprintf(stderr, "%s\n", curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/protofeats.c b/docs/examples/protofeats.c index 3e762218ae..73cbf1c221 100644 --- a/docs/examples/protofeats.c +++ b/docs/examples/protofeats.c @@ -26,18 +26,21 @@ * */ #include + #include -#if !CURL_AT_LEAST_VERSION(7,87,0) +#if !CURL_AT_LEAST_VERSION(7, 87, 0) #error "too old libcurl" #endif int main(void) { curl_version_info_data *ver; - const char *const *ptr; + const char * const *ptr; - curl_global_init(CURL_GLOBAL_ALL); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; ver = curl_version_info(CURLVERSION_NOW); printf("Protocols:\n"); diff --git a/docs/examples/range.c b/docs/examples/range.c index c8229fca4d..857ee92600 100644 --- a/docs/examples/range.c +++ b/docs/examples/range.c @@ -30,16 +30,21 @@ int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://curl.se/"); curl_easy_setopt(curl, CURLOPT_RANGE, "200-999"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/resolve.c b/docs/examples/resolve.c index 6514e93d8d..9c87bc0086 100644 --- a/docs/examples/resolve.c +++ b/docs/examples/resolve.c @@ -27,12 +27,12 @@ * */ #include + #include int main(void) { CURL *curl; - CURLcode res = CURLE_OK; /* Each single name resolve string should be written using the format HOST:PORT:ADDRESS where HOST is the name libcurl tries to resolve, PORT @@ -42,17 +42,22 @@ int main(void) struct curl_slist *host = curl_slist_append(NULL, "example.com:443:127.0.0.1"); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_RESOLVE, host); curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* always cleanup */ curl_easy_cleanup(curl); } curl_slist_free_all(host); + curl_global_cleanup(); - return (int)res; + return (int)result; } diff --git a/docs/examples/rtsp-options.c b/docs/examples/rtsp-options.c index d1ddbf01ff..c7800460b4 100644 --- a/docs/examples/rtsp-options.c +++ b/docs/examples/rtsp-options.c @@ -22,16 +22,20 @@ * ***************************************************************************/ /* - * Very simple RTSP request sending OPTIONS. + * Simple RTSP request sending OPTIONS. * */ #include + #include int main(void) { CURL *curl; - CURLcode res; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -41,15 +45,16 @@ int main(void) curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } + curl_global_cleanup(); return 0; } diff --git a/docs/examples/sendrecv.c b/docs/examples/sendrecv.c index 8cbb5b0722..a8d0697028 100644 --- a/docs/examples/sendrecv.c +++ b/docs/examples/sendrecv.c @@ -25,20 +25,17 @@ * Demonstrate curl_easy_send() and curl_easy_recv() usage. * */ - #include #include + #include /* Avoid warning in FD_SET() with pre-2020 Cygwin/MSYS releases: * warning: conversion to 'long unsigned int' from 'curl_socket_t' {aka 'int'} * may change the sign of the result [-Wsign-conversion] */ -#ifdef __GNUC__ +#ifdef __GNUC__ /* keep outside functions and without push/pop for GCC <4.6 */ #pragma GCC diagnostic ignored "-Wsign-conversion" -#ifdef __DJGPP__ -#pragma GCC diagnostic ignored "-Warith-conversion" -#endif #elif defined(_MSC_VER) #pragma warning(disable:4127) /* conditional expression is constant */ #endif @@ -83,6 +80,10 @@ int main(void) const char *request = "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n"; size_t request_len = strlen(request); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + /* A general note of caution here: if you are using curl_easy_recv() or curl_easy_send() to implement HTTP or _any_ other protocol libcurl supports "natively", you are doing it wrong and you should stop. @@ -93,25 +94,24 @@ int main(void) curl = curl_easy_init(); if(curl) { - CURLcode res; curl_socket_t sockfd; size_t nsent_total = 0; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Do not do the transfer - only connect to host */ curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(res != CURLE_OK) { - printf("Error: %s\n", curl_easy_strerror(res)); + if(result != CURLE_OK) { + printf("Error: %s\n", curl_easy_strerror(result)); return 1; } /* Extract the socket from the curl handle - we need it for waiting. */ - res = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd); + result = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd); - if(res != CURLE_OK) { - printf("Error: %s\n", curl_easy_strerror(res)); + if(result != CURLE_OK) { + printf("Error: %s\n", curl_easy_strerror(result)); return 1; } @@ -124,18 +124,18 @@ int main(void) size_t nsent; do { nsent = 0; - res = curl_easy_send(curl, request + nsent_total, - request_len - nsent_total, &nsent); + result = curl_easy_send(curl, request + nsent_total, + request_len - nsent_total, &nsent); nsent_total += nsent; - if(res == CURLE_AGAIN && !wait_on_socket(sockfd, 0, 60000L)) { + if(result == CURLE_AGAIN && !wait_on_socket(sockfd, 0, 60000L)) { printf("Error: timeout.\n"); return 1; } - } while(res == CURLE_AGAIN); + } while(result == CURLE_AGAIN); - if(res != CURLE_OK) { - printf("Error: %s\n", curl_easy_strerror(res)); + if(result != CURLE_OK) { + printf("Error: %s\n", curl_easy_strerror(result)); return 1; } @@ -151,16 +151,16 @@ int main(void) size_t nread; do { nread = 0; - res = curl_easy_recv(curl, buf, sizeof(buf), &nread); + result = curl_easy_recv(curl, buf, sizeof(buf), &nread); - if(res == CURLE_AGAIN && !wait_on_socket(sockfd, 1, 60000L)) { + if(result == CURLE_AGAIN && !wait_on_socket(sockfd, 1, 60000L)) { printf("Error: timeout.\n"); return 1; } - } while(res == CURLE_AGAIN); + } while(result == CURLE_AGAIN); - if(res != CURLE_OK) { - printf("Error: %s\n", curl_easy_strerror(res)); + if(result != CURLE_OK) { + printf("Error: %s\n", curl_easy_strerror(result)); break; } @@ -175,5 +175,6 @@ int main(void) /* always cleanup */ curl_easy_cleanup(curl); } + curl_global_cleanup(); return 0; } diff --git a/docs/examples/sepheaders.c b/docs/examples/sepheaders.c index 31a3201241..c9fee6c2ba 100644 --- a/docs/examples/sepheaders.c +++ b/docs/examples/sepheaders.c @@ -25,12 +25,18 @@ * Simple HTTP GET that stores the headers in a separate file * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen() */ +#endif +#endif + #include #include #include -static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *stream) { size_t written = fwrite(ptr, size, nmemb, (FILE *)stream); return written; @@ -38,58 +44,66 @@ static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) int main(void) { - CURL *curl_handle; - static const char *headerfilename = "head.out"; - FILE *headerfile; - static const char *bodyfilename = "body.out"; - FILE *bodyfile; + CURL *curl; - curl_global_init(CURL_GLOBAL_ALL); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; /* init the curl session */ - curl_handle = curl_easy_init(); + curl = curl_easy_init(); + if(curl) { + static const char *headerfilename = "head.out"; + FILE *headerfile; + static const char *bodyfilename = "body.out"; + FILE *bodyfile; - /* set URL to get */ - curl_easy_setopt(curl_handle, CURLOPT_URL, "https://example.com"); + /* set URL to get */ + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - /* no progress meter please */ - curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L); + /* no progress meter please */ + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); - /* send all data to this function */ - curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data); + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); - /* open the header file */ - headerfile = fopen(headerfilename, "wb"); - if(!headerfile) { - curl_easy_cleanup(curl_handle); - return -1; - } + /* open the header file */ + headerfile = fopen(headerfilename, "wb"); + if(!headerfile) { + curl_easy_cleanup(curl); + curl_global_cleanup(); + return -1; + } - /* open the body file */ - bodyfile = fopen(bodyfilename, "wb"); - if(!bodyfile) { - curl_easy_cleanup(curl_handle); + /* open the body file */ + bodyfile = fopen(bodyfilename, "wb"); + if(!bodyfile) { + curl_easy_cleanup(curl); + fclose(headerfile); + curl_global_cleanup(); + return -1; + } + + /* we want the headers be written to this file handle */ + curl_easy_setopt(curl, CURLOPT_HEADERDATA, headerfile); + + /* we want the body be written to this file handle instead of stdout */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, bodyfile); + + /* get it! */ + result = curl_easy_perform(curl); + + /* close the header file */ fclose(headerfile); - return -1; + + /* close the body file */ + fclose(bodyfile); + + /* cleanup curl stuff */ + curl_easy_cleanup(curl); } - /* we want the headers be written to this file handle */ - curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, headerfile); + curl_global_cleanup(); - /* we want the body be written to this file handle instead of stdout */ - curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, bodyfile); - - /* get it! */ - curl_easy_perform(curl_handle); - - /* close the header file */ - fclose(headerfile); - - /* close the body file */ - fclose(bodyfile); - - /* cleanup curl stuff */ - curl_easy_cleanup(curl_handle); - - return 0; + return (int)result; } diff --git a/docs/examples/sessioninfo.c b/docs/examples/sessioninfo.c index 225b1ae5a5..b9150e72f5 100644 --- a/docs/examples/sessioninfo.c +++ b/docs/examples/sessioninfo.c @@ -29,33 +29,40 @@ /* Note that this example currently requires curl to be linked against GnuTLS (and this program must also be linked against -lgnutls). */ +/* Requires: USE_GNUTLS */ + +#ifndef CURL_DISABLE_DEPRECATION +#define CURL_DISABLE_DEPRECATION +#endif + #include #include + #include #include static CURL *curl; -static size_t wrfu(void *ptr, size_t size, size_t nmemb, void *stream) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *stream) { const struct curl_tlssessioninfo *info; - unsigned int cert_list_size; - const gnutls_datum_t *chainp; - CURLcode res; + CURLcode result; (void)stream; (void)ptr; - res = CURL_IGNORE_DEPRECATION( - curl_easy_getinfo(curl, CURLINFO_TLS_SESSION, &info)); + result = curl_easy_getinfo(curl, CURLINFO_TLS_SESSION, &info); + + if(result == CURLE_OK) { + unsigned int cert_list_size; + const gnutls_datum_t *chainp; - if(!res) { switch(info->backend) { case CURLSSLBACKEND_GNUTLS: /* info->internals is now the gnutls_session_t */ chainp = gnutls_certificate_get_peers(info->internals, &cert_list_size); - if((chainp) && (cert_list_size)) { + if(chainp && cert_list_size) { unsigned int i; for(i = 0; i < cert_list_size; i++) { @@ -67,7 +74,8 @@ static size_t wrfu(void *ptr, size_t size, size_t nmemb, void *stream) gnutls_x509_crt_import(cert, &chainp[i], GNUTLS_X509_FMT_DER)) { if(GNUTLS_E_SUCCESS == gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL, &dn)) { - fprintf(stderr, "Certificate #%u: %.*s", i, dn.size, dn.data); + fprintf(stderr, "Certificate #%u: %.*s", i, (int)dn.size, + dn.data); gnutls_free(dn.data); } @@ -89,25 +97,22 @@ static size_t wrfu(void *ptr, size_t size, size_t nmemb, void *stream) int main(void) { - curl_global_init(CURL_GLOBAL_DEFAULT); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); - - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, wrfu); - - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); - + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); - (void)curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/sftpget.c b/docs/examples/sftpget.c index 4d33939a40..53bc81ea6d 100644 --- a/docs/examples/sftpget.c +++ b/docs/examples/sftpget.c @@ -25,6 +25,11 @@ * Gets a file using an SFTP URL. * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen() */ +#endif +#endif #include @@ -45,8 +50,7 @@ struct FtpFile { FILE *stream; }; -static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, - void *stream) +static size_t write_cb(char *buffer, size_t size, size_t nmemb, void *stream) { struct FtpFile *out = (struct FtpFile *)stream; if(!out->stream) { @@ -58,17 +62,17 @@ static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, return fwrite(buffer, size, nmemb, out->stream); } - int main(void) { CURL *curl; - CURLcode res; struct FtpFile ftpfile = { "yourfile.bin", /* name to store the file as if successful */ NULL }; - curl_global_init(CURL_GLOBAL_DEFAULT); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -78,7 +82,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "sftp://user@server/home/user/file.txt"); /* Define our callback to get called when there is data to be written */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); /* Set a pointer to our struct to pass to the callback */ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile); @@ -92,14 +96,14 @@ int main(void) /* Switch on full protocol/debug output */ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* always cleanup */ curl_easy_cleanup(curl); - if(CURLE_OK != res) { + if(result != CURLE_OK) { /* we failed */ - fprintf(stderr, "curl told us %d\n", res); + fprintf(stderr, "curl told us %d\n", result); } } @@ -108,5 +112,5 @@ int main(void) curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/sftpuploadresume.c b/docs/examples/sftpuploadresume.c index f1f7b0c275..153883640b 100644 --- a/docs/examples/sftpuploadresume.c +++ b/docs/examples/sftpuploadresume.c @@ -25,13 +25,19 @@ * Upload to SFTP, resuming a previously aborted transfer. * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen() */ +#endif +#endif #include #include + #include /* read data to upload */ -static size_t readfunc(char *ptr, size_t size, size_t nmemb, void *stream) +static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { FILE *f = (FILE *)stream; size_t n; @@ -49,34 +55,35 @@ static size_t readfunc(char *ptr, size_t size, size_t nmemb, void *stream) */ static curl_off_t sftpGetRemoteFileSize(const char *i_remoteFile) { - CURLcode result = CURLE_GOT_NOTHING; curl_off_t remoteFileSizeByte = -1; - CURL *curlHandlePtr = curl_easy_init(); + CURL *curl = curl_easy_init(); - curl_easy_setopt(curlHandlePtr, CURLOPT_VERBOSE, 1L); + if(curl) { + CURLcode result; - curl_easy_setopt(curlHandlePtr, CURLOPT_URL, i_remoteFile); - curl_easy_setopt(curlHandlePtr, CURLOPT_NOPROGRESS, 1L); - curl_easy_setopt(curlHandlePtr, CURLOPT_NOBODY, 1L); - curl_easy_setopt(curlHandlePtr, CURLOPT_HEADER, 1L); - curl_easy_setopt(curlHandlePtr, CURLOPT_FILETIME, 1L); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - result = curl_easy_perform(curlHandlePtr); - if(CURLE_OK == result) { - result = curl_easy_getinfo(curlHandlePtr, - CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, - &remoteFileSizeByte); - if(result) - return -1; - printf("filesize: %lu\n", (unsigned long)remoteFileSizeByte); + curl_easy_setopt(curl, CURLOPT_URL, i_remoteFile); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); + curl_easy_setopt(curl, CURLOPT_HEADER, 1L); + curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); + + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, + &remoteFileSizeByte); + if(result != CURLE_OK) + return -1; + printf("filesize: %" CURL_FORMAT_CURL_OFF_T "\n", remoteFileSizeByte); + } + curl_easy_cleanup(curl); } - curl_easy_cleanup(curlHandlePtr); return remoteFileSizeByte; } - -static int sftpResumeUpload(CURL *curlhandle, const char *remotepath, +static int sftpResumeUpload(CURL *curl, const char *remotepath, const char *localpath) { FILE *f = NULL; @@ -90,24 +97,22 @@ static int sftpResumeUpload(CURL *curlhandle, const char *remotepath, f = fopen(localpath, "rb"); if(!f) { -#ifndef UNDER_CE perror(NULL); -#endif return 0; } - curl_easy_setopt(curlhandle, CURLOPT_UPLOAD, 1L); - curl_easy_setopt(curlhandle, CURLOPT_URL, remotepath); - curl_easy_setopt(curlhandle, CURLOPT_READFUNCTION, readfunc); - curl_easy_setopt(curlhandle, CURLOPT_READDATA, f); + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + curl_easy_setopt(curl, CURLOPT_URL, remotepath); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); + curl_easy_setopt(curl, CURLOPT_READDATA, f); -#if defined(_WIN32) && !defined(UNDER_CE) +#ifdef _WIN32 _fseeki64(f, remoteFileSizeByte, SEEK_SET); #else fseek(f, (long)remoteFileSizeByte, SEEK_SET); #endif - curl_easy_setopt(curlhandle, CURLOPT_APPEND, 1L); - result = curl_easy_perform(curlhandle); + curl_easy_setopt(curl, CURLOPT_APPEND, 1L); + result = curl_easy_perform(curl); fclose(f); @@ -121,18 +126,23 @@ static int sftpResumeUpload(CURL *curlhandle, const char *remotepath, int main(void) { - const char *remote = "sftp://user:pass@example.com/path/filename"; - const char *filename = "filename"; - CURL *curlhandle = NULL; + CURL *curl = NULL; - curl_global_init(CURL_GLOBAL_ALL); - curlhandle = curl_easy_init(); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - if(!sftpResumeUpload(curlhandle, remote, filename)) { - printf("resumed upload using curl %s failed\n", curl_version()); + curl = curl_easy_init(); + if(curl) { + const char *remote = "sftp://user:pass@example.com/path/filename"; + const char *filename = "filename"; + + if(!sftpResumeUpload(curl, remote, filename)) { + printf("resumed upload using curl %s failed\n", curl_version()); + } + + curl_easy_cleanup(curl); } - - curl_easy_cleanup(curlhandle); curl_global_cleanup(); return 0; diff --git a/docs/examples/shared-connection-cache.c b/docs/examples/shared-connection-cache.c index dc6805a9f1..00ebc3b759 100644 --- a/docs/examples/shared-connection-cache.c +++ b/docs/examples/shared-connection-cache.c @@ -26,21 +26,22 @@ * */ #include + #include -static void my_lock(CURL *handle, curl_lock_data data, - curl_lock_access laccess, void *useptr) +static void my_lock(CURL *curl, curl_lock_data data, curl_lock_access laccess, + void *useptr) { - (void)handle; + (void)curl; (void)data; (void)laccess; (void)useptr; fprintf(stderr, "-> Mutex lock\n"); } -static void my_unlock(CURL *handle, curl_lock_data data, void *useptr) +static void my_unlock(CURL *curl, curl_lock_data data, void *useptr) { - (void)handle; + (void)curl; (void)data; (void)useptr; fprintf(stderr, "<- Mutex unlock\n"); @@ -51,6 +52,10 @@ int main(void) CURLSH *share; int i; + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + share = curl_share_init(); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); @@ -63,19 +68,17 @@ int main(void) for(i = 0; i < 3; i++) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; - curl_easy_setopt(curl, CURLOPT_URL, "https://curl.se/"); /* use the share object */ curl_easy_setopt(curl, CURLOPT_SHARE, share); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); @@ -83,5 +86,6 @@ int main(void) } curl_share_cleanup(share); - return 0; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/simple.c b/docs/examples/simple.c index 53c8e4754f..b7b48d7777 100644 --- a/docs/examples/simple.c +++ b/docs/examples/simple.c @@ -22,16 +22,20 @@ * ***************************************************************************/ /* - * Very simple HTTP GET + * Simple HTTP GET * */ #include + #include int main(void) { CURL *curl; - CURLcode res; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -39,15 +43,16 @@ int main(void) /* example.com is redirected, so we tell libcurl to follow redirection */ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } + curl_global_cleanup(); return 0; } diff --git a/docs/examples/simplepost.c b/docs/examples/simplepost.c index 7ced982fe0..d2540151ce 100644 --- a/docs/examples/simplepost.c +++ b/docs/examples/simplepost.c @@ -22,20 +22,24 @@ * ***************************************************************************/ /* - * Very simple HTTP POST + * Simple HTTP POST * */ #include #include + #include int main(void) { - CURL *curl; - CURLcode res; - static const char *postthis = "moo mooo moo moo"; + CURL *curl; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); @@ -44,15 +48,16 @@ int main(void) /* if we do not provide POSTFIELDSIZE, libcurl calls strlen() by itself */ curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(postthis)); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } - return 0; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/simplessl.c b/docs/examples/simplessl.c index 220dc62bcd..f1c07a4ae2 100644 --- a/docs/examples/simplessl.c +++ b/docs/examples/simplessl.c @@ -25,6 +25,12 @@ * Shows HTTPS usage with client certs and optional ssl engine use. * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen() */ +#endif +#endif + #include #include @@ -40,17 +46,12 @@ 4. if you do not use a crypto engine: 4.1. set pKeyName to the filename of your client key 4.2. if the format of the key file is DER, set pKeyType to "DER" - - !! verify of the server certificate is not implemented here !! - - **** This example only works with libcurl 7.9.3 and later! **** - */ int main(void) { - CURL *curl; - CURLcode res; + CURL *curl = NULL; + CURLcode result; FILE *headerfile; const char *pPassphrase = NULL; @@ -61,94 +62,89 @@ int main(void) const char *pKeyName; const char *pKeyType; - const char *pEngine; - #ifdef USE_ENGINE - pKeyName = "rsa_test"; - pKeyType = "ENG"; - pEngine = "chil"; /* for nCipher HSM... */ + pKeyName = "rsa_test"; + pKeyType = "ENG"; #else - pKeyName = "testkey.pem"; - pKeyType = "PEM"; - pEngine = NULL; + pKeyName = "testkey.pem"; + pKeyType = "PEM"; #endif + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) { + return (int)result; + } + headerfile = fopen(pHeaderFile, "wb"); if(!headerfile) - return 1; - - curl_global_init(CURL_GLOBAL_DEFAULT); + goto error; curl = curl_easy_init(); - if(curl) { - /* what call to write: */ - curl_easy_setopt(curl, CURLOPT_URL, "HTTPS://secure.site.example"); - curl_easy_setopt(curl, CURLOPT_HEADERDATA, headerfile); + if(!curl) + goto error; -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4127) /* conditional expression is constant */ -#endif - do { /* dummy loop, just to break out from */ - if(pEngine) { - /* use crypto engine */ - if(curl_easy_setopt(curl, CURLOPT_SSLENGINE, pEngine) != CURLE_OK) { - /* load the crypto engine */ - fprintf(stderr, "cannot set crypto engine\n"); - break; - } - if(curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L) != CURLE_OK) { - /* set the crypto engine as default */ - /* only needed for the first time you load - an engine in a curl object... */ - fprintf(stderr, "cannot set crypto engine as default\n"); - break; - } - } - /* cert is stored PEM coded in file... */ - /* since PEM is default, we needn't set it for PEM */ - curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); + /* what call to write: */ + curl_easy_setopt(curl, CURLOPT_URL, "https://secure.site.example/"); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, headerfile); - /* set the cert for client authentication */ - curl_easy_setopt(curl, CURLOPT_SSLCERT, pCertFile); - - /* sorry, for engine we must set the passphrase - (if the key has one...) */ - if(pPassphrase) - curl_easy_setopt(curl, CURLOPT_KEYPASSWD, pPassphrase); - - /* if we use a key stored in a crypto engine, - we must set the key type to "ENG" */ - curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, pKeyType); - - /* set the private key (file or ID in engine) */ - curl_easy_setopt(curl, CURLOPT_SSLKEY, pKeyName); - - /* set the file with the certs validating the server */ - curl_easy_setopt(curl, CURLOPT_CAINFO, pCACertFile); - - /* disconnect if we cannot validate server's cert */ - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); - - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); - /* Check for errors */ - if(res != CURLE_OK) - fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); - - /* we are done... */ - } while(0); -#ifdef _MSC_VER -#pragma warning(pop) -#endif - /* always cleanup */ - curl_easy_cleanup(curl); +#ifdef USE_ENGINE + /* use crypto engine. nCipher HSM... */ + if(curl_easy_setopt(curl, CURLOPT_SSLENGINE, "chil") != CURLE_OK) { + /* load the crypto engine */ + fprintf(stderr, "cannot set crypto engine\n"); + goto error; } + if(curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L) != CURLE_OK) { + /* set the crypto engine as default */ + /* only needed for the first time you load + an engine in a curl object... */ + fprintf(stderr, "cannot set crypto engine as default\n"); + goto error; + } +#endif + + /* cert is stored PEM coded in file... */ + /* since PEM is default, we need not set it for PEM */ + curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); + + /* set the cert for client authentication */ + curl_easy_setopt(curl, CURLOPT_SSLCERT, pCertFile); + + /* sorry, for engine we must set the passphrase + (if the key has one...) */ + if(pPassphrase) + curl_easy_setopt(curl, CURLOPT_KEYPASSWD, pPassphrase); + + /* if we use a key stored in a crypto engine, + we must set the key type to "ENG" */ + curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, pKeyType); + + /* set the private key (file or ID in engine) */ + curl_easy_setopt(curl, CURLOPT_SSLKEY, pKeyName); + + /* set the file with the certs validating the server */ + curl_easy_setopt(curl, CURLOPT_CAINFO, pCACertFile); + + /* disconnect if we cannot validate server's cert */ + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); + /* Check for errors */ + if(result != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(result)); + +error: + + /* always cleanup */ + if(curl) + curl_easy_cleanup(curl); + + if(headerfile) + fclose(headerfile); curl_global_cleanup(); - fclose(headerfile); - - return 0; + return (int)result; } diff --git a/docs/examples/smooth-gtk-thread.c b/docs/examples/smooth-gtk-thread.c index 49a412d958..06eea1ff0e 100644 --- a/docs/examples/smooth-gtk-thread.c +++ b/docs/examples/smooth-gtk-thread.c @@ -22,8 +22,8 @@ * ***************************************************************************/ /* - * A multi threaded application that uses a progress bar to show - * status. It uses Gtk+ to make a smooth pulse. + * A multi-threaded application that uses a progress bar to show + * status. It uses Gtk+ to make a smooth pulse. * */ /* @@ -34,7 +34,6 @@ * gcc -ggdb `pkg-config --cflags --libs gtk+-2.0` -lcurl -lssl -lcrypto * -lgthread-2.0 -dl smooth-gtk-thread.c -o smooth-gtk-thread */ - #include #include #include @@ -45,10 +44,10 @@ #define NUMT 4 -pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; -int j = 0; -gint num_urls = 9; /* Just make sure this is less than urls[]*/ -const char * const urls[]= { +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +static int j = 0; +static gint num_urls = 9; /* make sure this is less than urls[] */ +static const char * const urls[] = { "90022", "90023", "90024", @@ -60,38 +59,41 @@ const char * const urls[]= { "90030" }; -size_t write_file(void *ptr, size_t size, size_t nmemb, FILE *stream) +static size_t write_cb(void *ptr, size_t size, size_t nmemb, FILE *stream) { return fwrite(ptr, size, nmemb, stream); } -static void run_one(gchar *http, int j) +static void run_one(const gchar *http, int j) { - FILE *outfile = fopen(urls[j], "wb"); CURL *curl; curl = curl_easy_init(); if(curl) { printf("j = %d\n", j); - /* Set the URL and transfer type */ - curl_easy_setopt(curl, CURLOPT_URL, http); + FILE *outfile = fopen(urls[j], "wb"); + if(outfile) { + /* Set the URL and transfer type */ + curl_easy_setopt(curl, CURLOPT_URL, http); - /* Write to the file */ - curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_file); - curl_easy_perform(curl); + /* Write to the file */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + (void)curl_easy_perform(curl); - fclose(outfile); + fclose(outfile); + } curl_easy_cleanup(curl); } } -void *pull_one_url(void *NaN) +static void *pull_one_url(void *NaN) { /* protect the reading and increasing of 'j' with a mutex */ pthread_mutex_lock(&lock); while(j < num_urls) { + gchar *http; int i = j; j++; pthread_mutex_unlock(&lock); @@ -106,11 +108,10 @@ void *pull_one_url(void *NaN) return NULL; } - -gboolean pulse_bar(gpointer data) +static gboolean pulse_bar(gpointer data) { gdk_threads_enter(); - gtk_progress_bar_pulse(GTK_PROGRESS_BAR (data)); + gtk_progress_bar_pulse(GTK_PROGRESS_BAR(data)); gdk_threads_leave(); /* Return true so the function is called again; returning false removes this @@ -119,19 +120,19 @@ gboolean pulse_bar(gpointer data) return TRUE; } -void *create_thread(void *progress_bar) +static void *create_thread(void *progress_bar) { pthread_t tid[NUMT]; int i; /* Make sure I do not create more threads than urls. */ - for(i = 0; i < NUMT && i < num_urls ; i++) { + for(i = 0; i < NUMT && i < num_urls; i++) { int error = pthread_create(&tid[i], NULL, /* default attributes please */ pull_one_url, NULL); if(error) - fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error); + fprintf(stderr, "Could not run thread number %d, errno %d\n", i, error); else fprintf(stderr, "Thread %d, gets %s\n", i, urls[i]); } @@ -151,11 +152,11 @@ void *create_thread(void *progress_bar) gtk_widget_destroy(progress_bar); /* [Un]Comment this out to kill the program rather than pushing close. */ - /* gtk_main_quit(); */ - +#if 0 + gtk_main_quit(); +#endif return NULL; - } static gboolean cb_delete(GtkWidget *window, gpointer data) @@ -164,12 +165,14 @@ static gboolean cb_delete(GtkWidget *window, gpointer data) return FALSE; } -int main(int argc, char **argv) +int main(int argc, const char **argv) { GtkWidget *top_window, *outside_frame, *inside_frame, *progress_bar; /* Must initialize libcurl before any threads are started */ - curl_global_init(CURL_GLOBAL_ALL); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; /* Init thread */ g_thread_init(NULL); @@ -194,7 +197,7 @@ int main(int argc, char **argv) /* Progress bar */ progress_bar = gtk_progress_bar_new(); - gtk_progress_bar_pulse(GTK_PROGRESS_BAR (progress_bar)); + gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progress_bar)); /* Make uniform pulsing */ gint pulse_ref = g_timeout_add(300, pulse_bar, progress_bar); g_object_set_data(G_OBJECT(progress_bar), "pulse_id", @@ -204,7 +207,7 @@ int main(int argc, char **argv) gtk_widget_show_all(top_window); printf("gtk_widget_show_all\n"); - g_signal_connect(G_OBJECT (top_window), "delete-event", + g_signal_connect(G_OBJECT(top_window), "delete-event", G_CALLBACK(cb_delete), NULL); if(!g_thread_create(&create_thread, progress_bar, FALSE, NULL) != 0) @@ -214,5 +217,7 @@ int main(int argc, char **argv) gdk_threads_leave(); printf("gdk_threads_leave\n"); + curl_global_cleanup(); + return 0; } diff --git a/docs/examples/smtp-authzid.c b/docs/examples/smtp-authzid.c index daaeab1694..fe91ba5e63 100644 --- a/docs/examples/smtp-authzid.c +++ b/docs/examples/smtp-authzid.c @@ -21,14 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Send email on behalf of another user with SMTP * */ - #include #include + #include /* @@ -41,13 +40,13 @@ /* The libcurl options want plain addresses, the viewable headers in the mail * can get a full name as well. */ -#define FROM_ADDR "" -#define SENDER_ADDR "" -#define TO_ADDR "" +#define FROM_ADDR "" +#define SENDER_ADDR "" +#define TO_ADDR "" -#define FROM_MAIL "Ursel " FROM_ADDR -#define SENDER_MAIL "Kurt " SENDER_ADDR -#define TO_MAIL "A Receiver " TO_ADDR +#define FROM_MAIL "Ursel " FROM_ADDR +#define SENDER_MAIL "Kurt " SENDER_ADDR +#define TO_MAIL "A Receiver " TO_ADDR static const char *payload_text = "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n" @@ -67,40 +66,41 @@ struct upload_status { size_t bytes_read; }; -static size_t payload_source(char *ptr, size_t size, size_t nmemb, void *userp) +static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *userp) { struct upload_status *upload_ctx = (struct upload_status *)userp; const char *data; size_t room = size * nmemb; + size_t len; - if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) { + if((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)) { return 0; } data = &payload_text[upload_ctx->bytes_read]; - if(data) { - size_t len = strlen(data); - if(room < len) - len = room; - memcpy(ptr, data, len); - upload_ctx->bytes_read += len; + len = strlen(data); + if(room < len) + len = room; + memcpy(ptr, data, len); + upload_ctx->bytes_read += len; - return len; - } - - return 0; + return len; } int main(void) { CURL *curl; - CURLcode res = CURLE_OK; - struct curl_slist *recipients = NULL; - struct upload_status upload_ctx = { 0 }; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { + struct curl_slist *recipients = NULL; + struct upload_status upload_ctx = { 0 }; + /* This is the URL for your mailserver. In this example we connect to the smtp-submission port as we require an authenticated connection. */ curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.example.com:587"); @@ -129,20 +129,20 @@ int main(void) recipients = curl_slist_append(recipients, TO_ADDR); curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); - /* We are using a callback function to specify the payload (the headers and - * body of the message). You could just use the CURLOPT_READDATA option to + /* We are using a callback function to specify the payload (the headers + * and body of the message). You can use the CURLOPT_READDATA option to * specify a FILE pointer to read from. */ - curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); /* Send the message */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Free the list of recipients */ curl_slist_free_all(recipients); @@ -158,5 +158,7 @@ int main(void) curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/smtp-expn.c b/docs/examples/smtp-expn.c index 202d1d089f..25b560f3ec 100644 --- a/docs/examples/smtp-expn.c +++ b/docs/examples/smtp-expn.c @@ -21,14 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Expand an SMTP email mailing list * */ - #include #include + #include /* This is a simple example showing how to expand an email mailing list. @@ -42,15 +41,19 @@ int main(void) { CURL *curl; - CURLcode res; - struct curl_slist *recipients = NULL; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { + struct curl_slist *recipients = NULL; + /* This is the URL for your mailserver */ curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.example.com"); - /* Note that the CURLOPT_MAIL_RCPT takes a list, not a char array */ + /* Note that the CURLOPT_MAIL_RCPT takes a list, not a char array */ recipients = curl_slist_append(recipients, "Friends"); curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); @@ -58,12 +61,12 @@ int main(void) curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "EXPN"); /* Perform the custom request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Free the list of recipients */ curl_slist_free_all(recipients); @@ -77,5 +80,7 @@ int main(void) curl_easy_cleanup(curl); } + curl_global_cleanup(); + return 0; } diff --git a/docs/examples/smtp-mail.c b/docs/examples/smtp-mail.c index 29918de7ff..b1590fd3af 100644 --- a/docs/examples/smtp-mail.c +++ b/docs/examples/smtp-mail.c @@ -21,14 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Send email with SMTP * */ - #include #include + #include /* @@ -38,9 +37,9 @@ /* The libcurl options want plain addresses, the viewable headers in the mail * can get a full name as well. */ -#define FROM_ADDR "" -#define TO_ADDR "" -#define CC_ADDR "" +#define FROM_ADDR "" +#define TO_ADDR "" +#define CC_ADDR "" #define FROM_MAIL "Sender Person " FROM_ADDR #define TO_MAIL "A Receiver " TO_ADDR @@ -64,40 +63,41 @@ struct upload_status { size_t bytes_read; }; -static size_t payload_source(char *ptr, size_t size, size_t nmemb, void *userp) +static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *userp) { struct upload_status *upload_ctx = (struct upload_status *)userp; const char *data; size_t room = size * nmemb; + size_t len; - if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) { + if((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)) { return 0; } data = &payload_text[upload_ctx->bytes_read]; - if(data) { - size_t len = strlen(data); - if(room < len) - len = room; - memcpy(ptr, data, len); - upload_ctx->bytes_read += len; + len = strlen(data); + if(room < len) + len = room; + memcpy(ptr, data, len); + upload_ctx->bytes_read += len; - return len; - } - - return 0; + return len; } int main(void) { CURL *curl; - CURLcode res = CURLE_OK; - struct curl_slist *recipients = NULL; - struct upload_status upload_ctx = { 0 }; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { + struct curl_slist *recipients = NULL; + struct upload_status upload_ctx = { 0 }; + /* This is the URL for your mailserver */ curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.example.com"); @@ -117,20 +117,20 @@ int main(void) recipients = curl_slist_append(recipients, CC_ADDR); curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); - /* We are using a callback function to specify the payload (the headers and - * body of the message). You could just use the CURLOPT_READDATA option to + /* We are using a callback function to specify the payload (the headers + * and body of the message). You can use the CURLOPT_READDATA option to * specify a FILE pointer to read from. */ - curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); /* Send the message */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Free the list of recipients */ curl_slist_free_all(recipients); @@ -146,5 +146,7 @@ int main(void) curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/smtp-mime.c b/docs/examples/smtp-mime.c index 7a2a9c618b..0ddba4ebb7 100644 --- a/docs/examples/smtp-mime.c +++ b/docs/examples/smtp-mime.c @@ -21,14 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Send SMTP mime emails * */ - #include #include + #include /* This is a simple example showing how to send mime mail using libcurl's SMTP @@ -38,9 +37,9 @@ * Note that this example requires libcurl 7.56.0 or above. */ -#define FROM "" -#define TO "" -#define CC "" +#define FROM "" +#define TO "" +#define CC "" static const char *headers_text[] = { "Date: Tue, 22 Aug 2017 14:08:43 +0100", @@ -48,7 +47,7 @@ static const char *headers_text[] = { "From: " FROM " (Example User)", "Cc: " CC " (Another example User)", "Message-ID: ", + "rfcpedant.example.org>", "Subject: example sending a MIME-formatted message", NULL }; @@ -67,11 +66,13 @@ static const char inline_html[] = "email viewers able to handle HTML.

" "\r\n"; - int main(void) { CURL *curl; - CURLcode res = CURLE_OK; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -139,12 +140,12 @@ int main(void) curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* Send the message */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Free lists. */ curl_slist_free_all(recipients); @@ -164,5 +165,7 @@ int main(void) curl_mime_free(mime); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/smtp-multi.c b/docs/examples/smtp-multi.c index 8837c57fd8..86545a1ef3 100644 --- a/docs/examples/smtp-multi.c +++ b/docs/examples/smtp-multi.c @@ -21,13 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Send SMTP email with the multi interface * */ - #include + #include /* This is an example showing how to send mail using libcurl's SMTP @@ -35,9 +34,9 @@ * libcurl's multi interface. */ -#define FROM_MAIL "" -#define TO_MAIL "" -#define CC_MAIL "" +#define FROM_MAIL "" +#define TO_MAIL "" +#define CC_MAIL "" static const char *payload_text = "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n" @@ -57,96 +56,97 @@ struct upload_status { size_t bytes_read; }; -static size_t payload_source(char *ptr, size_t size, size_t nmemb, void *userp) +static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *userp) { struct upload_status *upload_ctx = (struct upload_status *)userp; const char *data; size_t room = size * nmemb; + size_t len; - if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) { + if((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)) { return 0; } data = &payload_text[upload_ctx->bytes_read]; - if(data) { - size_t len = strlen(data); - if(room < len) - len = room; - memcpy(ptr, data, len); - upload_ctx->bytes_read += len; + len = strlen(data); + if(room < len) + len = room; + memcpy(ptr, data, len); + upload_ctx->bytes_read += len; - return len; - } - - return 0; + return len; } int main(void) { CURL *curl; - CURLM *mcurl; - int still_running = 1; - struct curl_slist *recipients = NULL; - struct upload_status upload_ctx = { 0 }; - curl_global_init(CURL_GLOBAL_DEFAULT); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); - if(!curl) - return 1; + if(curl) { + CURLM *multi; - mcurl = curl_multi_init(); - if(!mcurl) - return 2; + multi = curl_multi_init(); + if(multi) { + int still_running = 1; + struct curl_slist *recipients = NULL; + struct upload_status upload_ctx = { 0 }; - /* This is the URL for your mailserver */ - curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.example.com"); + /* This is the URL for your mailserver */ + curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.example.com"); - /* Note that this option is not strictly required, omitting it results in - * libcurl sending the MAIL FROM command with empty sender data. All - * autoresponses should have an empty reverse-path, and should be directed - * to the address in the reverse-path which triggered them. Otherwise, they - * could cause an endless loop. See RFC 5321 Section 4.5.5 for more details. - */ - curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM_MAIL); + /* Note that this option is not strictly required, omitting it results + * in libcurl sending the MAIL FROM command with empty sender data. All + * autoresponses should have an empty reverse-path, and should be + * directed to the address in the reverse-path which triggered them. + * Otherwise, they could cause an endless loop. See RFC 5321 Section + * 4.5.5 for more details. + */ + curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM_MAIL); - /* Add two recipients, in this particular case they correspond to the - * To: and Cc: addressees in the header, but they could be any kind of - * recipient. */ - recipients = curl_slist_append(recipients, TO_MAIL); - recipients = curl_slist_append(recipients, CC_MAIL); - curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); + /* Add two recipients, in this particular case they correspond to the + * To: and Cc: addressees in the header, but they could be any kind of + * recipient. */ + recipients = curl_slist_append(recipients, TO_MAIL); + recipients = curl_slist_append(recipients, CC_MAIL); + curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); - /* We are using a callback function to specify the payload (the headers and - * body of the message). You could just use the CURLOPT_READDATA option to - * specify a FILE pointer to read from. */ - curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source); - curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); - curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + /* We are using a callback function to specify the payload (the headers + * and body of the message). You can use the CURLOPT_READDATA option to + * specify a FILE pointer to read from. */ + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); + curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); - /* Tell the multi stack about our easy handle */ - curl_multi_add_handle(mcurl, curl); + /* Tell the multi stack about our easy handle */ + curl_multi_add_handle(multi, curl); - do { - CURLMcode mc = curl_multi_perform(mcurl, &still_running); + do { + CURLMcode mresult = curl_multi_perform(multi, &still_running); - if(still_running) - /* wait for activity, timeout or "nothing" */ - mc = curl_multi_poll(mcurl, NULL, 0, 1000, NULL); + if(still_running) + /* wait for activity, timeout or "nothing" */ + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); - if(mc) - break; + if(mresult) + break; - } while(still_running); + } while(still_running); - /* Free the list of recipients */ - curl_slist_free_all(recipients); + /* Free the list of recipients */ + curl_slist_free_all(recipients); + + /* Always cleanup */ + curl_multi_remove_handle(multi, curl); + curl_multi_cleanup(multi); + } + curl_easy_cleanup(curl); + } - /* Always cleanup */ - curl_multi_remove_handle(mcurl, curl); - curl_multi_cleanup(mcurl); - curl_easy_cleanup(curl); curl_global_cleanup(); return 0; diff --git a/docs/examples/smtp-ssl.c b/docs/examples/smtp-ssl.c index c88b4fe7ca..9cac9ecd52 100644 --- a/docs/examples/smtp-ssl.c +++ b/docs/examples/smtp-ssl.c @@ -21,14 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Send SMTP email using implicit SSL * */ - #include #include + #include /* This is a simple example showing how to send mail using libcurl's SMTP @@ -39,9 +38,9 @@ * Note that this example requires libcurl 7.20.0 or above. */ -#define FROM_MAIL "" -#define TO_MAIL "" -#define CC_MAIL "" +#define FROM_MAIL "" +#define TO_MAIL "" +#define CC_MAIL "" static const char *payload_text = "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n" @@ -61,40 +60,41 @@ struct upload_status { size_t bytes_read; }; -static size_t payload_source(char *ptr, size_t size, size_t nmemb, void *userp) +static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *userp) { struct upload_status *upload_ctx = (struct upload_status *)userp; const char *data; size_t room = size * nmemb; + size_t len; - if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) { + if((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)) { return 0; } data = &payload_text[upload_ctx->bytes_read]; - if(data) { - size_t len = strlen(data); - if(room < len) - len = room; - memcpy(ptr, data, len); - upload_ctx->bytes_read += len; + len = strlen(data); + if(room < len) + len = room; + memcpy(ptr, data, len); + upload_ctx->bytes_read += len; - return len; - } - - return 0; + return len; } int main(void) { CURL *curl; - CURLcode res = CURLE_OK; - struct curl_slist *recipients = NULL; - struct upload_status upload_ctx = { 0 }; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { + struct curl_slist *recipients = NULL; + struct upload_status upload_ctx = { 0 }; + /* Set username and password */ curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); @@ -115,7 +115,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); #endif - /* If the site you are connecting to uses a different host name that what + /* If the site you are connecting to uses a different hostname than what * they have mentioned in their server certificate's commonName (or * subjectAltName) fields, libcurl refuses to connect. You can skip this * check, but it makes the connection insecure. */ @@ -139,10 +139,10 @@ int main(void) recipients = curl_slist_append(recipients, CC_MAIL); curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); - /* We are using a callback function to specify the payload (the headers and - * body of the message). You could just use the CURLOPT_READDATA option to + /* We are using a callback function to specify the payload (the headers + * and body of the message). You can use the CURLOPT_READDATA option to * specify a FILE pointer to read from. */ - curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); @@ -152,12 +152,12 @@ int main(void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Send the message */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Free the list of recipients */ curl_slist_free_all(recipients); @@ -166,5 +166,7 @@ int main(void) curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/smtp-tls.c b/docs/examples/smtp-tls.c index 34a00bd548..da452318a6 100644 --- a/docs/examples/smtp-tls.c +++ b/docs/examples/smtp-tls.c @@ -21,14 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Send SMTP email using implicit TLS * */ - #include #include + #include /* This is a simple example showing how to send mail using libcurl's SMTP @@ -39,9 +38,9 @@ * Note that this example requires libcurl 7.20.0 or above. */ -#define FROM_MAIL "" -#define TO_MAIL "" -#define CC_MAIL "" +#define FROM_MAIL "" +#define TO_MAIL "" +#define CC_MAIL "" static const char *payload_text = "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n" @@ -61,40 +60,41 @@ struct upload_status { size_t bytes_read; }; -static size_t payload_source(char *ptr, size_t size, size_t nmemb, void *userp) +static size_t read_cb(char *ptr, size_t size, size_t nmemb, void *userp) { struct upload_status *upload_ctx = (struct upload_status *)userp; const char *data; size_t room = size * nmemb; + size_t len; - if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) { + if((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)) { return 0; } data = &payload_text[upload_ctx->bytes_read]; - if(data) { - size_t len = strlen(data); - if(room < len) - len = room; - memcpy(ptr, data, len); - upload_ctx->bytes_read += len; + len = strlen(data); + if(room < len) + len = room; + memcpy(ptr, data, len); + upload_ctx->bytes_read += len; - return len; - } - - return 0; + return len; } int main(void) { CURL *curl; - CURLcode res = CURLE_OK; - struct curl_slist *recipients = NULL; - struct upload_status upload_ctx = { 0 }; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { + struct curl_slist *recipients = NULL; + struct upload_status upload_ctx = { 0 }; + /* Set username and password */ curl_easy_setopt(curl, CURLOPT_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret"); @@ -141,10 +141,10 @@ int main(void) recipients = curl_slist_append(recipients, CC_MAIL); curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); - /* We are using a callback function to specify the payload (the headers and - * body of the message). You could just use the CURLOPT_READDATA option to + /* We are using a callback function to specify the payload (the headers + * and body of the message). You can use the CURLOPT_READDATA option to * specify a FILE pointer to read from. */ - curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); @@ -155,12 +155,12 @@ int main(void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Send the message */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Free the list of recipients */ curl_slist_free_all(recipients); @@ -169,5 +169,7 @@ int main(void) curl_easy_cleanup(curl); } - return (int)res; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/smtp-vrfy.c b/docs/examples/smtp-vrfy.c index 0135efef2d..65783d6903 100644 --- a/docs/examples/smtp-vrfy.c +++ b/docs/examples/smtp-vrfy.c @@ -21,14 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Verify an SMTP email address * */ - #include #include + #include /* This is a simple example showing how to verify an email address from an @@ -45,25 +44,29 @@ int main(void) { CURL *curl; - CURLcode res; - struct curl_slist *recipients = NULL; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { + struct curl_slist *recipients = NULL; + /* This is the URL for your mailserver */ curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.example.com"); - /* Note that the CURLOPT_MAIL_RCPT takes a list, not a char array */ + /* Note that the CURLOPT_MAIL_RCPT takes a list, not a char array */ recipients = curl_slist_append(recipients, ""); curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); /* Perform the VRFY */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* Free the list of recipients */ curl_slist_free_all(recipients); @@ -77,5 +80,7 @@ int main(void) curl_easy_cleanup(curl); } - return 0; + curl_global_cleanup(); + + return (int)result; } diff --git a/docs/examples/sslbackend.c b/docs/examples/sslbackend.c index fd2b57534e..e10eaaa217 100644 --- a/docs/examples/sslbackend.c +++ b/docs/examples/sslbackend.c @@ -38,9 +38,9 @@ * SSL backend has to be configured). * * **** This example only works with libcurl 7.56.0 and later! **** -*/ + */ -int main(int argc, char **argv) +int main(int argc, const char **argv) { const char *name = argc > 1 ? argv[1] : "openssl"; CURLsslset result; diff --git a/docs/examples/synctime.c b/docs/examples/synctime.c index ba5f09cb07..ed10f9d3b8 100644 --- a/docs/examples/synctime.c +++ b/docs/examples/synctime.c @@ -45,7 +45,7 @@ * so there is no way to get an accurate time. * 4. This software could only provide an accuracy of +- a few seconds, * as Round-Trip delay time is not taken into consideration. - * Compensation of network, firewall/proxy delay cannot be simply divide + * Compensation of network, firewall/proxy delay cannot be done by dividing * the Round-Trip delay time by half. * 5. Win32 SetSystemTime() API sets your computer clock according to * GMT/UTC time. Therefore your computer timezone must be properly set. @@ -55,117 +55,115 @@ * Usage: * This software synchronises your computer clock only when you issue * it with --synctime. By default, it only display the webserver's clock. - * - * Written by: Frank (contributed to libcurl) - * - * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, - * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY - * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - * - * IN NO EVENT SHALL THE AUTHOR OF THIS SOFTWARE BE LIABLE FOR - * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, - * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, - * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF - * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - * OF THIS SOFTWARE. - * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for _snprintf(), fopen(), gmtime(), + localtime(), sscanf() */ +#endif +#endif #include +#include #include + #include #ifdef _WIN32 +#if (defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0602)) || \ + defined(WINAPI_FAMILY) +# include +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \ + !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define CURL_WINDOWS_UWP +# endif #include -#else -#error "This example requires Windows." +#endif #endif - -#define MAX_STRING 256 -#define MAX_STRING1 MAX_STRING + 1 +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define snprintf _snprintf +#endif #define SYNCTIME_UA "synctime/1.0" struct conf { - char http_proxy[MAX_STRING1]; - char proxy_user[MAX_STRING1]; - char timeserver[MAX_STRING1]; + char http_proxy[256]; + char proxy_user[256]; + char timeserver[256]; }; -static const char DefaultTimeServer[3][MAX_STRING1] = -{ - "https://nist.time.gov/", - "https://www.google.com/" -}; - -static const char *DayStr[] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; -static const char *MthStr[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; - static int ShowAllHeader; static int AutoSyncTime; +#if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) static SYSTEMTIME SYSTime; static SYSTEMTIME LOCALTime; -#define HTTP_COMMAND_HEAD 0 -#define HTTP_COMMAND_GET 1 +static const char *DayStr[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; +static const char *MthStr[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; +#endif - -static size_t SyncTime_CURL_WriteOutput(void *ptr, size_t size, size_t nmemb, - void *stream) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *stream) { fwrite(ptr, size, nmemb, stream); return nmemb * size; } +/* Remember: do not assume headers are passed on null-terminated! */ static size_t SyncTime_CURL_WriteHeader(void *ptr, size_t size, size_t nmemb, void *stream) { - char TmpStr1[26], TmpStr2[26]; - (void)stream; if(ShowAllHeader == 1) - fprintf(stderr, "%s", (char *)(ptr)); + fprintf(stderr, "%.*s", (int)nmemb, (char *)ptr); - if(strncmp((char *)(ptr), "Date:", 5) == 0) { + if((nmemb >= 5) && !strncmp((const char *)ptr, "Date:", 5)) { if(ShowAllHeader == 0) - fprintf(stderr, "HTTP Server. %s", (char *)(ptr)); + fprintf(stderr, "HTTP Server. %.*s", (int)nmemb, (char *)ptr); if(AutoSyncTime == 1) { +#if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) + char TmpStr1[26], TmpStr2[26]; + int RetVal = 0; + char *field = ptr; *TmpStr1 = 0; *TmpStr2 = 0; - if(strlen((char *)(ptr)) > 50) /* Can prevent buffer overflow to - TmpStr1 & 2? */ - AutoSyncTime = 0; - else { - int RetVal = sscanf((char *)(ptr), "Date: %25s %hu %s %hu %hu:%hu:%hu", - TmpStr1, &SYSTime.wDay, TmpStr2, &SYSTime.wYear, - &SYSTime.wHour, &SYSTime.wMinute, - &SYSTime.wSecond); - - if(RetVal == 7) { - int i; - SYSTime.wMilliseconds = 500; /* adjust to midpoint, 0.5 sec */ - for(i = 0; i < 12; i++) { - if(strcmp(MthStr[i], TmpStr2) == 0) { - SYSTime.wMonth = (WORD)(i + 1); - break; - } - } - AutoSyncTime = 3; /* Computer clock is adjusted */ - } - else { - AutoSyncTime = 0; /* Error in sscanf() fields conversion */ - } + if(nmemb && (field[nmemb - 1] == '\n')) { + char header[100]; + size_t len = nmemb < sizeof(header) ? nmemb : sizeof(header) - 1; + memcpy(header, field, len); + header[len] = 0; /* null-terminate local copy */ + RetVal = sscanf(header, "Date: %25s %hu %25s %hu %hu:%hu:%hu", + TmpStr1, &SYSTime.wDay, TmpStr2, &SYSTime.wYear, + &SYSTime.wHour, &SYSTime.wMinute, + &SYSTime.wSecond); } + + if(RetVal == 7) { + int i; + SYSTime.wMilliseconds = 500; /* adjust to midpoint, 0.5 sec */ + for(i = 0; i < 12; i++) { + if(strcmp(MthStr[i], TmpStr2) == 0) { + SYSTime.wMonth = (WORD)(i + 1); + break; + } + } + AutoSyncTime = 3; /* Computer clock is adjusted */ + } + else { + AutoSyncTime = 0; /* Error in sscanf() fields conversion */ + } +#endif } } - if(strncmp((char *)(ptr), "X-Cache: HIT", 12) == 0) { + if((nmemb >= 12) && !strncmp((const char *)ptr, "X-Cache: HIT", 12)) { fprintf(stderr, "ERROR: HTTP Server data is cached." " Server Date is no longer valid.\n"); AutoSyncTime = 0; @@ -183,29 +181,20 @@ static void SyncTime_CURL_Init(CURL *curl, const char *proxy_port, curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_user_password); curl_easy_setopt(curl, CURLOPT_USERAGENT, SYNCTIME_UA); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, SyncTime_CURL_WriteOutput); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, SyncTime_CURL_WriteHeader); } -static CURLcode SyncTime_CURL_Fetch(CURL *curl, const char *URL_Str, - const char *OutFileName, int HttpGetBody) +static CURLcode SyncTime_CURL_FetchHead(CURL *curl, const char *URL_Str) { - FILE *outfile; - CURLcode res; - - outfile = NULL; - if(HttpGetBody == HTTP_COMMAND_HEAD) - curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); - else { - outfile = fopen(OutFileName, "wb"); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile); - } + CURLcode result; + curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); curl_easy_setopt(curl, CURLOPT_URL, URL_Str); - res = curl_easy_perform(curl); - if(outfile) - fclose(outfile); - return res; /* (CURLE_OK) */ + + result = curl_easy_perform(curl); + + return result; /* CURLE_OK */ } static void showUsage(void) @@ -225,36 +214,27 @@ static void showUsage(void) " port.\n"); fprintf(stderr, " --help Print this help.\n"); fprintf(stderr, "\n"); - return; } -static int conf_init(struct conf *conf) -{ - int i; - - *conf->http_proxy = 0; - for(i = 0; i < MAX_STRING1; i++) - conf->proxy_user[i] = 0; /* Clean up password from memory */ - *conf->timeserver = 0; - return 1; -} - -int main(int argc, char *argv[]) +int main(int argc, const char *argv[]) { + CURLcode result; CURL *curl; - struct conf conf[1]; + struct conf conf; int RetValue; - ShowAllHeader = 0; /* Do not show HTTP Header */ - AutoSyncTime = 0; /* Do not synchronise computer clock */ - RetValue = 0; /* Successful Exit */ - conf_init(conf); + ShowAllHeader = 0; /* Do not show HTTP Header */ + AutoSyncTime = 0; /* Do not synchronise computer clock */ + RetValue = 0; /* Successful Exit */ + + memset(&conf, 0, sizeof(conf)); if(argc > 1) { - int OptionIndex = 0; + int OptionIndex = 1; while(OptionIndex < argc) { if(strncmp(argv[OptionIndex], "--server=", 9) == 0) - snprintf(conf->timeserver, MAX_STRING, "%s", &argv[OptionIndex][9]); + snprintf(conf.timeserver, sizeof(conf.timeserver) - 1, "%s", + &argv[OptionIndex][9]); if(strcmp(argv[OptionIndex], "--showall") == 0) ShowAllHeader = 1; @@ -263,13 +243,15 @@ int main(int argc, char *argv[]) AutoSyncTime = 1; if(strncmp(argv[OptionIndex], "--proxy-user=", 13) == 0) - snprintf(conf->proxy_user, MAX_STRING, "%s", &argv[OptionIndex][13]); + snprintf(conf.proxy_user, sizeof(conf.proxy_user) - 1, "%s", + &argv[OptionIndex][13]); if(strncmp(argv[OptionIndex], "--proxy=", 8) == 0) - snprintf(conf->http_proxy, MAX_STRING, "%s", &argv[OptionIndex][8]); + snprintf(conf.http_proxy, sizeof(conf.http_proxy) - 1, "%s", + &argv[OptionIndex][8]); if((strcmp(argv[OptionIndex], "--help") == 0) || - (strcmp(argv[OptionIndex], "/?") == 0)) { + (strcmp(argv[OptionIndex], "/?") == 0)) { showUsage(); return 0; } @@ -277,11 +259,15 @@ int main(int argc, char *argv[]) } } - if(*conf->timeserver == 0) /* Use default server for time information */ - snprintf(conf->timeserver, MAX_STRING, "%s", DefaultTimeServer[0]); + if(*conf.timeserver == 0) /* Use default server for time information */ + snprintf(conf.timeserver, sizeof(conf.timeserver) - 1, "%s", + "https://www.ntp.org/"); /* Init CURL before usage */ - curl_global_init(CURL_GLOBAL_ALL); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + curl = curl_easy_init(); if(curl) { struct tm *lt; @@ -291,49 +277,47 @@ int main(int argc, char *argv[]) time_t tt_gmt; double tzonediffFloat; int tzonediffWord; - char timeBuf[61]; + char timeBuf[61] = ""; char tzoneBuf[16]; - SyncTime_CURL_Init(curl, conf->http_proxy, conf->proxy_user); + SyncTime_CURL_Init(curl, conf.http_proxy, conf.proxy_user); /* Calculating time diff between GMT and localtime */ tt = time(0); - /* !checksrc! disable BANNEDFUNC 1 */ lt = localtime(&tt); tt_local = mktime(lt); - /* !checksrc! disable BANNEDFUNC 1 */ gmt = gmtime(&tt); tt_gmt = mktime(gmt); tzonediffFloat = difftime(tt_local, tt_gmt); - tzonediffWord = (int)(tzonediffFloat/3600.0); + tzonediffWord = (int)(tzonediffFloat / 3600.0); - if((double)(tzonediffWord * 3600) == tzonediffFloat) + if(tzonediffWord == (int)(tzonediffFloat / 3600.0)) snprintf(tzoneBuf, sizeof(tzoneBuf), "%+03d'00'", tzonediffWord); else snprintf(tzoneBuf, sizeof(tzoneBuf), "%+03d'30'", tzonediffWord); +#if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) /* Get current system time and local time */ GetSystemTime(&SYSTime); GetLocalTime(&LOCALTime); snprintf(timeBuf, 60, "%s, %02d %s %04d %02d:%02d:%02d.%03d, ", DayStr[LOCALTime.wDayOfWeek], LOCALTime.wDay, - MthStr[LOCALTime.wMonth-1], LOCALTime.wYear, - LOCALTime.wHour, LOCALTime.wMinute, LOCALTime.wSecond, - LOCALTime.wMilliseconds); + MthStr[LOCALTime.wMonth - 1], LOCALTime.wYear, LOCALTime.wHour, + LOCALTime.wMinute, LOCALTime.wSecond, LOCALTime.wMilliseconds); +#endif - fprintf(stderr, "Fetch: %s\n\n", conf->timeserver); + fprintf(stderr, "Fetch: %s\n\n", conf.timeserver); fprintf(stderr, "Before HTTP. Date: %s%s\n\n", timeBuf, tzoneBuf); /* HTTP HEAD command to the Webserver */ - SyncTime_CURL_Fetch(curl, conf->timeserver, "index.htm", - HTTP_COMMAND_HEAD); + SyncTime_CURL_FetchHead(curl, conf.timeserver); +#if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) GetLocalTime(&LOCALTime); snprintf(timeBuf, 60, "%s, %02d %s %04d %02d:%02d:%02d.%03d, ", DayStr[LOCALTime.wDayOfWeek], LOCALTime.wDay, - MthStr[LOCALTime.wMonth-1], LOCALTime.wYear, - LOCALTime.wHour, LOCALTime.wMinute, LOCALTime.wSecond, - LOCALTime.wMilliseconds); + MthStr[LOCALTime.wMonth - 1], LOCALTime.wYear, LOCALTime.wHour, + LOCALTime.wMinute, LOCALTime.wSecond, LOCALTime.wMilliseconds); fprintf(stderr, "\nAfter HTTP. Date: %s%s\n", timeBuf, tzoneBuf); if(AutoSyncTime == 3) { @@ -347,16 +331,20 @@ int main(int argc, char *argv[]) GetLocalTime(&LOCALTime); snprintf(timeBuf, 60, "%s, %02d %s %04d %02d:%02d:%02d.%03d, ", DayStr[LOCALTime.wDayOfWeek], LOCALTime.wDay, - MthStr[LOCALTime.wMonth-1], LOCALTime.wYear, + MthStr[LOCALTime.wMonth - 1], LOCALTime.wYear, LOCALTime.wHour, LOCALTime.wMinute, LOCALTime.wSecond, LOCALTime.wMilliseconds); fprintf(stderr, "\nNew System's Date: %s%s\n", timeBuf, tzoneBuf); } } +#endif /* Cleanup before exit */ - conf_init(conf); + memset(&conf, 0, sizeof(conf)); curl_easy_cleanup(curl); } + + curl_global_cleanup(); + return RetValue; } diff --git a/docs/examples/threaded-ssl.c b/docs/examples/threaded.c similarity index 61% rename from docs/examples/threaded-ssl.c rename to docs/examples/threaded.c index 161182eec1..54b315bc57 100644 --- a/docs/examples/threaded-ssl.c +++ b/docs/examples/threaded.c @@ -22,69 +22,84 @@ * ***************************************************************************/ /* - * Show the required mutex callback setups for GnuTLS and OpenSSL when using - * libcurl multi-threaded. + * A multi-threaded program using pthreads to fetch several files at once * */ /* A multi-threaded example that uses pthreads and fetches 4 remote files at * once over HTTPS. * - * Recent versions of OpenSSL and GnuTLS are thread safe by design, assuming + * Recent versions of OpenSSL and GnuTLS are thread-safe by design, assuming * support for the underlying OS threading API is built-in. Older revisions * of this example demonstrated locking callbacks for the SSL library, which * are no longer necessary. An older revision with callbacks can be found at * https://github.com/curl/curl/blob/curl-7_88_1/docs/examples/threaded-ssl.c */ -#define USE_OPENSSL /* or USE_GNUTLS accordingly */ +/* Requires: HAVE_THREADS_POSIX */ +/* Also requires TLS support to run */ #include + #include + #include #define NUMT 4 -/* List of URLs to fetch.*/ -static const char * const urls[]= { - "https://www.example.com/", - "https://www2.example.com/", - "https://www3.example.com/", - "https://www4.example.com/", +/* List of URLs to fetch. */ +static const char * const urls[NUMT] = { + "https://curl.se/", + "ftp://example.com/", + "https://example.net/", + "www.example" }; -static void *pull_one_url(void *url) +struct targ { + const char *url; +}; + +static void *pull_one_url(void *p) { CURL *curl; curl = curl_easy_init(); - curl_easy_setopt(curl, CURLOPT_URL, url); - /* this example does not verify the server's certificate, which means we - might be downloading stuff from an impostor */ - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); - curl_easy_perform(curl); /* ignores error */ - curl_easy_cleanup(curl); + if(curl) { + struct targ *targ = p; + curl_easy_setopt(curl, CURLOPT_URL, targ->url); + (void)curl_easy_perform(curl); /* ignores error */ + curl_easy_cleanup(curl); + } return NULL; } -int main(int argc, char **argv) +/* + int pthread_create(pthread_t *new_thread_ID, + const pthread_attr_t *attr, + void * (*start_func)(void *), void *arg); +*/ + +int main(void) { + CURLcode result; pthread_t tid[NUMT]; + struct targ targs[NUMT]; int i; - (void)argc; - (void)argv; /* Must initialize libcurl before any threads are started */ - curl_global_init(CURL_GLOBAL_ALL); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; for(i = 0; i < NUMT; i++) { - int error = pthread_create(&tid[i], - NULL, /* default attributes please */ - pull_one_url, - (void *)urls[i]); + int error; + targs[i].url = urls[i]; + error = pthread_create(&tid[i], + NULL, /* default attributes please */ + pull_one_url, + (void *)&targs[i]); if(error) - fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error); + fprintf(stderr, "Could not run thread number %d, errno %d\n", i, error); else fprintf(stderr, "Thread %d, gets %s\n", i, urls[i]); } @@ -95,5 +110,7 @@ int main(int argc, char **argv) fprintf(stderr, "Thread %d terminated\n", i); } + curl_global_cleanup(); + return 0; } diff --git a/docs/examples/unixsocket.c b/docs/examples/unixsocket.c index 63acd4b6a2..4a8c34218e 100644 --- a/docs/examples/unixsocket.c +++ b/docs/examples/unixsocket.c @@ -26,6 +26,7 @@ * */ #include + #include #ifdef USE_ABSTRACT @@ -41,7 +42,10 @@ int main(void) { CURL *curl; - CURLcode res; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { @@ -53,15 +57,16 @@ int main(void) curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, PATH); #endif - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } - return 0; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/url2file.c b/docs/examples/url2file.c index 9ed7da5a84..ce8805a40e 100644 --- a/docs/examples/url2file.c +++ b/docs/examples/url2file.c @@ -25,63 +25,77 @@ * Download a given URL into a local file named page.out. * */ +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS /* for fopen() */ +#endif +#endif + #include #include #include -static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *stream) { size_t written = fwrite(ptr, size, nmemb, (FILE *)stream); return written; } -int main(int argc, char *argv[]) +int main(int argc, const char *argv[]) { - CURL *curl_handle; static const char *pagefilename = "page.out"; - FILE *pagefile; + + CURLcode result; + CURL *curl; if(argc < 2) { printf("Usage: %s \n", argv[0]); return 1; } - curl_global_init(CURL_GLOBAL_ALL); - - /* init the curl session */ - curl_handle = curl_easy_init(); - - /* set URL to get here */ - curl_easy_setopt(curl_handle, CURLOPT_URL, argv[1]); - - /* Switch on full protocol/debug output while testing */ - curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L); - - /* disable progress meter, set to 0L to enable it */ - curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L); - - /* send all data to this function */ - curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data); - - /* open the file */ - pagefile = fopen(pagefilename, "wb"); - if(pagefile) { - - /* write the page body to this file handle */ - curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile); - - /* get it! */ - curl_easy_perform(curl_handle); - - /* close the header file */ - fclose(pagefile); + result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) { + fprintf(stderr, "Could not init curl\n"); + return (int)result; } - /* cleanup curl stuff */ - curl_easy_cleanup(curl_handle); + /* init the curl session */ + curl = curl_easy_init(); + if(curl) { + FILE *pagefile; + + /* set URL to get here */ + curl_easy_setopt(curl, CURLOPT_URL, argv[1]); + + /* Switch on full protocol/debug output while testing */ + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + + /* disable progress meter, set to 0L to enable it */ + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + + /* send all data to this function */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + + /* open the file */ + pagefile = fopen(pagefilename, "wb"); + if(pagefile) { + + /* write the page body to this file handle */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, pagefile); + + /* get it! */ + result = curl_easy_perform(curl); + + /* close the header file */ + fclose(pagefile); + } + + /* cleanup curl stuff */ + curl_easy_cleanup(curl); + } curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/examples/urlapi.c b/docs/examples/urlapi.c index 2ed78eb11f..50175da76e 100644 --- a/docs/examples/urlapi.c +++ b/docs/examples/urlapi.c @@ -34,16 +34,15 @@ int main(void) { - CURL *curl; - CURLcode res; - + CURL *curl = NULL; CURLU *urlp; CURLUcode uc; - /* get a curl handle */ - curl = curl_easy_init(); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - /* init Curl URL */ + /* init curl URL */ urlp = curl_url(); uc = curl_url_set(urlp, CURLUPART_URL, "http://example.com/path/index.html", 0); @@ -53,6 +52,8 @@ int main(void) goto cleanup; } + /* get a curl handle */ + curl = curl_easy_init(); if(curl) { /* set urlp to use as working URL */ curl_easy_setopt(curl, CURLOPT_CURLU, urlp); @@ -61,17 +62,16 @@ int main(void) /* only allow HTTP, TFTP and SFTP */ curl_easy_setopt(curl, CURLOPT_PROTOCOLS_STR, "http,tftp,sftp"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); - - goto cleanup; + curl_easy_strerror(result)); } cleanup: curl_url_cleanup(urlp); curl_easy_cleanup(curl); - return 0; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/usercertinmem.c b/docs/examples/usercertinmem.c index 670ae4dc71..3e7865780f 100644 --- a/docs/examples/usercertinmem.c +++ b/docs/examples/usercertinmem.c @@ -22,45 +22,35 @@ * ***************************************************************************/ /* - * Use an in-memory user certificate and RSA key and retrieve an https page. + * Use in-memory user certificate and private key and retrieve an HTTPS page. * */ -/* Written by Ishan SinghLevett, based on Theo Borm's cacertinmem.c. - * Note that to maintain simplicity this example does not use a CA certificate - * for peer verification. However, some form of peer verification - * must be used in real circumstances when a secure connection is required. + +/* Written by Ishan SinghLevett, based on Theo Borm's cacertinmem.c. Note that + * to maintain simplicity this example does not use a CA certificate for peer + * verification. Some form of peer verification must be used in real + * circumstances when a secure connection is required. */ -#ifndef OPENSSL_SUPPRESS_DEPRECATED -#define OPENSSL_SUPPRESS_DEPRECATED -#endif +/* Requires: USE_OPENSSL */ #include -#include -#include -#include + #include -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic ignored "-Woverlength-strings" -#endif +#include -static size_t writefunction(void *ptr, size_t size, size_t nmemb, void *stream) +static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *stream) { - fwrite(ptr, size, nmemb, stream); + fwrite(ptr, size, nmemb, (FILE *)stream); return nmemb * size; } static CURLcode sslctx_function(CURL *curl, void *sslctx, void *pointer) { - X509 *cert = NULL; - BIO *bio = NULL; - BIO *kbio = NULL; - RSA *rsa = NULL; - int ret; - - const char *mypem = - /* replace the XXX with the actual CA certificate */ + /** This example uses a (fake) certificate and private key **/ + /* replace the XXX with the actual client/user certificate */ + static const char mypem[] = "-----BEGIN CERTIFICATE-----\n" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" @@ -72,69 +62,69 @@ static CURLcode sslctx_function(CURL *curl, void *sslctx, void *pointer) "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" "-----END CERTIFICATE-----\n"; - /* replace the XXX with the actual RSA key */ - const char *mykey = - "-----BEGIN RSA PRIVATE KEY-----\n" + /* replace the XXX with the actual private key */ + static const char mykey[] = + "-----BEGIN 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"; + "-----END PRIVATE KEY-----\n"; + + CURLcode result = CURLE_ABORTED_BY_CALLBACK; + X509 *cert = NULL; + BIO *bio = NULL; + BIO *kbio = NULL; + EVP_PKEY *pkey = NULL; + int ret; (void)curl; (void)pointer; /* get a BIO */ - bio = BIO_new_mem_buf((char *)mypem, -1); - + bio = BIO_new_mem_buf(mypem, sizeof(mypem) - 1); if(!bio) { - printf("BIO_new_mem_buf failed\n"); + printf("BIO_new_mem_buf() failed\n"); + goto out; } /* use it to read the PEM formatted certificate from memory into an X509 - * structure that SSL can use - */ + structure that SSL can use. */ cert = PEM_read_bio_X509(bio, NULL, 0, NULL); if(!cert) { - printf("PEM_read_bio_X509 failed...\n"); + printf("PEM_read_bio_X509() failed\n"); + goto out; } /* tell SSL to use the X509 certificate */ - ret = SSL_CTX_use_certificate((SSL_CTX*)sslctx, cert); + ret = SSL_CTX_use_certificate((SSL_CTX *)sslctx, cert); if(ret != 1) { - printf("Use certificate failed\n"); + printf("SSL_CTX_use_certificate() failed\n"); + goto out; } - /* create a bio for the RSA key */ - kbio = BIO_new_mem_buf((char *)mykey, -1); + /* create a bio for the private key */ + kbio = BIO_new_mem_buf(mykey, sizeof(mykey) - 1); if(!kbio) { - printf("BIO_new_mem_buf failed\n"); + printf("BIO_new_mem_buf() failed\n"); + goto out; } - /* read the key bio into an RSA object */ - rsa = PEM_read_bio_RSAPrivateKey(kbio, NULL, 0, NULL); - if(!rsa) { - printf("Failed to create key bio\n"); + pkey = PEM_read_bio_PrivateKey(kbio, NULL, NULL, NULL); + if(!pkey) { + printf("PEM_read_bio_PrivateKey() failed\n"); + goto out; } - /* tell SSL to use the RSA key from memory */ - ret = SSL_CTX_use_RSAPrivateKey((SSL_CTX*)sslctx, rsa); + /* tell SSL to use the private key from memory */ + ret = SSL_CTX_use_PrivateKey((SSL_CTX *)sslctx, pkey); if(ret != 1) { - printf("Use Key failed\n"); + printf("SSL_CTX_use_PrivateKey() failed\n"); + goto out; } + result = CURLE_OK; + +out: /* free resources that have been allocated by OpenSSL functions */ if(bio) BIO_free(bio); @@ -142,64 +132,64 @@ static CURLcode sslctx_function(CURL *curl, void *sslctx, void *pointer) if(kbio) BIO_free(kbio); - if(rsa) - RSA_free(rsa); + if(pkey) + EVP_PKEY_free(pkey); if(cert) X509_free(cert); /* all set to go */ - return CURLE_OK; + return result; } int main(void) { - CURL *ch; - CURLcode rv; + CURL *curl; - curl_global_init(CURL_GLOBAL_ALL); - ch = curl_easy_init(); - curl_easy_setopt(ch, CURLOPT_VERBOSE, 0L); - curl_easy_setopt(ch, CURLOPT_HEADER, 0L); - curl_easy_setopt(ch, CURLOPT_NOPROGRESS, 1L); - curl_easy_setopt(ch, CURLOPT_NOSIGNAL, 1L); - curl_easy_setopt(ch, CURLOPT_WRITEFUNCTION, writefunction); - curl_easy_setopt(ch, CURLOPT_WRITEDATA, stdout); - curl_easy_setopt(ch, CURLOPT_HEADERFUNCTION, writefunction); - curl_easy_setopt(ch, CURLOPT_HEADERDATA, stderr); - curl_easy_setopt(ch, CURLOPT_SSLCERTTYPE, "PEM"); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; - /* both VERIFYPEER and VERIFYHOST are set to 0 in this case because there is - no CA certificate */ + curl = curl_easy_init(); + if(curl) { + curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); + curl_easy_setopt(curl, CURLOPT_HEADER, 0L); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, stderr); + curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); - curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, 0L); - curl_easy_setopt(ch, CURLOPT_SSL_VERIFYHOST, 0L); - curl_easy_setopt(ch, CURLOPT_URL, "https://www.example.com/"); - curl_easy_setopt(ch, CURLOPT_SSLKEYTYPE, "PEM"); + /* both VERIFYPEER and VERIFYHOST are set to 0 in this case because there + is no CA certificate */ + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); - /* first try: retrieve page without user certificate and key -> fails */ - rv = curl_easy_perform(ch); - if(rv == CURLE_OK) { - printf("*** transfer succeeded ***\n"); + curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); + curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM"); + + /* first try: retrieve page without user certificate and key -> fails */ + result = curl_easy_perform(curl); + if(result == CURLE_OK) + printf("*** transfer succeeded ***\n"); + else + printf("*** transfer failed ***\n"); + + /* second try: retrieve page using user certificate and key -> succeeds to + * load the certificate and key by installing a function doing the + * necessary "modifications" to the SSL CONTEXT before link init + */ + curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctx_function); + result = curl_easy_perform(curl); + if(result == CURLE_OK) + printf("*** transfer succeeded ***\n"); + else + printf("*** transfer failed ***\n"); + + curl_easy_cleanup(curl); } - else { - printf("*** transfer failed ***\n"); - } - - /* second try: retrieve page using user certificate and key -> succeeds - * load the certificate and key by installing a function doing the necessary - * "modifications" to the SSL CONTEXT just before link init - */ - curl_easy_setopt(ch, CURLOPT_SSL_CTX_FUNCTION, sslctx_function); - rv = curl_easy_perform(ch); - if(rv == CURLE_OK) { - printf("*** transfer succeeded ***\n"); - } - else { - printf("*** transfer failed ***\n"); - } - - curl_easy_cleanup(ch); curl_global_cleanup(); - return (int)rv; + return (int)result; } diff --git a/docs/examples/version-check.pl b/docs/examples/version-check.pl index bc065770bc..e6ad784fc8 100755 --- a/docs/examples/version-check.pl +++ b/docs/examples/version-check.pl @@ -58,7 +58,6 @@ while() { $rem{$sym}=$a[2]; } } - } close(S); diff --git a/docs/examples/websocket-cb.c b/docs/examples/websocket-cb.c index 09d6c647dd..e73cc27a3f 100644 --- a/docs/examples/websocket-cb.c +++ b/docs/examples/websocket-cb.c @@ -28,13 +28,13 @@ #include #include -static size_t writecb(char *b, size_t size, size_t nitems, void *p) +static size_t write_cb(char *b, size_t size, size_t nitems, void *p) { - CURL *easy = p; + CURL *curl = p; size_t i; - const struct curl_ws_frame *frame = curl_ws_meta(easy); - fprintf(stderr, "Type: %s\n", frame->flags & CURLWS_BINARY ? - "binary" : "text"); + const struct curl_ws_frame *frame = curl_ws_meta(curl); + fprintf(stderr, "Type: %s\n", + frame->flags & CURLWS_BINARY ? "binary" : "text"); fprintf(stderr, "Bytes: %u", (unsigned int)(nitems * size)); for(i = 0; i < nitems; i++) fprintf(stderr, "%02x ", (unsigned char)b[i]); @@ -44,25 +44,29 @@ static size_t writecb(char *b, size_t size, size_t nitems, void *p) int main(void) { CURL *curl; - CURLcode res; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "wss://example.com"); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); /* pass the easy handle to the callback */ curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); } - return 0; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/websocket-updown.c b/docs/examples/websocket-updown.c index 0257c6077e..3000be0594 100644 --- a/docs/examples/websocket-updown.c +++ b/docs/examples/websocket-updown.c @@ -27,16 +27,17 @@ */ #include #include + #include -static size_t writecb(char *b, size_t size, size_t nitems, void *p) +static size_t write_cb(char *b, size_t size, size_t nitems, void *p) { - CURL *easy = p; + CURL *curl = p; size_t i; unsigned int blen = (unsigned int)(nitems * size); - const struct curl_ws_frame *frame = curl_ws_meta(easy); - fprintf(stderr, "Type: %s\n", frame->flags & CURLWS_BINARY ? - "binary" : "text"); + const struct curl_ws_frame *frame = curl_ws_meta(curl); + fprintf(stderr, "Type: %s\n", + frame->flags & CURLWS_BINARY ? "binary" : "text"); if(frame->flags & CURLWS_BINARY) { fprintf(stderr, "Bytes: %u", blen); for(i = 0; i < nitems; i++) @@ -49,25 +50,25 @@ static size_t writecb(char *b, size_t size, size_t nitems, void *p) } struct read_ctx { - CURL *easy; + CURL *curl; char buf[1024]; size_t blen; size_t nsent; }; -static size_t readcb(char *buf, size_t nitems, size_t buflen, void *p) +static size_t read_cb(char *buf, size_t nitems, size_t buflen, void *p) { struct read_ctx *ctx = p; size_t len = nitems * buflen; size_t left = ctx->blen - ctx->nsent; - CURLcode result; if(!ctx->nsent) { - /* On first call, set the FRAME information to be used (it defaults - * to CURLWS_BINARY otherwise). */ - result = curl_ws_start_frame(ctx->easy, CURLWS_TEXT, + CURLcode result; + /* On first call, set the FRAME information to be used (it defaults to + * CURLWS_BINARY otherwise). */ + result = curl_ws_start_frame(ctx->curl, CURLWS_TEXT, (curl_off_t)ctx->blen); - if(result) { + if(result != CURLE_OK) { fprintf(stderr, "error starting frame: %d\n", result); return CURL_READFUNC_ABORT; } @@ -85,41 +86,43 @@ static size_t readcb(char *buf, size_t nitems, size_t buflen, void *p) int main(int argc, const char *argv[]) { - CURL *easy; + CURL *curl; struct read_ctx rctx; - CURLcode res; const char *payload = "Hello, friend!"; + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; + memset(&rctx, 0, sizeof(rctx)); - easy = curl_easy_init(); - if(!easy) - return 1; + curl = curl_easy_init(); + if(curl) { + if(argc == 2) + curl_easy_setopt(curl, CURLOPT_URL, argv[1]); + else + curl_easy_setopt(curl, CURLOPT_URL, "wss://example.com"); - if(argc == 2) - curl_easy_setopt(easy, CURLOPT_URL, argv[1]); - else - curl_easy_setopt(easy, CURLOPT_URL, "wss://example.com"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl); + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb); + /* tell curl that we want to send the payload */ + rctx.curl = curl; + rctx.blen = strlen(payload); + memcpy(rctx.buf, payload, rctx.blen); + curl_easy_setopt(curl, CURLOPT_READDATA, &rctx); + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); - curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, writecb); - curl_easy_setopt(easy, CURLOPT_WRITEDATA, easy); - curl_easy_setopt(easy, CURLOPT_READFUNCTION, readcb); - /* tell curl that we want to send the payload */ - rctx.easy = easy; - rctx.blen = strlen(payload); - memcpy(rctx.buf, payload, rctx.blen); - curl_easy_setopt(easy, CURLOPT_READDATA, &rctx); - curl_easy_setopt(easy, CURLOPT_UPLOAD, 1L); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); + /* Check for errors */ + if(result != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(result)); - - /* Perform the request, res gets the return code */ - res = curl_easy_perform(easy); - /* Check for errors */ - if(res != CURLE_OK) - fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); - - /* always cleanup */ - curl_easy_cleanup(easy); - return 0; + /* always cleanup */ + curl_easy_cleanup(curl); + } + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/websocket.c b/docs/examples/websocket.c index be676839f7..ec445d88ed 100644 --- a/docs/examples/websocket.c +++ b/docs/examples/websocket.c @@ -30,7 +30,7 @@ #ifdef _WIN32 #include #include -#define sleep(s) Sleep((DWORD)(s)) +#define sleep(s) Sleep((DWORD)(s * 1000)) #else #include #endif @@ -39,41 +39,41 @@ static CURLcode ping(CURL *curl, const char *send_payload) { - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; const char *buf = send_payload; size_t sent, blen = strlen(send_payload); while(blen) { - res = curl_ws_send(curl, buf, blen, &sent, 0, CURLWS_PING); - if(!res) { + result = curl_ws_send(curl, buf, blen, &sent, 0, CURLWS_PING); + if(result == CURLE_OK) { buf += sent; /* deduct what was sent */ blen -= sent; } - else if(res == CURLE_AGAIN) { /* blocked on sending */ + else if(result == CURLE_AGAIN) { /* blocked on sending */ fprintf(stderr, "ws: sent PING blocked, waiting a second\n"); - sleep(1); /* either select() on socket or max timeout would - be good here. */ + sleep(1); /* either select() on socket or max timeout would + be good here. */ } else /* real error sending */ break; } - if(!res) + if(result == CURLE_OK) fprintf(stderr, "ws: sent PING with payload\n"); - return res; + return result; } static CURLcode recv_pong(CURL *curl, const char *expected_payload) { - size_t rlen; + size_t rlen = 0; const struct curl_ws_frame *meta; char buffer[256]; - CURLcode res; + CURLcode result; retry: - res = curl_ws_recv(curl, buffer, sizeof(buffer), &rlen, &meta); - if(!res) { + result = curl_ws_recv(curl, buffer, sizeof(buffer), &rlen, &meta); + if(result == CURLE_OK) { /* on small PING content, this example assumes the complete - * PONG content arrives in one go. Larger frames will arrive + * PONG content arrives in one go. Larger frames arrive * in chunks, however. */ if(meta->flags & CURLWS_PONG) { int same = 0; @@ -85,8 +85,7 @@ retry: same ? "same" : "different"); } else if(meta->flags & CURLWS_TEXT) { - fprintf(stderr, "ws: received TEXT frame '%.*s'\n", (int)rlen, - buffer); + fprintf(stderr, "ws: received TEXT frame '%.*s'\n", (int)rlen, buffer); } else if(meta->flags & CURLWS_BINARY) { fprintf(stderr, "ws: received BINARY frame of %u bytes\n", @@ -99,16 +98,16 @@ retry: goto retry; } } - else if(res == CURLE_AGAIN) { /* blocked on receiving */ + else if(result == CURLE_AGAIN) { /* blocked on receiving */ fprintf(stderr, "ws: PONG not there yet, waiting a second\n"); - sleep(1); /* either select() on socket or max timeout would - be good here. */ + sleep(1); /* either select() on socket or max timeout would + be good here. */ goto retry; } - if(res) + if(result != CURLE_OK) fprintf(stderr, "ws: curl_ws_recv returned %u, received %u\n", - (unsigned int)res, (unsigned int)rlen); - return res; + (unsigned int)result, (unsigned int)rlen); + return result; } /* close the connection */ @@ -120,49 +119,52 @@ static void websocket_close(CURL *curl) static CURLcode websocket(CURL *curl) { - CURLcode res; + CURLcode result; int i = 0; do { - res = ping(curl, "foobar"); - if(res) + result = ping(curl, "foobar"); + if(result != CURLE_OK) break; - res = recv_pong(curl, "foobar"); - if(res) + result = recv_pong(curl, "foobar"); + if(result != CURLE_OK) break; sleep(1); } while(i++ < 10); websocket_close(curl); - return res; + return result; } int main(int argc, const char *argv[]) { CURL *curl; - CURLcode res; + + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; curl = curl_easy_init(); - if(!curl) { - return 1; /* memory failure */ + if(curl) { + if(argc == 2) + curl_easy_setopt(curl, CURLOPT_URL, argv[1]); + else + curl_easy_setopt(curl, CURLOPT_URL, "wss://example.com"); + + curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */ + + /* Perform the request, result gets the return code */ + result = curl_easy_perform(curl); + /* Check for errors */ + if(result != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(result)); + else { + /* connected and ready */ + result = websocket(curl); + } + + /* always cleanup */ + curl_easy_cleanup(curl); } - if(argc == 2) - curl_easy_setopt(curl, CURLOPT_URL, argv[1]); - else - curl_easy_setopt(curl, CURLOPT_URL, "wss://example.com"); - - curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */ - - /* Perform the request, res gets the return code */ - res = curl_easy_perform(curl); - /* Check for errors */ - if(res != CURLE_OK) - fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); - else { - /* connected and ready */ - res = websocket(curl); - } - - /* always cleanup */ - curl_easy_cleanup(curl); - return (int)res; + curl_global_cleanup(); + return (int)result; } diff --git a/docs/examples/xmlstream.c b/docs/examples/xmlstream.c index 91c5387888..2ad132f282 100644 --- a/docs/examples/xmlstream.c +++ b/docs/examples/xmlstream.c @@ -32,12 +32,12 @@ * gcc -Wall -I/usr/local/include xmlstream.c -lcurl -lexpat -o xmlstream * */ - #include #include #include #include + #include struct MemoryStruct { @@ -55,7 +55,7 @@ struct ParserStruct { static void startElement(void *userData, const XML_Char *name, const XML_Char **atts) { - struct ParserStruct *state = (struct ParserStruct *) userData; + struct ParserStruct *state = (struct ParserStruct *)userData; state->tags++; state->depth++; @@ -70,7 +70,7 @@ static void startElement(void *userData, const XML_Char *name, static void characterDataHandler(void *userData, const XML_Char *s, int len) { - struct ParserStruct *state = (struct ParserStruct *) userData; + struct ParserStruct *state = (struct ParserStruct *)userData; struct MemoryStruct *mem = &state->characters; char *ptr = realloc(mem->memory, mem->size + (unsigned long)len + 1); @@ -89,18 +89,18 @@ static void characterDataHandler(void *userData, const XML_Char *s, int len) static void endElement(void *userData, const XML_Char *name) { - struct ParserStruct *state = (struct ParserStruct *) userData; + struct ParserStruct *state = (struct ParserStruct *)userData; state->depth--; printf("%5lu %10lu %s\n", state->depth, state->characters.size, name); } -static size_t parseStreamCallback(void *contents, size_t length, size_t nmemb, - void *userp) +static size_t write_cb(void *contents, size_t length, size_t nmemb, + void *userp) { - XML_Parser parser = (XML_Parser) userp; + XML_Parser parser = (XML_Parser)userp; size_t real_size = length * nmemb; - struct ParserStruct *state = (struct ParserStruct *) XML_GetUserData(parser); + struct ParserStruct *state = (struct ParserStruct *)XML_GetUserData(parser); /* Only parse if we are not already in a failure state. */ if(state->ok && XML_Parse(parser, contents, (int)real_size, 0) == 0) { @@ -116,55 +116,62 @@ static size_t parseStreamCallback(void *contents, size_t length, size_t nmemb, int main(void) { - CURL *curl_handle; - CURLcode res; - XML_Parser parser; - struct ParserStruct state; + CURL *curl; - /* Initialize the state structure for parsing. */ - memset(&state, 0, sizeof(state)); - state.ok = 1; - - /* Initialize a namespace-aware parser. */ - parser = XML_ParserCreateNS(NULL, '\0'); - XML_SetUserData(parser, &state); - XML_SetElementHandler(parser, startElement, endElement); - XML_SetCharacterDataHandler(parser, characterDataHandler); + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if(result != CURLE_OK) + return (int)result; /* Initialize a libcurl handle. */ - curl_global_init(CURL_GLOBAL_DEFAULT); - curl_handle = curl_easy_init(); - curl_easy_setopt(curl_handle, CURLOPT_URL, - "https://www.w3schools.com/xml/simple.xml"); - curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, parseStreamCallback); - curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)parser); + curl = curl_easy_init(); + if(curl) { + XML_Parser parser; + struct ParserStruct state; - printf("Depth Characters Closing Tag\n"); + /* Initialize the state structure for parsing. */ + memset(&state, 0, sizeof(state)); + state.ok = 1; - /* Perform the request and any follow-up parsing. */ - res = curl_easy_perform(curl_handle); - if(res != CURLE_OK) { - fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); - } - else if(state.ok) { - /* Expat requires one final call to finalize parsing. */ - if(XML_Parse(parser, NULL, 0, 1) == 0) { - enum XML_Error error_code = XML_GetErrorCode(parser); - fprintf(stderr, "Finalizing parsing failed with error code %d (%s).\n", - error_code, XML_ErrorString(error_code)); + /* Initialize a namespace-aware parser. */ + parser = XML_ParserCreateNS(NULL, '\0'); + XML_SetUserData(parser, &state); + XML_SetElementHandler(parser, startElement, endElement); + XML_SetCharacterDataHandler(parser, characterDataHandler); + + curl_easy_setopt(curl, CURLOPT_URL, + "https://www.w3schools.com/xml/simple.xml"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)parser); + + printf("Depth Characters Closing Tag\n"); + + /* Perform the request and any follow-up parsing. */ + result = curl_easy_perform(curl); + if(result != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(result)); } - else { - printf(" --------------\n"); - printf(" %lu tags total\n", state.tags); + else if(state.ok) { + /* Expat requiresult one final call to finalize parsing. */ + if(XML_Parse(parser, NULL, 0, 1) == 0) { + enum XML_Error error_code = XML_GetErrorCode(parser); + fprintf(stderr, "Finalizing parsing failed with error code %d (%s).\n", + error_code, XML_ErrorString(error_code)); + } + else { + printf(" --------------\n"); + printf(" %lu tags total\n", state.tags); + } } + + /* Clean up. */ + free(state.characters.memory); + XML_ParserFree(parser); + + curl_easy_cleanup(curl); } - /* Clean up. */ - free(state.characters.memory); - XML_ParserFree(parser); - curl_easy_cleanup(curl_handle); curl_global_cleanup(); - return 0; + return (int)result; } diff --git a/docs/internals/BUFQ.md b/docs/internals/BUFQ.md index 6028711d2c..9d926537d6 100644 --- a/docs/internals/BUFQ.md +++ b/docs/internals/BUFQ.md @@ -14,8 +14,7 @@ to and read from. It manages read and write positions and has a maximum size. Its basic read/write functions have a similar signature and return code handling as many internal curl read and write ones. - -``` +```c ssize_t Curl_bufq_write(struct bufq *q, const unsigned char *buf, size_t len, CURLcode *err); - returns the length written into `q` or -1 on error. @@ -30,7 +29,7 @@ ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, CURLcode To pass data into a `bufq` without an extra copy, read callbacks can be used. -``` +```c typedef ssize_t Curl_bufq_reader(void *reader_ctx, unsigned char *buf, size_t len, CURLcode *err); @@ -46,7 +45,7 @@ once or only read in a maximum amount of bytes. The analog mechanism for write out buffer data is: -``` +```c typedef ssize_t Curl_bufq_writer(void *writer_ctx, const unsigned char *buf, size_t len, CURLcode *err); @@ -61,16 +60,16 @@ remove the amount that `writer` reports. It is possible to get access to the memory of data stored in a `bufq` with: -``` +```c bool Curl_bufq_peek(const struct bufq *q, const unsigned char **pbuf, size_t *plen); ``` On returning TRUE, `pbuf` points to internal memory with `plen` bytes that one may read. This is only valid until another operation on `bufq` is performed. -Instead of reading `bufq` data, one may simply skip it: +Instead of reading `bufq` data, one may skip it: -``` +```c void Curl_bufq_skip(struct bufq *q, size_t amount); ``` @@ -81,7 +80,7 @@ This removes `amount` number of bytes from the `bufq`. `bufq` is initialized and freed similar to the `dynbuf` module. Code using `bufq` holds a `struct bufq` somewhere. Before it uses it, it invokes: -``` +```c void Curl_bufq_init(struct bufq *q, size_t chunk_size, size_t max_chunks); ``` @@ -92,12 +91,13 @@ about memory management. The user of the `bufq` has the responsibility to call: -``` +```c void Curl_bufq_free(struct bufq *q); ``` + to free all resources held by `q`. It is possible to reset a `bufq` to empty via: -``` +```c void Curl_bufq_reset(struct bufq *q); ``` @@ -150,13 +150,12 @@ reports **full**, but one can **still** write. This option is necessary, if partial writes need to be avoided. It means that you need other checks to keep the `bufq` from growing ever larger and larger. - ## pools A `struct bufc_pool` may be used to create chunks for a `bufq` and keep spare ones around. It is initialized and used via: -``` +```c void Curl_bufcp_init(struct bufc_pool *pool, size_t chunk_size, size_t spare_max); void Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool, size_t max_chunks, int opts); diff --git a/docs/internals/BUFREF.md b/docs/internals/BUFREF.md index 9a8b506b56..09413dea03 100644 --- a/docs/internals/BUFREF.md +++ b/docs/internals/BUFREF.md @@ -54,10 +54,11 @@ specified as `NULL`: this is the case when the referenced buffer is static. if `buffer` is NULL, `length` must be zero. -## `memdup` +## `memdup0` ```c -CURLcode Curl_bufref_memdup(struct bufref *br, const void *data, size_t length); +CURLcode Curl_bufref_memdup0(struct bufref *br, const void *data, + size_t length); ``` Releases the previously referenced buffer, then duplicates the `length`-byte @@ -72,7 +73,15 @@ Returns `CURLE_OK` if successful, else `CURLE_OUT_OF_MEMORY`. ## `ptr` ```c -const unsigned char *Curl_bufref_ptr(const struct bufref *br); +const char *Curl_bufref_ptr(const struct bufref *br); +``` + +Returns a `const char *` to the referenced buffer. + +## `uptr` + +```c +const unsigned char *Curl_bufref_uptr(const struct bufref *br); ``` Returns a `const unsigned char *` to the referenced buffer. @@ -84,3 +93,12 @@ size_t Curl_bufref_len(const struct bufref *br); ``` Returns the stored length of the referenced buffer. + +## `dup` + +```c +char *Curl_bufref_dup(const struct bufref *br); +``` + +Returns a strdup() version of the buffer. Note that this assumes that the +bufref is null-terminated. diff --git a/docs/internals/CHECKSRC.md b/docs/internals/CHECKSRC.md index 16eb96c75b..ea6f260cf1 100644 --- a/docs/internals/CHECKSRC.md +++ b/docs/internals/CHECKSRC.md @@ -76,7 +76,8 @@ warnings are: - `EXCLAMATIONSPACE`: space found after exclamations mark -- `FOPENMODE`: `fopen()` needs a macro for the mode string, use it +- `FOPENMODE`: `curlx_fopen()`, `curlx_freopen()` need a macro for the mode + string, use it - `INDENTATION`: detected a wrong start column for code. Note that this warning only checks some specific places and can certainly miss many bad @@ -106,7 +107,7 @@ warnings are: `sizeof(int)` style. - `SNPRINTF` - Found use of `snprintf()`. Since we use an internal replacement - with a different return code etc, we prefer `msnprintf()`. + with a different return code etc, we prefer `curl_msnprintf()`. - `SPACEAFTERPAREN`: there was a space after open parenthesis, `( text`. @@ -130,11 +131,11 @@ warnings are: ### Extended warnings -Some warnings are quite computationally expensive to perform, so they are -turned off by default. To enable these warnings, place a `.checksrc` file in -the directory where they should be activated with commands to enable the -warnings you are interested in. The format of the file is to enable one -warning per line like so: `enable ` +Some warnings are computationally expensive to perform, so they are turned off +by default. To enable these warnings, place a `.checksrc` file in the directory +where they should be activated with commands to enable the warnings you are +interested in. The format of the file is to enable one warning per line like +so: `enable ` Currently these are the extended warnings which can be enabled: @@ -171,8 +172,8 @@ This ignores the warning for overly long lines until it is re-enabled with: If the enabling is not performed before the end of the file, it is enabled again automatically for the next file. -You can also opt to ignore just N violations so that if you have a single long -line you just cannot shorten and is agreed to be fine anyway: +You can also opt to ignore N violations so that if you have a single long line +you cannot shorten and is agreed to be fine anyway: /* !checksrc! disable LONGLINE 1 */ diff --git a/docs/internals/CLIENT-READERS.md b/docs/internals/CLIENT-READERS.md index 35caaa6bfc..11cf86da68 100644 --- a/docs/internals/CLIENT-READERS.md +++ b/docs/internals/CLIENT-READERS.md @@ -6,30 +6,45 @@ SPDX-License-Identifier: curl # curl client readers -Client readers is a design in the internals of libcurl, not visible in its public API. They were started -in curl v8.7.0. This document describes the concepts, its high level implementation and the motivations. +Client readers is a design in the internals of libcurl, not visible in its +public API. They were started in curl v8.7.0. This document describes +the concepts, its high level implementation and the motivations. ## Naming -`libcurl` operates between clients and servers. A *client* is the application using libcurl, like the command line tool `curl` itself. Data to be uploaded to a server is **read** from the client and **sent** to the server, the servers response is **received** by `libcurl` and then **written** to the client. +`libcurl` operates between clients and servers. A *client* is the application +using libcurl, like the command line tool `curl` itself. Data to be uploaded +to a server is **read** from the client and **sent** to the server, the servers +response is **received** by `libcurl` and then **written** to the client. -With this naming established, client readers are concerned with providing data from the application to the server. Applications register callbacks via `CURLOPT_READFUNCTION`, data via `CURLOPT_POSTFIELDS` and other options to be used by `libcurl` when the request is send. +With this naming established, client readers are concerned with providing data +from the application to the server. Applications register callbacks via +`CURLOPT_READFUNCTION`, data via `CURLOPT_POSTFIELDS` and other options to be +used by `libcurl` when the request is send. ## Invoking -The transfer loop that sends and receives, is using `Curl_client_read()` to get more data to send for a transfer. If no specific reader has been installed yet, the default one that uses `CURLOPT_READFUNCTION` is added. The prototype is +The transfer loop that sends and receives, is using `Curl_client_read()` to get +more data to send for a transfer. If no specific reader has been installed yet, +the default one that uses `CURLOPT_READFUNCTION` is added. The prototype is -``` +```c CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, size_t *nread, bool *eos); ``` -The arguments are the transfer to read for, a buffer to hold the read data, its length, the actual number of bytes placed into the buffer and the `eos` (*end of stream*) flag indicating that no more data is available. The `eos` flag may be set for a read amount, if that amount was the last. That way curl can avoid to read an additional time. +The arguments are the transfer to read for, a buffer to hold the read data, its +length, the actual number of bytes placed into the buffer and the `eos` (*end of +stream*) flag indicating that no more data is available. The `eos` flag may be +set for a read amount, if that amount was the last. That way curl can avoid to +read an additional time. -The implementation of `Curl_client_read()` uses a chain of *client reader* instances to get the data. This is similar to the design of *client writers*. The chain of readers allows processing of the data to send. +The implementation of `Curl_client_read()` uses a chain of *client reader* +instances to get the data. This is similar to the design of *client writers*. +The chain of readers allows processing of the data to send. The definition of a reader is: -``` +```c struct Curl_crtype { const char *name; /* writer name. */ CURLcode (*do_init)(struct Curl_easy *data, struct Curl_creader *writer); @@ -51,13 +66,19 @@ struct Curl_creader { }; ``` -`Curl_creader` is a reader instance with a `next` pointer to form the chain. It as a type `crt` which provides the implementation. The main callback is `do_read()` which provides the data to the caller. The others are for setup and tear down. `needs_rewind()` is explained further below. +`Curl_creader` is a reader instance with a `next` pointer to form the chain. +It as a type `crt` which provides the implementation. The main callback is +`do_read()` which provides the data to the caller. The others are for setup +and tear down. `needs_rewind()` is explained further below. ## Phases and Ordering -Since client readers may transform the data being read through the chain, the order in which they are called is relevant for the outcome. When a reader is created, it gets the `phase` property in which it operates. Reader phases are defined like: +Since client readers may transform the data being read through the chain, +the order in which they are called is relevant for the outcome. When a reader +is created, it gets the `phase` property in which it operates. Reader phases +are defined like: -``` +```c typedef enum { CURL_CR_NET, /* data send to the network (connection filters) */ CURL_CR_TRANSFER_ENCODE, /* add transfer-encodings */ @@ -67,65 +88,115 @@ typedef enum { } Curl_creader_phase; ``` -If a reader for phase `PROTOCOL` is added to the chain, it is always added *after* any `NET` or `TRANSFER_ENCODE` readers and *before* and `CONTENT_ENCODE` and `CLIENT` readers. If there is already a reader for the same phase, the new reader is added before the existing one(s). +If a reader for phase `PROTOCOL` is added to the chain, it is always added +*after* any `NET` or `TRANSFER_ENCODE` readers and *before* and +`CONTENT_ENCODE` and `CLIENT` readers. If there is already a reader for the +same phase, the new reader is added before the existing one(s). ### Example: `chunked` reader -In `http_chunks.c` a client reader for chunked uploads is implemented. This one operates at phase `CURL_CR_TRANSFER_ENCODE`. Any data coming from the reader "below" has the HTTP/1.1 chunk handling applied and returned to the caller. +In `http_chunks.c` a client reader for chunked uploads is implemented. This +one operates at phase `CURL_CR_TRANSFER_ENCODE`. Any data coming from the +reader "below" has the HTTP/1.1 chunk handling applied and returned to the +caller. -When this reader sees an `eos` from below, it generates the terminal chunk, adding trailers if provided by the application. When that last chunk is fully returned, it also sets `eos` to the caller. +When this reader sees an `eos` from below, it generates the terminal chunk, +adding trailers if provided by the application. When that last chunk is fully +returned, it also sets `eos` to the caller. ### Example: `lineconv` reader -In `sendf.c` a client reader that does line-end conversions is implemented. It operates at `CURL_CR_CONTENT_ENCODE` and converts any "\n" to "\r\n". This is used for FTP ASCII uploads or when the general `crlf` options has been set. +In `sendf.c` a client reader that does line-end conversions is implemented. It +operates at `CURL_CR_CONTENT_ENCODE` and converts any "\n" to "\r\n". This is +used for FTP ASCII uploads or when the general `crlf` options has been set. ### Example: `null` reader -Implemented in `sendf.c` for phase `CURL_CR_CLIENT`, this reader has the simple job of providing transfer bytes of length 0 to the caller, immediately indicating an `eos`. This reader is installed by HTTP for all GET/HEAD requests and when authentication is being negotiated. +Implemented in `sendf.c` for phase `CURL_CR_CLIENT`, this reader has the +simple job of providing transfer bytes of length 0 to the caller, immediately +indicating an `eos`. This reader is installed by HTTP for all GET/HEAD +requests and when authentication is being negotiated. ### Example: `buf` reader -Implemented in `sendf.c` for phase `CURL_CR_CLIENT`, this reader get a buffer pointer and a length and provides exactly these bytes. This one is used in HTTP for sending `postfields` provided by the application. +Implemented in `sendf.c` for phase `CURL_CR_CLIENT`, this reader get a buffer +pointer and a length and provides exactly these bytes. This one is used in +HTTP for sending `postfields` provided by the application. ## Request retries -Sometimes it is necessary to send a request with client data again. Transfer handling can inquire via `Curl_client_read_needs_rewind()` if a rewind (e.g. a reset of the client data) is necessary. This asks all installed readers if they need it and give `FALSE` of none does. +Sometimes it is necessary to send a request with client data again. Transfer +handling can inquire via `Curl_client_read_needs_rewind()` if a rewind (e.g. a +reset of the client data) is necessary. This asks all installed readers if +they need it and give `FALSE` of none does. ## Upload Size -Many protocols need to know the amount of bytes delivered by the client readers in advance. They may invoke `Curl_creader_total_length(data)` to retrieve that. However, not all reader chains know the exact value beforehand. In that case, the call returns `-1` for "unknown". +Many protocols need to know the amount of bytes delivered by the client +readers in advance. They may invoke `Curl_creader_total_length(data)` to +retrieve that. Not all reader chains know the exact value beforehand. In that +case, the call returns `-1` for "unknown". -Even if the length of the "raw" data is known, the length that is send may not. Example: with option `--crlf` the uploaded content undergoes line-end conversion. The line converting reader does not know in advance how many newlines it may encounter. Therefore it must return `-1` for any positive raw content length. +Even if the length of the "raw" data is known, the length that is send may +not. Example: with option `--crlf` the uploaded content undergoes line-end +conversion. The line converting reader does not know in advance how many +newlines it may encounter. Therefore it must return `-1` for any positive raw +content length. -In HTTP, once the correct client readers are installed, the protocol asks the readers for the total length. If that is known, it can set `Content-Length:` accordingly. If not, it may choose to add an HTTP "chunked" reader. +In HTTP, once the correct client readers are installed, the protocol asks the +readers for the total length. If that is known, it can set `Content-Length:` +accordingly. If not, it may choose to add an HTTP "chunked" reader. -In addition, there is `Curl_creader_client_length(data)` which gives the total length as reported by the reader in phase `CURL_CR_CLIENT` without asking other readers that may transform the raw data. This is useful in estimating the size of an upload. The HTTP protocol uses this to determine if `Expect: 100-continue` shall be done. +In addition, there is `Curl_creader_client_length(data)` which gives the total +length as reported by the reader in phase `CURL_CR_CLIENT` without asking +other readers that may transform the raw data. This is useful in estimating +the size of an upload. The HTTP protocol uses this to determine if `Expect: +100-continue` shall be done. ## Resuming -Uploads can start at a specific offset, if so requested. The "resume from" that offset. This applies to the reader in phase `CURL_CR_CLIENT` that delivers the "raw" content. Resumption can fail if the installed reader does not support it or if the offset is too large. +Uploads can start at a specific offset, if so requested. The "resume from" +that offset. This applies to the reader in phase `CURL_CR_CLIENT` that +delivers the "raw" content. Resumption can fail if the installed reader does +not support it or if the offset is too large. -The total length reported by the reader changes when resuming. Example: resuming an upload of 100 bytes by 25 reports a total length of 75 afterwards. +The total length reported by the reader changes when resuming. Example: +resuming an upload of 100 bytes by 25 reports a total length of 75 afterwards. -If `resume_from()` is invoked twice, it is additive. There is currently no way to undo a resume. +If `resume_from()` is invoked twice, it is additive. There is currently no way +to undo a resume. ## Rewinding -When a request is retried, installed client readers are discarded and replaced by new ones. This works only if the new readers upload the same data. For many readers, this is not an issue. The "null" reader always does the same. Also the `buf` reader, initialized with the same buffer, does this. +When a request is retried, installed client readers are discarded and replaced +by new ones. This works only if the new readers upload the same data. For many +readers, this is not an issue. The "null" reader always does the same. Also +the `buf` reader, initialized with the same buffer, does this. -Readers operating on callbacks to the application need to "rewind" the underlying content. For example, when reading from a `FILE*`, the reader needs to `fseek()` to the beginning. The following methods are used: - -1. `Curl_creader_needs_rewind(data)`: tells if a rewind is necessary, given the current state of the reader chain. If nothing really has been read so far, this returns `FALSE`. -2. `Curl_creader_will_rewind(data)`: tells if the reader chain rewinds at the start of the next request. -3. `Curl_creader_set_rewind(data, TRUE)`: marks the reader chain for rewinding at the start of the next request. -4. `Curl_client_start(data)`: tells the readers that a new request starts and they need to rewind if requested. +Readers operating on callbacks to the application need to "rewind" the +underlying content. For example, when reading from a `FILE*`, the reader needs +to `fseek()` to the beginning. The following methods are used: +1. `Curl_creader_needs_rewind(data)`: tells if a rewind is necessary, given + the current state of the reader chain. If nothing really has been read so + far, this returns `FALSE`. +2. `Curl_creader_will_rewind(data)`: tells if the reader chain rewinds at + the start of the next request. +3. `Curl_creader_set_rewind(data, TRUE)`: marks the reader chain for rewinding + at the start of the next request. +4. `Curl_client_start(data)`: tells the readers that a new request starts and + they need to rewind if requested. ## Summary and Outlook -By adding the client reader interface, any protocol can control how/if it wants the curl transfer to send bytes for a request. The transfer loop becomes then blissfully ignorant of the specifics. +By adding the client reader interface, any protocol can control how/if it wants +the curl transfer to send bytes for a request. The transfer loop becomes then +blissfully ignorant of the specifics. -The protocols on the other hand no longer have to care to package data most efficiently. At any time, should more data be needed, it can be read from the client. This is used when sending HTTP requests headers to add as much request body data to the initial sending as there is room for. +The protocols on the other hand no longer have to care to package data most +efficiently. At any time, should more data be needed, it can be read from the +client. This is used when sending HTTP requests headers to add as much request +body data to the initial sending as there is room for. Future enhancements based on the client readers: * `expect-100` handling: place that into an HTTP specific reader at diff --git a/docs/internals/CLIENT-WRITERS.md b/docs/internals/CLIENT-WRITERS.md index 9f7197d228..e00cd263be 100644 --- a/docs/internals/CLIENT-WRITERS.md +++ b/docs/internals/CLIENT-WRITERS.md @@ -6,42 +6,57 @@ SPDX-License-Identifier: curl # curl client writers -Client writers is a design in the internals of libcurl, not visible in its public API. They were started -in curl v8.5.0. This document describes the concepts, its high level implementation and the motivations. +Client writers is a design in the internals of libcurl, not visible in its +public API. They were started in curl v8.5.0. This document describes the +concepts, its high level implementation and the motivations. ## Naming -`libcurl` operates between clients and servers. A *client* is the application using libcurl, like the command line tool `curl` itself. Data to be uploaded to a server is **read** from the client and **send** to the server, the servers response is **received** by `libcurl` and then **written** to the client. +`libcurl` operates between clients and servers. A *client* is the application +using libcurl, like the command line tool `curl` itself. Data to be uploaded +to a server is **read** from the client and **send** to the server, the +servers response is **received** by `libcurl` and then **written** to the +client. -With this naming established, client writers are concerned with writing responses from the server to the application. Applications register callbacks via `CURLOPT_WRITEFUNCTION` and `CURLOPT_HEADERFUNCTION` to be invoked by `libcurl` when the response is received. +With this naming established, client writers are concerned with writing +responses from the server to the application. Applications register callbacks +via `CURLOPT_WRITEFUNCTION` and `CURLOPT_HEADERFUNCTION` to be invoked by +`libcurl` when the response is received. ## Invoking -All code in `libcurl` that handles response data is ultimately expected to forward this data via `Curl_client_write()` to the application. The exact prototype of this function is: +All code in `libcurl` that handles response data is ultimately expected to +forward this data via `Curl_client_write()` to the application. The exact +prototype of this function is: -``` +```c CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf, size_t blen); ``` -The `type` argument specifies what the bytes in `buf` actually are. The following bits are defined: -``` -#define CLIENTWRITE_BODY (1<<0) /* non-meta information, BODY */ -#define CLIENTWRITE_INFO (1<<1) /* meta information, not a HEADER */ -#define CLIENTWRITE_HEADER (1<<2) /* meta information, HEADER */ -#define CLIENTWRITE_STATUS (1<<3) /* a special status HEADER */ -#define CLIENTWRITE_CONNECT (1<<4) /* a CONNECT related HEADER */ -#define CLIENTWRITE_1XX (1<<5) /* a 1xx response related HEADER */ -#define CLIENTWRITE_TRAILER (1<<6) /* a trailer HEADER */ +The `type` argument specifies what the bytes in `buf` actually are. +The following bits are defined: +```c +#define CLIENTWRITE_BODY (1 << 0) /* non-meta information, BODY */ +#define CLIENTWRITE_INFO (1 << 1) /* meta information, not a HEADER */ +#define CLIENTWRITE_HEADER (1 << 2) /* meta information, HEADER */ +#define CLIENTWRITE_STATUS (1 << 3) /* a special status HEADER */ +#define CLIENTWRITE_CONNECT (1 << 4) /* a CONNECT related HEADER */ +#define CLIENTWRITE_1XX (1 << 5) /* a 1xx response related HEADER */ +#define CLIENTWRITE_TRAILER (1 << 6) /* a trailer HEADER */ ``` The main types here are `CLIENTWRITE_BODY` and `CLIENTWRITE_HEADER`. They are -mutually exclusive. The other bits are enhancements to `CLIENTWRITE_HEADER` to -specify what the header is about. They are only used in HTTP and related +mutually exclusive. The other bits are enhancements to `CLIENTWRITE_HEADER` +to specify what the header is about. They are only used in HTTP and related protocols (RTSP and WebSocket). -The implementation of `Curl_client_write()` uses a chain of *client writer* instances to process the call and make sure that the bytes reach the proper application callbacks. This is similar to the design of connection filters: client writers can be chained to process the bytes written through them. The definition is: +The implementation of `Curl_client_write()` uses a chain of *client writer* +instances to process the call and make sure that the bytes reach the proper +application callbacks. This is similar to the design of connection filters: +client writers can be chained to process the bytes written through them. The +definition is: -``` +```c struct Curl_cwtype { const char *name; CURLcode (*do_init)(struct Curl_easy *data, @@ -60,13 +75,19 @@ struct Curl_cwriter { }; ``` -`Curl_cwriter` is a writer instance with a `next` pointer to form the chain. It has a type `cwt` which provides the implementation. The main callback is `do_write()` that processes the data and calls then the `next` writer. The others are for setup and tear down. +`Curl_cwriter` is a writer instance with a `next` pointer to form the chain. +It has a type `cwt` which provides the implementation. The main callback is +`do_write()` that processes the data and calls then the `next` writer. The +others are for setup and tear down. ## Phases and Ordering -Since client writers may transform the bytes written through them, the order in which the are called is relevant for the outcome. When a writer is created, one property it gets is the `phase` in which it operates. Writer phases are defined like: +Since client writers may transform the bytes written through them, the order +in which the are called is relevant for the outcome. When a writer is created, +one property it gets is the `phase` in which it operates. Writer phases are +defined like: -``` +```c typedef enum { CURL_CW_RAW, /* raw data written, before any decoding */ CURL_CW_TRANSFER_DECODE, /* remove transfer-encodings */ @@ -76,15 +97,28 @@ typedef enum { } Curl_cwriter_phase; ``` -If a writer for phase `PROTOCOL` is added to the chain, it is always added *after* any `RAW` or `TRANSFER_DECODE` and *before* any `CONTENT_DECODE` and `CLIENT` phase writer. If there is already a writer for the same phase present, the new writer is inserted just before that one. +If a writer for phase `PROTOCOL` is added to the chain, it is always added +*after* any `RAW` or `TRANSFER_DECODE` and *before* any `CONTENT_DECODE` and +`CLIENT` phase writer. If there is already a writer for the same phase +present, the new writer is inserted before that one. -All transfers have a chain of 3 writers by default. A specific protocol handler may alter that by adding additional writers. The 3 standard writers are (name, phase): +All transfers have a chain of 3 writers by default. A specific protocol +handler may alter that by adding additional writers. The 3 standard writers +are (name, phase): -1. `"raw", CURL_CW_RAW `: if the transfer is verbose, it forwards the body data to the debug function. -1. `"download", CURL_CW_PROTOCOL`: checks that protocol limits are kept and updates progress counters. When a download has a known length, it checks that it is not exceeded and errors otherwise. -1. `"client", CURL_CW_CLIENT`: the main work horse. It invokes the application callbacks or writes to the configured file handles. It chops large writes into smaller parts, as documented for `CURLOPT_WRITEFUNCTION`. If also handles *pausing* of transfers when the application callback returns `CURL_WRITEFUNC_PAUSE`. +1. `"raw", CURL_CW_RAW `: if the transfer is verbose, it forwards the body data + to the debug function. +1. `"download", CURL_CW_PROTOCOL`: checks that protocol limits are kept and + updates progress counters. When a download has a known length, it checks + that it is not exceeded and errors otherwise. +1. `"client", CURL_CW_CLIENT`: the main work horse. It invokes the application + callbacks or writes to the configured file handles. It chops large writes + into smaller parts, as documented for `CURLOPT_WRITEFUNCTION`. If also + handles *pausing* of transfers when the application callback returns + `CURL_WRITEFUNC_PAUSE`. -With these writers always in place, libcurl's protocol handlers automatically have these implemented. +With these writers always in place, libcurl's protocol handlers automatically +have these implemented. ## Enhanced Use @@ -112,12 +146,23 @@ which always is ordered before writers in phase `CURL_CW_CONTENT_DECODE`. What else? -Well, HTTP servers may also apply a `Transfer-Encoding` to the body of a response. The most well-known one is `chunked`, but algorithms like `gzip` and friends could also be applied. The difference to content encodings is that decoding needs to happen *before* protocol checks, for example on length, are done. +Well, HTTP servers may also apply a `Transfer-Encoding` to the body of a +response. The most well-known one is `chunked`, but algorithms like `gzip` and +friends could also be applied. The difference to content encodings is that +decoding needs to happen *before* protocol checks, for example on length, are +done. -That is why transfer decoding writers are added for phase `CURL_CW_TRANSFER_DECODE`. Which makes their operation happen *before* phase `CURL_CW_PROTOCOL` where length may be checked. +That is why transfer decoding writers are added for phase +`CURL_CW_TRANSFER_DECODE`. Which makes their operation happen *before* phase +`CURL_CW_PROTOCOL` where length may be checked. ## Summary -By adding the common behavior of all protocols into `Curl_client_write()` we make sure that they do apply everywhere. Protocol handler have less to worry about. Changes to default behavior can be done without affecting handler implementations. +By adding the common behavior of all protocols into `Curl_client_write()` we +make sure that they do apply everywhere. Protocol handler have less to worry +about. Changes to default behavior can be done without affecting handler +implementations. -Having a writer chain as implementation allows protocol handlers with extra needs, like HTTP, to add to this for special behavior. The common way of writing the actual response data stays the same. +Having a writer chain as implementation allows protocol handlers with extra +needs, like HTTP, to add to this for special behavior. The common way of +writing the actual response data stays the same. diff --git a/docs/internals/CODE_STYLE.md b/docs/internals/CODE_STYLE.md index dadec934dd..31c333af82 100644 --- a/docs/internals/CODE_STYLE.md +++ b/docs/internals/CODE_STYLE.md @@ -19,13 +19,13 @@ Our C code has a few style rules. Most of them are verified and upheld by the by the build system when built after `./configure --enable-debug` has been used. -It is normally not a problem for anyone to follow the guidelines, as you just -need to copy the style already used in the source code and there are no -particularly unusual rules in our set of rules. +It is normally not a problem for anyone to follow the guidelines, copy +the style already used in the source code and there are no particularly +unusual rules in our set of rules. We also work hard on writing code that are warning-free on all the major -platforms and in general on as many platforms as possible. Code that obviously -causes warnings is not accepted as-is. +platforms and in general on as many platforms as possible. Code that causes +warnings is not accepted as-is. ## Readability @@ -39,7 +39,7 @@ understand it when debugging. Try using a non-confusing naming scheme for your new functions and variable names. It does not necessarily have to mean that you should use the same as in -other places of the code, just that the names should be logical, +other places of the code, only that the names should be logical, understandable and be named according to what they are used for. File-local functions should be made static. We like lower case names. @@ -332,17 +332,45 @@ makes us write better code. This is the full list of functions generally banned. _access + _fstati64 + _lseeki64 _mbscat _mbsncat + _open _tcscat + _tcsdup _tcsncat + _tcsncpy _waccess _wcscat _wcsdup _wcsncat + _wfopen + _wfreopen + _wopen + accept + accept4 access + aprintf + atoi + atol + calloc + close + CreateFile + CreateFileA + CreateFileW + fclose + fdopen + fopen + fprintf + free + freeaddrinfo + freopen + fstat + getaddrinfo gets gmtime + llseek LoadLibrary LoadLibraryA LoadLibraryEx @@ -350,10 +378,29 @@ This is the full list of functions generally banned. LoadLibraryExW LoadLibraryW localtime + lseek + malloc + mbstowcs + MoveFileEx + MoveFileExA + MoveFileExW + msnprintf + mvsnprintf + open + printf + realloc + recv + rename + send snprintf + socket + socketpair sprintf sscanf + stat strcat + strcpy + strdup strerror strncat strncpy @@ -361,6 +408,15 @@ This is the full list of functions generally banned. strtok_r strtol strtoul - vsnprint + vaprintf + vfprintf + vprintf + vsnprintf vsprintf + wcscpy wcsdup + wcsncpy + wcstombs + WSASocket + WSASocketA + WSASocketW diff --git a/docs/internals/CONNECTION-FILTERS.md b/docs/internals/CONNECTION-FILTERS.md index 09acc7ad95..619ca0e340 100644 --- a/docs/internals/CONNECTION-FILTERS.md +++ b/docs/internals/CONNECTION-FILTERS.md @@ -34,15 +34,14 @@ Curl_easy *data connectdata *conn cf-ssl cf-socket |https://curl.se/|----> | properties |----> | keys |---> | socket |--> OS --> network +----------------+ +-----------------+ +-------+ +--------+ - Curl_write(data, buffer) +Curl_write(data, buffer) --> Curl_cfilter_write(data, data->conn, buffer) - ---> conn->filter->write(conn->filter, data, buffer) + --> conn->filter->write(conn->filter, data, buffer) ``` While connection filters all do different things, they look the same from the "outside". The code in `data` and `conn` does not really know **which** -filters are installed. `conn` just writes into the first filter, whatever that -is. +filters are installed. `conn` writes into the first filter, whatever that is. Same is true for filters. Each filter has a pointer to the `next` filter. When SSL has encrypted the data, it does not write to a socket, it writes to the @@ -76,11 +75,10 @@ etc. Each filter does in principle the following: -``` -static CURLcode -myfilter_cf_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) +```c +static CURLcode myfilter_cf_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) { CURLcode result; @@ -110,7 +108,7 @@ transfers. The memory footprint of a filter is relatively small: -``` +```c struct Curl_cfilter { const struct Curl_cftype *cft; /* the type providing implementation */ struct Curl_cfilter *next; /* next filter in chain */ @@ -125,27 +123,28 @@ The filter type `cft` is a singleton, one static struct for each type of filter. The `ctx` is where a filter holds its specific data. That varies by filter type. An http-proxy filter keeps the ongoing state of the CONNECT here, free it after its has been established. The SSL filter keeps the `SSL*` (if -OpenSSL is used) here until the connection is closed. So, this varies. +OpenSSL is used) here until the connection is closed. This varies. `conn` is a reference to the connection this filter belongs to, so nothing extra besides the pointer itself. Several things, that before were kept in `struct connectdata`, now goes into -the `filter->ctx` *when needed*. So, the memory footprint for connections that -do *not* use an http proxy, or socks, or https is lower. +the `filter->ctx` *when needed*. The memory footprint for connections that do +*not* use an http proxy, or socks, or https is lower. As to transfer efficiency, writing and reading through a filter comes at near zero cost *if the filter does not transform the data*. An http proxy or socks -filter, once it is connected, just passes the calls through. Those filters +filter, once it is connected, passes the calls through. Those filters implementations look like this: -``` -ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) +```c +ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, CURLcode *err) { return cf->next->cft->do_send(cf->next, data, buf, len, err); } ``` + The `recv` implementation is equivalent. ## Filter Types @@ -232,8 +231,8 @@ Users of `curl` may activate them by adding the name of the filter type to the `--trace-config` argument. For example, in order to get more detailed tracing of an HTTP/2 request, invoke curl with: -``` -> curl -v --trace-config ids,time,http/2 https://curl.se +```sh +> curl -v --trace-config ids,time,http/2 https://curl.se/ ``` Which gives you trace output with time information, transfer+connection ids @@ -260,7 +259,7 @@ into IPv4 and IPv6 and makes parallel attempts. The connection filter chain looks like this: ``` -* create connection for http://curl.se +* create connection for http://curl.se/ conn[curl.se] --> SETUP[TCP] --> HAPPY-EYEBALLS --> NULL * start connect conn[curl.se] --> SETUP[TCP] --> HAPPY-EYEBALLS --> NULL @@ -271,12 +270,17 @@ conn[curl.se] --> SETUP[TCP] --> HAPPY-EYEBALLS --> TCP[2a04:4e42:c00::347]:443 * transfer ``` -The modular design of connection filters and that we can plug them into each other is used to control the parallel attempts. When a `TCP` filter does not connect (in time), it is torn down and another one is created for the next address. This keeps the `TCP` filter simple. +The modular design of connection filters and that we can plug them into each +other is used to control the parallel attempts. When a `TCP` filter does not +connect (in time), it is torn down and another one is created for the next +address. This keeps the `TCP` filter simple. -The `HAPPY-EYEBALLS` on the other hand stays focused on its side of the problem. We can use it also to make other type of connection by just giving it another filter type to try to have happy eyeballing for QUIC: +The `HAPPY-EYEBALLS` on the other hand stays focused on its side of the +problem. We can use it also to make other type of connection by giving it +another filter type to try to have happy eyeballing for QUIC: ``` -* create connection for --http3-only https://curl.se +* create connection for --http3-only https://curl.se/ conn[curl.se] --> SETUP[QUIC] --> HAPPY-EYEBALLS --> NULL * start connect conn[curl.se] --> SETUP[QUIC] --> HAPPY-EYEBALLS --> NULL @@ -292,7 +296,7 @@ type that is used for `--http3` when **both** HTTP/3 and HTTP/2 or HTTP/1.1 shall be attempted: ``` -* create connection for --http3 https://curl.se +* create connection for --http3 https://curl.se/ conn[curl.se] --> HTTPS-CONNECT --> NULL * start connect conn[curl.se] --> HTTPS-CONNECT --> NULL diff --git a/docs/internals/LLIST.md b/docs/internals/LLIST.md index b9e192c6f4..b77e86598f 100644 --- a/docs/internals/LLIST.md +++ b/docs/internals/LLIST.md @@ -20,8 +20,8 @@ of `llist.c`). Use the functions. initialized with a call to `Curl_llist_init()` before it can be used To clean up a list, call `Curl_llist_destroy()`. Since the linked lists -themselves do not allocate memory, it can also be fine to just *not* clean up -the list. +themselves do not allocate memory, it can also be fine to *not* clean up the +list. ## Add a node @@ -47,9 +47,9 @@ Example: to add a `struct foobar` to a linked list. Add a node struct within it: struct foobar { - char *random; - struct Curl_llist_node storage; /* can be anywhere in the struct */ - char *data; + char *random; + struct Curl_llist_node storage; /* can be anywhere in the struct */ + char *data; }; struct Curl_llist barlist; /* the list for foobar entries */ @@ -77,18 +77,18 @@ To iterate over a list: first get the head entry and then iterate over the nodes as long there is a next. Each node has an *element* associated with it, the custom pointer you stored there. Usually a struct pointer or similar. - struct Curl_llist_node *iter; + struct Curl_llist_node *iter; - /* get the first entry of the 'barlist' */ - iter = Curl_llist_head(&barlist); + /* get the first entry of the 'barlist' */ + iter = Curl_llist_head(&barlist); - while(iter) { - /* extract the element pointer from the node */ - struct foobar *elem = Curl_node_elem(iter); + while(iter) { + /* extract the element pointer from the node */ + struct foobar *elem = Curl_node_elem(iter); - /* advance to the next node in the list */ - iter = Curl_node_next(iter); - } + /* advance to the next node in the list */ + iter = Curl_node_next(iter); + } # Function overview diff --git a/docs/internals/MID.md b/docs/internals/MID.md index 17b11ea792..5f1105008d 100644 --- a/docs/internals/MID.md +++ b/docs/internals/MID.md @@ -27,16 +27,16 @@ An `mid` is an `unsigned int`. There are two reserved values: this value. They get it assigned again when removed from a multi handle. -This makes potential range of `mid`s from `1` to `UINT_MAX - 1` *inside -the same multi handle at the same time*. However, the `multi->xfers` table -reuses `mid` values from previous transfers that have been removed. +This makes potential range of `mid`s from `1` to `UINT_MAX - 1` *inside the +same multi handle at the same time*. The `multi->xfers` table reuses `mid` +values from previous transfers that have been removed. `multi->xfers` is created with an initial capacity. At the time of this writing that is `16` for "multi_easy" handles (used in `curl_easy_perform()` and `512` for multi handles created with `curl_multi_init()`. The first added easy handle gets `mid == 1` assigned. The second one receives `2`, -even when the fist one has been removed already. Every added handle gets an +even when the first one has been removed already. Every added handle gets an `mid` one larger than the previously assigned one. Until the capacity of the table is reached and it starts looking for a free id at `1` again (`0` is always in the table). diff --git a/docs/internals/MQTT.md b/docs/internals/MQTT.md index 9b8d0efaae..8379130173 100644 --- a/docs/internals/MQTT.md +++ b/docs/internals/MQTT.md @@ -12,6 +12,9 @@ A plain "GET" subscribes to the topic and prints all published messages. Doing a "POST" publishes the post data to the topic and exits. +## TLS protection + +Use `mqtts://` to do MQTT over TLS: MQTTS. ### Subscribing @@ -26,9 +29,10 @@ Example subscribe: This sends an MQTT SUBSCRIBE packet for the topic `bedroom/temp` and listen in for incoming PUBLISH packets. -You can set the upkeep interval ms option to make curl send MQTT ping requests to the -server at an internal, to prevent the connection to get closed because of idleness. -You might then need to use the progress callback to cancel the operation. +You can set the upkeep interval ms option to make curl send MQTT ping requests +to the server at an internal, to prevent the connection to get closed because +of idleness. You might then need to use the progress callback to cancel the +operation. ### Publishing @@ -45,13 +49,13 @@ payload `75`. ## What does curl deliver as a response to a subscribe -Whenever a PUBLISH packet is received, curl outputs two bytes topic length (MSB | LSB), the topic followed by the -payload. +Whenever a PUBLISH packet is received, curl outputs two bytes topic length +(MSB | LSB), the topic followed by the payload. ## Caveats Remaining limitations: - - Only QoS level 0 is implemented for publish - - No way to set retain flag for publish - - No TLS (mqtts) support - - Naive EAGAIN handling does not handle split messages + +- Only QoS level 0 is implemented for publish +- No way to set retain flag for publish +- Naive EAGAIN handling does not handle split messages diff --git a/docs/internals/MULTI-EV.md b/docs/internals/MULTI-EV.md index 745955d5b6..f5d2fa831c 100644 --- a/docs/internals/MULTI-EV.md +++ b/docs/internals/MULTI-EV.md @@ -115,12 +115,12 @@ in the middle of things. Also, a transfer might be interested in several sockets at the same time (resolving, eye balling, ftp are all examples of those). -### And Come Again +### Come Again -While transfer and connection identifier are practically unique in a -libcurl application, sockets are not. Operating systems are keen on reusing -their resources, and the next socket may get the same identifier as -one just having been closed with high likelihood. +While transfer and connection identifiers are practically unique in a libcurl +application, sockets are not. Operating systems are keen on reusing their +resources, and the next socket may get the same identifier as a recently +closed one with high likelihood. This means that multi event handling needs to be informed *before* a close, clean up all its tracking and be ready to see that same socket identifier diff --git a/docs/internals/NEW-PROTOCOL.md b/docs/internals/NEW-PROTOCOL.md index 35beba6edb..832f8e843d 100644 --- a/docs/internals/NEW-PROTOCOL.md +++ b/docs/internals/NEW-PROTOCOL.md @@ -57,9 +57,9 @@ There should be a documented URL format. If there is an RFC for it there is no question about it but the syntax does not have to be a published RFC. It could be enough if it is already in use by other implementations. -If you make up the syntax just in order to be able to propose it to curl, then -you are in a bad place. URLs are designed and defined for interoperability. -There should at least be a good chance that other clients and servers can be +If you make up the syntax in order to be able to propose it to curl, then you +are in a bad place. URLs are designed and defined for interoperability. There +should at least be a good chance that other clients and servers can be implemented supporting the same URL syntax and work the same or similar way. URLs work on registered 'schemes'. There is a register of [all officially @@ -91,8 +91,8 @@ to curl and immediately once the code had been merged, the originator vanished from the face of the earth. That is fine, but we need to take the necessary precautions so when it happens we are still fine. -Our test infrastructure is powerful enough to test just about every possible -protocol - but it might require a bit of an effort to make it happen. +Our test infrastructure is powerful enough to test almost every protocol - but +it might require a bit of an effort to make it happen. ## Documentation diff --git a/docs/internals/RATELIMITS.md b/docs/internals/RATELIMITS.md new file mode 100644 index 0000000000..af72211b78 --- /dev/null +++ b/docs/internals/RATELIMITS.md @@ -0,0 +1,100 @@ + + +# Rate Limiting Transfers + +Rate limiting a transfer means that no more than "n bytes per second" +shall be sent or received. It can be set individually for both directions +via `CURLOPT_MAX_RECV_SPEED_LARGE` and `CURLOPT_MAX_SEND_SPEED_LARGE`. These +options may be adjusted for an ongoing transfer. + +### Implementation Base + +`ratelimit.[ch]` implements `struct Curl_rlimit` and functions to manage +such limits. It has the following properties: + +* `rate_per_sec`: how many "tokens" can be used per second, 0 for infinite. +* `tokens`: the currently available tokens to consume +* `burst_per_sec`: an upper limit on tokens available +* `ts`: the microsecond timestamp of the last tokens update +* `spare_us`: elapsed microseconds that have not counted yet for a token update +* `blocked`: if the limit is blocked + +Tokens can be *drained* from an `rlimit`. This reduces `tokens`, even to +negative values. To enforce the limits, tokens should not be drained +further when they reach 0, but such things may happen. + +An `rlimit`can be asked how long to wait until `tokens` are positive again. +This is given in milliseconds. When token are available, this wait +time is 0. + +Ideally a user of `rlimit` would consume the available tokens to 0, then +get a wait times of 1000ms, after which the set rate of tokens has +regenerated. Rinse and repeat. + +Should a user drain twice the amount of the rate, tokens are negative +and the wait time is 2 seconds. The `spare_us` account for the +time that has passed for the consumption. When a user takes 250ms to +consume the rate, the wait time is then 750ms. + +When a user drains nothing for two seconds, the available tokens would +grow to twice the rate, unless a burst rate is set. + +Finally, an `rlimit` may be set to `blocked` and later unblocked again. +A blocked `rlimit` has no tokens available. This works also when the rate +is unlimited (`rate_per_sec` set to 0). + +### Downloads + +`rlimit` is in `data->progress.dl.rlimit`. `setopt.c` initializes it whenever +the application sets `CURLOPT_MAX_RECV_SPEED_LARGE`. This may be done +in the middle of a transfer. + +`rlimit` tokens are drained in the "protocol" client writer. Checks for +capacity depend on the protocol: + +* HTTP and other plain protocols: `transfer.c:sendrecv_dl()` reads only +up to capacity. +* HTTP/2: capacity is used to adjust a stream's window size. Since all +streams start with `64kb`, `rlimit` takes a few seconds to take effect. +* HTTP/3: ngtcp2 acknowledges stream data according to capacity. It +keeps track of bytes not acknowledged yet. This has the same effect as HTTP/2 +window sizes. + +(The quiche API does not offer control of `ACK`s and `rlimits` for download +do not work in that backend.) + +### Uploads + +`rlimit` is in `data->progress.ul.rlimit`. `setopt.c` initializes it whenever +the application sets `CURLOPT_MAX_SEND_SPEED_LARGE`. This may be done +in the middle of a transfer. + +The upload capacity is checked in `Curl_client_read()` and readers are +only asked to read bytes up to the `rlimit` capacity. This limits upload +of data for all protocols in the same way. + +### Pause/Unpause + +Pausing of up-/downloads sets the corresponding `rlimit` to blocked. Unpausing +removes that block. + +### Suspending transfers + +While obeying the `rlimit` for up-/download leads to the desired transfer +rates, the other issue that needs care is CPU consumption. + +`rlimits` are inspected when computing the "pollset" of a transfer. When +a transfer wants to send, but not send tokens are available, the `POLLOUT` +is removed from the pollset. Same for receiving. + +For a transfer that is, due to `rlimit`, not able to progress, the pollset +is then empty. No socket events are monitored, no CPU activity +happens. For paused transfers, this is sufficient. + +Draining `rlimit` happens when a transfer is in `PERFORM` state and +exhausted limits cause the timer `TOOFAST` to be set. When the fires, +the transfer runs again and `rlimit`s are re-evaluated. diff --git a/docs/internals/SCORECARD.md b/docs/internals/SCORECARD.md index 7839e0b61f..049e1567b6 100644 --- a/docs/internals/SCORECARD.md +++ b/docs/internals/SCORECARD.md @@ -12,41 +12,43 @@ curl/libcurl in a reproducible fashion to judge improvements or detect regressions. They are not intended to represent real world scenarios as such. -This script is not part of any official interface and we may -change it in the future according to the project's needs. +This script is not part of any official interface and we may change it in +the future according to the project's needs. ## setup -When you are able to run curl's `pytest` suite, scorecard should work -for you as well. They start a local Apache httpd or Caddy server and -invoke the locally build `src/curl` (by default). +When you are able to run curl's `pytest` suite, scorecard should work for you +as well. They start a local Apache httpd or Caddy server and invoke the +locally build `src/curl` (by default). ## invocation A typical invocation for measuring performance of HTTP/2 downloads would be: -``` +```sh curl> python3 tests/http/scorecard.py -d h2 ``` -and this prints a table with the results. The last argument is the protocol to test and -it can be `h1`, `h2` or `h3`. You can add `--json` to get results in JSON instead of text. +and this prints a table with the results. The last argument is the protocol to +test and it can be `h1`, `h2` or `h3`. You can add `--json` to get results in +JSON instead of text. Help for all command line options are available via: -``` +```sh curl> python3 tests/http/scorecard.py -h ``` ## scenarios -Apart from `-d/--downloads` there is `-u/--uploads` and `-r/--requests`. These are run with -a variation of resource sizes and parallelism by default. You can specify these in some way -if you are just interested in a particular case. +Apart from `-d/--downloads` there is `-u/--uploads` and `-r/--requests`. These +are run with a variation of resource sizes and parallelism by default. You can +specify these in some way if you are interested in a particular case. -For example, to run downloads of a 1 MB resource only, 100 times with at max 6 parallel transfers, use: +For example, to run downloads of a 1 MB resource only, 100 times with at max 6 +parallel transfers, use: -``` +```sh curl> python3 tests/http/scorecard.py -d --download-sizes=1mb --download-count=100 --download-parallel=6 h2 ``` @@ -59,20 +61,30 @@ If you have configured curl with `--with-test-danted=` for a with arguments `--socks4` or `--socks5` to test performance with a SOCKS proxy involved. (Note: this does not work for HTTP/3) -## dtrace - -With the `--dtrace` option, scorecard produces a dtrace sample of the user stacks in `tests/http/gen/curl/curl.user_stacks`. On many platforms, `dtrace` requires **special permissions**. It is therefore invoked via `sudo` and you should make sure that sudo works for the run without prompting for a password. - -Note: the file is the trace of the last curl invocation by scorecard. Use the parameters to narrow down the runs to the particular case you are interested in. - ## flame graphs -With the excellent [Flame Graph](https://github.com/brendangregg/FlameGraph) by Brendan Gregg, scorecard can turn the `dtrace` samples into an interactive SVG. Set the environment variable `FLAMEGRAPH` to the location of your clone of that project and invoked scorecard with the `--flame` option. Like +With the excellent [Flame Graph](https://github.com/brendangregg/FlameGraph) +by Brendan Gregg, scorecard can turn `perf`/`dtrace` samples into an +interactive SVG. Either clone the `Flamegraph` repository next to your `curl` +project or set the environment variable `FLAMEGRAPH` to the location of your +clone. Then run scorecard with the `--flame` option, like -``` +```sh curl> FLAMEGRAPH=/Users/sei/projects/FlameGraph python3 tests/http/scorecard.py \ -r --request-count=50000 --request-parallels=100 --samples=1 --flame h2 ``` -and the SVG of the run is in `tests/http/gen/curl/curl.flamegraph.svg`. You can open that in Firefox and zoom in/out of stacks of interest. -Note: as with `dtrace`, the flame graph is for the last invocation of curl done by scorecard. +and the SVG of the run is in `tests/http/gen/curl/curl.flamegraph.svg`. You +can open that in Firefox and zoom in/out of stacks of interest. + +The flame graph is about the last run of `curl`. That is why you should add +scorecard arguments that restrict measurements to a single run. + +### Measures/Privileges + +The `--flame` option uses `perf` on linux and `dtrace` on macOS. Since both +tools require special privileges, they are run via the `sudo` command by +scorecard. This means you need to issue a `sudo` recently enough before +running scorecard, so no new password check is needed. + +There is no support right now for measurements on other platforms. diff --git a/docs/internals/STRPARSE.md b/docs/internals/STRPARSE.md index 7d1a3a402f..8d0e8ba515 100644 --- a/docs/internals/STRPARSE.md +++ b/docs/internals/STRPARSE.md @@ -161,7 +161,7 @@ string. int curlx_str_number(char **linep, curl_size_t *nump, size_t max); ~~~ -Get an unsigned decimal number not larger than `max`. Leading zeroes are just +Get an unsigned decimal number not larger than `max`. Leading zeroes are swallowed. Return non-zero on error. Returns error if there was not a single digit. @@ -181,8 +181,8 @@ int curlx_str_hex(char **linep, curl_size_t *nump, size_t max); ~~~ Get an unsigned hexadecimal number not larger than `max`. Leading zeroes are -just swallowed. Return non-zero on error. Returns error if there was not a -single digit. Does *not* handled `0x` prefix. +swallowed. Return non-zero on error. Returns error if there was not a single +digit. Does *not* handled `0x` prefix. ## `curlx_str_octal` @@ -190,7 +190,7 @@ single digit. Does *not* handled `0x` prefix. int curlx_str_octal(char **linep, curl_size_t *nump, size_t max); ~~~ -Get an unsigned octal number not larger than `max`. Leading zeroes are just +Get an unsigned octal number not larger than `max`. Leading zeroes are swallowed. Return non-zero on error. Returns error if there was not a single digit. diff --git a/docs/internals/THRDPOOL-AND-QUEUE.md b/docs/internals/THRDPOOL-AND-QUEUE.md new file mode 100644 index 0000000000..e55f8a2be3 --- /dev/null +++ b/docs/internals/THRDPOOL-AND-QUEUE.md @@ -0,0 +1,112 @@ + + +# Thread Pool and Queue + +The thread pool and queue manage asynchronous processing of "work items" +to a user. The "work items" are opaque to the pool and queue, represented +by a `void *`, handled via callback functions. + +Thread pool and queue are available with `pthreads` or native Win32 +builds. + +## `Curl_thrdpool` + +This data structure manages a pool of threads for asynchronous operations. + +### Properties + +A pool's properties are: + +- minimum number of threads running, default 0 +- maximum number of threads running +- timeout for idle threads before they shut down + +The minimum number of threads is started at creation of the pool and +kept always running. On demand, when more work is available but all +existing threads are busy, it starts new threads, up to maximum. + +When work ceases, the threads "above" the minimum number exit again +after the given idle time. + +### Operation + +The pool is created with providing three callback functions: + +- `take`: the pool calls this to take a new "work item" for processing. From + the pool's point of view, a work item is a `void *`. "take" is called from + the pool's threads. When getting anything besides `NULL`, the thread is + "busy". On getting `NULL`, the thread becomes idle. +- `process`: called by a pool thread to process a work item. This can not + return any error. Any error handling must be done via properties in + the work item itself, opaque to the pool. +- `return`: after processing, the work item is returned and the pool has + no longer have any memory of it. + +The pool only tries to "take" new work items when told to. Calling +`Curl_thrdpool_signal(pool, n)` awakens up to `n`threads which then +take new work items. This may cause new threads being started. The other +time a pool thread "take"s work it when it has finished +processing and returned another item. + +A thread pool can be destroyed via `Curl_thrdpool_destroy(pool, join)` where +`join` determines if active threads shall be joined or detached. + +### Safety + +The thread pool operates use a mutex and condition variables to manage +concurrency. All interactions and callback invocation are done under +the pool's mutex lock, *except* the "process" callback which is invoked +unlocked. + +To avoid deadlocks, no callback must invoked other pool functions. Also, +any call of pool functions may result in callback invocations. + +The "work items", once "taken" by the pool, should not be referenced +from any other place. Thread pools **always** invoke the "return" +callback on a work item, even after the pool has been destroyed by +detaching the threads. + +There is a `user_data` in the pool's creation that is passed to "take" +and "return" callbacks. Once a pool is destroyed, this `user_data` is +cleared and "return" callbacks always see a `NULL`. This way, +the "return" callback may act on that fact. + +## `Curl_thrdq` + +A `thrdq` is a two-way queue with a thread pool. Users of a thread queue may +"send" work items into the queue and "receive" processed items back. + +### Properties + +A queue's properties are: + +- The properties of the thread pool to create +- the maximum length of the "send" queue, 0 for unlimited + +### Operation + +The queue is created with providing three callback functions: + +- `free`: called to free a work item that is in the queue but is + no longer returned (or processed). This happens when the queue is + destroyed or when work items are removed for other reasons. +- `process`: process the item. Can not fail. +- `event`: called when work items have been added to the "receive" list. + +Users of a thread queue call `Curl_thrdq_send()` to add a work item to +the queue. Calling `Curl_thrdq_recv()` delivers processed items back. + +### Safety + +The thread queue operates use a mutex and condition variables to manage +concurrency. All interactions and callback invocation are done under +the queue's mutex lock, *except* the "process" callback which is invoked +unlocked. + +Users of a thread queue should not hold any reference to work items sent +into the queue. The provided "free" callback has to take care of any +resources allocated by work items. diff --git a/docs/internals/TIME-KEEPING.md b/docs/internals/TIME-KEEPING.md new file mode 100644 index 0000000000..b43daae0be --- /dev/null +++ b/docs/internals/TIME-KEEPING.md @@ -0,0 +1,53 @@ + + +# Keeping Time + +Transfers need the current time to handle timeouts and keep a record of +events. The current time function is `curlx_now()` and it uses a **monotonic** +clock on most platforms. This ensures that time only ever increases (the +timestamps it gives are however not the "real" world clock). + +## Initial Approach (now historic) + +The loop processing functions called `curlx_now()` at the beginning and then +passed a pointer to the `struct curltime now` to functions to save them the +calls. Passing this pointer down to all functions possibly involved was not +done as this pollutes the internal APIs. + +Some functions continued to call `curlx_now()` on their own while others used +the passed pointer *to a timestamp in the past*. This led to a transfer +experiencing *jumps* in time, reversing cause and effect. On fast systems, +this was mostly not noticeable. On slow machines or in CI, this led to rare +and annoying test failures. + +(Especially when we added assertions that the reported "timeline" of a +transfer was in the correct order: *queue -> nameloopup -> connect -> +appconnect ->...*.) + +## Revised Approach + +The strategy of handling transfer's time is now: + +* Keep a "now" timestamp in the multi handle. Keep a fallback "now" timestamp + in the easy handle. +* Always use `Curl_pgrs_now(data)` to get the current time of a transfer. +* Do not use `curlx_now()` directly for transfer handling (exceptions apply + for loops). + +This has the following advantages: + +* No need to pass a `struct curltime` around or pass a pointer to an outdated + timestamp to other functions. +* No need to calculate the exact `now` until it is really used. +* Passing a `const` pointer is better than struct passing. Updating and + passing a pointer to the same memory location for all transfers is even + better. + +Caveats: + +* do not store the pointer returned by `Curl_pgrs_now(data)` anywhere that + outlives the current code invocation. diff --git a/docs/internals/TLS-SESSIONS.md b/docs/internals/TLS-SESSIONS.md index 8f887d2e07..b108fbfcfb 100644 --- a/docs/internals/TLS-SESSIONS.md +++ b/docs/internals/TLS-SESSIONS.md @@ -52,23 +52,21 @@ Examples: same as the previous, except it is configured to use TLSv1.2 as min and max versions. -Different configurations produce different keys which is just what -curl needs when handling SSL session tickets. +Different configurations produce different keys which is what curl needs when +handling SSL session tickets. One important thing: peer keys do not contain confidential information. If you configure a client certificate or SRP authentication with username/password, these are not part of the peer key. -However, peer keys carry the hostnames you use curl for. They *do* -leak the privacy of your communication. We recommend to *not* persist -peer keys for this reason. +Peer keys carry the hostnames you use curl for. They *do* leak the privacy of +your communication. We recommend to *not* persist peer keys for this reason. **Caveat**: The key may contain filenames or paths. It does not reflect the *contents* in the file system. If you change `/etc/ssl/cert.pem` and reuse a previous ticket, curl might trust a server which no longer has a root certificate in the file. - ## Session Cache Access #### Lookups @@ -106,7 +104,7 @@ then configures its TLS backend and *returns* the ticket to the cache. The cache needs to treat tickets from TLSv1.2 and 1.3 differently. 1.2 tickets should be reused, but 1.3 tickets SHOULD NOT (RFC 8446). The session cache -simply drops 1.3 tickets when they are returned after use, but keeps a 1.2 +drops 1.3 tickets when they are returned after use, but keeps a 1.2 ticket. When a ticket is *put* into the cache, there is also a difference. There @@ -122,8 +120,8 @@ concurrent connections do not reuse the same ticket. #### Privacy and Security As mentioned above, ssl peer keys are not intended for storage in a file -system. They clearly show which hosts the user talked to. This maybe "just" -privacy relevant, but has security implications as an attacker might find +system. They clearly show which hosts the user talked to. This is not only +privacy relevant, but also has security implications as an attacker might find worthy targets among your peer keys. Also, we do not recommend to persist TLSv1.2 tickets. @@ -139,11 +137,11 @@ The salt is generated randomly for each peer key on export. The SHA256 makes sure that the peer key cannot be reversed and that a slightly different key still produces a different result. -This means an attacker cannot just "grep" a session file for a particular -entry, e.g. if they want to know if you accessed a specific host. They *can* -however compute the SHA256 hashes for all salts in the file and find a -specific entry. They *cannot* find a hostname they do not know. They would -have to brute force by guessing. +This means an attacker cannot "grep" a session file for a particular entry, +e.g. if they want to know if you accessed a specific host. They *can* however +compute the SHA256 hashes for all salts in the file and find a specific entry. +They *cannot* find a hostname they do not know. They would have to brute force +by guessing. #### Import diff --git a/docs/internals/UINT_SETS.md b/docs/internals/UINT_SETS.md index de00b9b47a..963cc82d48 100644 --- a/docs/internals/UINT_SETS.md +++ b/docs/internals/UINT_SETS.md @@ -4,35 +4,34 @@ Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl --> -# Unsigned Int Sets +# `uint32_t` Sets -The multi handle tracks added easy handles via an unsigned int -it calls an `mid`. There are four data structures for unsigned int -optimized for the multi use case. +The multi handle tracks added easy handles via an `uint32_t` it calls an +`mid`. There are four data structures for `uint32_t` optimized for the multi +use case. -## `uint_tbl` +## `uint32_tbl` -`uint_table`, implemented in `uint-table.[ch]` manages an array -of `void *`. The unsigned int are the index into this array. It is -created with a *capacity* which can be *resized*. The table assigns -the index when a `void *` is *added*. It keeps track of the last -assigned index and uses the next available larger index for a -subsequent add. Reaching *capacity* it wraps around. +`uint32_table`, implemented in `uint-table.[ch]` manages an array of `void *`. +The `uint32_t` is the index into this array. It is created with a *capacity* +which can be *resized*. The table assigns the index when a `void *` is +*added*. It keeps track of the last assigned index and uses the next available +larger index for a subsequent add. Reaching *capacity* it wraps around. The table *can not* store `NULL` values. The largest possible index -is `UINT_MAX - 1`. +is `UINT32_MAX - 1`. -The table is iterated over by asking for the *first* existing index, -meaning the smallest number that has an entry, if the table is not -empty. To get the *next* entry, one passes the index of the previous -iteration step. It does not matter if the previous index is still -in the table. Sample code for a table iteration would look like this: +The table is iterated over by asking for the *first* existing index, meaning +the smallest number that has an entry, if the table is not empty. To get +the *next* entry, one passes the index of the previous iteration step. It does +not matter if the previous index is still in the table. Sample code for a table +iteration would look like this: ```c -unsigned int mid; +uint32_t int mid; void *entry; -if(Curl_uint_tbl_first(tbl, &mid, &entry)) { +if(Curl_uint32_tbl_first(tbl, &mid, &entry)) { do { /* operate on entry with index mid */ } @@ -45,51 +44,56 @@ This iteration has the following properties: * entries in the table can be added/removed safely. * all entries that are not removed during the iteration are visited. -* the table may be resized to a larger capacity without affecting visited entries. +* the table may be resized to a larger capacity without affecting visited + entries. * entries added with a larger index than the current are visited. ### Memory -For storing 1000 entries, the table would allocate one block of 8KB on a 64-bit system, -plus the 2 pointers and 3 unsigned int in its base `struct uint_tbl`. A resize -allocates a completely new pointer array, copy the existing entries and free the previous one. +For storing 1000 entries, the table would allocate one block of 8KB on +a 64-bit system, plus the 2 pointers and 3 `uint32_t` in its base `struct +uint32_tbl`. A resize allocates a completely new pointer array, copy +the existing entries and free the previous one. ### Performance -Lookups of entries are only an index into the array, O(1) with a tiny 1. Adding -entries and iterations are more work: +Lookups of entries are only an index into the array, O(1) with a tiny 1. +Adding entries and iterations are more work: -1. adding an entry means "find the first free index larger than the previous assigned - one". Worst case for this is a table with only a single free index where `capacity - 1` - checks on `NULL` values would be performed, O(N). If the single free index is randomly - distributed, this would be O(N/2). -2. iterating a table scans for the first not `NULL` entry after the start index. This - makes a complete iteration O(N) work. +1. adding an entry means "find the first free index larger than the previous + assigned one". Worst case for this is a table with only a single free index + where `capacity - 1` checks on `NULL` values would be performed, O(N). If + the single free index is randomly distributed, this would be O(N/2). +2. iterating a table scans for the first not `NULL` entry after the start + index. This makes a complete iteration O(N) work. -In the multi use case, point 1 is remedied by growing the table so that a good chunk -of free entries always exists. +In the multi use case, point 1 is remedied by growing the table so that a good +chunk of free entries always exists. -Point 2 is less of an issue for a multi, since it does not really matter when the -number of transfer is relatively small. A multi managing a larger set needs to operate -event based anyway and table iterations rarely are needed. +Point 2 is less of an issue for a multi, since it does not really matter when +the number of transfer is relatively small. A multi managing a larger set +needs to operate event based anyway and table iterations rarely are needed. For these reasons, the simple implementation was preferred. Should this become -a concern, there are options like "free index lists" or, alternatively, an internal -bitset that scans better. +a concern, there are options like "free index lists" or, alternatively, an +internal bitset that scans better. -## `uint_bset` +## `uint32_bset` -A bitset for unsigned integers, allowing fast add/remove operations. It is initialized -with a *capacity*, meaning it can store only the numbers in the range `[0, capacity-1]`. -It can be *resized* and safely *iterated*. `uint_bset` is designed to operate in combination with `uint_tbl`. +A bitset for `uint32_t` values, allowing fast add/remove operations. It is +initialized with a *capacity*, meaning it can store only the numbers in the +range `[0, capacity-1]`. It can be *resized* and safely *iterated*. +`uint32_bset` is designed to operate in combination with `uint_tbl`. -The bitset keeps an array of `curl_uint64_t`. The first array entry keeps the numbers 0 to 63, the -second 64 to 127 and so on. A bitset with capacity 1024 would therefore allocate an array -of 16 64-bit values (128 bytes). Operations for an unsigned int divide it by 64 for the array index and then check/set/clear the bit of the remainder. +The bitset keeps an array of `uint64_t`. The first array entry keeps the +numbers 0 to 63, the second 64 to 127 and so on. A bitset with capacity 1024 +would therefore allocate an array of 16 64-bit values (128 bytes). Operations +for an unsigned int divide it by 64 for the array index and then +check/set/clear the bit of the remainder. -Iterator works the same as with `uint_tbl`: ask the bitset for the *first* number present and -then use that to get the *next* higher number present. Like the table, this safe for -adds/removes and growing the set while iterating. +Iterator works the same as with `uint32_tbl`: ask the bitset for the *first* +number present and then use that to get the *next* higher number present. Like +the table, this safe for adds/removes and growing the set while iterating. ### Memory @@ -98,31 +102,36 @@ A bitset for 40000 transfers occupies 5KB of memory. ## Performance -Operations for add/remove/check are O(1). Iteration needs to scan for the next bit set. The -number of scans is small (see memory footprint) and, for checking bits, many compilers -offer primitives for special CPU instructions. +Operations for add/remove/check are O(1). Iteration needs to scan for the next +bit set. The number of scans is small (see memory footprint) and, for checking +bits, many compilers offer primitives for special CPU instructions. -## `uint_spbset` +## `uint32_spbset` -While the memory footprint of `uint_bset` is good, it still needs 5KB to store the single number 40000. This -is not optimal when many are needed. For example, in event based processing, each socket needs to -keep track of the transfers involved. There are many sockets potentially, but each one mostly tracks -a single transfer or few (on HTTP/2 connection borderline up to 100). +While the memory footprint of `uint32_bset` is good, it still needs 5KB to +store the single number 40000. This is not optimal when many are needed. For +example, in event based processing, each socket needs to keep track of the +transfers involved. There are many sockets potentially, but each one mostly +tracks a single transfer or few (on HTTP/2 connection borderline up to 100). -For such uses cases, the `uint_spbset` is intended: track a small number of unsigned int, potentially -rather "close" together. It keeps "chunks" with an offset and has no capacity limit. +For such uses cases, the `uint32_spbset` is intended: track a small number of +unsigned int, potentially rather "close" together. It keeps "chunks" with an +offset and has no capacity limit. -Example: adding the number 40000 to an empty sparse bitset would have one chunk with offset 39936, keeping -track of the numbers 39936 to 40192 (a chunk has 4 64-bit values). The numbers in that range can be handled -without further allocations. +Example: adding the number 40000 to an empty sparse bitset would have one +chunk with offset 39936, keeping track of the numbers 39936 to 40192 (a chunk +has 4 64-bit values). The numbers in that range can be handled without further +allocations. -The worst case is then storing 100 numbers that lie in separate intervals. Then 100 chunks -would need to be allocated and linked, resulting in overall 4 KB of memory used. +The worst case is then storing 100 numbers that lie in separate intervals. +Then 100 chunks would need to be allocated and linked, resulting in overall 4 +KB of memory used. Iterating a sparse bitset works the same as for bitset and table. -## `uint_hash` +## `uint32_hash` -At last, there are places in libcurl such as the HTTP/2 and HTTP/3 protocol implementations that need -to store their own data related to a transfer. `uint_hash` allows then to associate an unsigned int, -e.g. the transfer's `mid`, to their own data. +At last, there are places in libcurl such as the HTTP/2 and HTTP/3 protocol +implementations that need to store their own data related to a transfer. +`uint32_hash` allows then to associate an unsigned int, e.g. the transfer's +`mid`, to their own data. diff --git a/docs/internals/WEBSOCKET.md b/docs/internals/WEBSOCKET.md index 230bb91249..b389475815 100644 --- a/docs/internals/WEBSOCKET.md +++ b/docs/internals/WEBSOCKET.md @@ -38,17 +38,17 @@ WebSocket with libcurl can be done two ways. The new options to `curl_easy_setopt()`: - `CURLOPT_WS_OPTIONS` - to control specific behavior. `CURLWS_RAW_MODE` makes - libcurl provide all WebSocket traffic raw in the callback. `CURLWS_NOAUTOPONG` - disables automatic `PONG` replies. +`CURLOPT_WS_OPTIONS` - to control specific behavior. `CURLWS_RAW_MODE` makes +libcurl provide all WebSocket traffic raw in the callback. `CURLWS_NOAUTOPONG` +disables automatic `PONG` replies. The new function calls: - `curl_ws_recv()` - receive a WebSocket frame +`curl_ws_recv()` - receive a WebSocket frame - `curl_ws_send()` - send a WebSocket frame +`curl_ws_send()` - send a WebSocket frame - `curl_ws_meta()` - return WebSocket metadata within a write callback +`curl_ws_meta()` - return WebSocket metadata within a write callback ## Max frame size @@ -88,13 +88,13 @@ work has not been started. Ideas: - - Read stdin and send off as messages. Consider newline as end of fragment. - (default to text? offer option to set binary) - - Respond to PINGs automatically - - Issue PINGs at some default interval (option to switch off/change interval?) - - Allow `-d` to specify (initial) data to send (should the format allow for - multiple separate frames?) - - Exit after N messages received, where N can be zero. +- Read stdin and send off as messages. Consider newline as end of fragment. + (default to text? offer option to set binary) +- Respond to PINGs automatically +- Issue PINGs at some default interval (option to switch off/change interval?) +- Allow `-d` to specify (initial) data to send (should the format allow for + multiple separate frames?) +- Exit after N messages received, where N can be zero. ## Future work diff --git a/docs/libcurl/ABI.md b/docs/libcurl/ABI.md index 0ec9928b98..9cd2ad801b 100644 --- a/docs/libcurl/ABI.md +++ b/docs/libcurl/ABI.md @@ -4,65 +4,64 @@ Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl --> -ABI - Application Binary Interface -================================== +# ABI - Application Binary Interface - "ABI" describes the low-level interface between an application program and a - library. Calling conventions, function arguments, return values, struct - sizes/defines and more. +"ABI" describes the low-level interface between an application program and a +library. Calling conventions, function arguments, return values, struct +sizes/defines and more. - [Wikipedia has a longer description](https://en.wikipedia.org/wiki/Application_binary_interface) +[Wikipedia has a longer description](https://en.wikipedia.org/wiki/Application_binary_interface) ## Upgrades - A libcurl upgrade does not break the ABI or change established and documented - behavior. Your application can remain using libcurl just as before, only with - fewer bugs and possibly with added new features. +A libcurl upgrade does not break the ABI or change established and documented +behavior. Your application can remain using libcurl like before, only with +fewer bugs and possibly with added new features. ## Version Numbers - In libcurl land, you cannot tell by the libcurl version number if that - libcurl is binary compatible or not with another libcurl version. As a rule, - we do not break the ABI so you can *always* upgrade to a later version without - any loss or change in functionality. +In libcurl land, you cannot tell by the libcurl version number if that +libcurl is binary compatible or not with another libcurl version. As a rule, +we do not break the ABI so you can *always* upgrade to a later version without +any loss or change in functionality. ## SONAME Bumps - Whenever there are changes done to the library that causes an ABI breakage, - that may require your application to get attention or possibly be changed to - adhere to new things, we bump the SONAME. Then the library gets a different - output name and thus can in fact be installed in parallel with an older - installed lib (on most systems). Thus, old applications built against the - previous ABI version remains working and using the older lib, while newer - applications build and use the newer one. +Whenever there are changes done to the library that causes an ABI breakage, +that may require your application to get attention or possibly be changed to +adhere to new things, we bump the SONAME. Then the library gets a different +output name and thus can in fact be installed in parallel with an older +installed lib (on most systems). Thus, old applications built against the +previous ABI version remains working and using the older lib, while newer +applications build and use the newer one. - During the first seven years of libcurl releases, there have only been four - ABI breakages. +During the first seven years of libcurl releases, there have only been four +ABI breakages. - We are determined to bump the SONAME as rarely as possible. Ideally, we never - do it again. +We are determined to bump the SONAME as rarely as possible. Ideally, we never +do it again. ## Downgrades - Going to an older libcurl version from one you are currently using can be a - tricky thing. Mostly we add features and options to newer libcurls as that - does not break ABI or hamper existing applications. This has the implication - that going backwards may get you in a situation where you pick a libcurl that - does not support the options your application needs. Or possibly you even - downgrade so far so you cross an ABI break border and thus a different - SONAME, and then your application may need to adapt to the modified ABI. +Going to an older libcurl version from one you are currently using can be a +tricky thing. Mostly we add features and options to newer libcurls as that +does not break ABI or hamper existing applications. This has the implication +that going backwards may get you in a situation where you pick a libcurl that +does not support the options your application needs. Or possibly you even +downgrade so far so you cross an ABI break border and thus a different +SONAME, and then your application may need to adapt to the modified ABI. ## History - The previous major library SONAME number bumps (breaking backwards - compatibility) happened the following times: +The previous major library SONAME number bumps (breaking backwards +compatibility) happened the following times: - 0 - libcurl 7.1, August 2000 +0 - libcurl 7.1, August 2000 - 1 - libcurl 7.5 December 2000 +1 - libcurl 7.5 December 2000 - 2 - libcurl 7.7 March 2001 +2 - libcurl 7.7 March 2001 - 3 - libcurl 7.12.0 June 2004 +3 - libcurl 7.12.0 June 2004 - 4 - libcurl 7.16.0 October 2006 +4 - libcurl 7.16.0 October 2006 diff --git a/docs/libcurl/CMakeLists.txt b/docs/libcurl/CMakeLists.txt index 2102dd8ee3..bee1021f72 100644 --- a/docs/libcurl/CMakeLists.txt +++ b/docs/libcurl/CMakeLists.txt @@ -52,11 +52,9 @@ function(curl_add_manual_pages _listname) endif() list(APPEND _rofffiles "${CMAKE_CURRENT_BINARY_DIR}/${_file}") - if(_file STREQUAL "libcurl-symbols.3") - # Special case, an auto-generated file. - string(REPLACE ".3" ".md" _mdfile "${CMAKE_CURRENT_BINARY_DIR}/${_file}") - else() - string(REPLACE ".3" ".md" _mdfile "${_file}") + string(REPLACE ".3" ".md" _mdfile "${_file}") + if(_file STREQUAL "libcurl-symbols.3") # Special case for auto-generated file + set(_mdfile "${CMAKE_CURRENT_BINARY_DIR}/${_mdfile}") endif() list(APPEND _mdfiles "${_mdfile}") endforeach() diff --git a/docs/libcurl/Makefile.inc b/docs/libcurl/Makefile.inc index 9bc665d1c6..b75782d1ce 100644 --- a/docs/libcurl/Makefile.inc +++ b/docs/libcurl/Makefile.inc @@ -24,105 +24,107 @@ # Shared between CMakeLists.txt and Makefile.am man_MANS = \ - curl_easy_cleanup.3 \ - curl_easy_duphandle.3 \ - curl_easy_escape.3 \ - curl_easy_getinfo.3 \ - curl_easy_header.3 \ - curl_easy_init.3 \ - curl_easy_nextheader.3 \ - curl_easy_option_by_id.3 \ - curl_easy_option_by_name.3 \ - curl_easy_option_next.3 \ - curl_easy_pause.3 \ - curl_easy_perform.3 \ - curl_easy_recv.3 \ - curl_easy_reset.3 \ - curl_easy_send.3 \ - curl_easy_setopt.3 \ - curl_easy_ssls_export.3 \ - curl_easy_ssls_import.3 \ - curl_easy_strerror.3 \ - curl_easy_unescape.3 \ - curl_easy_upkeep.3 \ - curl_escape.3 \ - curl_formadd.3 \ - curl_formfree.3 \ - curl_formget.3 \ - curl_free.3 \ - curl_getdate.3 \ - curl_getenv.3 \ - curl_global_cleanup.3 \ - curl_global_init.3 \ - curl_global_init_mem.3 \ - curl_global_sslset.3 \ - curl_global_trace.3 \ - curl_mime_addpart.3 \ - curl_mime_data.3 \ - curl_mime_data_cb.3 \ - curl_mime_encoder.3 \ - curl_mime_filedata.3 \ - curl_mime_filename.3 \ - curl_mime_free.3 \ - curl_mime_headers.3 \ - curl_mime_init.3 \ - curl_mime_name.3 \ - curl_mime_subparts.3 \ - curl_mime_type.3 \ - curl_mprintf.3 \ - curl_multi_add_handle.3 \ - curl_multi_assign.3 \ - curl_multi_cleanup.3 \ - curl_multi_fdset.3 \ - curl_multi_get_handles.3 \ - curl_multi_get_offt.3 \ - curl_multi_info_read.3 \ - curl_multi_init.3 \ - curl_multi_perform.3 \ - curl_multi_poll.3 \ - curl_multi_remove_handle.3 \ - curl_multi_setopt.3 \ - curl_multi_socket.3 \ - curl_multi_socket_action.3 \ - curl_multi_socket_all.3 \ - curl_multi_strerror.3 \ - curl_multi_timeout.3 \ - curl_multi_wait.3 \ - curl_multi_waitfds.3 \ - curl_multi_wakeup.3 \ - curl_pushheader_byname.3 \ - curl_pushheader_bynum.3 \ - curl_share_cleanup.3 \ - curl_share_init.3 \ - curl_share_setopt.3 \ - curl_share_strerror.3 \ - curl_slist_append.3 \ - curl_slist_free_all.3 \ - curl_strequal.3 \ - curl_strnequal.3 \ - curl_unescape.3 \ - curl_url.3 \ - curl_url_cleanup.3 \ - curl_url_dup.3 \ - curl_url_get.3 \ - curl_url_set.3 \ - curl_url_strerror.3 \ - curl_version.3 \ - curl_version_info.3 \ - curl_ws_meta.3 \ - curl_ws_recv.3 \ - curl_ws_send.3 \ - curl_ws_start_frame.3 \ - libcurl-easy.3 \ - libcurl-env-dbg.3 \ - libcurl-env.3 \ - libcurl-errors.3 \ - libcurl-multi.3 \ - libcurl-security.3 \ - libcurl-share.3 \ - libcurl-symbols.3 \ - libcurl-thread.3 \ - libcurl-tutorial.3 \ - libcurl-url.3 \ - libcurl-ws.3 \ - libcurl.3 + curl_easy_cleanup.3 \ + curl_easy_duphandle.3 \ + curl_easy_escape.3 \ + curl_easy_getinfo.3 \ + curl_easy_header.3 \ + curl_easy_init.3 \ + curl_easy_nextheader.3 \ + curl_easy_option_by_id.3 \ + curl_easy_option_by_name.3 \ + curl_easy_option_next.3 \ + curl_easy_pause.3 \ + curl_easy_perform.3 \ + curl_easy_recv.3 \ + curl_easy_reset.3 \ + curl_easy_send.3 \ + curl_easy_setopt.3 \ + curl_easy_ssls_export.3 \ + curl_easy_ssls_import.3 \ + curl_easy_strerror.3 \ + curl_easy_unescape.3 \ + curl_easy_upkeep.3 \ + curl_escape.3 \ + curl_formadd.3 \ + curl_formfree.3 \ + curl_formget.3 \ + curl_free.3 \ + curl_getdate.3 \ + curl_getenv.3 \ + curl_global_cleanup.3 \ + curl_global_init.3 \ + curl_global_init_mem.3 \ + curl_global_sslset.3 \ + curl_global_trace.3 \ + curl_mime_addpart.3 \ + curl_mime_data.3 \ + curl_mime_data_cb.3 \ + curl_mime_encoder.3 \ + curl_mime_filedata.3 \ + curl_mime_filename.3 \ + curl_mime_free.3 \ + curl_mime_headers.3 \ + curl_mime_init.3 \ + curl_mime_name.3 \ + curl_mime_subparts.3 \ + curl_mime_type.3 \ + curl_mprintf.3 \ + curl_multi_add_handle.3 \ + curl_multi_assign.3 \ + curl_multi_cleanup.3 \ + curl_multi_fdset.3 \ + curl_multi_get_handles.3 \ + curl_multi_get_offt.3 \ + curl_multi_info_read.3 \ + curl_multi_init.3 \ + curl_multi_notify_disable.3 \ + curl_multi_notify_enable.3 \ + curl_multi_perform.3 \ + curl_multi_poll.3 \ + curl_multi_remove_handle.3 \ + curl_multi_setopt.3 \ + curl_multi_socket.3 \ + curl_multi_socket_action.3 \ + curl_multi_socket_all.3 \ + curl_multi_strerror.3 \ + curl_multi_timeout.3 \ + curl_multi_wait.3 \ + curl_multi_waitfds.3 \ + curl_multi_wakeup.3 \ + curl_pushheader_byname.3 \ + curl_pushheader_bynum.3 \ + curl_share_cleanup.3 \ + curl_share_init.3 \ + curl_share_setopt.3 \ + curl_share_strerror.3 \ + curl_slist_append.3 \ + curl_slist_free_all.3 \ + curl_strequal.3 \ + curl_strnequal.3 \ + curl_unescape.3 \ + curl_url.3 \ + curl_url_cleanup.3 \ + curl_url_dup.3 \ + curl_url_get.3 \ + curl_url_set.3 \ + curl_url_strerror.3 \ + curl_version.3 \ + curl_version_info.3 \ + curl_ws_meta.3 \ + curl_ws_recv.3 \ + curl_ws_send.3 \ + curl_ws_start_frame.3 \ + libcurl-easy.3 \ + libcurl-env-dbg.3 \ + libcurl-env.3 \ + libcurl-errors.3 \ + libcurl-multi.3 \ + libcurl-security.3 \ + libcurl-share.3 \ + libcurl-symbols.3 \ + libcurl-thread.3 \ + libcurl-tutorial.3 \ + libcurl-url.3 \ + libcurl-ws.3 \ + libcurl.3 diff --git a/docs/libcurl/curl_easy_cleanup.md b/docs/libcurl/curl_easy_cleanup.md index fc653e3a56..bf9b18b363 100644 --- a/docs/libcurl/curl_easy_cleanup.md +++ b/docs/libcurl/curl_easy_cleanup.md @@ -62,11 +62,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(res) - printf("error: %s\n", curl_easy_strerror(res)); + result = curl_easy_perform(curl); + if(result != CURLE_OK) + printf("error: %s\n", curl_easy_strerror(result)); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/curl_easy_duphandle.md b/docs/libcurl/curl_easy_duphandle.md index 45e738f301..5ea9c4fba0 100644 --- a/docs/libcurl/curl_easy_duphandle.md +++ b/docs/libcurl/curl_easy_duphandle.md @@ -56,11 +56,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; CURL *nother; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); nother = curl_easy_duphandle(curl); - res = curl_easy_perform(nother); + result = curl_easy_perform(nother); curl_easy_cleanup(nother); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/curl_easy_escape.md b/docs/libcurl/curl_easy_escape.md index b10500ac26..6ff7c5ac53 100644 --- a/docs/libcurl/curl_easy_escape.md +++ b/docs/libcurl/curl_easy_escape.md @@ -30,11 +30,11 @@ char *curl_easy_escape(CURL *curl, const char *string, int length); This function converts the given input *string* to a URL encoded string and returns that as a new allocated string. All input characters that are not a-z, A-Z, 0-9, '-', '.', '_' or '~' are converted to their "URL escaped" version -(**%NN** where **NN** is a two-digit hexadecimal number). +(**%NN** where **NN** is a two-digit hexadecimal number). Although not +constrained by its type, the returned string may not be altered. If *length* is set to 0 (zero), curl_easy_escape(3) uses strlen() on the input -*string* to find out the size. This function does not accept input strings -longer than **CURL_MAX_INPUT_LENGTH** (8 MB). +*string* to find out the size. You must curl_free(3) the returned string when you are done with it. @@ -52,7 +52,7 @@ to the function is encoded correctly. # URLs URLs are by definition *URL encoded*. To create a proper URL from a set of -components that may not be URL encoded already, you cannot just URL encode the +components that may not be URL encoded already, you cannot URL encode the entire URL string with curl_easy_escape(3), because it then also converts colons, slashes and other symbols that you probably want untouched. diff --git a/docs/libcurl/curl_easy_getinfo.md b/docs/libcurl/curl_easy_getinfo.md index dff0504477..6fc5555b38 100644 --- a/docs/libcurl/curl_easy_getinfo.md +++ b/docs/libcurl/curl_easy_getinfo.md @@ -20,7 +20,7 @@ curl_easy_getinfo - extract information from a curl handle ~~~c #include -CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... ); +CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); ~~~ # DESCRIPTION @@ -45,7 +45,7 @@ The session's active socket. See CURLINFO_ACTIVESOCKET(3) ## CURLINFO_APPCONNECT_TIME The time it took from the start until the SSL connect/handshake with the -remote host was completed as a double in number of seconds. (Added in 7.19.0) +remote host was completed as a double in number of seconds. ## CURLINFO_APPCONNECT_TIME_T @@ -196,16 +196,15 @@ In microseconds. (Added in 8.10.0) See CURLINFO_POSTTRANSFER_TIME_T(3) ## CURLINFO_PRETRANSFER_TIME -The time it took from the start until the file transfer is just about to -begin. This includes all pre-transfer commands and negotiations that are -specific to the particular protocol(s) involved. See -CURLINFO_PRETRANSFER_TIME(3) +The time it took from the start until the file transfer is about to begin. +This includes all pre-transfer commands and negotiations that are specific to +the particular protocol(s) involved. See CURLINFO_PRETRANSFER_TIME(3) ## CURLINFO_PRETRANSFER_TIME_T -The time it took from the start until the file transfer is just about to -begin. This includes all pre-transfer commands and negotiations that are -specific to the particular protocol(s) involved. In microseconds. See +The time it took from the start until the file transfer is about to begin. +This includes all pre-transfer commands and negotiations that are specific to +the particular protocol(s) involved. In microseconds. See CURLINFO_PRETRANSFER_TIME_T(3) ## CURLINFO_PRIMARY_IP @@ -222,7 +221,7 @@ User's private data pointer. See CURLINFO_PRIVATE(3) ## CURLINFO_PROTOCOL -(**Deprecated**) The protocol used for the connection. (Added in 7.52.0) See +(**Deprecated**) The protocol used for the connection. See CURLINFO_PROTOCOL(3) ## CURLINFO_PROXYAUTH_AVAIL @@ -254,14 +253,14 @@ Total number of redirects that were followed. See CURLINFO_REDIRECT_COUNT(3) ## CURLINFO_REDIRECT_TIME The time it took for all redirection steps include name lookup, connect, -pretransfer and transfer before final transaction was started. So, this is -zero if no redirection took place. As a double. See CURLINFO_REDIRECT_TIME(3) +pretransfer and transfer before final transaction was started. This is zero if +no redirection took place. As a double. See CURLINFO_REDIRECT_TIME(3) ## CURLINFO_REDIRECT_TIME_T The time it took for all redirection steps include name lookup, connect, -pretransfer and transfer before final transaction was started. So, this is -zero if no redirection took place. In number of microseconds. See +pretransfer and transfer before final transaction was started. This is zero if +no redirection took place. In number of microseconds. See CURLINFO_REDIRECT_TIME_T(3) ## CURLINFO_REDIRECT_URL @@ -303,7 +302,11 @@ RTSP session ID. See CURLINFO_RTSP_SESSION_ID(3) ## CURLINFO_SCHEME -The scheme used for the connection. (Added in 7.52.0) See CURLINFO_SCHEME(3) +The scheme used for the connection. See CURLINFO_SCHEME(3) + +## CURLINFO_SIZE_DELIVERED + +Number of bytes passed to the write callback. See CURLINFO_SIZE_DELIVERED(3) ## CURLINFO_SIZE_DOWNLOAD @@ -398,12 +401,11 @@ An overview of the time values available from curl_easy_getinfo(3) |--|--|--|--|--|--|--|--TOTAL |--|--|--|--|--|--|--|--REDIRECT - - CURLINFO_QUEUE_TIME_T(3), CURLINFO_NAMELOOKUP_TIME_T(3), - CURLINFO_CONNECT_TIME_T(3), CURLINFO_APPCONNECT_TIME_T(3), - CURLINFO_PRETRANSFER_TIME_T(3), CURLINFO_POSTTRANSFER_TIME_T(3), - CURLINFO_STARTTRANSFER_TIME_T(3), CURLINFO_TOTAL_TIME_T(3), - CURLINFO_REDIRECT_TIME_T(3) +CURLINFO_QUEUE_TIME_T(3), CURLINFO_NAMELOOKUP_TIME_T(3), +CURLINFO_CONNECT_TIME_T(3), CURLINFO_APPCONNECT_TIME_T(3), +CURLINFO_PRETRANSFER_TIME_T(3), CURLINFO_POSTTRANSFER_TIME_T(3), +CURLINFO_STARTTRANSFER_TIME_T(3), CURLINFO_TOTAL_TIME_T(3), +CURLINFO_REDIRECT_TIME_T(3) # %PROTOCOLS% @@ -414,16 +416,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(CURLE_OK == res) { + if(result == CURLE_OK) { char *ct; /* ask for the content-type */ - res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct); + result = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct); - if((CURLE_OK == res) && ct) + if((result == CURLE_OK) && ct) printf("We received Content-Type: %s\n", ct); } diff --git a/docs/libcurl/curl_easy_header.md b/docs/libcurl/curl_easy_header.md index d4ac893810..1e48a728c2 100644 --- a/docs/libcurl/curl_easy_header.md +++ b/docs/libcurl/curl_easy_header.md @@ -26,7 +26,7 @@ curl_easy_header - get an HTTP header CURLHcode curl_easy_header(CURL *easy, const char *name, - size_t index, + size_t nameindex, unsigned int origin, int request, struct curl_header **hout); @@ -38,10 +38,10 @@ curl_easy_header(3) returns a pointer to a "curl_header" struct in **hout** with data for the HTTP response header *name*. The case insensitive null-terminated header name should be specified without colon. -*index* 0 means asking for the first instance of the header. If the returned -header struct has **amount** set larger than 1, it means there are more -instances of the same header name available to get. Asking for a too big index -makes **CURLHE_BADINDEX** get returned. +*nameindex* 0 means asking for the first instance of the header. If the +returned header struct has **amount** set larger than 1, it means there are +more instances of the same header name available to get. Asking for a too big +index makes **CURLHE_BADINDEX** get returned. The *origin* argument is for specifying which headers to receive, as a single HTTP transfer might provide headers from several different places and they may @@ -81,12 +81,12 @@ the time it is called. ~~~c struct curl_header { - char *name; - char *value; - size_t amount; - size_t index; - unsigned int origin; - void *anchor; + char *name; + char *value; + size_t amount; + size_t index; + unsigned int origin; + void *anchor; }; ~~~ diff --git a/docs/libcurl/curl_easy_init.md b/docs/libcurl/curl_easy_init.md index 909f3c7ac7..34dbc640b9 100644 --- a/docs/libcurl/curl_easy_init.md +++ b/docs/libcurl/curl_easy_init.md @@ -60,9 +60,9 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/curl_easy_nextheader.md b/docs/libcurl/curl_easy_nextheader.md index 2dbdf61ccd..42314996b9 100644 --- a/docs/libcurl/curl_easy_nextheader.md +++ b/docs/libcurl/curl_easy_nextheader.md @@ -61,6 +61,10 @@ The memory for the struct this points to, is owned and managed by libcurl and is associated with the easy handle. Applications must copy the data if they want it to survive subsequent API calls or the life-time of the easy handle. +The *prev* pointer is only valid until another transfer is done using the +*easy* handle. Once a new transfer has started, a new *prev* must be retrieved +by calling curl_easy_nextheader(3) again with NULL as the fourth argument. + # %PROTOCOLS% # EXAMPLE @@ -83,7 +87,7 @@ int main(void) } /* extract the normal headers + 1xx + trailers from the last request */ - unsigned int origin = CURLH_HEADER| CURLH_1XX | CURLH_TRAILER; + unsigned int origin = CURLH_HEADER | CURLH_1XX | CURLH_TRAILER; while((h = curl_easy_nextheader(curl, origin, -1, prev))) { printf("%s: %s\n", h->name, h->value); prev = h; diff --git a/docs/libcurl/curl_easy_pause.md b/docs/libcurl/curl_easy_pause.md index 2f47c4ef3f..31e1cffe77 100644 --- a/docs/libcurl/curl_easy_pause.md +++ b/docs/libcurl/curl_easy_pause.md @@ -21,7 +21,7 @@ curl_easy_pause - pause and unpause a connection ~~~c #include -CURLcode curl_easy_pause(CURL *handle, int bitmask ); +CURLcode curl_easy_pause(CURL *handle, int action); ~~~ # DESCRIPTION @@ -32,11 +32,10 @@ most other libcurl functions, curl_easy_pause(3) can be used from within callbacks. A connection can be paused by using this function or by letting the read or -the write callbacks return the proper magic return code -(*CURL_READFUNC_PAUSE* and *CURL_WRITEFUNC_PAUSE*). A write callback -that returns pause signals to the library that it could not take care of any -data at all, and that data is then delivered again to the callback when the -transfer is unpaused. +the write callbacks return the proper return code (*CURL_READFUNC_PAUSE* and +*CURL_WRITEFUNC_PAUSE*). A write callback that returns pause signals to the +library that it could not take care of any data at all, and that data is then +delivered again to the callback when the transfer is unpaused. While it may feel tempting, take care and notice that you cannot call this function from another thread. To unpause, you may for example call it from the @@ -55,7 +54,7 @@ A paused transfer is excluded from low speed cancels via the CURLOPT_LOW_SPEED_LIMIT(3) option and unpausing a transfer resets the time period required for the low speed limit to be met. -The **bitmask** argument is a set of bits that sets the new state of the +The **action** argument is a set of bits that sets the new state of the connection. The following bits can be used: ## CURLPAUSE_RECV @@ -109,15 +108,14 @@ int main(void) if(curl) { /* pause a transfer in both directions */ curl_easy_pause(curl, CURLPAUSE_RECV | CURLPAUSE_SEND); - } } ~~~ # MEMORY USE -When pausing a download transfer by returning the magic return code from a -write callback, the read data is already in libcurl's internal buffers so it +When pausing a download transfer by returning the appropriate return code from +a write callback, the read data is already in libcurl's internal buffers so it has to keep it in an allocated buffer until the receiving is again unpaused using this function. diff --git a/docs/libcurl/curl_easy_perform.md b/docs/libcurl/curl_easy_perform.md index 3d19669a53..cc61232f87 100644 --- a/docs/libcurl/curl_easy_perform.md +++ b/docs/libcurl/curl_easy_perform.md @@ -68,9 +68,9 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/curl_easy_recv.md b/docs/libcurl/curl_easy_recv.md index e89c03592b..aeb16c0ce5 100644 --- a/docs/libcurl/curl_easy_recv.md +++ b/docs/libcurl/curl_easy_recv.md @@ -67,22 +67,22 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Do not do the transfer - only connect to host */ curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(res == CURLE_OK) { + if(result == CURLE_OK) { char buf[256]; size_t nread; curl_socket_t sockfd; /* Extract the socket from the curl handle - we need it for waiting. */ - res = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd); + result = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd); /* read data */ - res = curl_easy_recv(curl, buf, sizeof(buf), &nread); + result = curl_easy_recv(curl, buf, sizeof(buf), &nread); } } } diff --git a/docs/libcurl/curl_easy_reset.md b/docs/libcurl/curl_easy_reset.md index 979419de63..f27e86cbb6 100644 --- a/docs/libcurl/curl_easy_reset.md +++ b/docs/libcurl/curl_easy_reset.md @@ -30,7 +30,7 @@ void curl_easy_reset(CURL *handle); Re-initializes all options previously set on a specified curl handle to the default values. This puts back the handle to the same state as it was in when -it was just created with curl_easy_init(3). +it was created with curl_easy_init(3). It does not change the following information kept in the handle: live connections, the Session ID cache, the DNS cache, the cookies, the shares or diff --git a/docs/libcurl/curl_easy_send.md b/docs/libcurl/curl_easy_send.md index 885b37f1bc..016a6756a8 100644 --- a/docs/libcurl/curl_easy_send.md +++ b/docs/libcurl/curl_easy_send.md @@ -62,20 +62,20 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Do not do the transfer - only connect to host */ curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(res == CURLE_OK) { + if(result == CURLE_OK) { curl_socket_t sockfd; size_t sent; /* Extract the socket from the curl handle - we need it for waiting. */ - res = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd); + result = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd); /* send data */ - res = curl_easy_send(curl, "hello", 5, &sent); + result = curl_easy_send(curl, "hello", 5, &sent); } } } diff --git a/docs/libcurl/curl_easy_setopt.md b/docs/libcurl/curl_easy_setopt.md index ccca56de6b..13d966f3c3 100644 --- a/docs/libcurl/curl_easy_setopt.md +++ b/docs/libcurl/curl_easy_setopt.md @@ -540,7 +540,7 @@ Client key password. See CURLOPT_KEYPASSWD(3) ## CURLOPT_KRBLEVEL -Kerberos security level. See CURLOPT_KRBLEVEL(3) +**OBSOLETE**. Kerberos security level. See CURLOPT_KRBLEVEL(3) ## CURLOPT_LOCALPORT @@ -686,6 +686,10 @@ Port number to connect to. See CURLOPT_PORT(3) Make an HTTP POST. See CURLOPT_POST(3) +## CURLOPT_POSTFIELDS + +Send a POST with this data - does not copy it. See CURLOPT_POSTFIELDS(3) + ## CURLOPT_POSTFIELDSIZE The POST data is this big. See CURLOPT_POSTFIELDSIZE(3) @@ -704,7 +708,7 @@ How to act on redirects after POST. See CURLOPT_POSTREDIR(3) ## CURLOPT_PREQUOTE -Commands to run just before transfer. See CURLOPT_PREQUOTE(3) +Commands to run immediately before transfer. See CURLOPT_PREQUOTE(3) ## CURLOPT_PREREQDATA @@ -1352,9 +1356,9 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/curl_easy_ssls_export.md b/docs/libcurl/curl_easy_ssls_export.md index 32f2fff240..fdefa408e5 100644 --- a/docs/libcurl/curl_easy_ssls_export.md +++ b/docs/libcurl/curl_easy_ssls_export.md @@ -83,8 +83,7 @@ a cryptographic hash of the salt and **session_key**. The salt is generated for every session individually. Storing **shmac** is recommended when placing session tickets in a file, for example. -A third party may brute-force known hostnames, but cannot just "grep" for -them. +A third party may brute-force known hostnames, but cannot "grep" for them. ## Session Data @@ -139,7 +138,7 @@ int main(void) { CURLSHcode sh; CURLSH *share = curl_share_init(); - CURLcode rc; + CURLcode result; CURL *curl; sh = curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); @@ -156,7 +155,7 @@ int main(void) curl_easy_perform(curl); /* export the TLS sessions collected in the share */ - rc = curl_easy_ssls_export(curl, my_export_cb, NULL); + result = curl_easy_ssls_export(curl, my_export_cb, NULL); /* always cleanup */ curl_easy_cleanup(curl); diff --git a/docs/libcurl/curl_easy_ssls_import.md b/docs/libcurl/curl_easy_ssls_import.md index f512c2fe69..178c10654c 100644 --- a/docs/libcurl/curl_easy_ssls_import.md +++ b/docs/libcurl/curl_easy_ssls_import.md @@ -53,7 +53,7 @@ int main(void) { CURLSHcode sh; CURLSH *share = curl_share_init(); - CURLcode rc; + CURLcode result; CURL *curl; sh = curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); @@ -68,7 +68,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_SHARE, share); /* read shmac and sdata from storage */ - rc = curl_easy_ssls_import(curl, NULL, shmac, hlen, sdata, slen); + result = curl_easy_ssls_import(curl, NULL, shmac, hlen, sdata, slen); /* always cleanup */ curl_easy_cleanup(curl); diff --git a/docs/libcurl/curl_easy_strerror.md b/docs/libcurl/curl_easy_strerror.md index 37040a1c1e..bec2bdc1a3 100644 --- a/docs/libcurl/curl_easy_strerror.md +++ b/docs/libcurl/curl_easy_strerror.md @@ -43,14 +43,14 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; /* set options */ /* Perform the entire transfer */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); } } ~~~ diff --git a/docs/libcurl/curl_easy_unescape.md b/docs/libcurl/curl_easy_unescape.md index 2e78ad5d9f..49e4c6bf26 100644 --- a/docs/libcurl/curl_easy_unescape.md +++ b/docs/libcurl/curl_easy_unescape.md @@ -30,7 +30,8 @@ char *curl_easy_unescape(CURL *curl, const char *input, This function converts the URL encoded string **input** to a "plain string" and returns that in an allocated memory area. All input characters that are URL encoded (%XX where XX is a two-digit hexadecimal number) are converted to their -binary versions. +binary versions. Although not constrained by its type, the returned data may +not be altered. If the **length** argument is set to 0 (zero), curl_easy_unescape(3) uses strlen() on **input** to find out the size. diff --git a/docs/libcurl/curl_escape.md b/docs/libcurl/curl_escape.md index c24d1890d4..967d375a97 100644 --- a/docs/libcurl/curl_escape.md +++ b/docs/libcurl/curl_escape.md @@ -31,7 +31,8 @@ Obsolete function. Use curl_easy_escape(3) instead. This function converts the given input **string** to a URL encoded string and return that as a new allocated string. All input characters that are not a-z, A-Z or 0-9 are converted to their "URL escaped" version (**%NN** where -**NN** is a two-digit hexadecimal number). +**NN** is a two-digit hexadecimal number). Although not constrained by its +type, the returned string may not be altered. If the **length** argument is set to 0, curl_escape(3) uses strlen() on **string** to find out the size. diff --git a/docs/libcurl/curl_formadd.md b/docs/libcurl/curl_formadd.md index 6bef05523a..94e6c269bd 100644 --- a/docs/libcurl/curl_formadd.md +++ b/docs/libcurl/curl_formadd.md @@ -101,8 +101,6 @@ If you pass a 0 (zero) for this option, libcurl calls strlen() on the contents to figure out the size. If you really want to send a zero byte content then you must make sure strlen() on the data pointer returns zero. -(Option added in 7.46.0) - ## CURLFORM_CONTENTSLENGTH (This option is deprecated. Use *CURLFORM_CONTENTLEN* instead.) @@ -118,7 +116,7 @@ you must make sure strlen() on the data pointer returns zero. followed by a filename, causes that file to be read and its contents used as data in this part. This part does *not* automatically become a file -upload part simply because its data was read from a file. +upload part due to its data being read from a file. The specified file needs to kept around until the associated transfer is done. @@ -171,13 +169,12 @@ long which gives the length of the buffer. ## CURLFORM_STREAM -Tells libcurl to use the CURLOPT_READFUNCTION(3) callback to get -data. The parameter you pass to *CURLFORM_STREAM* is the pointer passed on -to the read callback's fourth argument. If you want the part to look like a -file upload one, set the *CURLFORM_FILENAME* parameter as well. Note that -when using *CURLFORM_STREAM*, *CURLFORM_CONTENTSLENGTH* must also be -set with the total expected length of the part unless the formpost is sent -chunked encoded. (Option added in libcurl 7.18.2) +Tells libcurl to use the CURLOPT_READFUNCTION(3) callback to get data. The +parameter you pass to *CURLFORM_STREAM* is the pointer passed on to the read +callback's fourth argument. If you want the part to look like a file upload +one, set the *CURLFORM_FILENAME* parameter as well. Note that when using +*CURLFORM_STREAM*, *CURLFORM_CONTENTSLENGTH* must also be set with the total +expected length of the part unless the formpost is sent chunked encoded. ## CURLFORM_ARRAY @@ -228,7 +225,7 @@ int main(void) char file2[] = "your-face.jpg"; /* add null character into htmlbuffer, to demonstrate that transfers of buffers containing null characters actually work - */ + */ htmlbuffer[8] = '\0'; /* Add simple name/content section */ diff --git a/docs/libcurl/curl_formget.md b/docs/libcurl/curl_formget.md index 264eb5cbcb..0da0150a90 100644 --- a/docs/libcurl/curl_formget.md +++ b/docs/libcurl/curl_formget.md @@ -34,8 +34,8 @@ accepts a void pointer as second argument named *userp* which is passed as the first argument to the curl_formget_callback function. ~~~c - typedef size_t (*curl_formget_callback)(void *userp, const char *buf, - size_t len);" +typedef size_t (*curl_formget_callback)(void *userp, const char *buf, + size_t len); ~~~ The *curl_formget_callback* is invoked for each part of the HTTP POST chain. @@ -54,7 +54,7 @@ This, because first then does libcurl known which actual read callback to use. size_t print_httppost_callback(void *arg, const char *buf, size_t len) { fwrite(buf, len, 1, stdout); - (*(size_t *) arg) += len; + *((size_t *)arg) += len; return len; } @@ -62,7 +62,7 @@ size_t print_httppost(struct curl_httppost *post) { size_t total_size = 0; if(curl_formget(post, &total_size, print_httppost_callback)) { - return (size_t) -1; + return (size_t)-1; } return total_size; } diff --git a/docs/libcurl/curl_getenv.md b/docs/libcurl/curl_getenv.md index 2cfb58148c..047c913f25 100644 --- a/docs/libcurl/curl_getenv.md +++ b/docs/libcurl/curl_getenv.md @@ -29,7 +29,8 @@ curl_getenv() is a portable wrapper for the getenv() function, meant to emulate its behavior and provide an identical interface for all operating systems libcurl builds on (including Windows). -You must curl_free(3) the returned string when you are done with it. +You must curl_free(3) the returned string when you are done with it and, +although not constrained by its type, it may not be altered. # %PROTOCOLS% diff --git a/docs/libcurl/curl_global_cleanup.md b/docs/libcurl/curl_global_cleanup.md index 60a761ed5c..e6f8e42fd7 100644 --- a/docs/libcurl/curl_global_cleanup.md +++ b/docs/libcurl/curl_global_cleanup.md @@ -37,11 +37,11 @@ curl_version_info(3) has the CURL_VERSION_THREADSAFE feature bit set (most platforms). If this is not thread-safe, you must not call this function when any other -thread in the program (i.e. a thread sharing the same memory) is running. -This does not just mean no other thread that is using libcurl. Because -curl_global_cleanup(3) calls functions of other libraries that are -similarly thread unsafe, it could conflict with any other thread that uses -these other libraries. +thread in the program (i.e. a thread sharing the same memory) is running. This +does not only mean other threads that use libcurl. Because +curl_global_cleanup(3) calls functions of other libraries that are similarly +thread-unsafe, it could conflict with any other thread that uses these other +libraries. See the description in libcurl(3) of global environment requirements for details of how to use this function. diff --git a/docs/libcurl/curl_global_init.md b/docs/libcurl/curl_global_init.md index 198b6e9f30..c57daa534f 100644 --- a/docs/libcurl/curl_global_init.md +++ b/docs/libcurl/curl_global_init.md @@ -50,10 +50,10 @@ the `threadsafe` feature set (added in 7.84.0). If this is not thread-safe (the bit mentioned above is not set), you must not call this function when any other thread in the program (i.e. a thread sharing -the same memory) is running. This does not just mean no other thread that is -using libcurl. Because curl_global_init(3) calls functions of other libraries -that are similarly thread unsafe, it could conflict with any other thread that -uses these other libraries. +the same memory) is running. This does not only mean other threads that use +libcurl. Because curl_global_init(3) calls functions of other libraries that +are similarly thread-unsafe, it could conflict with any other thread that uses +these other libraries. If you are initializing libcurl from a Windows DLL you should not initialize it from *DllMain* or a static initializer because Windows holds the loader @@ -108,7 +108,7 @@ This bit has no point since 7.69.0 but its behavior is instead the default. Before 7.69.0: when this flag is set, curl acknowledges EINTR condition when connecting or when waiting for data. Otherwise, curl waits until full timeout -elapses. (Added in 7.30.0) +elapses. # %PROTOCOLS% @@ -117,11 +117,16 @@ elapses. (Added in 7.30.0) ~~~c int main(void) { - curl_global_init(CURL_GLOBAL_DEFAULT); + CURLcode result; - /* use libcurl, then before exiting... */ + result = curl_global_init(CURL_GLOBAL_DEFAULT); - curl_global_cleanup(); + if(result == CURLE_OK) { + + /* use libcurl, then before exiting... */ + + curl_global_cleanup(); + } } ~~~ diff --git a/docs/libcurl/curl_global_init_mem.md b/docs/libcurl/curl_global_init_mem.md index 91f104ee57..a39471c729 100644 --- a/docs/libcurl/curl_global_init_mem.md +++ b/docs/libcurl/curl_global_init_mem.md @@ -36,9 +36,9 @@ allows the application to set callbacks to replace the otherwise used internal memory functions. If you are using libcurl from multiple threads or libcurl was built with the -threaded resolver option then the callback functions must be thread safe. The +threaded resolver option then the callback functions must be thread-safe. The threaded resolver is a common build option to enable (and in some cases the -default) so we strongly urge you to make your callback functions thread safe. +default) so we strongly urge you to make your callback functions thread-safe. All callback arguments must be set to valid function pointers. The prototypes for the given callbacks must match these: @@ -84,9 +84,18 @@ extern void *calloc_cb(size_t, size_t); int main(void) { - curl_global_init_mem(CURL_GLOBAL_DEFAULT, malloc_cb, - free_cb, realloc_cb, - strdup_cb, calloc_cb); + CURLcode result; + + result = curl_global_init_mem(CURL_GLOBAL_DEFAULT, malloc_cb, + free_cb, realloc_cb, + strdup_cb, calloc_cb); + + if(result == CURLE_OK) { + + /* use libcurl, then before exiting... */ + + curl_global_cleanup(); + } } ~~~ diff --git a/docs/libcurl/curl_global_sslset.md b/docs/libcurl/curl_global_sslset.md index 217b28816f..8ef0ca9992 100644 --- a/docs/libcurl/curl_global_sslset.md +++ b/docs/libcurl/curl_global_sslset.md @@ -62,7 +62,7 @@ curl_version_info(3) has the CURL_VERSION_THREADSAFE feature bit set If this is not thread-safe, you must not call this function when any other thread in the program (i.e. a thread sharing the same memory) is running. -This does not just mean no other thread that is using libcurl. +This does not only mean no other thread that is using libcurl. # Names @@ -72,7 +72,7 @@ Schannel, wolfSSL The name "OpenSSL" is used for all versions of OpenSSL and its associated forks/flavors in this function. OpenSSL, BoringSSL, LibreSSL, quictls and AmiSSL are all supported by libcurl, but in the eyes of curl_global_sslset(3) -they are all just "OpenSSL". They all mostly provide the same API. +they are all called "OpenSSL". They all mostly provide the same API. curl_version_info(3) can return more specific info about the exact OpenSSL flavor and version number in use. diff --git a/docs/libcurl/curl_global_trace.md b/docs/libcurl/curl_global_trace.md index 4a21bdc455..f78f2ca0b0 100644 --- a/docs/libcurl/curl_global_trace.md +++ b/docs/libcurl/curl_global_trace.md @@ -41,9 +41,9 @@ the CURL_VERSION_THREADSAFE feature bit set (most platforms). If this is not thread-safe, you must not call this function when any other thread in the program (i.e. a thread sharing the same memory) is running. This -does not just mean no other thread that is using libcurl. Because +does not only mean no other thread that is using libcurl. Because curl_global_init(3) may call functions of other libraries that are similarly -thread unsafe, it could conflict with any other thread that uses these other +thread-unsafe, it could conflict with any other thread that uses these other libraries. If you are initializing libcurl from a Windows DLL you should not initialize @@ -130,6 +130,10 @@ states. Traces reading of upload data from the application in order to send it to the server. +## `ssh` + +Tracing of SSH related protocols SCP and SFTP. + ## `ssls` Tracing of SSL Session handling, e.g. caching/import/export. @@ -138,6 +142,14 @@ Tracing of SSL Session handling, e.g. caching/import/export. Tracing of SMTP operations when this protocol is enabled in your build. +## `threads` + +Tracing of thread queue and pools, used in threaded DNS resolving. + +## `timer` + +Tracing of timers set for transfers. + ## `write` Traces writing of download data, received from the server, to the application. diff --git a/docs/libcurl/curl_mime_data.md b/docs/libcurl/curl_mime_data.md index 9af7a91d8a..a03f6b555b 100644 --- a/docs/libcurl/curl_mime_data.md +++ b/docs/libcurl/curl_mime_data.md @@ -68,7 +68,7 @@ int main(void) /* add a part */ part = curl_mime_addpart(mime); - /* add data to the part */ + /* add data to the part */ curl_mime_data(part, "raw contents to send", CURL_ZERO_TERMINATED); } } diff --git a/docs/libcurl/curl_mime_data_cb.md b/docs/libcurl/curl_mime_data_cb.md index 9b3fdd5593..199e4f7037 100644 --- a/docs/libcurl/curl_mime_data_cb.md +++ b/docs/libcurl/curl_mime_data_cb.md @@ -120,8 +120,8 @@ struct ctl { size_t read_callback(char *buffer, size_t size, size_t nitems, void *arg) { - struct ctl *p = (struct ctl *) arg; - curl_off_t sz = p->size - p->position; + struct ctl *p = (struct ctl *)arg; + size_t sz = (size_t)(p->size - p->position); nitems *= size; if(sz > nitems) diff --git a/docs/libcurl/curl_mime_filename.md b/docs/libcurl/curl_mime_filename.md index d52ce739e0..d54f36032b 100644 --- a/docs/libcurl/curl_mime_filename.md +++ b/docs/libcurl/curl_mime_filename.md @@ -68,7 +68,7 @@ int main(void) /* send image data from memory */ curl_mime_data(part, imagebuf, sizeof(imagebuf)); - /* set a file name to make it look like a file upload */ + /* set a filename to make it look like a file upload */ curl_mime_filename(part, "image.png"); /* set name */ diff --git a/docs/libcurl/curl_mprintf.md b/docs/libcurl/curl_mprintf.md index 4f5c090579..72ee0a1f02 100644 --- a/docs/libcurl/curl_mprintf.md +++ b/docs/libcurl/curl_mprintf.md @@ -67,7 +67,7 @@ the value of *ap* is undefined after the call. The functions **curl_maprintf()** and **curl_mvaprintf()** return the output string as pointer to a newly allocated memory area. The returned string -must be curl_free(3)ed by the receiver. +may not be overwritten and must be curl_free(3)ed by the receiver. All of these functions write the output under the control of a format string that specifies how subsequent arguments are converted for output. @@ -159,14 +159,14 @@ An optional precision in the form of a period ('.') followed by an optional decimal digit string. Instead of a decimal digit string one may write "*" or "*m$" (for some decimal integer m) to specify that the precision is given in the next argument, or in the *m-th* argument, respectively, which must be of -type int. If the precision is given as just '.', the precision is taken to be -zero. A negative precision is taken as if the precision were omitted. This -gives the minimum number of digits to appear for **d**, **i**, **o**, -**u**, **x**, and **X** conversions, the number of digits to appear -after the radix character for **a**, **A**, **e**, **E**, **f**, and -**F** conversions, the maximum number of significant digits for **g** and -**G** conversions, or the maximum number of characters to be printed from a -string for **s** and **S** conversions. +type int. If the precision is given as a single '.', the precision is taken to +be zero. A negative precision is taken as if the precision were omitted. This +gives the minimum number of digits to appear for **d**, **i**, **o**, **u**, +**x**, and **X** conversions, the number of digits to appear after the radix +character for **a**, **A**, **e**, **E**, **f**, and **F** conversions, the +maximum number of significant digits for **g** and **G** conversions, or the +maximum number of characters to be printed from a string for **s** and **S** +conversions. # Length modifier @@ -274,7 +274,7 @@ const char *name = "John"; int main(void) { curl_mprintf("My name is %s\n", name); - curl_mprintf("Pi is almost %f\n", (double)25.0/8); + curl_mprintf("Pi is almost %f\n", (double)25.0 / 8); } ~~~ diff --git a/docs/libcurl/curl_multi_assign.md b/docs/libcurl/curl_multi_assign.md index 36579fd9f8..279965f534 100644 --- a/docs/libcurl/curl_multi_assign.md +++ b/docs/libcurl/curl_multi_assign.md @@ -41,11 +41,6 @@ libcurl only keeps one single pointer associated with a socket, so calling this function several times for the same socket makes the last set pointer get used. -The idea here being that this association (socket to private pointer) is -something that just about every application that uses this API needs and then -libcurl can just as well do it since it already has the necessary -functionality. - It is acceptable to call this function from your multi callback functions. # %PROTOCOLS% @@ -60,9 +55,9 @@ int main(void) curl_socket_t fd = 0; /* file descriptor to associate our data with */ /* make our struct pointer associated with socket fd */ - CURLMcode mc = curl_multi_assign(multi, fd, &private); - if(mc) - printf("error: %s\n", curl_multi_strerror(mc)); + CURLMcode mresult = curl_multi_assign(multi, fd, &private); + if(mresult) + printf("error: %s\n", curl_multi_strerror(mresult)); } ~~~ diff --git a/docs/libcurl/curl_multi_fdset.md b/docs/libcurl/curl_multi_fdset.md index 1396738014..7ab65e424a 100644 --- a/docs/libcurl/curl_multi_fdset.md +++ b/docs/libcurl/curl_multi_fdset.md @@ -91,8 +91,8 @@ int main(void) fd_set fdexcep; int maxfd; int rc; - CURLMcode mc; - struct timeval timeout = {1, 0}; + CURLMcode mresult; + struct timeval timeout = { 1, 0 }; CURLM *multi = curl_multi_init(); @@ -105,17 +105,17 @@ int main(void) FD_ZERO(&fdexcep); /* get file descriptors from the transfers */ - mc = curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd); + mresult = curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &maxfd); - if(mc != CURLM_OK) { - fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mc); + if(mresult != CURLM_OK) { + fprintf(stderr, "curl_multi_fdset() failed, code %d.\n", mresult); break; } /* wait for activity on one of the sockets */ rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); - } while(!mc); + } while(!mresult); } ~~~ diff --git a/docs/libcurl/curl_multi_get_handles.md b/docs/libcurl/curl_multi_get_handles.md index f94436a889..56da272b8a 100644 --- a/docs/libcurl/curl_multi_get_handles.md +++ b/docs/libcurl/curl_multi_get_handles.md @@ -41,7 +41,8 @@ are out of sync. The order of the easy handles within the array is not guaranteed. -The returned array must be freed with a call to curl_free(3) after use. +The returned array may not be overwritten and must be freed with a call to +curl_free(3) after use. # %PROTOCOLS% diff --git a/docs/libcurl/curl_multi_info_read.md b/docs/libcurl/curl_multi_info_read.md index 17f8842343..35f96c2db7 100644 --- a/docs/libcurl/curl_multi_info_read.md +++ b/docs/libcurl/curl_multi_info_read.md @@ -27,10 +27,10 @@ CURLMsg *curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue); # DESCRIPTION -Ask the multi handle if there are any messages from the individual -transfers. Messages may include information such as an error code from the -transfer or just the fact that a transfer is completed. More details on these -should be written down as well. +Ask the multi handle if there are any messages from the individual transfers. +Messages may include information such as an 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 returns a new struct each time, until a NULL is returned as a signal that there is no more to get at this point. The @@ -52,18 +52,18 @@ in that struct and can be used in subsequent regular curl_easy_getinfo(3) calls (or similar): ~~~c - struct CURLMsg { - CURLMSG msg; /* what this message means */ - CURL *easy_handle; /* the handle it concerns */ - union { - void *whatever; /* message-specific data */ - CURLcode result; /* return code for transfer */ - } data; - }; +struct CURLMsg { + CURLMSG msg; /* what this message means */ + CURL *easy_handle; /* the handle it concerns */ + union { + void *whatever; /* message-specific data */ + CURLcode result; /* return code for transfer */ + } data; +}; ~~~ When **msg** is *CURLMSG_DONE*, the message identifies a transfer that is done, and then **result** contains the return code for the easy handle -that just completed. +that completed. At this point, there are no other **msg** types defined. diff --git a/docs/libcurl/curl_multi_notify_disable.md b/docs/libcurl/curl_multi_notify_disable.md new file mode 100644 index 0000000000..11113a5898 --- /dev/null +++ b/docs/libcurl/curl_multi_notify_disable.md @@ -0,0 +1,66 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Title: curl_multi_notify_disable +Section: 3 +Source: libcurl +See-also: + - CURLMOPT_NOTIFYFUNCTION (3) + - CURLMOPT_NOTIFYDATA (3) + - curl_multi_notify_enable (3) +Protocol: + - All +Added-in: 8.17.0 +--- + +# NAME + +curl_multi_notify_disable - disable a notification type + +# SYNOPSIS + +~~~c +#include +CURLMcode curl_multi_notify_disable(CURLM *multi_handle, + unsigned int notification); +~~~ + +# DESCRIPTION + +Disables collecting the given notification type in the multi handle. A +callback function installed via CURLMOPT_NOTIFYFUNCTION(3) is no longer +called when this notification happens. + +Only when a notification callback is installed *and* a notification +is enabled are these collected and dispatched to the callback. + +Several notification types can be enabled at the same time. Disabling +an already disabled notification is not an error. + +A notification can be enabled again via curl_multi_notify_enable(3). + +# %PROTOCOLS% + +# EXAMPLE + +~~~c +int main(void) +{ + int rc; + CURLM *multi = curl_multi_init(); + + rc = curl_multi_notify_disable(multi, CURLMNOTIFY_INFO_READ); +} +~~~ + +# %AVAILABILITY% + +# RETURN VALUE + +This function returns a CURLMcode indicating success or error. + +CURLM_OK (0) means everything was OK, non-zero means an error occurred, see +libcurl-errors(3). + +The return code is for the whole multi stack. Problems still might have +occurred on individual transfers even when one of these functions return OK. diff --git a/docs/libcurl/curl_multi_notify_enable.md b/docs/libcurl/curl_multi_notify_enable.md new file mode 100644 index 0000000000..4a0da77714 --- /dev/null +++ b/docs/libcurl/curl_multi_notify_enable.md @@ -0,0 +1,66 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Title: curl_multi_notify_enable +Section: 3 +Source: libcurl +See-also: + - CURLMOPT_NOTIFYFUNCTION (3) + - CURLMOPT_NOTIFYDATA (3) + - curl_multi_notify_disable (3) +Protocol: + - All +Added-in: 8.17.0 +--- + +# NAME + +curl_multi_notify_enable - enable a notification type + +# SYNOPSIS + +~~~c +#include +CURLMcode curl_multi_notify_enable(CURLM *multi_handle, + unsigned int notification); +~~~ + +# DESCRIPTION + +Enables collecting the given notification type in the multi handle. A +callback function installed via CURLMOPT_NOTIFYFUNCTION(3) is called +when this notification happens. + +Only when a notification callback is installed *and* a notification +is enabled are these collected and dispatched to the callback. + +Several notification types can be enabled at the same time. Enabling +an already enabled notification is not an error. + +A notification can be disabled again via curl_multi_notify_disable(3). + +# %PROTOCOLS% + +# EXAMPLE + +~~~c +int main(void) +{ + int rc; + CURLM *multi = curl_multi_init(); + + rc = curl_multi_notify_enable(multi, CURLMNOTIFY_INFO_READ); +} +~~~ + +# %AVAILABILITY% + +# RETURN VALUE + +This function returns a CURLMcode indicating success or error. + +CURLM_OK (0) means everything was OK, non-zero means an error occurred, see +libcurl-errors(3). + +The return code is for the whole multi stack. Problems still might have +occurred on individual transfers even when one of these functions return OK. diff --git a/docs/libcurl/curl_multi_perform.md b/docs/libcurl/curl_multi_perform.md index 9ca13c6793..e0335d81a8 100644 --- a/docs/libcurl/curl_multi_perform.md +++ b/docs/libcurl/curl_multi_perform.md @@ -40,8 +40,8 @@ or a timeout has elapsed, the application should call this function to read/write whatever there is to read or write right now etc. curl_multi_perform(3) returns as soon as the reads/writes are done. This function does not require that there actually is any data available for -reading or that data can be written, it can be called just in case. It stores -the number of handles that still transfer data in the second argument's +reading or that data can be written, it can be called as a precaution. It +stores the number of handles that still transfer data in the second argument's integer-pointer. If the amount of *running_handles* is changed from the previous call (or @@ -73,20 +73,25 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { curl_multi_add_handle(multi, curl); - do { - CURLMcode mc = curl_multi_perform(multi, &still_running); - - if(!mc && still_running) - /* wait for activity, timeout or "nothing" */ - mc = curl_multi_poll(multi, NULL, 0, 1000, NULL); - - if(mc) { - fprintf(stderr, "curl_multi_poll() failed, code %d.\n", (int)mc); + for(;;) { + CURLMcode mresult = curl_multi_perform(multi, &still_running); + if(mresult != CURLM_OK) { + fprintf(stderr, "curl_multi_perform() failed, code %d.\n", + (int)mresult); break; } - /* if there are still transfers, loop */ - } while(still_running); + if(!still_running) { + break; + } + + /* wait for activity, timeout or "nothing" */ + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); + if(mresult != CURLM_OK) { + fprintf(stderr, "curl_multi_poll() failed, code %d.\n", (int)mresult); + break; + } + } /* if there are still transfers, loop */ } } ~~~ diff --git a/docs/libcurl/curl_multi_poll.md b/docs/libcurl/curl_multi_poll.md index 64aeac9d10..8c2488feb3 100644 --- a/docs/libcurl/curl_multi_poll.md +++ b/docs/libcurl/curl_multi_poll.md @@ -108,12 +108,12 @@ int main(void) curl_multi_add_handle(multi_handle, easy_handle); do { - CURLMcode mc; + CURLMcode mresult; int numfds; - mc = curl_multi_perform(multi_handle, &still_running); + mresult = curl_multi_perform(multi_handle, &still_running); - if(mc == CURLM_OK) { + if(mresult == CURLM_OK) { struct curl_waitfd myown; myown.fd = myfd; myown.events = CURL_WAIT_POLLIN; /* wait for input */ @@ -121,7 +121,7 @@ int main(void) /* wait for activity on curl's descriptors or on our own, or timeout */ - mc = curl_multi_poll(multi_handle, &myown, 1, 1000, &numfds); + mresult = curl_multi_poll(multi_handle, &myown, 1, 1000, &numfds); if(myown.revents) { /* did our descriptor receive an event? */ @@ -129,8 +129,8 @@ int main(void) } } - if(mc != CURLM_OK) { - fprintf(stderr, "curl_multi failed, code %d.\n", mc); + if(mresult != CURLM_OK) { + fprintf(stderr, "curl_multi failed, code %d.\n", mresult); break; } diff --git a/docs/libcurl/curl_multi_remove_handle.md b/docs/libcurl/curl_multi_remove_handle.md index b36a62d235..fed6439c44 100644 --- a/docs/libcurl/curl_multi_remove_handle.md +++ b/docs/libcurl/curl_multi_remove_handle.md @@ -37,7 +37,7 @@ Removing an easy handle while being in use is perfectly legal and effectively halts the transfer in progress involving that easy handle. All other easy handles and transfers remain unaffected. -It is fine to remove a handle at any time during a transfer, just not from +It is fine to remove a handle at any time during a transfer, but not from within any libcurl callback function. Removing an easy handle from the multi handle before the corresponding diff --git a/docs/libcurl/curl_multi_setopt.md b/docs/libcurl/curl_multi_setopt.md index e646eced62..0bf7438cc8 100644 --- a/docs/libcurl/curl_multi_setopt.md +++ b/docs/libcurl/curl_multi_setopt.md @@ -72,6 +72,14 @@ Max simultaneously open connections. See CURLMOPT_MAX_TOTAL_CONNECTIONS(3) Signal that the network has changed. See CURLMOPT_NETWORK_CHANGED(3) +## CURLMOPT_NOTIFYDATA + +Custom pointer passed to the notify callback. See CURLMOPT_NOTIFYDATA(3) + +## CURLMOPT_NOTIFYFUNCTION + +Callback that receives notifications. See CURLMOPT_NOTIFYFUNCTION(3) + ## CURLMOPT_PIPELINING Enable HTTP multiplexing. See CURLMOPT_PIPELINING(3) @@ -92,6 +100,14 @@ Pointer to pass to push callback. See CURLMOPT_PUSHDATA(3) Callback that approves or denies server pushes. See CURLMOPT_PUSHFUNCTION(3) +## CURLMOPT_QUICK_EXIT + +Enable a quicker cleanup of the multi handle. See CURLMOPT_QUICK_EXIT(3) + +## CURLMOPT_RESOLVE_THREADS_MAX + +Max threads used for threaded DNS resolver. See CURLMOPT_RESOLVE_THREADS_MAX(3) + ## CURLMOPT_SOCKETDATA Custom pointer passed to the socket callback. See CURLMOPT_SOCKETDATA(3) diff --git a/docs/libcurl/curl_multi_socket_action.md b/docs/libcurl/curl_multi_socket_action.md index 5d4271a386..44a06406c3 100644 --- a/docs/libcurl/curl_multi_socket_action.md +++ b/docs/libcurl/curl_multi_socket_action.md @@ -111,9 +111,9 @@ int main(void) CURLM *multi = curl_multi_init(); - CURLMcode mc = curl_multi_socket_action(multi, fd, bitmask, &running); - if(mc) - printf("error: %s\n", curl_multi_strerror(mc)); + CURLMcode mresult = curl_multi_socket_action(multi, fd, bitmask, &running); + if(mresult) + printf("error: %s\n", curl_multi_strerror(mresult)); } ~~~ diff --git a/docs/libcurl/curl_multi_socket_all.md b/docs/libcurl/curl_multi_socket_all.md index 5428b9786b..48fe980e71 100644 --- a/docs/libcurl/curl_multi_socket_all.md +++ b/docs/libcurl/curl_multi_socket_all.md @@ -38,8 +38,8 @@ still running easy handles within the multi handle. When this number reaches zero, all transfers are complete/done. Force libcurl to (re-)check all its internal sockets and transfers instead of -just a single one by calling curl_multi_socket_all(3). Note that there should -not be any reason to use this function. +a single one by calling curl_multi_socket_all(3). Note that there should not +be any reason to use this function. # %PROTOCOLS% diff --git a/docs/libcurl/curl_multi_strerror.md b/docs/libcurl/curl_multi_strerror.md index d419ab2e82..39b5b2d524 100644 --- a/docs/libcurl/curl_multi_strerror.md +++ b/docs/libcurl/curl_multi_strerror.md @@ -41,9 +41,9 @@ int main(void) int still_running; CURLM *multi = curl_multi_init(); - CURLMcode mc = curl_multi_perform(multi, &still_running); - if(mc) - printf("error: %s\n", curl_multi_strerror(mc)); + CURLMcode mresult = curl_multi_perform(multi, &still_running); + if(mresult) + printf("error: %s\n", curl_multi_strerror(mresult)); } ~~~ diff --git a/docs/libcurl/curl_multi_timeout.md b/docs/libcurl/curl_multi_timeout.md index 273ab79538..a4a7fa66fa 100644 --- a/docs/libcurl/curl_multi_timeout.md +++ b/docs/libcurl/curl_multi_timeout.md @@ -37,7 +37,7 @@ curl_multi_socket_action(3) function with the **sockfd** argument set to CURL_SOCKET_TIMEOUT, or call curl_multi_perform(3) if you are using the simpler and older multi interface approach. -The timeout value returned in the long **timeout** points to, is in number +The timeout value returned in the long **timeout_ms** points to, is in number of milliseconds at this moment. If 0, it means you should proceed immediately without waiting for anything. If it returns -1, there is no timeout at all set. @@ -45,9 +45,9 @@ An application that uses the *multi_socket* API should not use this function. It should instead use the CURLMOPT_TIMERFUNCTION(3) option for proper and desired behavior. -Note: if libcurl returns a -1 timeout here, it just means that libcurl -currently has no stored timeout value. You must not wait too long (more than a -few seconds perhaps) before you call curl_multi_perform(3) again. +Note: if libcurl returns a -1 timeout here, it means that libcurl currently +has no stored timeout value. You must not wait too long (more than a few +seconds perhaps) before you call curl_multi_perform(3) again. # %PROTOCOLS% diff --git a/docs/libcurl/curl_multi_wait.md b/docs/libcurl/curl_multi_wait.md index b8f5a18e67..f9a7b7b644 100644 --- a/docs/libcurl/curl_multi_wait.md +++ b/docs/libcurl/curl_multi_wait.md @@ -97,18 +97,18 @@ int main(void) curl_multi_add_handle(multi, easy); do { - CURLMcode mc; + CURLMcode mresult; int numfds; - mc = curl_multi_perform(multi, &still_running); + mresult = curl_multi_perform(multi, &still_running); - if(mc == CURLM_OK) { + if(mresult == CURLM_OK) { /* wait for activity, timeout or "nothing" */ - mc = curl_multi_wait(multi, NULL, 0, 1000, &numfds); + mresult = curl_multi_wait(multi, NULL, 0, 1000, &numfds); } - if(mc != CURLM_OK) { - fprintf(stderr, "curl_multi failed, code %d.\n", mc); + if(mresult != CURLM_OK) { + fprintf(stderr, "curl_multi failed, code %d.\n", mresult); break; } diff --git a/docs/libcurl/curl_multi_waitfds.md b/docs/libcurl/curl_multi_waitfds.md index 7d828a4d7c..7a4dac894b 100644 --- a/docs/libcurl/curl_multi_waitfds.md +++ b/docs/libcurl/curl_multi_waitfds.md @@ -48,7 +48,7 @@ If the *fd_count* argument is not a null pointer, it points to a variable that on return specifies the number of descriptors used by the multi_handle to be checked for being ready to read or write. -The client code can pass *size* equal to zero just to get the number of the +The client code can pass *size* equal to zero to get the number of the descriptors and allocate appropriate storage for them to be used in a subsequent function call. In this case, *fd_count* receives a number greater than or equal to the number of descriptors. @@ -62,7 +62,7 @@ than or equal to the number of descriptors. int main(void) { - CURLMcode mc; + CURLMcode mresult; struct curl_waitfd *ufds; CURLM *multi = curl_multi_init(); @@ -73,10 +73,10 @@ int main(void) /* get the count of file descriptors from the transfers */ unsigned int fd_count = 0; - mc = curl_multi_waitfds(multi, NULL, 0, &fd_count); + mresult = curl_multi_waitfds(multi, NULL, 0, &fd_count); - if(mc != CURLM_OK) { - fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mc); + if(mresult != CURLM_OK) { + fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mresult); break; } @@ -87,10 +87,10 @@ int main(void) ufds = malloc(fd_count * sizeof(struct curl_waitfd)); /* get wait descriptors from the transfers and put them into array. */ - mc = curl_multi_waitfds(multi, ufds, fd_count, &fd_count); + mresult = curl_multi_waitfds(multi, ufds, fd_count, &fd_count); - if(mc != CURLM_OK) { - fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mc); + if(mresult != CURLM_OK) { + fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mresult); free(ufds); break; } @@ -98,7 +98,7 @@ int main(void) /* Do polling on descriptors in ufds */ free(ufds); - } while(!mc); + } while(!mresult); } ~~~ diff --git a/docs/libcurl/curl_multi_wakeup.md b/docs/libcurl/curl_multi_wakeup.md index 78597bf88f..d95f9eb6b8 100644 --- a/docs/libcurl/curl_multi_wakeup.md +++ b/docs/libcurl/curl_multi_wakeup.md @@ -61,14 +61,14 @@ int main(void) /* this is thread 1 */ do { - CURLMcode mc; + CURLMcode mresult; int numfds; - mc = curl_multi_perform(multi, &still_running); + mresult = curl_multi_perform(multi, &still_running); - if(mc == CURLM_OK) { + if(mresult == CURLM_OK) { /* wait for activity, timeout or wakeup */ - mc = curl_multi_poll(multi, NULL, 0, 10000, &numfds); + mresult = curl_multi_poll(multi, NULL, 0, 10000, &numfds); } if(time_to_die()) diff --git a/docs/libcurl/curl_pushheader_byname.md b/docs/libcurl/curl_pushheader_byname.md index d066ea7b85..e2968fcd46 100644 --- a/docs/libcurl/curl_pushheader_byname.md +++ b/docs/libcurl/curl_pushheader_byname.md @@ -33,7 +33,8 @@ elsewhere and it has no function then. It returns the value for the given header field name (or NULL) for the incoming server push request. This is a shortcut so that the application does not have to loop through all headers to find the one it is interested in. The -data this function points to is freed when this callback returns. If more than +data this function points to is freed when this callback returns and, +although not constrained by its type, may not be altered. If more than one header field use the same name, this returns only the first one. # %PROTOCOLS% diff --git a/docs/libcurl/curl_pushheader_bynum.md b/docs/libcurl/curl_pushheader_bynum.md index 12555c57f0..429710f001 100644 --- a/docs/libcurl/curl_pushheader_bynum.md +++ b/docs/libcurl/curl_pushheader_bynum.md @@ -33,7 +33,8 @@ elsewhere and it has no function then. It returns the value for the header field at the given index **num**, for the incoming server push request or NULL. The data pointed to is freed by libcurl when this callback returns. The returned pointer points to a -"name:value" string that gets freed when this callback returns. +"name:value" string that gets freed when this callback returns; although +not constrained by its type, this string may not be altered. # %PROTOCOLS% @@ -50,10 +51,10 @@ static int push_cb(CURL *parent, int i = 0; char *field; do { - field = curl_pushheader_bynum(headers, i); - if(field) - fprintf(stderr, "Push header: %s\n", field); - i++; + field = curl_pushheader_bynum(headers, i); + if(field) + fprintf(stderr, "Push header: %s\n", field); + i++; } while(field); return CURL_PUSH_OK; /* permission granted */ } diff --git a/docs/libcurl/curl_share_cleanup.md b/docs/libcurl/curl_share_cleanup.md index afb06d008c..e8390ff3d6 100644 --- a/docs/libcurl/curl_share_cleanup.md +++ b/docs/libcurl/curl_share_cleanup.md @@ -27,7 +27,8 @@ CURLSHcode curl_share_cleanup(CURLSH *share_handle); # DESCRIPTION This function deletes a shared object. The share handle cannot be used anymore -when this function has been called. +when this function has been called. The share fails the call if it is +still being used in any easy handle. Passing in a NULL pointer in *share_handle* makes this function return immediately with no action. @@ -35,6 +36,11 @@ immediately with no action. Any use of the **share_handle** after this function has been called and have returned, is illegal. +For applications that use a share in several threads, it is critical that +the destruction of the share is only done when all other threads have stopped +using it. While libcurl tracks how many easy handles are using a share, +it can not observe how many pointers to the share the application has. + # %PROTOCOLS% # EXAMPLE diff --git a/docs/libcurl/curl_slist_append.md b/docs/libcurl/curl_slist_append.md index 75c6a57afd..3ebc19e354 100644 --- a/docs/libcurl/curl_slist_append.md +++ b/docs/libcurl/curl_slist_append.md @@ -28,12 +28,13 @@ struct curl_slist *curl_slist_append(struct curl_slist *list, curl_slist_append(3) appends a string to a linked list of strings. The existing **list** should be passed as the first argument and the new list is -returned from this function. Pass in NULL in the **list** argument to create -a new list. The specified **string** has been appended when this function -returns. curl_slist_append(3) copies the string. +returned from this function. Pass in NULL in the **list** argument to create a +new list. The specified **string** has been appended when this function +returns. curl_slist_append(3) copies the string. The **string** argument must +be a valid string pointer and cannot be NULL. -The list should be freed again (after usage) with -curl_slist_free_all(3). +The list should be freed (after usage) with curl_slist_free_all(3). +Its nodes and pointed content may not be altered outside this function. # %PROTOCOLS% diff --git a/docs/libcurl/curl_unescape.md b/docs/libcurl/curl_unescape.md index 2600f14361..453fd53145 100644 --- a/docs/libcurl/curl_unescape.md +++ b/docs/libcurl/curl_unescape.md @@ -33,7 +33,8 @@ Deprecated. Use curl_easy_unescape(3) instead. This function converts the URL encoded string **input** to a "plain string" and return that as a new allocated string. All input characters that are URL encoded (%XX where XX is a two-digit hexadecimal number) are converted to -their plain text versions. +their plain text versions. Although not constrained by its type, the returned +data may not be altered. If the **length** argument is set to 0, curl_unescape(3) calls strlen() on **input** to find out the size. diff --git a/docs/libcurl/curl_url.md b/docs/libcurl/curl_url.md index 5872134e13..8053e630b8 100644 --- a/docs/libcurl/curl_url.md +++ b/docs/libcurl/curl_url.md @@ -38,6 +38,9 @@ single URL. When the object is first created, there is of course no components stored. They are then set in the object with the curl_url_set(3) function. +The object must be destroyed with a call to curl_url_cleanup(3) when no +longer needed. + # %PROTOCOLS% # EXAMPLE diff --git a/docs/libcurl/curl_url_get.md b/docs/libcurl/curl_url_get.md index a9bb0bdc23..9916166f97 100644 --- a/docs/libcurl/curl_url_get.md +++ b/docs/libcurl/curl_url_get.md @@ -43,6 +43,7 @@ allocated string with the contents. The *flags* argument is a bitmask with individual features. The returned content pointer must be freed with curl_free(3) after use. +Although not constrained by its type, the pointed string may not be altered. # FLAGS @@ -186,8 +187,8 @@ If the hostname is a numeric IPv6 address, this field might also be set. ## CURLUPART_PORT -A port cannot be URL decoded on get. This number is returned in a string just -like all other parts. That string is guaranteed to hold a valid port number in +A port cannot be URL decoded on get. This number is returned in a string like +all other parts. That string is guaranteed to hold a valid port number in ASCII using base 10. ## CURLUPART_PATH diff --git a/docs/libcurl/curl_version_info.md b/docs/libcurl/curl_version_info.md index 8a8671dbf9..fd589a834c 100644 --- a/docs/libcurl/curl_version_info.md +++ b/docs/libcurl/curl_version_info.md @@ -26,19 +26,10 @@ curl_version_info_data *curl_version_info(CURLversion age); # DESCRIPTION Returns a pointer to a filled in static struct with information about various -features in the running version of libcurl. *age* should be set to the -version of this functionality by the time you write your program. This way, -libcurl always returns a proper struct that your program understands, while -programs in the future might get a different struct. **CURLVERSION_NOW** is -the most recent one for the library you have installed: -~~~c - data = curl_version_info(CURLVERSION_NOW); -~~~ -Applications should use this information to judge if things are possible to do -or not, instead of using compile-time checks, as dynamic/DLL libraries can be -changed independent of applications. +features in the running version of libcurl. The input argument *age* has no +use and we recommend you set it to `CURLVERSION_NOW`. -This function can alter the returned static data as long as +This function may alter the returned static data as long as curl_global_init(3) has not been called. It is therefore not thread-safe before libcurl initialization occurs. @@ -111,14 +102,15 @@ typedef struct { } curl_version_info_data; ~~~ -*age* describes what the age of this struct is. The number depends on how -new the libcurl you are using is. You are however guaranteed to get a struct -that you have a matching struct for in the header, as you tell libcurl your -"age" with the input argument. +*age* describes what the age of this struct is. That number is different +depending on how recent your libcurl is. The documentation above describes +which struct fields that were added at which age. Trying to access a struct +field that is newer than the age of your struct may cause undefined behavior +and possibly crashes. -*version* is just an ASCII string for the libcurl version. +*version* is an ASCII string for the libcurl version. -*version_num* is a 24 bit number created like this: \<8 bits major number\> | +*version_num* is a 24-bit number created like this: \<8 bits major number\> | \<8 bits minor number\> | \<8 bits patch number\>. Version 7.9.8 is therefore returned as 0x070908. @@ -159,19 +151,26 @@ entry. HTTP Alt-Svc parsing and the associated options (Added in 7.64.1) +## `AppleSecTrust` + +*features* mask bit: non-existent + +libcurl was built with support for Apple's SecTrust service to verify +server certificates (Added in 8.17.0). + ## `AsynchDNS` *features* mask bit: CURL_VERSION_ASYNCHDNS libcurl was built with support for asynchronous name lookups, which allows more exact timeouts (even on Windows) and less blocking when using the multi -interface. (added in 7.10.7) +interface. ## `brotli` *features* mask bit: CURL_VERSION_BROTLI -supports HTTP Brotli content encoding using libbrotlidec (Added in 7.57.0) +supports HTTP Brotli content encoding using libbrotlidec ## `asyn-rr` @@ -185,7 +184,7 @@ resolves, but uses the threaded resolver for "normal" resolves (Added in *features* mask bit: CURL_VERSION_DEBUG -libcurl was built with debug capabilities (added in 7.10.6) +libcurl was built with debug capabilities ## `ECH` @@ -207,7 +206,6 @@ authentication methods. (added in 7.76.0) libcurl was built with support for GSS-API. This makes libcurl use provided functions for Kerberos and SPNEGO authentication. It also allows libcurl to use the current user credentials without the app having to pass them on. -(Added in 7.38.0) ## `HSTS` @@ -221,7 +219,6 @@ libcurl was built with support for HSTS (HTTP Strict Transport Security) *features* mask bit: CURL_VERSION_HTTP2 libcurl was built with support for HTTP2. -(Added in 7.33.0) ## `HTTP3` @@ -234,7 +231,6 @@ HTTP/3 and QUIC support are built-in (Added in 7.66.0) *features* mask bit: CURL_VERSION_HTTPS_PROXY libcurl was built with support for HTTPS-proxy. -(Added in 7.52.0) ## `HTTPSRR` @@ -248,7 +244,7 @@ in 8.12.0) *features* mask bit: CURL_VERSION_IDN libcurl was built with support for IDNA, domain names with international -letters. (Added in 7.12.0) +letters. ## `IPv6` @@ -261,19 +257,19 @@ supports IPv6 *features* mask bit: CURL_VERSION_KERBEROS5 supports Kerberos V5 authentication for FTP, IMAP, LDAP, POP3, SMTP and -SOCKSv5 proxy. (Added in 7.40.0) +SOCKSv5 proxy. ## `Largefile` *features* mask bit: CURL_VERSION_LARGEFILE -libcurl was built with support for large files. (Added in 7.11.1) +libcurl was built with support for large files. ## `libz` *features* mask bit: CURL_VERSION_LIBZ -supports HTTP deflate using libz (Added in 7.10) +supports HTTP deflate using libz ## `MultiSSL` @@ -281,20 +277,26 @@ supports HTTP deflate using libz (Added in 7.10) libcurl was built with multiple SSL backends. For details, see curl_global_sslset(3). -(Added in 7.56.0) + +## `NativeCA` + +*features* mask bit: non-existent + +libcurl was built to enable native CA store, to verify server certificates +(Added in 8.19.0). ## `NTLM` *features* mask bit: CURL_VERSION_NTLM -supports HTTP NTLM (added in 7.10.6) +supports HTTP NTLM ## `NTLM_WB` *features* mask bit: CURL_VERSION_NTLM_WB -libcurl was built with support for NTLM delegation to a winbind helper. -(Added in 7.22.0) This feature was removed from curl in 8.8.0. +libcurl was built with support for NTLM delegation to a winbind helper. This +feature was removed from curl in 8.8.0. ## `PSL` @@ -302,20 +304,19 @@ libcurl was built with support for NTLM delegation to a winbind helper. libcurl was built with support for Mozilla's Public Suffix List. This makes libcurl ignore cookies with a domain that is on the list. -(Added in 7.47.0) ## `SPNEGO` *features* mask bit: CURL_VERSION_SPNEGO libcurl was built with support for SPNEGO authentication (Simple and Protected -GSS-API Negotiation Mechanism, defined in RFC 2478.) (added in 7.10.8) +GSS-API Negotiation Mechanism, defined in RFC 2478.) ## `SSL` *features* mask bit: CURL_VERSION_SSL -supports SSL (HTTPS/FTPS) (Added in 7.10) +supports SSL (HTTPS/FTPS) ## `SSLS-EXPORT` @@ -331,7 +332,7 @@ libcurl was built with SSL session import/export support libcurl was built with support for SSPI. This is only available on Windows and makes libcurl use Windows-provided functions for Kerberos, NTLM, SPNEGO and Digest authentication. It also allows libcurl to use the current user -credentials without the app having to pass them on. (Added in 7.13.2) +credentials without the app having to pass them on. ## `threadsafe` @@ -345,14 +346,7 @@ curl initialization. (Added in 7.84.0) See libcurl-thread(3) *features* mask bit: CURL_VERSION_TLSAUTH_SRP libcurl was built with support for TLS-SRP (in one or more of the built-in TLS -backends). (Added in 7.21.4) - -## `TrackMemory` - -*features* mask bit: CURL_VERSION_CURLDEBUG - -libcurl was built with memory tracking debug capabilities. This is mainly of -interest for libcurl hackers. (added in 7.19.6) +backends). ## `Unicode` @@ -366,7 +360,6 @@ characters work in filenames and options passed to libcurl. (Added in 7.72.0) *features* mask bit: CURL_VERSION_UNIX_SOCKETS libcurl was built with support for Unix domain sockets. -(Added in 7.40.0) ## `zstd` @@ -379,13 +372,21 @@ supports HTTP zstd content encoding using zstd library (Added in 7.72.0) *features* mask bit: CURL_VERSION_CONV libcurl was built with support for character conversions provided by -callbacks. Always 0 since 7.82.0. (Added in 7.15.4, deprecated.) +callbacks. Always 0 since 7.82.0. Deprecated. + +## no name + +*features* mask bit: CURL_VERSION_CURLDEBUG + +libcurl was built with memory tracking debug capabilities. This is mainly of +interest for libcurl hackers. Always the same as CURL_VERSION_DEBUG since +8.19.0. Deprecated. ## no name *features* mask bit: CURL_VERSION_GSSNEGOTIATE -supports HTTP GSS-Negotiate (added in 7.10.6, deprecated in 7.38.0) +supports HTTP GSS-Negotiate. Deprecated. ## no name diff --git a/docs/libcurl/curl_ws_meta.md b/docs/libcurl/curl_ws_meta.md index e34e547676..978520afd8 100644 --- a/docs/libcurl/curl_ws_meta.md +++ b/docs/libcurl/curl_ws_meta.md @@ -137,8 +137,7 @@ struct customdata { void *ptr; }; -static size_t writecb(char *buffer, - size_t size, size_t nitems, void *p) +static size_t writecb(char *buffer, size_t size, size_t nitems, void *p) { struct customdata *c = (struct customdata *)p; const struct curl_ws_frame *m = curl_ws_meta(c->easy); @@ -158,7 +157,6 @@ int main(void) curl_easy_setopt(curl, CURLOPT_WRITEDATA, &custom); curl_easy_perform(curl); - } return 0; } diff --git a/docs/libcurl/curl_ws_recv.md b/docs/libcurl/curl_ws_recv.md index 8c5cd55a4c..0b56c578ba 100644 --- a/docs/libcurl/curl_ws_recv.md +++ b/docs/libcurl/curl_ws_recv.md @@ -74,7 +74,7 @@ int main(void) { char buffer[256]; size_t offset = 0; - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; CURL *curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "wss://example.com/"); @@ -82,30 +82,32 @@ int main(void) /* start HTTPS connection and upgrade to WSS, then return control */ curl_easy_perform(curl); - /* Note: This example neglects fragmented messages. (CURLWS_CONT bit) - A real application must handle them appropriately. */ + /* Note: This example neglects fragmented messages. + (CURLWS_CONT bit) A real application must handle them + appropriately. */ - while(!res) { + while(!result) { size_t recv; const struct curl_ws_frame *meta; - res = curl_ws_recv(curl, buffer + offset, sizeof(buffer) - offset, &recv, - &meta); + result = curl_ws_recv(curl, buffer + offset, + sizeof(buffer) - offset, &recv, + &meta); offset += recv; - if(res == CURLE_OK) { + if(result == CURLE_OK) { if(meta->bytesleft == 0) break; /* finished receiving */ - if(meta->bytesleft > sizeof(buffer) - offset) - res = CURLE_TOO_LARGE; + if(meta->bytesleft > (curl_off_t)(sizeof(buffer) - offset)) + result = CURLE_TOO_LARGE; } - if(res == CURLE_AGAIN) + if(result == CURLE_AGAIN) /* in real application: wait for socket here, e.g. using select() */ - res = CURLE_OK; + result = CURLE_OK; } curl_easy_cleanup(curl); - return (int)res; + return (int)result; } ~~~ diff --git a/docs/libcurl/curl_ws_send.md b/docs/libcurl/curl_ws_send.md index ac8f020321..dc6e927ca8 100644 --- a/docs/libcurl/curl_ws_send.md +++ b/docs/libcurl/curl_ws_send.md @@ -88,7 +88,7 @@ int main(void) { const char *buffer = "PAYLOAD"; size_t offset = 0; - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; CURL *curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "wss://example.com/"); @@ -96,24 +96,25 @@ int main(void) /* start HTTPS connection and upgrade to WSS, then return control */ curl_easy_perform(curl); - while(!res) { + while(!result) { size_t sent; - res = curl_ws_send(curl, buffer + offset, strlen(buffer) - offset, &sent, - 0, CURLWS_TEXT); + result = curl_ws_send(curl, buffer + offset, + strlen(buffer) - offset, &sent, + 0, CURLWS_TEXT); offset += sent; - if(res == CURLE_OK) { + if(result == CURLE_OK) { if(offset == strlen(buffer)) break; /* finished sending */ } - if(res == CURLE_AGAIN) + if(result == CURLE_AGAIN) /* in real application: wait for socket here, e.g. using select() */ - res = CURLE_OK; + result = CURLE_OK; } curl_easy_cleanup(curl); - return (int)res; + return (int)result; } ~~~ diff --git a/docs/libcurl/curl_ws_start_frame.md b/docs/libcurl/curl_ws_start_frame.md index 166c5cb7e0..e02487759c 100644 --- a/docs/libcurl/curl_ws_start_frame.md +++ b/docs/libcurl/curl_ws_start_frame.md @@ -43,16 +43,15 @@ To send larger frames or frames of a different type, call curl_ws_start_frame() from within the read function and then return the data belonging to the frame. -The function fails, if a previous frame has not been completely -read yet. Also it fails in *CURLWS_RAW_MODE*. +The function fails, if a previous frame has not been completely read yet. Also +it fails in *CURLWS_RAW_MODE*. -The read function in libcurl usually treats a return value of 0 -as the end of file indication and stops any further reads. This -would prevent sending WebSocket frames of length 0. +The read function in libcurl usually treats a return value of 0 as the end of +file indication and stops any further reads. This would prevent sending +WebSocket frames of length 0. -If the read function calls `curl_ws_start_frame()` however, a return -value of 0 is *not* treated as an end of file and libcurl calls -the read function again. +If the read function calls `curl_ws_start_frame()`, a return value of 0 is +*not* treated as an end of file and libcurl calls the read function again. # FLAGS @@ -77,13 +76,13 @@ static size_t readcb(char *buf, size_t nitems, size_t buflen, void *p) struct read_ctx *ctx = p; size_t len = nitems * buflen; size_t left = ctx->msg_len - ctx->nsent; - CURLcode result; if(!ctx->nsent) { + CURLcode result; /* Want to send TEXT frame. */ result = curl_ws_start_frame(ctx->easy, CURLWS_TEXT, (curl_off_t)ctx->msg_len); - if(result) { + if(result != CURLE_OK) { fprintf(stderr, "error starting frame: %d\n", result); return CURL_READFUNC_ABORT; } @@ -102,7 +101,7 @@ int main(void) { CURL *easy; struct read_ctx rctx; - CURLcode res; + CURLcode result; easy = curl_easy_init(); if(!easy) @@ -118,12 +117,12 @@ int main(void) curl_easy_setopt(easy, CURLOPT_READDATA, &rctx); curl_easy_setopt(easy, CURLOPT_UPLOAD, 1L); - /* Perform the request, res gets the return code */ - res = curl_easy_perform(easy); + /* Perform the request, result gets the return code */ + result = curl_easy_perform(easy); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(easy); diff --git a/docs/libcurl/libcurl-env-dbg.md b/docs/libcurl/libcurl-env-dbg.md index d142e94410..2471855bcc 100644 --- a/docs/libcurl/libcurl-env-dbg.md +++ b/docs/libcurl/libcurl-env-dbg.md @@ -58,6 +58,10 @@ only send 800. The percentage of send() calls that should be answered with EAGAIN at random. QUIC only. +## `CURL_DBG_SOCK_FAIL_IPV6` + +Fail opening of sockets for the IPv6 address family. + ## `CURL_DEBUG` Trace logging behavior as an alternative to calling curl_global_trace(3). @@ -83,11 +87,6 @@ When built with c-ares for name resolving, setting this environment variable to `[IP:port]` makes libcurl use that DNS server instead of the system default. This is used by the curl test suite. -## `CURL_DNS_DELAY_MS` - -Delay the DNS resolve by this many milliseconds. This is used in the test -suite to check proper handling of CURLOPT_CONNECTTIMEOUT(3). - ## `CURL_FTP_PWD_STOP` When set, the first transfer - when using ftp: - returns before sending @@ -112,6 +111,11 @@ A fixed faked value to use instead of a proper random number so that functions in libcurl that are otherwise getting random outputs can be tested for what they generate. +## `CURL_SIGPIPE_DEBUG` + +When present, `curl` does not set `SIGPIPE` to ignore. This allows +verification that `libcurl` does not cause `SIGPIPE` to be raised. + ## `CURL_SMALLREQSEND` An alternative size of HTTP data to be sent at a time only if smaller than the @@ -170,7 +174,23 @@ a multi handle is destroyed. This implicitly triggers for easy handles that are run via easy_perform. The value of the environment variable gives the shutdown timeout in milliseconds. -## `CURL_H2_STREAM_WIN_MAX` +## `CURL_DBG_RESOLV_MAX_THREADS` -Set to a positive 32-bit number to override the HTTP/2 stream window's -default of 10MB. Used in testing to verify correct window update handling. +Overrides the maximum number of threads for resolver. + +## `CURL_DBG_RESOLV_DELAY` + +Makes ever threaded resolve experience an initial delay in milliseconds. + +## `CURL_DBG_RESOLV_FAIL_DELAY` + +With a threaded resolver, delay each lookup by the given milliseconds +and give a negative answer. + +## `CURL_DBG_RESOLV_FAIL_IPV6` + +Make libcurl fail a resolve for IPv6 only. + +## `CURL_QUICK_EXIT` + +Make `curl` use the quick exit option, even when built in debug mode. diff --git a/docs/libcurl/libcurl-errors.md b/docs/libcurl/libcurl-errors.md index 773601dae7..7ae1319dcf 100644 --- a/docs/libcurl/libcurl-errors.md +++ b/docs/libcurl/libcurl-errors.md @@ -29,11 +29,10 @@ Why they occur and possibly what you can do to fix the problem are also included # CURLcode Almost all "easy" interface functions return a CURLcode error code. No matter -what, using the curl_easy_setopt(3) option CURLOPT_ERRORBUFFER(3) -is a good idea as it gives you a human readable error string that may offer -more details about the cause of the error than just the error code. -curl_easy_strerror(3) can be called to get an error string from a given -CURLcode number. +what, using the curl_easy_setopt(3) option CURLOPT_ERRORBUFFER(3) is a good +idea as it gives you a human readable error string that may offer more details +about the cause of the error than the error code alone. curl_easy_strerror(3) +can be called to get an error string from a given CURLcode number. CURLcode is one of the following: @@ -45,8 +44,7 @@ All fine. Proceed as usual. The URL you passed to libcurl used a protocol that this libcurl does not support. The support might be a compile-time option that you did not use, it -can be a misspelled protocol string or just a protocol libcurl has no code -for. +can be a misspelled protocol string or a protocol libcurl has no code for. ## CURLE_FAILED_INIT (2) @@ -349,7 +347,7 @@ Initiating the SSL Engine failed. ## CURLE_LOGIN_DENIED (67) -The remote server denied curl to login (Added in 7.13.1) +The remote server denied curl to login ## CURLE_TFTP_NOTFOUND (68) @@ -403,22 +401,20 @@ Failed to shut down the SSL connection. Socket is not ready for send/recv. Wait until it is ready and try again. This return code is only returned from curl_easy_recv(3) and curl_easy_send(3) -(Added in 7.18.2) ## CURLE_SSL_CRL_BADFILE (82) -Failed to load CRL file (Added in 7.19.0) +Failed to load CRL file ## CURLE_SSL_ISSUER_ERROR (83) -Issuer check failed (Added in 7.19.0) +Issuer check failed ## CURLE_FTP_PRET_FAILED (84) The FTP server does not understand the PRET command at all or does not support the given argument. Be careful when using CURLOPT_CUSTOMREQUEST(3), a -custom LIST command is sent with the PRET command before PASV as well. (Added -in 7.20.0) +custom LIST command is sent with the PRET command before PASV as well. ## CURLE_RTSP_CSEQ_ERROR (85) @@ -439,7 +435,7 @@ Chunk callback reported error. ## CURLE_NO_CONNECTION_AVAILABLE (89) (For internal use only, is never returned by libcurl) No connection available, -the session is queued. (added in 7.30.0) +the session is queued. ## CURLE_SSL_PINNEDPUBKEYNOTMATCH (90) @@ -488,7 +484,7 @@ An internal call to poll() or select() returned error that is not recoverable. A value or data field grew larger than allowed. -## CURLE_ECH_REQUIRED (101)" +## CURLE_ECH_REQUIRED (101) ECH was attempted but failed. @@ -509,7 +505,6 @@ used. An alias for *CURLM_CALL_MULTI_PERFORM*. Never returned by modern libcurl versions. -(Added in 7.15.5) ## CURLM_OK (0) @@ -536,17 +531,15 @@ This can only be returned if libcurl bugs. Please report it to us. ## CURLM_BAD_SOCKET (5) The passed-in socket is not a valid one that libcurl already knows about. -(Added in 7.15.4) ## CURLM_UNKNOWN_OPTION (6) curl_multi_setopt() with unsupported option -(Added in 7.15.4) ## CURLM_ADDED_ALREADY (7) An easy handle already added to a multi handle was attempted to get added a -second time. (Added in 7.32.1) +second time. ## CURLM_RECURSIVE_API_CALL (8) @@ -592,12 +585,11 @@ An invalid share object was passed to the function. ## CURLSHE_NOMEM (4) Not enough memory was available. -(Added in 7.12.0) ## CURLSHE_NOT_BUILT_IN (5) The requested sharing could not be done because the library you use do not have -that particular feature enabled. (Added in 7.23.0) +that particular feature enabled. # CURLUcode diff --git a/docs/libcurl/libcurl-multi.md b/docs/libcurl/libcurl-multi.md index 3becb648de..b400f61a03 100644 --- a/docs/libcurl/libcurl-multi.md +++ b/docs/libcurl/libcurl-multi.md @@ -130,13 +130,12 @@ using large numbers of simultaneous connections. curl_multi_socket_action(3) is then used instead of curl_multi_perform(3). -When using this API, you add easy handles to the multi handle just as with the +When using this API, you add easy handles to the multi handle like with the normal multi interface. Then you also set two callbacks with the -CURLMOPT_SOCKETFUNCTION(3) and CURLMOPT_TIMERFUNCTION(3) options -to curl_multi_setopt(3). They are two callback functions that libcurl -calls with information about what sockets to wait for, and for what activity, -and what the current timeout time is - if that expires libcurl should be -notified. +CURLMOPT_SOCKETFUNCTION(3) and CURLMOPT_TIMERFUNCTION(3) options to +curl_multi_setopt(3). They are two callback functions that libcurl calls with +information about what sockets to wait for, and for what activity, and what +the current timeout time is - if that expires libcurl should be notified. The multi_socket API is designed to inform your application about which sockets libcurl is currently using and for what activities (read and/or write) @@ -166,7 +165,9 @@ get activity on the sockets you have been asked to wait on, or if the timeout timer expires. You can poll curl_multi_info_read(3) to see if any transfer has -completed, as it then has a message saying so. +completed, as it then has a message saying so. To know how many transfers are +currently queued, running, pending or done, you can use the +curl_multi_get_offt(3) function. # BLOCKING @@ -175,7 +176,7 @@ multi interface. While we certainly want and intend for these to get fixed in the future, you should be aware of the following current restrictions: ~~~c - - Name resolves unless the c-ares or threaded-resolver backends are used - - file:// transfers - - TELNET transfers +- Name resolves unless the c-ares or threaded-resolver backends are used +- file:// transfers +- TELNET transfers ~~~ diff --git a/docs/libcurl/libcurl-security.md b/docs/libcurl/libcurl-security.md index c6a10cd271..cca0c31e84 100644 --- a/docs/libcurl/libcurl-security.md +++ b/docs/libcurl/libcurl-security.md @@ -20,9 +20,9 @@ libcurl-security - security considerations when using libcurl The libcurl project takes security seriously. The library is written with caution and precautions are taken to mitigate many kinds of risks encountered while operating with potentially malicious servers on the Internet. It is a -powerful library, however, which allows application writers to make trade-offs -between ease of writing and exposure to potential risky operations. If used -the right way, you can use libcurl to transfer data pretty safely. +powerful library that allows application writers to make trade-offs between +ease of writing and exposure to potential risky operations. If used the right +way, you can use libcurl to transfer data pretty safely. Many applications are used in closed networks where users and servers can (possibly) be trusted, but many others are used on arbitrary servers and are @@ -64,8 +64,8 @@ plain text anywhere. Many of the protocols libcurl supports send name and password unencrypted as clear text (HTTP Basic authentication, FTP, TELNET etc). It is easy for anyone -on your network or a network nearby yours to just fire up a network analyzer -tool and eavesdrop on your passwords. Do not let the fact that HTTP Basic uses +on your network or a network nearby yours to fire up a network analyzer tool +and eavesdrop on your passwords. Do not let the fact that HTTP Basic uses base64 encoded passwords fool you. They may not look readable at a first glance, but they are easily "deciphered" by anyone within seconds. @@ -98,13 +98,31 @@ Use authenticated protocols protected with HTTPS or SSH. Never ever switch off certificate verification. +# Certificates and Long-running Connections + +Certificate validation of encrypted connections is performed immediately after +a connection is established. That connection could be used for many subsequent +transfers, even if the certificate used for validation expires or is revoked, +the local certificate bundle is changed in a way that would have caused that +certificate to fail validation, the server changes its certificate to one +that would have failed validation, or even if a completely different server is +brought up under the same hostname. This could continue for many hours (or +even years) after such a change occurs, which may not be desired behavior for +some applications. + +Remedies: + +Use the CURLOPT_MAXLIFETIME_CONN(3) option to limit the amount of time that +connections are used after they have been successfully validated. Further +transfers require a new connection with validation performed again. + # Redirects -The CURLOPT_FOLLOWLOCATION(3) option automatically follows HTTP -redirects sent by a remote server. These redirects can refer to any kind of -URL, not just HTTP. libcurl restricts the protocols allowed to be used in -redirects for security reasons: only HTTP, HTTPS, FTP and FTPS are -enabled by default. Applications may opt to restrict that set further. +The CURLOPT_FOLLOWLOCATION(3) option automatically follows HTTP redirects sent +by a remote server. These redirects can refer to any kind of URL, not only +HTTP. libcurl restricts the protocols allowed to be used in redirects for +security reasons: only HTTP, HTTPS, FTP and FTPS are enabled by default. +Applications may opt to restrict that set further. A redirect to a file: URL would cause the libcurl to read (or write) arbitrary files from the local file system. If the application returns the data back to @@ -113,8 +131,8 @@ leverage this to read otherwise forbidden data (e.g. **file://localhost/etc/passwd**). If authentication credentials are stored in the ~/.netrc file, or Kerberos is -in use, any other URL type (not just file:) that requires authentication is -also at risk. A redirect such as **ftp://some-internal-server/private-file** would +in use, any other URL type (except file:) that requires authentication is also +at risk. A redirect such as **ftp://some-internal-server/private-file** would then return data even when the server is password protected. In the same way, if an unencrypted SSH private key has been configured for the @@ -126,9 +144,10 @@ The CURLOPT_REDIR_PROTOCOLS_STR(3) and CURLOPT_NETRC(3) options can be used to mitigate against this kind of attack. A redirect can also specify a location available only on the machine running -libcurl, including servers hidden behind a firewall from the attacker. -E.g. **http://127.0.0.1/** or **http://intranet/delete-stuff.cgi?delete=all** or -**tftp://bootp-server/pc-config-data** +libcurl, including servers hidden behind a firewall from the attacker. E.g. +**https://127.0.0.1/** or +**https://intranet.example/delete-stuff.cgi?delete=all** or +**tftp://bootp-server.example/pc-config-data** Applications can mitigate against this by disabling CURLOPT_FOLLOWLOCATION(3) and handling redirects itself, sanitizing URLs @@ -154,13 +173,12 @@ way you did not intend such as injecting new headers. A user who can control the DNS server of a domain being passed in within a URL can change the address of the host to a local, private address which a server-side libcurl-using application could then use. E.g. the innocuous URL -**http://fuzzybunnies.example.com/** could actually resolve to the IP -address of a server behind a firewall, such as 127.0.0.1 or -10.1.2.3. Applications can mitigate against this by setting a -CURLOPT_OPENSOCKETFUNCTION(3) or CURLOPT_PREREQFUNCTION(3) and -checking the address before a connection. +**https://fuzzybunnies.example.com/** could actually resolve to the IP address +of a server behind a firewall, such as 127.0.0.1 or 10.1.2.3. Applications can +mitigate against this by setting a CURLOPT_OPENSOCKETFUNCTION(3) or +CURLOPT_PREREQFUNCTION(3) and checking the address before a connection. -All the malicious scenarios regarding redirected URLs apply just as well to +All the malicious scenarios regarding redirected URLs apply equally to non-redirected URLs, if the user is allowed to specify an arbitrary URL that could point to a private resource. For example, a web app providing a translation service might happily translate **file://localhost/etc/passwd** @@ -174,7 +192,7 @@ behind a firewall. Applications can mitigate against this by using the CURLOPT_FTP_SKIP_PASV_IP(3) option or CURLOPT_FTPPORT(3). Local servers sometimes assume local access comes from friends and trusted -users. An application that expects https://example.com/file_to_read that and +users. An application that expects https://example.com/file_to_read and instead gets http://192.168.0.1/my_router_config might print a file that would otherwise be protected by the firewall. @@ -193,15 +211,15 @@ or a mix of decimal, octal or hexadecimal encoding. # IPv6 Addresses -libcurl handles IPv6 addresses transparently and just as easily as IPv4 -addresses. That means that a sanitizing function that filters out addresses -like 127.0.0.1 is not sufficient - the equivalent IPv6 addresses **::1**, -**::**, **0:00::0:1**, **::127.0.0.1** and **::ffff:7f00:1** supplied -somehow by an attacker would all bypass a naive filter and could allow access -to undesired local resources. IPv6 also has special address blocks like -link-local and site-local that generally should not be accessed by a -server-side libcurl-using application. A poorly configured firewall installed -in a data center, organization or server may also be configured to limit IPv4 +libcurl handles IPv6 addresses transparently and as easily as IPv4 addresses. +That means that a sanitizing function that filters out addresses like +127.0.0.1 is not sufficient - the equivalent IPv6 addresses **::1**, **::**, +**0:00::0:1**, **::127.0.0.1** and **::ffff:7f00:1** supplied somehow by an +attacker would all bypass a naive filter and could allow access to undesired +local resources. IPv6 also has special address blocks like link-local and +site-local that generally should not be accessed by a server-side +libcurl-using application. A poorly configured firewall installed in a data +center, organization or server may also be configured to limit IPv4 connections but leave IPv6 connections wide open. In some cases, setting CURLOPT_IPRESOLVE(3) to CURL_IPRESOLVE_V4 can be used to limit resolved addresses to IPv4 only and bypass these issues. @@ -236,7 +254,7 @@ the request. If cookies are enabled and cached, then a user could craft a URL which performs some malicious action to a site whose authentication is already stored in a cookie. E.g. -**http://mail.example.com/delete-stuff.cgi?delete=all** Applications can +**https://mail.example.com/delete-stuff.cgi?delete=all** Applications can mitigate against this by disabling cookies or clearing them between requests. # Dangerous SCP URLs @@ -269,19 +287,19 @@ When first realizing this, the curl team tried to filter out such attempts in order to protect applications for inadvertent probes of for example internal networks etc. This resulted in CVE-2019-15601 and the associated security fix. -However, we have since been made aware of the fact that the previous fix was far -from adequate as there are several other ways to accomplish more or less the -same thing: accessing a remote host over the network instead of the local file +We have since been made aware of the fact that the previous fix was far from +adequate as there are several other ways to accomplish more or less the same +thing: accessing a remote host over the network instead of the local file system. The conclusion we have come to is that this is a weakness or feature in the Windows operating system itself, that we as an application cannot safely -protect users against. It would just be a whack-a-mole race we do not want to +protect users against. It would make a whack-a-mole race we do not want to participate in. There are too many ways to do it and there is no knob we can use to turn off the practice. If you use curl or libcurl on Windows (any version), disable the use of the -FILE protocol in curl or be prepared that accesses to a range of "magic paths" +FILE protocol in curl or be prepared that accesses to a set of special paths potentially make your system access other hosts on your network. curl cannot protect you against this. @@ -297,8 +315,8 @@ if creative use of special characters are applied? If the user can set the URL, the user can also specify the scheme part to other protocols that you did not intend for users to use and perhaps did not -consider. curl supports over 20 different URL schemes. "http://" might be what -you thought, "ftp://" or "imap://" might be what the user gives your +consider. curl supports over 27 different URL schemes. `https://` might be +what you expect, `ftp://` or `imap://` might be what the user gives your application. Also, cross-protocol operations might be done by using a particular scheme in the URL but point to a server doing a different protocol on a non-standard port. @@ -315,8 +333,11 @@ libcurl programs can use CURLOPT_PROTOCOLS_STR(3) to limit what URL schemes it a ## consider not allowing the user to set the full URL -Maybe just let the user provide data for parts of it? Or maybe filter input to -only allow specific choices? +Maybe let the user provide data for parts of it? Or maybe filter input to only +allow specific choices? Remember that the naive approach of appending a +user-specified string to a base URL could still allow unexpected results +through use of characters like ../ or ? or Unicode characters or hiding +characters using various escaping means. # RFC 3986 vs WHATWG URL @@ -369,16 +390,16 @@ hard to avoid. # Active FTP passes on the local IP address If you use curl/libcurl to do *active* FTP transfers, curl passes on the -address of your local IP to the remote server - even when for example using a -SOCKS or HTTP proxy in between curl and the target server. +address of your local IP interface to the remote server - even when for example +using a SOCKS or HTTP proxy in between curl and the target server. # Denial of Service A malicious server could cause libcurl to effectively hang by sending data -slowly, or even no data at all but just keeping the TCP connection open. This -could effectively result in a denial-of-service attack. The -CURLOPT_TIMEOUT(3) and/or CURLOPT_LOW_SPEED_LIMIT(3) options can -be used to mitigate against this. +slowly, or even no data at all but keeping the TCP connection open. This could +effectively result in a denial-of-service attack. The CURLOPT_TIMEOUT(3) +and/or CURLOPT_LOW_SPEED_LIMIT(3) options can be used to mitigate against +this. A malicious server could cause libcurl to download an infinite amount of data, potentially causing system resources to be exhausted resulting in a system or @@ -434,8 +455,8 @@ passwords, things like URLs, cookies or even filenames could also hold sensitive data. To avoid this problem, you must of course use your common sense. Often, you -can just edit out the sensitive data or just search/replace your true -information with faked data. +can edit out the sensitive data or search/replace your true information with +faked data. # setuid applications using libcurl @@ -494,6 +515,6 @@ cookies. # Report Security Problems -Should you detect or just suspect a security problem in libcurl or curl, -contact the project curl security team immediately. See +Should you detect or suspect a security problem in libcurl or curl, contact +the project curl security team immediately. See https://curl.se/dev/secprocess.html for details. diff --git a/docs/libcurl/libcurl-thread.md b/docs/libcurl/libcurl-thread.md index ef7ae9b7d9..8ef893f075 100644 --- a/docs/libcurl/libcurl-thread.md +++ b/docs/libcurl/libcurl-thread.md @@ -17,7 +17,7 @@ libcurl-thread - libcurl thread safety # Multi-threading with libcurl -libcurl is thread safe but has no internal thread synchronization. You may have +libcurl is thread-safe but has no internal thread synchronization. You may have to provide your own locking should you meet any of the thread safety exceptions below. @@ -61,6 +61,13 @@ does this cleanup automatically and there is no leak (added in libcurl 8.8.0). Please review the OpenSSL documentation for a full list of circumstances: https://docs.openssl.org/3.0/man3/OPENSSL_init_crypto/#notes +## mbedTLS + +mbedTLS can be used safely in a multi-threaded environment provided that mbedTLS is +compiled with MBEDTLS_THREADING_C enabled. + +https://mbed-tls.readthedocs.io/en/latest/kb/development/thread-safety-and-multi-threading + # Signals Signals are used for timing out name resolves (during DNS lookup) - when built @@ -71,7 +78,7 @@ When using multiple threads you should set the CURLOPT_NOSIGNAL(3) option to 1L for all handles. Everything works fine except that timeouts cannot be honored during DNS lookups - which you can work around by building libcurl with c-ares or threaded-resolver support. c-ares is a library that -provides asynchronous name resolves. On some platforms, libcurl simply cannot +provides asynchronous name resolves. On some platforms, libcurl cannot function properly multi-threaded unless the CURLOPT_NOSIGNAL(3) option is set. @@ -84,10 +91,10 @@ the former signal handler while another thread should still ignore it. # Name resolving The **gethostbyname** or **getaddrinfo** and other name resolving system -calls used by libcurl are provided by your operating system and must be thread -safe. It is important that libcurl can find and use thread safe versions of -these and other system calls, as otherwise it cannot function fully thread -safe. Some operating systems are known to have faulty thread +calls used by libcurl are provided by your operating system and must be +thread-safe. It is important that libcurl can find and use thread-safe versions +of these and other system calls, as otherwise it cannot function fully +thread-safe. Some operating systems are known to have faulty thread implementations. We have previously received problem reports on *BSD (at least in the past, they may be working fine these days). Some operating systems that are known to have solid and working thread support are Linux, Solaris and @@ -110,7 +117,7 @@ libcurl(3) section **GLOBAL CONSTANTS**. # Memory functions These functions, provided either by your operating system or your own -replacements, must be thread safe. You can use curl_global_init_mem(3) +replacements, must be thread-safe. You can use curl_global_init_mem(3) to set your own replacement memory functions. # Non-safe functions diff --git a/docs/libcurl/libcurl-tutorial.md b/docs/libcurl/libcurl-tutorial.md index e515b3b7b0..704ee03564 100644 --- a/docs/libcurl/libcurl-tutorial.md +++ b/docs/libcurl/libcurl-tutorial.md @@ -46,7 +46,7 @@ Your compiler needs to know where the libcurl headers are located. Therefore you must set your compiler's include path to point to the directory where you installed them. The 'curl-config'[3] tool can be used to get this information: ~~~c - $ curl-config --cflags + $ curl-config --cflags ~~~ ## Linking the Program with libcurl @@ -58,7 +58,7 @@ OpenSSL libraries, but even some standard OS libraries may be needed on the command line. To figure out which flags to use, once again the 'curl-config' tool comes to the rescue: ~~~c - $ curl-config --libs + $ curl-config --libs ~~~ ## SSL or Not @@ -71,7 +71,7 @@ installed libcurl has been built with SSL support enabled, use *curl-config* like this: ~~~c - $ curl-config --feature + $ curl-config --feature ~~~ If SSL is supported, the keyword *SSL* is written to stdout, possibly together @@ -92,9 +92,9 @@ The people behind libcurl have put a considerable effort to make libcurl work on a large amount of different operating systems and environments. You program libcurl the same way on all platforms that libcurl runs on. There -are only a few minor details that differ. If you just make sure to write your -code portable enough, you can create a portable program. libcurl should not -stop you from that. +are only a few minor details that differ. If you make sure to write your code +portable enough, you can create a portable program. libcurl should not stop +you from that. # Global Preparation @@ -102,7 +102,7 @@ The program must initialize some of the libcurl functionality globally. That means it should be done exactly once, no matter how many times you intend to use the library. Once for your program's entire life time. This is done using ~~~c - curl_global_init() + curl_global_init() ~~~ and it takes one parameter which is a bit pattern that tells libcurl what to initialize. Using *CURL_GLOBAL_ALL* makes it initialize all known internal @@ -162,16 +162,16 @@ understanding. # Handle the Easy libcurl To use the easy interface, you must first create yourself an easy handle. You -need one handle for each easy session you want to perform. Basically, you -should use one handle for every thread you plan to use for transferring. You -must never share the same handle in multiple threads. +need one handle for each easy session you want to perform. You should use one +handle for every thread you plan to use for transferring. You must never share +the same handle in multiple threads. Get an easy handle with ~~~c - handle = curl_easy_init(); + handle = curl_easy_init(); ~~~ It returns an easy handle. Using that you proceed to the next step: setting -up your preferred actions. A handle is just a logic entity for the upcoming +up your preferred actions. A handle is a logic entity for the upcoming transfer or series of transfers. You set properties and options for this handle using @@ -194,33 +194,37 @@ One of the most basic properties to set in the handle is the URL. You set your preferred URL to transfer with CURLOPT_URL(3) in a manner similar to: ~~~c - curl_easy_setopt(handle, CURLOPT_URL, "http://example.com/"); + curl_easy_setopt(handle, CURLOPT_URL, "https://example.com/"); ~~~ Let's assume for a while that you want to receive data as the URL identifies a remote resource you want to get here. Since you write a sort of application that needs this transfer, I assume that you would like to get the data passed -to you directly instead of simply getting it passed to stdout. So, you write -your own function that matches this prototype: +to you directly instead of getting it passed to stdout. You write your +own function that matches this prototype: ~~~c - size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp); + size_t write_data(char *buffer, size_t size, size_t nmemb, void *userp); ~~~ + You tell libcurl to pass all data to this function by issuing a function similar to this: + ~~~c - curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_data); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_data); ~~~ + You can control what data your callback function gets in the fourth argument by setting another property: + ~~~c - curl_easy_setopt(handle, CURLOPT_WRITEDATA, &internal_struct); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, &internal_struct); ~~~ Using that property, you can easily pass local data between your application and the function that gets invoked by libcurl. libcurl itself does not touch the data you pass with CURLOPT_WRITEDATA(3). libcurl offers its own default internal callback that takes care of the data -if you do not set the callback with CURLOPT_WRITEFUNCTION(3). It simply +if you do not set the callback with CURLOPT_WRITEFUNCTION(3). It outputs the received data to stdout. You can have the default callback write the data to a different file handle by passing a 'FILE *' to a file opened for writing with the CURLOPT_WRITEDATA(3) option. @@ -243,7 +247,7 @@ There are of course many more options you can set, and we get back to a few of them later. Let's instead continue to the actual transfer: ~~~c - success = curl_easy_perform(handle); + success = curl_easy_perform(handle); ~~~ curl_easy_perform(3) connects to the remote site, does the necessary commands @@ -267,12 +271,12 @@ previous connection. For some protocols, downloading a file can involve a complicated process of logging in, setting the transfer mode, changing the current directory and finally transferring the file data. libcurl takes care of all that -complication for you. Given simply the URL to a file, libcurl takes care of +complication for you. Given the URL to a file, libcurl takes care of all the details needed to get the file moved from one machine to another. # Multi-threading Issues -libcurl is thread safe but there are a few exceptions. Refer to +libcurl is thread-safe but there are a few exceptions. Refer to libcurl-thread(3) for more information. # When It does not Work @@ -291,11 +295,11 @@ a better understanding why the server behaves the way it does. Include headers in the normal body output with CURLOPT_HEADER(3) set 1. Of course, there are bugs left. We need to know about them to be able to fix -them, so we are quite dependent on your bug reports. When you do report -suspected bugs in libcurl, please include as many details as you possibly can: -a protocol dump that CURLOPT_VERBOSE(3) produces, library version, as -much as possible of your code that uses libcurl, operating system name and -version, compiler name and version etc. +them, so we are dependent on your bug reports. When you do report suspected +bugs in libcurl, please include as many details as you possibly can: a protocol +dump that CURLOPT_VERBOSE(3) produces, library version, as much as possible of +your code that uses libcurl, operating system name and version, compiler name +and version etc. If CURLOPT_VERBOSE(3) is not enough, you increase the level of debug data your application receive by using the CURLOPT_DEBUGFUNCTION(3). @@ -311,15 +315,15 @@ uploading to a remote FTP site is similar to uploading data to an HTTP server with a PUT request. Of course, first you either create an easy handle or you reuse one existing -one. Then you set the URL to operate on just like before. This is the remote -URL, that we now upload. +one. Then you set the URL to operate on like before. This is the remote URL, +that we now upload. Since we write an application, we most likely want libcurl to get the upload data by asking us for it. To make it do that, we set the read callback and the custom pointer libcurl passes to our read callback. The read callback should have a prototype similar to: ~~~c - size_t function(char *bufptr, size_t size, size_t nitems, void *userp); + size_t function(char *bufptr, size_t size, size_t nitems, void *userp); ~~~ Where *bufptr* is the pointer to a buffer we fill in with data to upload and *nitems* is the size of the buffer and therefore also the maximum @@ -327,21 +331,21 @@ amount of data we can return to libcurl in this call. The *userp* pointer is the custom pointer we set to point to a struct of ours to pass private data between the application and the callback. ~~~c - curl_easy_setopt(handle, CURLOPT_READFUNCTION, read_function); + curl_easy_setopt(handle, CURLOPT_READFUNCTION, read_function); - curl_easy_setopt(handle, CURLOPT_READDATA, &filedata); + curl_easy_setopt(handle, CURLOPT_READDATA, &filedata); ~~~ Tell libcurl that we want to upload: ~~~c - curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L); + curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L); ~~~ A few protocols do not behave properly when uploads are done without any prior -knowledge of the expected file size. So, set the upload file size using the +knowledge of the expected file size. Set the upload file size using the CURLOPT_INFILESIZE_LARGE(3) for all known file sizes like this[1]: ~~~c - /* in this example, file_size must be an curl_off_t variable */ - curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, file_size); + /* in this example, file_size must be an curl_off_t variable */ + curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, file_size); ~~~ When you call curl_easy_perform(3) this time, it performs all the @@ -361,7 +365,7 @@ Most protocols support that you specify the name and password in the URL itself. libcurl detects this and use them accordingly. This is written like this: ~~~c - protocol://user:password@example.com/path/ + protocol://user:password@example.com/path/ ~~~ If you need any odd letters in your username or password, you should enter them URL encoded, as %XX where XX is a two-digit hexadecimal number. @@ -372,16 +376,16 @@ CURLOPT_USERPWD(3) option. The argument passed to libcurl should be a char * to a string in the format "user:password". In a manner like this: ~~~c - curl_easy_setopt(handle, CURLOPT_USERPWD, "myname:thesecret"); + curl_easy_setopt(handle, CURLOPT_USERPWD, "myname:thesecret"); ~~~ Another case where name and password might be needed at times, is for those users who need to authenticate themselves to a proxy they use. libcurl offers -another option for this, the CURLOPT_PROXYUSERPWD(3). It is used quite similar -to the CURLOPT_USERPWD(3) option like this: +another option for this, the CURLOPT_PROXYUSERPWD(3). Its use is similar to the +CURLOPT_USERPWD(3) option, like this: ~~~c - curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, "myname:thesecret"); + curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, "myname:thesecret"); ~~~ There is a long time Unix "standard" way of storing FTP usernames and @@ -396,15 +400,15 @@ non-FTP protocols such as HTTP. To make curl use this file, use the CURLOPT_NETRC(3) option: ~~~c - curl_easy_setopt(handle, CURLOPT_NETRC, 1L); + curl_easy_setopt(handle, CURLOPT_NETRC, 1L); ~~~ A basic example of how such a .netrc file may look like: ~~~c - machine myhost.mydomain.com - login userlogin - password secretword + machine myhost.mydomain.com + login userlogin + password secretword ~~~ All these examples have been cases where the password has been optional, or @@ -414,7 +418,7 @@ you are using an SSL private key for secure transfers. To pass the known private key password to libcurl: ~~~c - curl_easy_setopt(handle, CURLOPT_KEYPASSWD, "keypassword"); + curl_easy_setopt(handle, CURLOPT_KEYPASSWD, "keypassword"); ~~~ # HTTP Authentication @@ -431,15 +435,14 @@ Negotiate (SPNEGO). You can tell libcurl which one to use with CURLOPT_HTTPAUTH(3) as in: ~~~c - curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); - + curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); ~~~ When you send authentication to a proxy, you can also set authentication type the same way but instead with CURLOPT_PROXYAUTH(3): ~~~c - curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM); + curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM); ~~~ Both these options allow you to set multiple types (by ORing them together), @@ -448,7 +451,7 @@ claims to support. This method does however add a round-trip since libcurl must first ask the server what it supports: ~~~c - curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST|CURLAUTH_BASIC); + curl_easy_setopt(handle, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST | CURLAUTH_BASIC); ~~~ For convenience, you can use the *CURLAUTH_ANY* define (instead of a list with @@ -468,9 +471,9 @@ pages using the \ tag uses. We provide a pointer to the data and tell libcurl to post it all to the remote site: ~~~c - char *data="name=daniel&project=curl"; + char *data = "name=daniel&project=curl"; curl_easy_setopt(handle, CURLOPT_POSTFIELDS, data); - curl_easy_setopt(handle, CURLOPT_URL, "http://posthere.com/"); + curl_easy_setopt(handle, CURLOPT_URL, "https://posthere.example/"); curl_easy_perform(handle); /* post away! */ ~~~ @@ -487,21 +490,21 @@ done in a generic way, by building a list of our own headers and then passing that list to libcurl. ~~~c - struct curl_slist *headers=NULL; - headers = curl_slist_append(headers, "Content-Type: text/xml"); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Content-Type: text/xml"); - /* post binary data */ - curl_easy_setopt(handle, CURLOPT_POSTFIELDS, binaryptr); + /* post binary data */ + curl_easy_setopt(handle, CURLOPT_POSTFIELDS, binaryptr); - /* set the size of the postfields data */ - curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, 23L); + /* set the size of the postfields data */ + curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, 23L); - /* pass our list of custom made headers */ - curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); + /* pass our list of custom made headers */ + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); - curl_easy_perform(handle); /* post away! */ + curl_easy_perform(handle); /* post away! */ - curl_slist_free_all(headers); /* free the header list */ + curl_slist_free_all(headers); /* free the header list */ ~~~ While the simple examples above cover the majority of all cases where HTTP @@ -531,24 +534,24 @@ The following example sets two simple text parts with plain textual contents, and then a file with binary contents and uploads the whole thing. ~~~c - curl_mime *multipart = curl_mime_init(handle); - curl_mimepart *part = curl_mime_addpart(multipart); - curl_mime_name(part, "name"); - curl_mime_data(part, "daniel", CURL_ZERO_TERMINATED); - part = curl_mime_addpart(multipart); - curl_mime_name(part, "project"); - curl_mime_data(part, "curl", CURL_ZERO_TERMINATED); - part = curl_mime_addpart(multipart); - curl_mime_name(part, "logotype-image"); - curl_mime_filedata(part, "curl.png"); + curl_mime *multipart = curl_mime_init(handle); + curl_mimepart *part = curl_mime_addpart(multipart); + curl_mime_name(part, "name"); + curl_mime_data(part, "daniel", CURL_ZERO_TERMINATED); + part = curl_mime_addpart(multipart); + curl_mime_name(part, "project"); + curl_mime_data(part, "curl", CURL_ZERO_TERMINATED); + part = curl_mime_addpart(multipart); + curl_mime_name(part, "logotype-image"); + curl_mime_filedata(part, "curl.png"); - /* Set the form info */ - curl_easy_setopt(handle, CURLOPT_MIMEPOST, multipart); + /* Set the form info */ + curl_easy_setopt(handle, CURLOPT_MIMEPOST, multipart); - curl_easy_perform(handle); /* post away! */ + curl_easy_perform(handle); /* post away! */ - /* free the post data again */ - curl_mime_free(multipart); + /* free the post data again */ + curl_mime_free(multipart); ~~~ To post multiple files for a single form field, you must supply each file in @@ -559,8 +562,8 @@ multiple files posting is deprecated by RFC 7578, chapter 4.3. To set the data source from an already opened FILE pointer, use: ~~~c - curl_mime_data_cb(part, filesize, (curl_read_callback) fread, - (curl_seek_callback) fseek, NULL, filepointer); + curl_mime_data_cb(part, filesize, (curl_read_callback)fread, + (curl_seek_callback)fseek, NULL, filepointer); ~~~ A deprecated curl_formadd(3) function is still supported in libcurl. @@ -574,25 +577,25 @@ parts, you post the whole form. The MIME API example above is expressed as follows using this function: ~~~c - struct curl_httppost *post=NULL; - struct curl_httppost *last=NULL; - curl_formadd(&post, &last, - CURLFORM_COPYNAME, "name", - CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END); - curl_formadd(&post, &last, - CURLFORM_COPYNAME, "project", - CURLFORM_COPYCONTENTS, "curl", CURLFORM_END); - curl_formadd(&post, &last, - CURLFORM_COPYNAME, "logotype-image", - CURLFORM_FILECONTENT, "curl.png", CURLFORM_END); + struct curl_httppost *post = NULL; + struct curl_httppost *last = NULL; + curl_formadd(&post, &last, + CURLFORM_COPYNAME, "name", + CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END); + curl_formadd(&post, &last, + CURLFORM_COPYNAME, "project", + CURLFORM_COPYCONTENTS, "curl", CURLFORM_END); + curl_formadd(&post, &last, + CURLFORM_COPYNAME, "logotype-image", + CURLFORM_FILECONTENT, "curl.png", CURLFORM_END); - /* Set the form info */ - curl_easy_setopt(handle, CURLOPT_HTTPPOST, post); + /* Set the form info */ + curl_easy_setopt(handle, CURLOPT_HTTPPOST, post); - curl_easy_perform(handle); /* post away! */ + curl_easy_perform(handle); /* post away! */ - /* free the post data again */ - curl_formfree(post); + /* free the post data again */ + curl_formfree(post); ~~~ Multipart formposts are chains of parts using MIME-style separators and @@ -605,31 +608,33 @@ shows how you set headers to one specific part when you add that to the post handle: ~~~c - struct curl_slist *headers=NULL; - headers = curl_slist_append(headers, "Content-Type: text/xml"); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Content-Type: text/xml"); - curl_formadd(&post, &last, - CURLFORM_COPYNAME, "logotype-image", - CURLFORM_FILECONTENT, "curl.xml", - CURLFORM_CONTENTHEADER, headers, - CURLFORM_END); + curl_formadd(&post, &last, + CURLFORM_COPYNAME, "logotype-image", + CURLFORM_FILECONTENT, "curl.xml", + CURLFORM_CONTENTHEADER, headers, + CURLFORM_END); - curl_easy_perform(handle); /* post away! */ + curl_easy_perform(handle); /* post away! */ - curl_formfree(post); /* free post */ - curl_slist_free_all(headers); /* free custom header list */ + curl_formfree(post); /* free post */ + curl_slist_free_all(headers); /* free custom header list */ ~~~ Since all options on an easy handle are "sticky", they remain the same until -changed even if you do call curl_easy_perform(3), you may need to tell -curl to go back to a plain GET request if you intend to do one as your next -request. You force an easy handle to go back to GET by using the -CURLOPT_HTTPGET(3) option: +changed even if you do call curl_easy_perform(3), you may need to tell curl to +go back to a plain GET request if you intend to do one as your next request. +You force an easy handle to go back to GET by using the CURLOPT_HTTPGET(3) +option: + ~~~c - curl_easy_setopt(handle, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(handle, CURLOPT_HTTPGET, 1L); ~~~ -Just setting CURLOPT_POSTFIELDS(3) to "" or NULL does *not* stop libcurl -from doing a POST. It just makes it POST without any data to send! + +Setting CURLOPT_POSTFIELDS(3) to "" or NULL does *not* stop libcurl from doing +a POST. It makes it POST without any data to send! # Converting from deprecated form API to MIME API @@ -647,18 +652,18 @@ CURLOPT_MIMEPOST(3) instead of CURLOPT_HTTPPOST(3). Here are some example of *curl_formadd* calls to MIME API sequences: ~~~c - curl_formadd(&post, &last, - CURLFORM_COPYNAME, "id", - CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END); - CURLFORM_CONTENTHEADER, headers, - CURLFORM_END); + curl_formadd(&post, &last, + CURLFORM_COPYNAME, "id", + CURLFORM_COPYCONTENTS, "daniel", CURLFORM_END); + CURLFORM_CONTENTHEADER, headers, + CURLFORM_END); ~~~ becomes: ~~~c - part = curl_mime_addpart(multipart); - curl_mime_name(part, "id"); - curl_mime_data(part, "daniel", CURL_ZERO_TERMINATED); - curl_mime_headers(part, headers, FALSE); + part = curl_mime_addpart(multipart); + curl_mime_name(part, "id"); + curl_mime_data(part, "daniel", CURL_ZERO_TERMINATED); + curl_mime_headers(part, headers, FALSE); ~~~ Setting the last curl_mime_headers(3) argument to TRUE would have caused @@ -666,16 +671,16 @@ the headers to be automatically released upon destroyed the multi-part, thus saving a clean-up call to curl_slist_free_all(3). ~~~c - curl_formadd(&post, &last, - CURLFORM_PTRNAME, "logotype-image", - CURLFORM_FILECONTENT, "-", - CURLFORM_END); + curl_formadd(&post, &last, + CURLFORM_PTRNAME, "logotype-image", + CURLFORM_FILECONTENT, "-", + CURLFORM_END); ~~~ becomes: ~~~c - part = curl_mime_addpart(multipart); - curl_mime_name(part, "logotype-image"); - curl_mime_data_cb(part, (curl_off_t) -1, fread, fseek, NULL, stdin); + part = curl_mime_addpart(multipart); + curl_mime_name(part, "logotype-image"); + curl_mime_data_cb(part, (curl_off_t)-1, fread, fseek, NULL, stdin); ~~~ curl_mime_name(3) always copies the field name. The special filename "-" is @@ -684,79 +689,79 @@ source using fread(). The transfer is be chunk-encoded since the data size is unknown. ~~~c - curl_formadd(&post, &last, - CURLFORM_COPYNAME, "datafile[]", - CURLFORM_FILE, "file1", - CURLFORM_FILE, "file2", - CURLFORM_END); + curl_formadd(&post, &last, + CURLFORM_COPYNAME, "datafile[]", + CURLFORM_FILE, "file1", + CURLFORM_FILE, "file2", + CURLFORM_END); ~~~ becomes: ~~~c - part = curl_mime_addpart(multipart); - curl_mime_name(part, "datafile[]"); - curl_mime_filedata(part, "file1"); - part = curl_mime_addpart(multipart); - curl_mime_name(part, "datafile[]"); - curl_mime_filedata(part, "file2"); + part = curl_mime_addpart(multipart); + curl_mime_name(part, "datafile[]"); + curl_mime_filedata(part, "file1"); + part = curl_mime_addpart(multipart); + curl_mime_name(part, "datafile[]"); + curl_mime_filedata(part, "file2"); ~~~ The deprecated multipart/mixed implementation of multiple files field is translated to two distinct parts with the same name. ~~~c - curl_easy_setopt(handle, CURLOPT_READFUNCTION, myreadfunc); - curl_formadd(&post, &last, - CURLFORM_COPYNAME, "stream", - CURLFORM_STREAM, arg, - CURLFORM_CONTENTLEN, (curl_off_t) datasize, - CURLFORM_FILENAME, "archive.zip", - CURLFORM_CONTENTTYPE, "application/zip", - CURLFORM_END); + curl_easy_setopt(handle, CURLOPT_READFUNCTION, myreadfunc); + curl_formadd(&post, &last, + CURLFORM_COPYNAME, "stream", + CURLFORM_STREAM, arg, + CURLFORM_CONTENTLEN, (curl_off_t)datasize, + CURLFORM_FILENAME, "archive.zip", + CURLFORM_CONTENTTYPE, "application/zip", + CURLFORM_END); ~~~ becomes: ~~~c - part = curl_mime_addpart(multipart); - curl_mime_name(part, "stream"); - curl_mime_data_cb(part, (curl_off_t) datasize, - myreadfunc, NULL, NULL, arg); - curl_mime_filename(part, "archive.zip"); - curl_mime_type(part, "application/zip"); + part = curl_mime_addpart(multipart); + curl_mime_name(part, "stream"); + curl_mime_data_cb(part, (curl_off_t)datasize, + myreadfunc, NULL, NULL, arg); + curl_mime_filename(part, "archive.zip"); + curl_mime_type(part, "application/zip"); ~~~ CURLOPT_READFUNCTION(3) callback is not used: it is replace by directly setting the part source data from the callback read function. ~~~c - curl_formadd(&post, &last, - CURLFORM_COPYNAME, "memfile", - CURLFORM_BUFFER, "memfile.bin", - CURLFORM_BUFFERPTR, databuffer, - CURLFORM_BUFFERLENGTH, (long) sizeof databuffer, - CURLFORM_END); + curl_formadd(&post, &last, + CURLFORM_COPYNAME, "memfile", + CURLFORM_BUFFER, "memfile.bin", + CURLFORM_BUFFERPTR, databuffer, + CURLFORM_BUFFERLENGTH, (long)sizeof(databuffer), + CURLFORM_END); ~~~ becomes: ~~~c - part = curl_mime_addpart(multipart); - curl_mime_name(part, "memfile"); - curl_mime_data(part, databuffer, (curl_off_t) sizeof databuffer); - curl_mime_filename(part, "memfile.bin"); + part = curl_mime_addpart(multipart); + curl_mime_name(part, "memfile"); + curl_mime_data(part, databuffer, (curl_off_t)sizeof(databuffer)); + curl_mime_filename(part, "memfile.bin"); ~~~ curl_mime_data(3) always copies the initial data: data buffer is thus free for immediate reuse. ~~~c - curl_formadd(&post, &last, - CURLFORM_COPYNAME, "message", - CURLFORM_FILECONTENT, "msg.txt", - CURLFORM_END); + curl_formadd(&post, &last, + CURLFORM_COPYNAME, "message", + CURLFORM_FILECONTENT, "msg.txt", + CURLFORM_END); ~~~ becomes: ~~~c - part = curl_mime_addpart(multipart); - curl_mime_name(part, "message"); - curl_mime_filedata(part, "msg.txt"); - curl_mime_filename(part, NULL); + part = curl_mime_addpart(multipart); + curl_mime_name(part, "message"); + curl_mime_filedata(part, "msg.txt"); + curl_mime_filename(part, NULL); ~~~ Use of curl_mime_filedata(3) sets the remote filename as a side effect: it is @@ -771,20 +776,20 @@ terminal. Switch on the progress meter by, oddly enough, setting CURLOPT_NOPROGRESS(3) to zero. This option is set to 1 by default. -For most applications however, the built-in progress meter is useless and what -instead is interesting is the ability to specify a progress callback. The -function pointer you pass to libcurl is then called on irregular intervals -with information about the current transfer. +For most applications, the built-in progress meter is useless and what instead +is interesting is the ability to specify a progress callback. The function +pointer you pass to libcurl is then called on irregular intervals with +information about the current transfer. Set the progress callback by using CURLOPT_PROGRESSFUNCTION(3). Pass a pointer to a function that matches this prototype: ~~~c - int progress_callback(void *clientp, - double dltotal, - double dlnow, - double ultotal, - double ulnow); + int progress_callback(void *clientp, + double dltotal, + double dlnow, + double ultotal, + double ulnow); ~~~ If any of the input arguments is unknown, a 0 is provided. The first argument, @@ -793,21 +798,21 @@ CURLOPT_PROGRESSDATA(3). libcurl does not touch it. # libcurl with C++ -There is basically only one thing to keep in mind when using C++ instead of C -when interfacing libcurl: +There is only one thing to keep in mind when using C++ instead of C when +interfacing libcurl: The callbacks CANNOT be non-static class member functions Example C++ code: ~~~c -class AClass { - static size_t write_data(void *ptr, size_t size, size_t nmemb, - void *ourpointer) - { - /* do what you want with the data */ + class AClass { + static size_t write_data(char *ptr, size_t size, size_t nmemb, + void *ourpointer) + { + /* do what you want with the data */ + } } - } ~~~ # Proxies @@ -825,8 +830,8 @@ libcurl supports SOCKS and HTTP proxies. When a given URL is wanted, libcurl asks the proxy for it instead of trying to connect to the actual remote host identified in the URL. -If you are using a SOCKS proxy, you may find that libcurl does not quite support -all operations through it. +If you are using a SOCKS proxy, you may find that libcurl does not support all +operations through it. For HTTP proxies: the fact that the proxy is an HTTP proxy puts certain restrictions on what can actually happen. A requested URL that might not be a @@ -840,12 +845,12 @@ commands or even proper FTP directory listings. To tell libcurl to use a proxy at a given port number: ~~~c - curl_easy_setopt(handle, CURLOPT_PROXY, "proxy-host.com:8080"); + curl_easy_setopt(handle, CURLOPT_PROXY, "proxy-host.com:8080"); ~~~ Some proxies require user authentication before allowing a request, and you pass that information similar to this: ~~~c - curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, "user:password"); + curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, "user:password"); ~~~ If you want to, you can specify the hostname only in the CURLOPT_PROXY(3) option, and set the port number separately with @@ -854,7 +859,7 @@ CURLOPT_PROXYPORT(3). Tell libcurl what kind of proxy it is with CURLOPT_PROXYTYPE(3) (if not, it defaults to assuming an HTTP proxy): ~~~c - curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); + curl_easy_setopt(handle, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4); ~~~ ## Environment Variables @@ -865,7 +870,7 @@ following an old tradition and are built up as "[protocol]_proxy" (note the lower casing). Which makes the variable 'http_proxy' checked for a name of a proxy to use when the input URL is HTTP. Following the same rule, the variable named 'ftp_proxy' is checked for FTP URLs. Again, the proxies are always HTTP -proxies, the different names of the variables simply allows different HTTP +proxies, the different names of the variables allow different HTTP proxies to be used. The proxy environment variable contents should be in the format @@ -921,7 +926,7 @@ rarely allowed. Tell libcurl to use proxy tunneling like this: ~~~c - curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1L); + curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1L); ~~~ In fact, there might even be times when you want to do plain HTTP operations using a tunnel like this, as it then enables you to operate on the remote @@ -930,8 +935,8 @@ for such innovative actions either! ## Proxy Auto-Config -Netscape first came up with this. It is basically a webpage (usually using a -.pac extension) with a JavaScript that when executed by the browser with the +Netscape first came up with this. It is a webpage (usually using a .pac +extension) with a JavaScript that when executed by the browser with the requested URL as input, returns information to the browser on how to connect to the URL. The returned information might be "DIRECT" (which means no proxy should be used), "PROXY host:port" (to tell the browser where the proxy for @@ -957,10 +962,10 @@ Mozilla JavaScript engine in the past. Re-cycling the same easy handle several times when doing multiple requests is the way to go. -After each single curl_easy_perform(3) operation, libcurl keeps the -connection alive and open. A subsequent request using the same easy handle to -the same host might just be able to use the already open connection! This -reduces network impact a lot. +After each single curl_easy_perform(3) operation, libcurl keeps the connection +alive and open. A subsequent request using the same easy handle to the same +host might be able to reuse the already open connection! This reduces network +impact a lot. Even if the connection is dropped, all connections involving SSL to the same host again, benefit from libcurl's session ID cache that drastically reduces @@ -979,9 +984,9 @@ may also be added in the future. Each easy handle attempts to keep the last few connections alive for a while in case they are to be used again. You can set the size of this "cache" with -the CURLOPT_MAXCONNECTS(3) option. Default is 5. There is rarely any -point in changing this value, and if you think of changing this it is often -just a matter of thinking again. +the CURLOPT_MAXCONNECTS(3) option. Default is 5. There is rarely any point in +changing this value, and if you think of changing this it is often a reason to +think again. To force your upcoming request to not use an already existing connection, you can do that by setting CURLOPT_FRESH_CONNECT(3) to 1. In a similar @@ -1026,12 +1031,12 @@ libcurl is your friend here too. ## CURLOPT_CUSTOMREQUEST -If just changing the actual HTTP request keyword is what you want, like when -GET, HEAD or POST is not good enough for you, CURLOPT_CUSTOMREQUEST(3) -is there for you. It is simple to use: +If changing the actual HTTP request keyword is what you want, like when GET, +HEAD or POST is not good enough for you, CURLOPT_CUSTOMREQUEST(3) is there for +you. It is simple to use: ~~~c -curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MYOWNREQUEST"); + curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "MYOWNREQUEST"); ~~~ When using the custom request, you change the request keyword of the actual @@ -1046,26 +1051,26 @@ request, and you are free to pass any amount of extra headers that you think fit. Adding headers is this easy: ~~~c -struct curl_slist *headers=NULL; /* init to NULL is important */ + struct curl_slist *headers = NULL; /* init to NULL is important */ -headers = curl_slist_append(headers, "Hey-server-hey: how are you?"); -headers = curl_slist_append(headers, "X-silly-content: yes"); + headers = curl_slist_append(headers, "Hey-server-hey: how are you?"); + headers = curl_slist_append(headers, "X-silly-content: yes"); -/* pass our list of custom made headers */ -curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); + /* pass our list of custom made headers */ + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); -curl_easy_perform(handle); /* transfer http */ + curl_easy_perform(handle); /* transfer http */ -curl_slist_free_all(headers); /* free the header list */ + curl_slist_free_all(headers); /* free the header list */ ~~~ ... and if you think some of the internally generated headers, such as Accept: or Host: do not contain the data you want them to contain, you can replace -them by simply setting them too: +them by setting them too: ~~~c -headers = curl_slist_append(headers, "Accept: Agent-007"); -headers = curl_slist_append(headers, "Host: munged.host.line"); + headers = curl_slist_append(headers, "Accept: Agent-007"); + headers = curl_slist_append(headers, "Host: munged.host.line"); ~~~ ## Delete Headers @@ -1075,7 +1080,9 @@ header from being sent. For instance, if you want to completely prevent the "Accept:" header from being sent, you can disable it with code similar to this: - headers = curl_slist_append(headers, "Accept:"); +~~~c + headers = curl_slist_append(headers, "Accept:"); +~~~ Both replacing and canceling internal headers should be done with careful consideration and you should be aware that you may violate the HTTP protocol @@ -1096,7 +1103,9 @@ we support. libcurl speaks HTTP 1.1 by default. Some old servers do not like getting 1.1-requests and when dealing with stubborn old things like that, you can tell libcurl to use 1.0 instead by doing something like this: - curl_easy_setopt(handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); +~~~c + curl_easy_setopt(handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); +~~~ ## FTP Custom Commands @@ -1116,14 +1125,14 @@ correct remote directory. A little example that deletes a given file before an operation: ~~~c - headers = curl_slist_append(headers, "DELE file-to-remove"); + headers = curl_slist_append(headers, "DELE file-to-remove"); - /* pass the list of custom commands to the handle */ - curl_easy_setopt(handle, CURLOPT_QUOTE, headers); + /* pass the list of custom commands to the handle */ + curl_easy_setopt(handle, CURLOPT_QUOTE, headers); - curl_easy_perform(handle); /* transfer ftp data! */ + curl_easy_perform(handle); /* transfer ftp data! */ - curl_slist_free_all(headers); /* free the header list */ + curl_slist_free_all(headers); /* free the header list */ ~~~ If you would instead want this operation (or chain of operations) to happen @@ -1149,8 +1158,8 @@ content transfer is performed. ## FTP Custom CURLOPT_CUSTOMREQUEST If you do want to list the contents of an FTP directory using your own defined -FTP command, CURLOPT_CUSTOMREQUEST(3) does just that. "NLST" is the default -one for listing directories but you are free to pass in your idea of a good +FTP command, CURLOPT_CUSTOMREQUEST(3) does that. "NLST" is the default one for +listing directories but you are free to pass in your idea of a good alternative. # Cookies Without Chocolate Chips @@ -1167,11 +1176,11 @@ update them. Server use cookies to "track" users and to keep "sessions". Cookies are sent from server to clients with the header Set-Cookie: and they are sent from clients to servers with the Cookie: header. -To just send whatever cookie you want to a server, you can use -CURLOPT_COOKIE(3) to set a cookie string like this: +To send whatever cookie you want to a server, you can use CURLOPT_COOKIE(3) to +set a cookie string like this: ~~~c - curl_easy_setopt(handle, CURLOPT_COOKIE, "name1=var1; name2=var2;"); + curl_easy_setopt(handle, CURLOPT_COOKIE, "name1=var1; name2=var2;"); ~~~ In many cases, that is not enough. You might want to dynamically save whatever @@ -1183,16 +1192,15 @@ when you make a request, you tell libcurl to read the previous headers to figure out which cookies to use. Set the header file to read cookies from with CURLOPT_COOKIEFILE(3). -The CURLOPT_COOKIEFILE(3) option also automatically enables the cookie -parser in libcurl. Until the cookie parser is enabled, libcurl does not parse -or understand incoming cookies and they are just be ignored. However, when the -parser is enabled the cookies are understood and the cookies are kept in -memory and used properly in subsequent requests when the same handle is -used. Many times this is enough, and you may not have to save the cookies to -disk at all. Note that the file you specify to CURLOPT_COOKIEFILE(3) -does not have to exist to enable the parser, so a common way to just enable -the parser and not read any cookies is to use the name of a file you know does -not exist. +The CURLOPT_COOKIEFILE(3) option also automatically enables the cookie parser +in libcurl. Until the cookie parser is enabled, libcurl does not parse or +understand incoming cookies and they are instead ignored. When the parser is +enabled the cookies are understood and the cookies are kept in memory and used +properly in subsequent requests when the same handle is used. Many times this +is enough, and you may not have to save the cookies to disk at all. Note that +the file you specify to CURLOPT_COOKIEFILE(3) does not have to exist to enable +the parser, so a common way to enable the parser and not read any cookies is +to use the name of a file you know does not exist. If you would rather use existing cookies that you have previously received with your Netscape or Mozilla browsers, you can make libcurl use that cookie @@ -1271,43 +1279,43 @@ Here is an example building an email message with an inline plain/html text alternative and a file attachment encoded in base64: ~~~c - curl_mime *message = curl_mime_init(handle); + curl_mime *message = curl_mime_init(handle); - /* The inline part is an alternative proposing the html and the text - versions of the email. */ - curl_mime *alt = curl_mime_init(handle); + /* The inline part is an alternative proposing the html and the text + versions of the email. */ + curl_mime *alt = curl_mime_init(handle); - /* HTML message. */ - curl_mimepart *part = curl_mime_addpart(alt); - curl_mime_data(part, "

This is HTML

", - CURL_ZERO_TERMINATED); - curl_mime_type(part, "text/html"); + /* HTML message. */ + curl_mimepart *part = curl_mime_addpart(alt); + curl_mime_data(part, "

This is HTML

", + CURL_ZERO_TERMINATED); + curl_mime_type(part, "text/html"); - /* Text message. */ - part = curl_mime_addpart(alt); - curl_mime_data(part, "This is plain text message", - CURL_ZERO_TERMINATED); + /* Text message. */ + part = curl_mime_addpart(alt); + curl_mime_data(part, "This is plain text message", + CURL_ZERO_TERMINATED); - /* Create the inline part. */ - part = curl_mime_addpart(message); - curl_mime_subparts(part, alt); - curl_mime_type(part, "multipart/alternative"); - struct curl_slist *headers = curl_slist_append(NULL, - "Content-Disposition: inline"); - curl_mime_headers(part, headers, TRUE); + /* Create the inline part. */ + part = curl_mime_addpart(message); + curl_mime_subparts(part, alt); + curl_mime_type(part, "multipart/alternative"); + struct curl_slist *headers = curl_slist_append(NULL, + "Content-Disposition: inline"); + curl_mime_headers(part, headers, TRUE); - /* Add the attachment. */ - part = curl_mime_addpart(message); - curl_mime_filedata(part, "manual.pdf"); - curl_mime_encoder(part, "base64"); + /* Add the attachment. */ + part = curl_mime_addpart(message); + curl_mime_filedata(part, "manual.pdf"); + curl_mime_encoder(part, "base64"); - /* Build the mail headers. */ - headers = curl_slist_append(NULL, "From: me@example.com"); - headers = curl_slist_append(headers, "To: you@example.com"); + /* Build the mail headers. */ + headers = curl_slist_append(NULL, "From: me@example.com"); + headers = curl_slist_append(headers, "To: you@example.com"); - /* Set these into the easy handle. */ - curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(handle, CURLOPT_MIMEPOST, mime); + /* Set these into the easy handle. */ + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(handle, CURLOPT_MIMEPOST, mime); ~~~ It should be noted that appending a message to an IMAP directory requires @@ -1362,14 +1370,14 @@ on the multi_socket event based API, this description here is for the select() oriented one. To use this interface, you are better off if you first understand the basics -of how to use the easy interface. The multi interface is simply a way to make +of how to use the easy interface. The multi interface is a way to make multiple transfers at the same time by adding up multiple easy handles into a "multi stack". You create the easy handles you want, one for each concurrent transfer, and -you set all the options just like you learned above, and then you create a -multi handle with curl_multi_init(3) and add all those easy handles to -that multi handle with curl_multi_add_handle(3). +you set all the options like you learned above, and then you create a multi +handle with curl_multi_init(3) and add all those easy handles to that multi +handle with curl_multi_add_handle(3). When you have added the handles you have for the moment (you can still add new ones at any time), you start the transfers by calling @@ -1412,7 +1420,7 @@ to figure out success on each individual transfer. # SSL, Certificates and Other Tricks - [ seeding, passwords, keys, certificates, ENGINE, ca certs ] +[ seeding, passwords, keys, certificates, ENGINE, ca certs ] # Sharing Data Between Easy Handles @@ -1442,9 +1450,8 @@ size. ## [2] -This happens on Windows machines when libcurl is built and used as a -DLL. However, you can still do this on Windows if you link with a static -library. +This happens on Windows machines when libcurl is built and used as a DLL. You +can still do this on Windows if you link with a static library. ## [3] diff --git a/docs/libcurl/libcurl-url.md b/docs/libcurl/libcurl-url.md index 82de7e7821..b39d0304d5 100644 --- a/docs/libcurl/libcurl-url.md +++ b/docs/libcurl/libcurl-url.md @@ -45,7 +45,7 @@ When done with it, clean it up with curl_url_cleanup(3) # DUPLICATE -When you need a copy of a handle, just duplicate it with curl_url_dup(3): +When you need a copy of a handle, duplicate it with curl_url_dup(3): ~~~c CURLU *nh = curl_url_dup(h); ~~~ diff --git a/docs/libcurl/libcurl.m4 b/docs/libcurl/libcurl.m4 index 4c89511f06..7f8211bc9a 100644 --- a/docs/libcurl/libcurl.m4 +++ b/docs/libcurl/libcurl.m4 @@ -20,7 +20,7 @@ # # SPDX-License-Identifier: curl # -########################################################################### +#*************************************************************************** # LIBCURL_CHECK_CONFIG([DEFAULT-ACTION], [MINIMUM-VERSION], # [ACTION-IF-YES], [ACTION-IF-NO]) # ---------------------------------------------------------- @@ -46,7 +46,7 @@ # variables $libcurl_feature_xxx and $libcurl_protocol_yyy are also # defined to 'yes' for those features and protocols that were found. # Note that xxx and yyy keep the same capitalization as in the -# curl-config list (e.g. it's "HTTP" and not "http"). +# curl-config list (e.g. it is "HTTP" and not "http"). # # Users may override the detected values by doing something like: # LIBCURL="-lcurl" LIBCURL_CPPFLAGS="-I/usr/myinclude" ./configure @@ -86,7 +86,7 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG], AS_HELP_STRING([--with-libcurl=PREFIX],[look for the curl library in PREFIX/lib and headers in PREFIX/include]), [_libcurl_with=$withval],[_libcurl_with=ifelse([$1],,[yes],[$1])]) - if test "$_libcurl_with" != "no"; then + if test "x$_libcurl_with" != "xno"; then AC_PROG_AWK @@ -102,7 +102,7 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG], AC_PATH_PROG([_libcurl_config],[curl-config],[],[$PATH]) fi - if test x$_libcurl_config != "x"; then + if test -n "$_libcurl_config"; then AC_CACHE_CHECK([for the version of libcurl], [libcurl_cv_lib_curl_version], [libcurl_cv_lib_curl_version=`$_libcurl_config --version | $AWK '{print $[]2}'`]) @@ -110,11 +110,11 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG], _libcurl_version=`echo $libcurl_cv_lib_curl_version | $_libcurl_version_parse` _libcurl_wanted=`echo ifelse([$2],,[0],[$2]) | $_libcurl_version_parse` - if test $_libcurl_wanted -gt 0; then + if test "$_libcurl_wanted" -gt 0; then AC_CACHE_CHECK([for libcurl >= version $2], [libcurl_cv_lib_version_ok], [ - if test $_libcurl_version -ge $_libcurl_wanted; then + if test "$_libcurl_version" -ge "$_libcurl_wanted"; then libcurl_cv_lib_version_ok=yes else libcurl_cv_lib_version_ok=no @@ -122,16 +122,16 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG], ]) fi - if test $_libcurl_wanted -eq 0 || test x$libcurl_cv_lib_version_ok = xyes; then - if test x"$LIBCURL_CPPFLAGS" = "x"; then + if test "$_libcurl_wanted" -eq 0 || test "$libcurl_cv_lib_version_ok" = "yes"; then + if test -z "$LIBCURL_CPPFLAGS"; then LIBCURL_CPPFLAGS=`$_libcurl_config --cflags` fi - if test x"$LIBCURL" = "x"; then + if test -z "$LIBCURL"; then LIBCURL=`$_libcurl_config --libs` - # This is so silly, but Apple actually has a bug in their - # curl-config script. Fixed in Tiger, but there are still - # lots of Panther installs around. + dnl This is so silly, but Apple actually has a bug in their + dnl curl-config script. Fixed in Tiger, but there are still + dnl lots of Panther installs around. case "${host}" in powerpc-apple-darwin7*) LIBCURL=`echo $LIBCURL | sed -e 's|-arch i386||g'` @@ -139,11 +139,11 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG], esac fi - # All curl-config scripts support --feature + dnl All curl-config scripts support --feature _libcurl_features=`$_libcurl_config --feature` - # Is it modern enough to have --protocols? (7.12.4) - if test $_libcurl_version -ge 461828; then + dnl Is it modern enough to have --protocols? (7.12.4) + if test "$_libcurl_version" -ge 461828; then _libcurl_protocols=`$_libcurl_config --protocols` fi else @@ -153,10 +153,10 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG], unset _libcurl_wanted fi - if test $_libcurl_try_link = yes; then + if test "$_libcurl_try_link" = "yes"; then - # we did not find curl-config, so let's see if the user-supplied - # link line (or failing that, "-lcurl") is enough. + dnl we did not find curl-config, so let's see if the user-supplied + dnl link line (or failing that, "-lcurl") is enough. LIBCURL=${LIBCURL-"$_libcurl_ldflags -lcurl"} AC_CACHE_CHECK([whether libcurl is usable], @@ -178,7 +178,7 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG], x=CURLOPT_ERRORBUFFER; x=CURLOPT_STDERR; x=CURLOPT_VERBOSE; - if (x) {;} + if(x) {;} ]])],libcurl_cv_lib_curl_usable=yes,libcurl_cv_lib_curl_usable=no) CPPFLAGS=$_libcurl_save_cppflags @@ -187,10 +187,10 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG], unset _libcurl_save_libs ]) - if test $libcurl_cv_lib_curl_usable = yes; then + if test "$libcurl_cv_lib_curl_usable" = "yes"; then - # Does curl_free() exist in this version of libcurl? - # If not, fake it with free() + dnl Does curl_free() exist in this version of libcurl? + dnl If not, fake it with free() _libcurl_save_cppflags=$CPPFLAGS CPPFLAGS="$CPPFLAGS $LIBCURL_CPPFLAGS" @@ -217,25 +217,25 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG], eval AS_TR_SH(libcurl_feature_$_libcurl_feature)=yes done - if test "x$_libcurl_protocols" = "x"; then + if test -z "$_libcurl_protocols"; then - # We do not have --protocols, so just assume that all - # protocols are available + dnl We do not have --protocols; assume that all + dnl protocols are available _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP" - if test x$libcurl_feature_SSL = xyes; then + if test "$libcurl_feature_SSL" = "yes"; then _libcurl_protocols="$_libcurl_protocols HTTPS" - # FTPS was not standards-compliant until version - # 7.11.0 (0x070b00 == 461568) - if test $_libcurl_version -ge 461568; then + dnl FTPS was not standards-compliant until version + dnl 7.11.0 (0x070b00 == 461568) + if test "$_libcurl_version" -ge 461568; then _libcurl_protocols="$_libcurl_protocols FTPS" fi fi - # RTSP, IMAP, POP3 and SMTP were added in - # 7.20.0 (0x071400 == 463872) - if test $_libcurl_version -ge 463872; then + dnl RTSP, IMAP, POP3 and SMTP were added in + dnl 7.20.0 (0x071400 == 463872) + if test "$_libcurl_version" -ge 463872; then _libcurl_protocols="$_libcurl_protocols RTSP IMAP POP3 SMTP" fi fi @@ -261,11 +261,11 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG], unset _libcurl_ldflags fi - if test x$_libcurl_with = xno || test x$libcurl_cv_lib_curl_usable != xyes; then - # This is the IF-NO path + if test "x$_libcurl_with" = "xno" || test "$libcurl_cv_lib_curl_usable" != "yes"; then + dnl This is the IF-NO path ifelse([$4],,:,[$4]) else - # This is the IF-YES path + dnl This is the IF-YES path ifelse([$3],,:,[$3]) fi diff --git a/docs/libcurl/libcurl.md b/docs/libcurl/libcurl.md index b6d4af36e3..96f10656ee 100644 --- a/docs/libcurl/libcurl.md +++ b/docs/libcurl/libcurl.md @@ -82,8 +82,8 @@ builds a linked list. See curl_slist_append(3) ## Sharing data between transfers You can have multiple easy handles share certain data, even if they are used -in different threads. This magic is setup using the share interface, as -described in the libcurl-share(3) man page. +in different threads. Set that up using the share interface, as described in +the libcurl-share(3) man page. ## URL Parsing @@ -106,7 +106,7 @@ link with the particular version of libcurl you have installed. See the *curl-config(1)* man page for further details. Unix-like operating system that ship libcurl as part of their distributions -often do not provide the curl-config tool, but simply install the library and +often do not provide the curl-config tool, but only install the library and headers in the common path for this purpose. Many Linux and similar systems use pkg-config to provide build and link @@ -129,7 +129,7 @@ the same, on any of the platforms it compiles and builds on. # THREADS -libcurl is thread safe but there are a few exceptions. Refer to +libcurl is thread-safe but there are a few exceptions. Refer to libcurl-thread(3) for more information. # PERSISTENT CONNECTIONS @@ -176,7 +176,7 @@ The global constant functions are thread-safe since libcurl 7.84.0 if curl_version_info(3) has the CURL_VERSION_THREADSAFE feature bit set (most platforms). Read libcurl-thread(3) for thread safety guidelines. -If the global constant functions are *not thread safe*, then you must +If the global constant functions are *not thread-safe*, then you must not call them when any other thread in the program is running. It is not good enough that no other thread is using libcurl at the time, because these functions internally call similar functions of other @@ -184,7 +184,7 @@ libraries, and those functions are similarly thread-unsafe. You cannot generally know what these libraries are, or whether other threads are using them. -If the global constant functions are *not thread safe*, then the basic rule +If the global constant functions are *not thread-safe*, then the basic rule for constructing a program that uses libcurl is this: Call curl_global_init(3), with a *CURL_GLOBAL_ALL* argument, immediately after the program starts, while it is still only one thread and before it uses @@ -192,8 +192,8 @@ libcurl at all. Call curl_global_cleanup(3) immediately before the program exits, when the program is again only one thread and after its last use of libcurl. -It is not actually required that the functions be called at the beginning -and end of the program -- that is just usually the easiest way to do it. +It is not actually required that the functions be called at the beginning and +end of the program -- that is usually the easiest way to do it. You can call both of these multiple times, as long as all calls meet these requirements and the number of calls to each is the same. @@ -205,13 +205,13 @@ other parts of the program -- it does not know whether they use libcurl or not. Its code does not necessarily run at the start and end of the whole program. -A module like this must have global constant functions of its own, just like -curl_global_init(3) and curl_global_cleanup(3). The module thus -has control at the beginning and end of the program and has a place to call -the libcurl functions. If multiple modules in the program use libcurl, they -all separately call the libcurl functions, and that is OK because only the -first curl_global_init(3) and the last curl_global_cleanup(3) in a -program change anything. (libcurl uses a reference count in static memory). +A module like this must have global constant functions of its own, like +curl_global_init(3) and curl_global_cleanup(3). The module thus has control at +the beginning and end of the program and has a place to call the libcurl +functions. If multiple modules in the program use libcurl, they all separately +call the libcurl functions, and that is OK because only the first +curl_global_init(3) and the last curl_global_cleanup(3) in a program change +anything. (libcurl uses a reference count in static memory). In a C++ module, it is common to deal with the global constant situation by defining a special class that represents the global constant environment of @@ -233,10 +233,10 @@ parts of the program of which it is part. A special part of the global constant environment is the identity of the memory allocator. curl_global_init(3) selects the system default memory -allocator, but you can use curl_global_init_mem(3) to supply one of your -own. However, there is no way to use curl_global_init_mem(3) in a -modular program -- all modules in the program that might use libcurl would -have to agree on one allocator. +allocator, but you can use curl_global_init_mem(3) to supply one of your own. +There is no way to use curl_global_init_mem(3) in a modular program -- all +modules in the program that might use libcurl would have to agree on one +allocator. There is a failsafe in libcurl that makes it usable in simple situations without you having to worry about the global constant environment at all: diff --git a/docs/libcurl/opts/CMakeLists.txt b/docs/libcurl/opts/CMakeLists.txt index c3e2c2cc6b..f8b5aaa2de 100644 --- a/docs/libcurl/opts/CMakeLists.txt +++ b/docs/libcurl/opts/CMakeLists.txt @@ -28,6 +28,7 @@ include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") curl_add_manual_pages(man_MANS) add_custom_target(curl-opts-man DEPENDS ${man_MANS}) add_dependencies(curl-man curl-opts-man) + if(NOT CURL_DISABLE_INSTALL) set(_src "") foreach(_file IN LISTS man_MANS) diff --git a/docs/libcurl/opts/CURLINFO_ACTIVESOCKET.md b/docs/libcurl/opts/CURLINFO_ACTIVESOCKET.md index 0bbb52ff14..9dbf4f8573 100644 --- a/docs/libcurl/opts/CURLINFO_ACTIVESOCKET.md +++ b/docs/libcurl/opts/CURLINFO_ACTIVESOCKET.md @@ -16,7 +16,7 @@ Added-in: 7.45.0 # NAME -CURLINFO_ACTIVESOCKET - get the active socket +CURLINFO_ACTIVESOCKET - active socket # SYNOPSIS @@ -50,22 +50,22 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_socket_t sockfd; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Do not do the transfer - only connect to host */ curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); - res = curl_easy_perform(curl); - if(res != CURLE_OK) { - printf("Error: %s\n", curl_easy_strerror(res)); + result = curl_easy_perform(curl); + if(result != CURLE_OK) { + printf("Error: %s\n", curl_easy_strerror(result)); curl_easy_cleanup(curl); return 1; } /* Extract the socket from the curl handle */ - res = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd); - if(!res && sockfd != CURL_SOCKET_BAD) { + result = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd); + if(!result && sockfd != CURL_SOCKET_BAD) { /* operate on sockfd */ } diff --git a/docs/libcurl/opts/CURLINFO_APPCONNECT_TIME.md b/docs/libcurl/opts/CURLINFO_APPCONNECT_TIME.md index 1c4a9cafb5..bb137c120d 100644 --- a/docs/libcurl/opts/CURLINFO_APPCONNECT_TIME.md +++ b/docs/libcurl/opts/CURLINFO_APPCONNECT_TIME.md @@ -15,7 +15,7 @@ Added-in: 7.19.0 # NAME -CURLINFO_APPCONNECT_TIME - get the time until the SSL/SSH handshake is completed +CURLINFO_APPCONNECT_TIME - time until the SSL/SSH handshake is completed # SYNOPSIS @@ -47,13 +47,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; double connect; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &connect); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &connect); + if(result == CURLE_OK) { printf("Time: %.1f", connect); } } diff --git a/docs/libcurl/opts/CURLINFO_APPCONNECT_TIME_T.md b/docs/libcurl/opts/CURLINFO_APPCONNECT_TIME_T.md index ecaa9dce32..4eb595b4d3 100644 --- a/docs/libcurl/opts/CURLINFO_APPCONNECT_TIME_T.md +++ b/docs/libcurl/opts/CURLINFO_APPCONNECT_TIME_T.md @@ -47,13 +47,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_off_t connect; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME_T, &connect); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME_T, &connect); + if(result == CURLE_OK) { printf("Time: %" CURL_FORMAT_CURL_OFF_T ".%06ld", connect / 1000000, (long)(connect % 1000000)); } diff --git a/docs/libcurl/opts/CURLINFO_CAINFO.md b/docs/libcurl/opts/CURLINFO_CAINFO.md index 686e7b9d4e..23502d2296 100644 --- a/docs/libcurl/opts/CURLINFO_CAINFO.md +++ b/docs/libcurl/opts/CURLINFO_CAINFO.md @@ -17,7 +17,7 @@ Added-in: 7.84.0 # NAME -CURLINFO_CAINFO - get the default built-in CA certificate path +CURLINFO_CAINFO - default built-in CA certificate path # SYNOPSIS diff --git a/docs/libcurl/opts/CURLINFO_CAPATH.md b/docs/libcurl/opts/CURLINFO_CAPATH.md index c441805ca1..c58930e0e9 100644 --- a/docs/libcurl/opts/CURLINFO_CAPATH.md +++ b/docs/libcurl/opts/CURLINFO_CAPATH.md @@ -20,7 +20,7 @@ Added-in: 7.84.0 # NAME -CURLINFO_CAPATH - get the default built-in CA path string +CURLINFO_CAPATH - default built-in CA path string # SYNOPSIS diff --git a/docs/libcurl/opts/CURLINFO_CERTINFO.md b/docs/libcurl/opts/CURLINFO_CERTINFO.md index d1127afb9b..0882cbccd1 100644 --- a/docs/libcurl/opts/CURLINFO_CERTINFO.md +++ b/docs/libcurl/opts/CURLINFO_CERTINFO.md @@ -14,13 +14,13 @@ TLS-backend: - OpenSSL - GnuTLS - Schannel - - rustls + - Rustls Added-in: 7.19.1 --- # NAME -CURLINFO_CERTINFO - get the TLS certificate chain +CURLINFO_CERTINFO - TLS certificate chain # SYNOPSIS @@ -60,7 +60,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); /* connect to any HTTPS site, trusted or not */ @@ -69,14 +69,14 @@ int main(void) curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { int i; struct curl_certinfo *ci; - res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &ci); + result = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &ci); - if(!res) { + if(result == CURLE_OK) { printf("%d certs!\n", ci->num_of_certs); for(i = 0; i < ci->num_of_certs; i++) { diff --git a/docs/libcurl/opts/CURLINFO_CONDITION_UNMET.md b/docs/libcurl/opts/CURLINFO_CONDITION_UNMET.md index 5f13126ad7..133070f5ad 100644 --- a/docs/libcurl/opts/CURLINFO_CONDITION_UNMET.md +++ b/docs/libcurl/opts/CURLINFO_CONDITION_UNMET.md @@ -16,7 +16,7 @@ Added-in: 7.19.4 # NAME -CURLINFO_CONDITION_UNMET - get info on unmet time conditional or 304 HTTP response. +CURLINFO_CONDITION_UNMET - unmet time conditional or 304 HTTP response # SYNOPSIS @@ -46,7 +46,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); @@ -57,14 +57,14 @@ int main(void) curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { /* check the time condition */ long unmet; - res = curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &unmet); - if(!res) { - printf("The time condition was %sfulfilled\n", unmet?"NOT":""); + result = curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &unmet); + if(result == CURLE_OK) { + printf("The time condition was %sfulfilled\n", unmet ? "NOT" : ""); } } } diff --git a/docs/libcurl/opts/CURLINFO_CONNECT_TIME.md b/docs/libcurl/opts/CURLINFO_CONNECT_TIME.md index c259e9af5b..e5f260a64b 100644 --- a/docs/libcurl/opts/CURLINFO_CONNECT_TIME.md +++ b/docs/libcurl/opts/CURLINFO_CONNECT_TIME.md @@ -15,7 +15,7 @@ Added-in: 7.4.1 # NAME -CURLINFO_CONNECT_TIME - get the time until connect +CURLINFO_CONNECT_TIME - time to connect # SYNOPSIS @@ -43,13 +43,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; double connect; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &connect); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME, &connect); + if(result == CURLE_OK) { printf("Time: %.1f", connect); } } diff --git a/docs/libcurl/opts/CURLINFO_CONNECT_TIME_T.md b/docs/libcurl/opts/CURLINFO_CONNECT_TIME_T.md index 89ae04c337..27d5ade44e 100644 --- a/docs/libcurl/opts/CURLINFO_CONNECT_TIME_T.md +++ b/docs/libcurl/opts/CURLINFO_CONNECT_TIME_T.md @@ -16,7 +16,7 @@ Added-in: 7.61.0 # NAME -CURLINFO_CONNECT_TIME_T - get the time until connect +CURLINFO_CONNECT_TIME_T - time to connect # SYNOPSIS @@ -45,13 +45,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_off_t connect; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME_T, &connect); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME_T, &connect); + if(result == CURLE_OK) { printf("Time: %" CURL_FORMAT_CURL_OFF_T ".%06ld", connect / 1000000, (long)(connect % 1000000)); } diff --git a/docs/libcurl/opts/CURLINFO_CONN_ID.md b/docs/libcurl/opts/CURLINFO_CONN_ID.md index 1239c1e332..3d111e6b62 100644 --- a/docs/libcurl/opts/CURLINFO_CONN_ID.md +++ b/docs/libcurl/opts/CURLINFO_CONN_ID.md @@ -15,7 +15,7 @@ Added-in: 8.2.0 # NAME -CURLINFO_CONN_ID - get the ID of the last connection used by the handle +CURLINFO_CONN_ID - ID of the last connection # SYNOPSIS @@ -44,17 +44,17 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { curl_off_t conn_id; - res = curl_easy_getinfo(curl, CURLINFO_CONN_ID, &conn_id); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_CONN_ID, &conn_id); + if(result == CURLE_OK) { printf("Connection used: %" CURL_FORMAT_CURL_OFF_T "\n", conn_id); } } diff --git a/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_DOWNLOAD.md b/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_DOWNLOAD.md index 60fae12acd..7173f8effb 100644 --- a/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_DOWNLOAD.md +++ b/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_DOWNLOAD.md @@ -15,7 +15,7 @@ Added-in: 7.6.1 # NAME -CURLINFO_CONTENT_LENGTH_DOWNLOAD - get content-length of download +CURLINFO_CONTENT_LENGTH_DOWNLOAD - content-length of download # SYNOPSIS @@ -29,8 +29,8 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, # DESCRIPTION Pass a pointer to a double to receive the content-length of the download. This -is the value read from the Content-Length: field. Since 7.19.4, this returns --1 if the size is not known. +is the value read from the Content-Length: field. This returns -1 if the size +is not known. CURLINFO_CONTENT_LENGTH_DOWNLOAD_T(3) is a newer replacement that returns a more sensible variable type. @@ -44,17 +44,17 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { /* check the size */ double cl; - res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cl); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cl); + if(result == CURLE_OK) { printf("Size: %.0f\n", cl); } } diff --git a/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_DOWNLOAD_T.md b/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_DOWNLOAD_T.md index 7279bd6b6d..d0a95f6144 100644 --- a/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_DOWNLOAD_T.md +++ b/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_DOWNLOAD_T.md @@ -9,13 +9,13 @@ See-also: - curl_easy_getinfo (3) - curl_easy_setopt (3) Protocol: - - HTTP + - All Added-in: 7.55.0 --- # NAME -CURLINFO_CONTENT_LENGTH_DOWNLOAD_T - get content-length of download +CURLINFO_CONTENT_LENGTH_DOWNLOAD_T - content-length of download # SYNOPSIS @@ -41,17 +41,18 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { /* check the size */ curl_off_t cl; - res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &cl); - if(!res) { + result = + curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &cl); + if(result == CURLE_OK) { printf("Download size: %" CURL_FORMAT_CURL_OFF_T "\n", cl); } } diff --git a/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD.md b/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD.md index 725b7f9a45..6ccd47f1ad 100644 --- a/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD.md +++ b/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD.md @@ -15,7 +15,7 @@ Added-in: 7.6.1 # NAME -CURLINFO_CONTENT_LENGTH_UPLOAD - get the specified size of the upload +CURLINFO_CONTENT_LENGTH_UPLOAD - specified size of the upload # SYNOPSIS @@ -43,17 +43,17 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the upload */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { /* check the size */ double cl; - res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_UPLOAD, &cl); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_UPLOAD, &cl); + if(result == CURLE_OK) { printf("Size: %.0f\n", cl); } } diff --git a/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD_T.md b/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD_T.md index 9b37e54d6d..219481f43d 100644 --- a/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD_T.md +++ b/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD_T.md @@ -15,7 +15,7 @@ Added-in: 7.55.0 # NAME -CURLINFO_CONTENT_LENGTH_UPLOAD_T - get the specified size of the upload +CURLINFO_CONTENT_LENGTH_UPLOAD_T - specified size of the upload # SYNOPSIS @@ -40,17 +40,17 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the upload */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { /* check the size */ curl_off_t cl; - res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_UPLOAD_T, &cl); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_UPLOAD_T, &cl); + if(result == CURLE_OK) { printf("Upload size: %" CURL_FORMAT_CURL_OFF_T "\n", cl); } } diff --git a/docs/libcurl/opts/CURLINFO_CONTENT_TYPE.md b/docs/libcurl/opts/CURLINFO_CONTENT_TYPE.md index 71e9c5122c..13feaca953 100644 --- a/docs/libcurl/opts/CURLINFO_CONTENT_TYPE.md +++ b/docs/libcurl/opts/CURLINFO_CONTENT_TYPE.md @@ -16,7 +16,7 @@ Added-in: 7.9.4 # NAME -CURLINFO_CONTENT_TYPE - get Content-Type +CURLINFO_CONTENT_TYPE - Content-Type of response # SYNOPSIS @@ -33,8 +33,8 @@ object. This is the value read from the Content-Type: field. If you get NULL, it means that the server did not send a valid Content-Type header or that the protocol used does not support this. -The **ct** pointer is set to NULL or pointing to private memory. You MUST -NOT free it - it gets freed when you call curl_easy_cleanup(3) on the +The **ct** pointer is NULL or points to private memory. You **must not** free +it. It gets freed automatically when you call curl_easy_cleanup(3) on the corresponding curl handle. The modern way to get this header from a response is to instead use the @@ -49,16 +49,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { /* extract the content-type */ char *ct = NULL; - res = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct); - if(!res && ct) { + result = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ct); + if(!result && ct) { printf("Content-Type: %s\n", ct); } } diff --git a/docs/libcurl/opts/CURLINFO_COOKIELIST.md b/docs/libcurl/opts/CURLINFO_COOKIELIST.md index b0074b990b..88cbd64c37 100644 --- a/docs/libcurl/opts/CURLINFO_COOKIELIST.md +++ b/docs/libcurl/opts/CURLINFO_COOKIELIST.md @@ -15,7 +15,7 @@ Added-in: 7.14.1 # NAME -CURLINFO_COOKIELIST - get all known cookies +CURLINFO_COOKIELIST - all known cookies # SYNOPSIS @@ -31,11 +31,11 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_COOKIELIST, Pass a pointer to a 'struct curl_slist *' to receive a linked-list of all cookies curl knows (expired ones, too). Do not forget to call curl_slist_free_all(3) on the list after it has been used. If there are no -cookies (cookies for the handle have not been enabled or simply none have been +cookies (cookies for the handle have not been enabled or none have been received) the 'struct curl_slist *' is made a NULL pointer. -Since 7.43.0 cookies that were imported in the Set-Cookie format without a -domain name are not exported by this option. +Cookies that were imported in the Set-Cookie format without a domain name are +not exported by this option. # %PROTOCOLS% @@ -46,19 +46,19 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* enable the cookie engine */ curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { /* extract all known cookies */ struct curl_slist *cookies = NULL; - res = curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &cookies); - if(!res && cookies) { + result = curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &cookies); + if(!result && cookies) { /* a linked list of cookies in cookie file format */ struct curl_slist *each = cookies; while(each) { diff --git a/docs/libcurl/opts/CURLINFO_EARLYDATA_SENT_T.md b/docs/libcurl/opts/CURLINFO_EARLYDATA_SENT_T.md index 6855572970..c326c31f45 100644 --- a/docs/libcurl/opts/CURLINFO_EARLYDATA_SENT_T.md +++ b/docs/libcurl/opts/CURLINFO_EARLYDATA_SENT_T.md @@ -16,7 +16,7 @@ Added-in: 8.11.0 # NAME -CURLINFO_EARLYDATA_SENT_T - get the number of bytes sent as TLS early data +CURLINFO_EARLYDATA_SENT_T - number of bytes sent as TLS early data # SYNOPSIS @@ -51,16 +51,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { curl_off_t amount; - res = curl_easy_getinfo(curl, CURLINFO_EARLYDATA_SENT_T, &amount); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_EARLYDATA_SENT_T, &amount); + if(result == CURLE_OK) { printf("TLS earlydata: %" CURL_FORMAT_CURL_OFF_T " bytes\n", amount); } } diff --git a/docs/libcurl/opts/CURLINFO_EFFECTIVE_METHOD.md b/docs/libcurl/opts/CURLINFO_EFFECTIVE_METHOD.md index 0a748063c6..5a1fcdc3e4 100644 --- a/docs/libcurl/opts/CURLINFO_EFFECTIVE_METHOD.md +++ b/docs/libcurl/opts/CURLINFO_EFFECTIVE_METHOD.md @@ -16,7 +16,7 @@ Added-in: 7.72.0 # NAME -CURLINFO_EFFECTIVE_METHOD - get the last used HTTP method +CURLINFO_EFFECTIVE_METHOD - last used HTTP request method # SYNOPSIS @@ -35,9 +35,9 @@ method. In cases when you have asked libcurl to follow redirects, the method may not be the same method the first request would use. -The **methodp** pointer is NULL or points to private memory. You MUST NOT -free - it gets freed when you call curl_easy_cleanup(3) on the -corresponding curl handle. +The **methodp** pointer is NULL or points to private memory. You +**must not** free it. The memory gets freed automatically when you call +curl_easy_cleanup(3) on the corresponding curl handle. # %PROTOCOLS% @@ -48,12 +48,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "data"); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { char *method = NULL; curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_METHOD, &method); if(method) diff --git a/docs/libcurl/opts/CURLINFO_EFFECTIVE_URL.md b/docs/libcurl/opts/CURLINFO_EFFECTIVE_URL.md index aa50901834..f7adcd810b 100644 --- a/docs/libcurl/opts/CURLINFO_EFFECTIVE_URL.md +++ b/docs/libcurl/opts/CURLINFO_EFFECTIVE_URL.md @@ -15,7 +15,7 @@ Added-in: 7.4 # NAME -CURLINFO_EFFECTIVE_URL - get the last used URL +CURLINFO_EFFECTIVE_URL - last used URL # SYNOPSIS @@ -32,9 +32,9 @@ Pass in a pointer to a char pointer and get the last used effective URL. In cases when you have asked libcurl to follow redirects, it may not be the same value you set with CURLOPT_URL(3). -The **urlp** pointer is NULL or points to private memory. You MUST NOT free -- it gets freed when you call curl_easy_cleanup(3) on the corresponding curl -handle. +The **urlp** pointer is NULL or points to private memory. You **must not** +free it. It memory gets freed automatically when you call curl_easy_cleanup(3) +on the corresponding curl handle. # %PROTOCOLS% @@ -45,11 +45,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { char *url = NULL; curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); if(url) diff --git a/docs/libcurl/opts/CURLINFO_FILETIME.md b/docs/libcurl/opts/CURLINFO_FILETIME.md index 9d4c414d54..f17716041f 100644 --- a/docs/libcurl/opts/CURLINFO_FILETIME.md +++ b/docs/libcurl/opts/CURLINFO_FILETIME.md @@ -17,7 +17,7 @@ Added-in: 7.5 # NAME -CURLINFO_FILETIME - get the remote time of the retrieved document +CURLINFO_FILETIME - remote time of the retrieved document # SYNOPSIS @@ -50,15 +50,15 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Ask for filetime */ curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { long filetime = 0; - res = curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime); - if((CURLE_OK == res) && (filetime != -1)) { + result = curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime); + if((result == CURLE_OK) && (filetime != -1)) { time_t file_time = (time_t)filetime; printf("filetime: %s", ctime(&file_time)); } diff --git a/docs/libcurl/opts/CURLINFO_FILETIME_T.md b/docs/libcurl/opts/CURLINFO_FILETIME_T.md index 2d15ba88d4..258576c68b 100644 --- a/docs/libcurl/opts/CURLINFO_FILETIME_T.md +++ b/docs/libcurl/opts/CURLINFO_FILETIME_T.md @@ -17,7 +17,7 @@ Added-in: 7.59.0 # NAME -CURLINFO_FILETIME_T - get the remote time of the retrieved document +CURLINFO_FILETIME_T - remote time of the retrieved resource # SYNOPSIS @@ -51,15 +51,15 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* Ask for filetime */ curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { curl_off_t filetime; - res = curl_easy_getinfo(curl, CURLINFO_FILETIME_T, &filetime); - if((CURLE_OK == res) && (filetime != -1)) { + result = curl_easy_getinfo(curl, CURLINFO_FILETIME_T, &filetime); + if((result == CURLE_OK) && (filetime != -1)) { time_t file_time = (time_t)filetime; printf("filetime: %s", ctime(&file_time)); } diff --git a/docs/libcurl/opts/CURLINFO_FTP_ENTRY_PATH.md b/docs/libcurl/opts/CURLINFO_FTP_ENTRY_PATH.md index a25ac0a72c..4efccececc 100644 --- a/docs/libcurl/opts/CURLINFO_FTP_ENTRY_PATH.md +++ b/docs/libcurl/opts/CURLINFO_FTP_ENTRY_PATH.md @@ -9,12 +9,13 @@ See-also: - curl_easy_setopt (3) Protocol: - FTP + - SFTP Added-in: 7.15.4 --- # NAME -CURLINFO_FTP_ENTRY_PATH - get entry path in FTP server +CURLINFO_FTP_ENTRY_PATH - entry path in FTP server # SYNOPSIS @@ -28,12 +29,12 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_FTP_ENTRY_PATH, char **path); Pass a pointer to a char pointer to receive a pointer to a string holding the path of the entry path. That is the initial path libcurl ended up in when -logging on to the remote FTP server. This stores a NULL as pointer if +logging on to the remote FTP or SFTP server. This stores a NULL as pointer if something is wrong. -The **path** pointer is NULL or points to private memory. You MUST NOT free -- it gets freed when you call curl_easy_cleanup(3) on the corresponding curl -handle. +The **path** pointer is NULL or points to private memory. You **must not** +free it. The memory gets freed automatically when you call +curl_easy_cleanup(3) on the corresponding curl handle. # %PROTOCOLS% @@ -44,16 +45,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { /* extract the entry path */ char *ep = NULL; - res = curl_easy_getinfo(curl, CURLINFO_FTP_ENTRY_PATH, &ep); - if(!res && ep) { + result = curl_easy_getinfo(curl, CURLINFO_FTP_ENTRY_PATH, &ep); + if(!result && ep) { printf("Entry path was: %s\n", ep); } } diff --git a/docs/libcurl/opts/CURLINFO_HEADER_SIZE.md b/docs/libcurl/opts/CURLINFO_HEADER_SIZE.md index 85907f3b09..c27856809e 100644 --- a/docs/libcurl/opts/CURLINFO_HEADER_SIZE.md +++ b/docs/libcurl/opts/CURLINFO_HEADER_SIZE.md @@ -16,7 +16,7 @@ Added-in: 7.4.1 # NAME -CURLINFO_HEADER_SIZE - get size of retrieved headers +CURLINFO_HEADER_SIZE - size of response headers # SYNOPSIS @@ -43,13 +43,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { long size; - res = curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &size); - if(!res) + result = curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &size); + if(result == CURLE_OK) printf("Header size: %ld bytes\n", size); } curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLINFO_HTTPAUTH_AVAIL.md b/docs/libcurl/opts/CURLINFO_HTTPAUTH_AVAIL.md index 4d001fb4b6..2a65509ede 100644 --- a/docs/libcurl/opts/CURLINFO_HTTPAUTH_AVAIL.md +++ b/docs/libcurl/opts/CURLINFO_HTTPAUTH_AVAIL.md @@ -16,7 +16,7 @@ Added-in: 7.10.8 # NAME -CURLINFO_HTTPAUTH_AVAIL - get available HTTP authentication methods +CURLINFO_HTTPAUTH_AVAIL - available HTTP authentication methods # SYNOPSIS @@ -41,24 +41,24 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { /* extract the available authentication types */ long auth; - res = curl_easy_getinfo(curl, CURLINFO_HTTPAUTH_AVAIL, &auth); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_HTTPAUTH_AVAIL, &auth); + if(result == CURLE_OK) { if(!auth) printf("No auth available, perhaps no 401?\n"); else { printf("%s%s%s%s\n", - auth & CURLAUTH_BASIC ? "Basic ":"", - auth & CURLAUTH_DIGEST ? "Digest ":"", - auth & CURLAUTH_NEGOTIATE ? "Negotiate ":"", - auth % CURLAUTH_NTLM ? "NTLM ":""); + auth & CURLAUTH_BASIC ? "Basic " : "", + auth & CURLAUTH_DIGEST ? "Digest " : "", + auth & CURLAUTH_NEGOTIATE ? "Negotiate " : "", + auth % CURLAUTH_NTLM ? "NTLM " : ""); } } } diff --git a/docs/libcurl/opts/CURLINFO_HTTPAUTH_USED.md b/docs/libcurl/opts/CURLINFO_HTTPAUTH_USED.md index 9b63121710..99db88d3f3 100644 --- a/docs/libcurl/opts/CURLINFO_HTTPAUTH_USED.md +++ b/docs/libcurl/opts/CURLINFO_HTTPAUTH_USED.md @@ -15,7 +15,7 @@ Added-in: 8.12.0 # NAME -CURLINFO_HTTPAUTH_USED - get used HTTP authentication method +CURLINFO_HTTPAUTH_USED - used HTTP authentication method # SYNOPSIS @@ -42,18 +42,18 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC | CURLAUTH_DIGEST); curl_easy_setopt(curl, CURLOPT_USERNAME, "shrek"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "swamp"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { long auth; - res = curl_easy_getinfo(curl, CURLINFO_HTTPAUTH_USED, &auth); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_HTTPAUTH_USED, &auth); + if(result == CURLE_OK) { if(!auth) printf("No auth used\n"); else { diff --git a/docs/libcurl/opts/CURLINFO_HTTP_CONNECTCODE.md b/docs/libcurl/opts/CURLINFO_HTTP_CONNECTCODE.md index a9231289c0..2ded9a121b 100644 --- a/docs/libcurl/opts/CURLINFO_HTTP_CONNECTCODE.md +++ b/docs/libcurl/opts/CURLINFO_HTTP_CONNECTCODE.md @@ -15,7 +15,7 @@ Added-in: 7.10.7 # NAME -CURLINFO_HTTP_CONNECTCODE - get the CONNECT response code +CURLINFO_HTTP_CONNECTCODE - CONNECT response code # SYNOPSIS @@ -40,16 +40,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* typically CONNECT is used to do HTTPS over HTTP proxies */ curl_easy_setopt(curl, CURLOPT_PROXY, "http://127.0.0.1"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { long code; - res = curl_easy_getinfo(curl, CURLINFO_HTTP_CONNECTCODE, &code); - if(!res && code) + result = curl_easy_getinfo(curl, CURLINFO_HTTP_CONNECTCODE, &code); + if(!result && code) printf("The CONNECT response code: %03ld\n", code); } curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLINFO_HTTP_VERSION.md b/docs/libcurl/opts/CURLINFO_HTTP_VERSION.md index 8fddc14f07..0062a043d8 100644 --- a/docs/libcurl/opts/CURLINFO_HTTP_VERSION.md +++ b/docs/libcurl/opts/CURLINFO_HTTP_VERSION.md @@ -15,7 +15,7 @@ Added-in: 7.50.0 # NAME -CURLINFO_HTTP_VERSION - get the http version used in the connection +CURLINFO_HTTP_VERSION - HTTP version used in the transfer # SYNOPSIS @@ -27,10 +27,10 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_HTTP_VERSION, long *p); # DESCRIPTION -Pass a pointer to a long to receive the version used in the last http -connection done using this handle. The returned value is -CURL_HTTP_VERSION_1_0, CURL_HTTP_VERSION_1_1, CURL_HTTP_VERSION_2_0, -CURL_HTTP_VERSION_3 or 0 if the version cannot be determined. +Pass a pointer to a long to receive the version used in the last http transfer +done using this handle. The returned value is CURL_HTTP_VERSION_1_0, +CURL_HTTP_VERSION_1_1, CURL_HTTP_VERSION_2_0, CURL_HTTP_VERSION_3 or 0 if the +version cannot be determined. # %PROTOCOLS% @@ -41,10 +41,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { long http_version; curl_easy_getinfo(curl, CURLINFO_HTTP_VERSION, &http_version); } diff --git a/docs/libcurl/opts/CURLINFO_LASTSOCKET.md b/docs/libcurl/opts/CURLINFO_LASTSOCKET.md index 1b4a22d650..62bc425125 100644 --- a/docs/libcurl/opts/CURLINFO_LASTSOCKET.md +++ b/docs/libcurl/opts/CURLINFO_LASTSOCKET.md @@ -16,7 +16,7 @@ Added-in: 7.15.2 # NAME -CURLINFO_LASTSOCKET - get the last socket used +CURLINFO_LASTSOCKET - last socket used # SYNOPSIS @@ -28,7 +28,7 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_LASTSOCKET, long *socket); # DESCRIPTION -Deprecated since 7.45.0. Use CURLINFO_ACTIVESOCKET(3) instead. +Deprecated. Use CURLINFO_ACTIVESOCKET(3) instead. Pass a pointer to a long to receive the last socket used by this curl session. If the socket is no longer valid, -1 is returned. When you finish @@ -50,22 +50,22 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; long sockfd; /* does not work on win64 */ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Do not do the transfer - only connect to host */ curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); - res = curl_easy_perform(curl); - if(res != CURLE_OK) { - printf("Error: %s\n", curl_easy_strerror(res)); + result = curl_easy_perform(curl); + if(result != CURLE_OK) { + printf("Error: %s\n", curl_easy_strerror(result)); curl_easy_cleanup(curl); return 1; } /* Extract the socket from the curl handle */ - res = curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockfd); - if(!res && sockfd != -1) { + result = curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockfd); + if(!result && sockfd != -1) { /* operate on sockfd */ } @@ -74,6 +74,10 @@ int main(void) } ~~~ +# DEPRECATED + +Deprecated since 7.45.0. + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLINFO_LOCAL_IP.md b/docs/libcurl/opts/CURLINFO_LOCAL_IP.md index a8b89914a0..270b7d5f26 100644 --- a/docs/libcurl/opts/CURLINFO_LOCAL_IP.md +++ b/docs/libcurl/opts/CURLINFO_LOCAL_IP.md @@ -17,7 +17,7 @@ Added-in: 7.21.0 # NAME -CURLINFO_LOCAL_IP - get local IP address of last connection +CURLINFO_LOCAL_IP - local IP address of last connection # SYNOPSIS @@ -35,9 +35,9 @@ with this **curl** handle. This string may be IPv6 when that is enabled. Note that you get a pointer to a memory area that is reused at next request so you need to copy the string if you want to keep the information. -The **ip** pointer is NULL or points to private memory. You MUST NOT free - it -gets freed when you call curl_easy_cleanup(3) on the corresponding CURL -handle. +The **ip** pointer is NULL or points to private memory. You **must not** free +it. The memory gets freed automatically when you call curl_easy_cleanup(3) on +the corresponding curl handle. # %PROTOCOLS% @@ -47,15 +47,15 @@ handle. int main(void) { char *ip; - CURLcode res; + CURLcode result; CURL *curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the transfer */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if((res == CURLE_OK) && + if((result == CURLE_OK) && !curl_easy_getinfo(curl, CURLINFO_LOCAL_IP, &ip) && ip) { printf("Local IP: %s\n", ip); } diff --git a/docs/libcurl/opts/CURLINFO_LOCAL_PORT.md b/docs/libcurl/opts/CURLINFO_LOCAL_PORT.md index 937327f45e..0860bfaf6d 100644 --- a/docs/libcurl/opts/CURLINFO_LOCAL_PORT.md +++ b/docs/libcurl/opts/CURLINFO_LOCAL_PORT.md @@ -17,7 +17,7 @@ Added-in: 7.21.0 # NAME -CURLINFO_LOCAL_PORT - get the latest local port number +CURLINFO_LOCAL_PORT - latest local port number # SYNOPSIS @@ -35,6 +35,9 @@ connection done with this **curl** handle. If the connection was done using QUIC, the port number is a UDP port number, otherwise it is a TCP port number. +If no connection was established or if the protocol does not use ports, -1 +is returned. + # %PROTOCOLS% # EXAMPLE @@ -43,18 +46,18 @@ otherwise it is a TCP port number. int main(void) { CURL *curl; - CURLcode res; + CURLcode result; curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(CURLE_OK == res) { + if(result == CURLE_OK) { long port; - res = curl_easy_getinfo(curl, CURLINFO_LOCAL_PORT, &port); + result = curl_easy_getinfo(curl, CURLINFO_LOCAL_PORT, &port); - if(CURLE_OK == res) { + if(result == CURLE_OK) { printf("We used local port: %ld\n", port); } } diff --git a/docs/libcurl/opts/CURLINFO_NAMELOOKUP_TIME.md b/docs/libcurl/opts/CURLINFO_NAMELOOKUP_TIME.md index fe29e0c436..66e501942d 100644 --- a/docs/libcurl/opts/CURLINFO_NAMELOOKUP_TIME.md +++ b/docs/libcurl/opts/CURLINFO_NAMELOOKUP_TIME.md @@ -15,7 +15,7 @@ Added-in: 7.4.1 # NAME -CURLINFO_NAMELOOKUP_TIME - get the name lookup time +CURLINFO_NAMELOOKUP_TIME - name lookup time # SYNOPSIS @@ -44,13 +44,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; double namelookup; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME, &namelookup); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME, &namelookup); + if(result == CURLE_OK) { printf("Time: %.1f", namelookup); } } diff --git a/docs/libcurl/opts/CURLINFO_NAMELOOKUP_TIME_T.md b/docs/libcurl/opts/CURLINFO_NAMELOOKUP_TIME_T.md index 7b34ef5c0c..1f75c07a61 100644 --- a/docs/libcurl/opts/CURLINFO_NAMELOOKUP_TIME_T.md +++ b/docs/libcurl/opts/CURLINFO_NAMELOOKUP_TIME_T.md @@ -15,7 +15,7 @@ Added-in: 7.61.0 # NAME -CURLINFO_NAMELOOKUP_TIME_T - get the name lookup time in microseconds +CURLINFO_NAMELOOKUP_TIME_T - name lookup time in microseconds # SYNOPSIS @@ -44,13 +44,14 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_off_t namelookup; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME_T, &namelookup); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME_T, + &namelookup); + if(result == CURLE_OK) { printf("Time: %" CURL_FORMAT_CURL_OFF_T ".%06ld", namelookup / 1000000, (long)(namelookup % 1000000)); } diff --git a/docs/libcurl/opts/CURLINFO_NUM_CONNECTS.md b/docs/libcurl/opts/CURLINFO_NUM_CONNECTS.md index eefb38ea75..efb568ad77 100644 --- a/docs/libcurl/opts/CURLINFO_NUM_CONNECTS.md +++ b/docs/libcurl/opts/CURLINFO_NUM_CONNECTS.md @@ -14,7 +14,7 @@ Added-in: 7.12.3 # NAME -CURLINFO_NUM_CONNECTS - get number of created connections +CURLINFO_NUM_CONNECTS - number of created connections # SYNOPSIS @@ -42,14 +42,14 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { long connects; - res = curl_easy_getinfo(curl, CURLINFO_NUM_CONNECTS, &connects); - if(!res) + result = curl_easy_getinfo(curl, CURLINFO_NUM_CONNECTS, &connects); + if(result == CURLE_OK) printf("It needed %ld connects\n", connects); } curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLINFO_OS_ERRNO.md b/docs/libcurl/opts/CURLINFO_OS_ERRNO.md index 25ddd6221e..f163a41819 100644 --- a/docs/libcurl/opts/CURLINFO_OS_ERRNO.md +++ b/docs/libcurl/opts/CURLINFO_OS_ERRNO.md @@ -14,7 +14,7 @@ Added-in: 7.12.2 # NAME -CURLINFO_OS_ERRNO - get errno number from last connect failure +CURLINFO_OS_ERRNO - errno number from last connect failure # SYNOPSIS @@ -47,13 +47,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(res != CURLE_OK) { + result = curl_easy_perform(curl); + if(result != CURLE_OK) { long error; - res = curl_easy_getinfo(curl, CURLINFO_OS_ERRNO, &error); - if(!res && error) { + result = curl_easy_getinfo(curl, CURLINFO_OS_ERRNO, &error); + if(!result && error) { printf("Errno: %ld\n", error); } } diff --git a/docs/libcurl/opts/CURLINFO_POSTTRANSFER_TIME_T.md b/docs/libcurl/opts/CURLINFO_POSTTRANSFER_TIME_T.md index e5f1b5002f..ae2c864f05 100644 --- a/docs/libcurl/opts/CURLINFO_POSTTRANSFER_TIME_T.md +++ b/docs/libcurl/opts/CURLINFO_POSTTRANSFER_TIME_T.md @@ -16,7 +16,7 @@ Added-in: 8.10.0 # NAME -CURLINFO_POSTTRANSFER_TIME_T - get the time until the last byte is sent +CURLINFO_POSTTRANSFER_TIME_T - time to last byte sent # SYNOPSIS @@ -45,15 +45,15 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { curl_off_t posttransfer; - res = curl_easy_getinfo(curl, CURLINFO_POSTTRANSFER_TIME_T, + result = curl_easy_getinfo(curl, CURLINFO_POSTTRANSFER_TIME_T, &posttransfer); - if(CURLE_OK == res) { - printf("Request sent after: %" CURL_FORMAT_CURL_OFF_T ".%06ld us", + if(result == CURLE_OK) { + printf("Request sent after: %" CURL_FORMAT_CURL_OFF_T ".%06ld s", posttransfer / 1000000, (long)(posttransfer % 1000000)); } } diff --git a/docs/libcurl/opts/CURLINFO_PRETRANSFER_TIME.md b/docs/libcurl/opts/CURLINFO_PRETRANSFER_TIME.md index 9442d19c0a..5f6ade455c 100644 --- a/docs/libcurl/opts/CURLINFO_PRETRANSFER_TIME.md +++ b/docs/libcurl/opts/CURLINFO_PRETRANSFER_TIME.md @@ -16,7 +16,7 @@ Added-in: 7.4.1 # NAME -CURLINFO_PRETRANSFER_TIME - get the time until the file transfer start +CURLINFO_PRETRANSFER_TIME - time to transfer start # SYNOPSIS @@ -30,7 +30,7 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_PRETRANSFER_TIME, # DESCRIPTION Pass a pointer to a double to receive the time, in seconds, it took from the -start until the file transfer is just about to begin. +start until the file transfer is about to begin. This time-stamp includes all pre-transfer commands and negotiations that are specific to the particular protocol(s) involved. It includes the sending of @@ -49,13 +49,14 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; double pretransfer; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, &pretransfer); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME, + &pretransfer); + if(result == CURLE_OK) { printf("Time: %.1f", pretransfer); } } diff --git a/docs/libcurl/opts/CURLINFO_PRETRANSFER_TIME_T.md b/docs/libcurl/opts/CURLINFO_PRETRANSFER_TIME_T.md index 2147f27969..1cc300341e 100644 --- a/docs/libcurl/opts/CURLINFO_PRETRANSFER_TIME_T.md +++ b/docs/libcurl/opts/CURLINFO_PRETRANSFER_TIME_T.md @@ -16,7 +16,7 @@ Added-in: 7.61.0 # NAME -CURLINFO_PRETRANSFER_TIME_T - get the time until the file transfer start +CURLINFO_PRETRANSFER_TIME_T - time to transfer start # SYNOPSIS @@ -30,7 +30,7 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_PRETRANSFER_TIME_T, # DESCRIPTION Pass a pointer to a curl_off_t to receive the time, in microseconds, it took -from the start until the file transfer is just about to begin. +from the start until the file transfer is about to begin. This time-stamp includes all pre-transfer commands and negotiations that are specific to the particular protocol(s) involved. It includes the sending of @@ -49,13 +49,14 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_off_t pretransfer; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME_T, &pretransfer); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME_T, + &pretransfer); + if(result == CURLE_OK) { printf("Time: %" CURL_FORMAT_CURL_OFF_T ".%06ld\n", pretransfer / 1000000, (long)(pretransfer % 1000000)); diff --git a/docs/libcurl/opts/CURLINFO_PRIMARY_IP.md b/docs/libcurl/opts/CURLINFO_PRIMARY_IP.md index e87be53e3c..683d97de16 100644 --- a/docs/libcurl/opts/CURLINFO_PRIMARY_IP.md +++ b/docs/libcurl/opts/CURLINFO_PRIMARY_IP.md @@ -17,7 +17,7 @@ Added-in: 7.19.0 # NAME -CURLINFO_PRIMARY_IP - get IP address of last connection +CURLINFO_PRIMARY_IP - IP address of last connection # SYNOPSIS @@ -35,9 +35,9 @@ string holding the IP address of the most recent connection done with this get a pointer to a memory area that is reused at next request so you need to copy the string if you want to keep the information. -The **ip** pointer is NULL or points to private memory. You MUST NOT free - it -gets freed when you call curl_easy_cleanup(3) on the corresponding curl -handle. +The **ip** pointer is NULL or points to private memory. You **must not** free +it. The memory gets freed automatically when you call curl_easy_cleanup(3) on +the corresponding curl handle. # %PROTOCOLS% @@ -47,15 +47,15 @@ handle. int main(void) { char *ip; - CURLcode res; + CURLcode result; CURL *curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the transfer */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if((res == CURLE_OK) && + if((result == CURLE_OK) && !curl_easy_getinfo(curl, CURLINFO_PRIMARY_IP, &ip) && ip) { printf("IP: %s\n", ip); } diff --git a/docs/libcurl/opts/CURLINFO_PRIMARY_PORT.md b/docs/libcurl/opts/CURLINFO_PRIMARY_PORT.md index d0bb632f44..f8a2deee97 100644 --- a/docs/libcurl/opts/CURLINFO_PRIMARY_PORT.md +++ b/docs/libcurl/opts/CURLINFO_PRIMARY_PORT.md @@ -16,7 +16,7 @@ Added-in: 7.21.0 # NAME -CURLINFO_PRIMARY_PORT - get the latest destination port number +CURLINFO_PRIMARY_PORT - last destination port number # SYNOPSIS @@ -36,6 +36,11 @@ If a proxy was used for the most recent transfer, this is the port number of the proxy, if no proxy was used it is the port number of the most recently accessed URL. +If the connection was done using QUIC, the port number is a UDP port number. + +If no connection was established or if the protocol does not use ports, -1 +is returned. + # %PROTOCOLS% # EXAMPLE @@ -45,13 +50,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { long port; - res = curl_easy_getinfo(curl, CURLINFO_PRIMARY_PORT, &port); - if(!res) + result = curl_easy_getinfo(curl, CURLINFO_PRIMARY_PORT, &port); + if(result == CURLE_OK) printf("Connected to remote port: %ld\n", port); } curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLINFO_PRIVATE.md b/docs/libcurl/opts/CURLINFO_PRIVATE.md index 7d4aff10f7..f76b277a70 100644 --- a/docs/libcurl/opts/CURLINFO_PRIVATE.md +++ b/docs/libcurl/opts/CURLINFO_PRIVATE.md @@ -15,7 +15,7 @@ Added-in: 7.10.3 # NAME -CURLINFO_PRIVATE - get the private pointer +CURLINFO_PRIVATE - private pointer # SYNOPSIS @@ -41,19 +41,19 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; void *pointer = (void *)0x2345454; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); /* set the private pointer */ curl_easy_setopt(curl, CURLOPT_PRIVATE, pointer); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* extract the private pointer again */ - res = curl_easy_getinfo(curl, CURLINFO_PRIVATE, &pointer); + result = curl_easy_getinfo(curl, CURLINFO_PRIVATE, &pointer); - if(res) - printf("error: %s\n", curl_easy_strerror(res)); + if(result != CURLE_OK) + printf("error: %s\n", curl_easy_strerror(result)); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLINFO_PROTOCOL.md b/docs/libcurl/opts/CURLINFO_PROTOCOL.md index a472a3d808..3f9346fa04 100644 --- a/docs/libcurl/opts/CURLINFO_PROTOCOL.md +++ b/docs/libcurl/opts/CURLINFO_PROTOCOL.md @@ -15,7 +15,7 @@ Added-in: 7.52.0 # NAME -CURLINFO_PROTOCOL - get the protocol used in the connection +CURLINFO_PROTOCOL - URL scheme used in transfer # SYNOPSIS @@ -27,12 +27,12 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_PROTOCOL, long *p); # DESCRIPTION -This option is deprecated. We strongly recommend using -CURLINFO_SCHEME(3) instead, because this option cannot return all -possible protocols. +This option is deprecated. We strongly recommend using CURLINFO_SCHEME(3) +instead, because this option cannot return all possible schemes. The scheme +might also sometimes be referred to as the protocol. -Pass a pointer to a long to receive the version used in the last http -connection. The returned value is set to one of these values: +Pass a pointer to a long to receive the scheme used in the last transfer. The +returned value is set to one of these values: ~~~c CURLPROTO_DICT, CURLPROTO_FILE, CURLPROTO_FTP, CURLPROTO_FTPS, @@ -53,12 +53,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { - long protocol; - curl_easy_getinfo(curl, CURLINFO_PROTOCOL, &protocol); + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + long scheme; + curl_easy_getinfo(curl, CURLINFO_PROTOCOL, &scheme); } curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLINFO_PROXYAUTH_AVAIL.md b/docs/libcurl/opts/CURLINFO_PROXYAUTH_AVAIL.md index c30794f278..c5110ef377 100644 --- a/docs/libcurl/opts/CURLINFO_PROXYAUTH_AVAIL.md +++ b/docs/libcurl/opts/CURLINFO_PROXYAUTH_AVAIL.md @@ -15,7 +15,7 @@ Added-in: 7.10.8 # NAME -CURLINFO_PROXYAUTH_AVAIL - get available HTTP proxy authentication methods +CURLINFO_PROXYAUTH_AVAIL - HTTP proxy authentication methods # SYNOPSIS @@ -41,25 +41,25 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); curl_easy_setopt(curl, CURLOPT_PROXY, "http://127.0.0.1:80"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { /* extract the available proxy authentication types */ long auth; - res = curl_easy_getinfo(curl, CURLINFO_PROXYAUTH_AVAIL, &auth); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_PROXYAUTH_AVAIL, &auth); + if(result == CURLE_OK) { if(!auth) printf("No proxy auth available, perhaps no 407?\n"); else { printf("%s%s%s%s\n", - auth & CURLAUTH_BASIC ? "Basic ":"", - auth & CURLAUTH_DIGEST ? "Digest ":"", - auth & CURLAUTH_NEGOTIATE ? "Negotiate ":"", - auth % CURLAUTH_NTLM ? "NTLM ":""); + auth & CURLAUTH_BASIC ? "Basic " : "", + auth & CURLAUTH_DIGEST ? "Digest " : "", + auth & CURLAUTH_NEGOTIATE ? "Negotiate " : "", + auth % CURLAUTH_NTLM ? "NTLM " : ""); } } } diff --git a/docs/libcurl/opts/CURLINFO_PROXYAUTH_USED.md b/docs/libcurl/opts/CURLINFO_PROXYAUTH_USED.md index c4a9c943af..9c2ee5b15f 100644 --- a/docs/libcurl/opts/CURLINFO_PROXYAUTH_USED.md +++ b/docs/libcurl/opts/CURLINFO_PROXYAUTH_USED.md @@ -15,7 +15,7 @@ Added-in: 8.12.0 # NAME -CURLINFO_PROXYAUTH_USED - get used HTTP proxy authentication method +CURLINFO_PROXYAUTH_USED - HTTP proxy authentication method # SYNOPSIS @@ -43,20 +43,20 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - curl_easy_setopt(curl, CURLOPT_PROXY, "http://proxy.example.com"); + curl_easy_setopt(curl, CURLOPT_PROXY, "http://proxy.example"); curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_DIGEST); curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, "shrek"); curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, "swamp"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { long auth; - res = curl_easy_getinfo(curl, CURLINFO_PROXYAUTH_USED, &auth); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_PROXYAUTH_USED, &auth); + if(result == CURLE_OK) { if(!auth) printf("No auth used\n"); else { diff --git a/docs/libcurl/opts/CURLINFO_PROXY_ERROR.md b/docs/libcurl/opts/CURLINFO_PROXY_ERROR.md index 772fa7cb75..0c2dab38fd 100644 --- a/docs/libcurl/opts/CURLINFO_PROXY_ERROR.md +++ b/docs/libcurl/opts/CURLINFO_PROXY_ERROR.md @@ -16,7 +16,7 @@ Added-in: 7.73.0 # NAME -CURLINFO_PROXY_ERROR - get the detailed (SOCKS) proxy error +CURLINFO_PROXY_ERROR - detailed (SOCKS) proxy error # SYNOPSIS @@ -81,15 +81,15 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); curl_easy_setopt(curl, CURLOPT_PROXY, "socks5://127.0.0.1"); - res = curl_easy_perform(curl); - if(res == CURLE_PROXY) { + result = curl_easy_perform(curl); + if(result == CURLE_PROXY) { long proxycode; - res = curl_easy_getinfo(curl, CURLINFO_PROXY_ERROR, &proxycode); - if(!res && proxycode) + result = curl_easy_getinfo(curl, CURLINFO_PROXY_ERROR, &proxycode); + if(!result && proxycode) printf("The detailed proxy error: %ld\n", proxycode); } curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLINFO_PROXY_SSL_VERIFYRESULT.md b/docs/libcurl/opts/CURLINFO_PROXY_SSL_VERIFYRESULT.md index dd65c4214b..f9cef23dac 100644 --- a/docs/libcurl/opts/CURLINFO_PROXY_SSL_VERIFYRESULT.md +++ b/docs/libcurl/opts/CURLINFO_PROXY_SSL_VERIFYRESULT.md @@ -18,7 +18,7 @@ Added-in: 7.52.0 # NAME -CURLINFO_PROXY_SSL_VERIFYRESULT - get the result of the proxy certificate verification +CURLINFO_PROXY_SSL_VERIFYRESULT - result of proxy certificate verification # SYNOPSIS @@ -46,22 +46,22 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; long verifyresult; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy:443"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example:443"); - res = curl_easy_perform(curl); - if(res) { - printf("error: %s\n", curl_easy_strerror(res)); + result = curl_easy_perform(curl); + if(result != CURLE_OK) { + printf("error: %s\n", curl_easy_strerror(result)); curl_easy_cleanup(curl); return 1; } - res = curl_easy_getinfo(curl, CURLINFO_PROXY_SSL_VERIFYRESULT, - &verifyresult); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_PROXY_SSL_VERIFYRESULT, + &verifyresult); + if(result == CURLE_OK) { printf("The peer verification said %s\n", (verifyresult ? "bad" : "fine")); } diff --git a/docs/libcurl/opts/CURLINFO_QUEUE_TIME_T.md b/docs/libcurl/opts/CURLINFO_QUEUE_TIME_T.md index acf9164131..c84c6bc7d3 100644 --- a/docs/libcurl/opts/CURLINFO_QUEUE_TIME_T.md +++ b/docs/libcurl/opts/CURLINFO_QUEUE_TIME_T.md @@ -45,13 +45,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_off_t queue; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_QUEUE_TIME_T, &queue); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_QUEUE_TIME_T, &queue); + if(result == CURLE_OK) { printf("Queued: %" CURL_FORMAT_CURL_OFF_T ".%06ld us", queue / 1000000, (long)(queue % 1000000)); } diff --git a/docs/libcurl/opts/CURLINFO_REDIRECT_COUNT.md b/docs/libcurl/opts/CURLINFO_REDIRECT_COUNT.md index 3f58cc09c4..c4796e4c96 100644 --- a/docs/libcurl/opts/CURLINFO_REDIRECT_COUNT.md +++ b/docs/libcurl/opts/CURLINFO_REDIRECT_COUNT.md @@ -16,7 +16,7 @@ Added-in: 7.9.7 # NAME -CURLINFO_REDIRECT_COUNT - get the number of redirects +CURLINFO_REDIRECT_COUNT - number of redirects # SYNOPSIS @@ -41,11 +41,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { long redirects; curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &redirects); } diff --git a/docs/libcurl/opts/CURLINFO_REDIRECT_TIME.md b/docs/libcurl/opts/CURLINFO_REDIRECT_TIME.md index f0b79e261f..262bac52cf 100644 --- a/docs/libcurl/opts/CURLINFO_REDIRECT_TIME.md +++ b/docs/libcurl/opts/CURLINFO_REDIRECT_TIME.md @@ -17,7 +17,7 @@ Added-in: 7.9.7 # NAME -CURLINFO_REDIRECT_TIME - get the time for all redirection steps +CURLINFO_REDIRECT_TIME - time for all redirection steps # SYNOPSIS @@ -46,13 +46,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; double redirect; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME, &redirect); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME, &redirect); + if(result == CURLE_OK) { printf("Time: %.1f", redirect); } } diff --git a/docs/libcurl/opts/CURLINFO_REDIRECT_TIME_T.md b/docs/libcurl/opts/CURLINFO_REDIRECT_TIME_T.md index 88a0830d37..84684893ca 100644 --- a/docs/libcurl/opts/CURLINFO_REDIRECT_TIME_T.md +++ b/docs/libcurl/opts/CURLINFO_REDIRECT_TIME_T.md @@ -17,7 +17,7 @@ Added-in: 7.61.0 # NAME -CURLINFO_REDIRECT_TIME_T - get the time for all redirection steps +CURLINFO_REDIRECT_TIME_T - time for all redirection steps # SYNOPSIS @@ -47,13 +47,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_off_t redirect; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME_T, &redirect); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME_T, &redirect); + if(result == CURLE_OK) { printf("Time: %" CURL_FORMAT_CURL_OFF_T ".%06ld", redirect / 1000000, (long)(redirect % 1000000)); } diff --git a/docs/libcurl/opts/CURLINFO_REDIRECT_URL.md b/docs/libcurl/opts/CURLINFO_REDIRECT_URL.md index d1197e0b28..df3a320b92 100644 --- a/docs/libcurl/opts/CURLINFO_REDIRECT_URL.md +++ b/docs/libcurl/opts/CURLINFO_REDIRECT_URL.md @@ -17,7 +17,7 @@ Added-in: 7.18.2 # NAME -CURLINFO_REDIRECT_URL - get the URL a redirect would go to +CURLINFO_REDIRECT_URL - URL a redirect would go to # SYNOPSIS @@ -32,11 +32,11 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_REDIRECT_URL, char **urlp); Pass a pointer to a char pointer to receive the URL a redirect *would* take you to if you would enable CURLOPT_FOLLOWLOCATION(3). This can come handy if you think using the built-in libcurl redirect logic is not good enough for you -but you would still prefer to avoid implementing all the magic of figuring out +but you would still prefer to avoid implementing all the logic of figuring out the new URL. This URL is also set if the CURLOPT_MAXREDIRS(3) limit prevented a redirect to -happen (since 7.54.1). +happen. # %PROTOCOLS% @@ -47,10 +47,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { char *url = NULL; curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &url); if(url) diff --git a/docs/libcurl/opts/CURLINFO_REFERER.md b/docs/libcurl/opts/CURLINFO_REFERER.md index bd278dd603..6542965fa9 100644 --- a/docs/libcurl/opts/CURLINFO_REFERER.md +++ b/docs/libcurl/opts/CURLINFO_REFERER.md @@ -16,7 +16,7 @@ Added-in: 7.76.0 # NAME -CURLINFO_REFERER - get the used referrer request header +CURLINFO_REFERER - used HTTP referrer request header # SYNOPSIS @@ -31,9 +31,9 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_REFERER, char **hdrp); Pass in a pointer to a char pointer and get the referrer header used in the most recent request. -The **hdrp** pointer is NULL or points to private memory you MUST NOT free - -it gets freed when you call curl_easy_cleanup(3) on the corresponding curl -handle. +The **hdrp** pointer is NULL or points to private memory. You **must not** +free it. The memory gets freed automatically when you call +curl_easy_cleanup(3) on the corresponding curl handle. # %PROTOCOLS% @@ -44,11 +44,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); curl_easy_setopt(curl, CURLOPT_REFERER, "https://example.org/referrer"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { char *hdr = NULL; curl_easy_getinfo(curl, CURLINFO_REFERER, &hdr); if(hdr) diff --git a/docs/libcurl/opts/CURLINFO_REQUEST_SIZE.md b/docs/libcurl/opts/CURLINFO_REQUEST_SIZE.md index fedefc0cc6..35f8aafd7d 100644 --- a/docs/libcurl/opts/CURLINFO_REQUEST_SIZE.md +++ b/docs/libcurl/opts/CURLINFO_REQUEST_SIZE.md @@ -16,7 +16,7 @@ Added-in: 7.4.1 # NAME -CURLINFO_REQUEST_SIZE - get size of sent request +CURLINFO_REQUEST_SIZE - size of sent request # SYNOPSIS @@ -41,13 +41,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { long req; - res = curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &req); - if(!res) + result = curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &req); + if(result == CURLE_OK) printf("Request size: %ld bytes\n", req); } curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLINFO_RESPONSE_CODE.md b/docs/libcurl/opts/CURLINFO_RESPONSE_CODE.md index db75eb94a0..aeb8aeab8a 100644 --- a/docs/libcurl/opts/CURLINFO_RESPONSE_CODE.md +++ b/docs/libcurl/opts/CURLINFO_RESPONSE_CODE.md @@ -18,7 +18,7 @@ Added-in: 7.10.8 # NAME -CURLINFO_RESPONSE_CODE - get the last response code +CURLINFO_RESPONSE_CODE - last response code # SYNOPSIS @@ -47,10 +47,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { long response_code; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); } diff --git a/docs/libcurl/opts/CURLINFO_RETRY_AFTER.md b/docs/libcurl/opts/CURLINFO_RETRY_AFTER.md index 0065f3f32d..3e36aa3d71 100644 --- a/docs/libcurl/opts/CURLINFO_RETRY_AFTER.md +++ b/docs/libcurl/opts/CURLINFO_RETRY_AFTER.md @@ -54,10 +54,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { curl_off_t wait = 0; curl_easy_getinfo(curl, CURLINFO_RETRY_AFTER, &wait); if(wait) diff --git a/docs/libcurl/opts/CURLINFO_RTSP_CLIENT_CSEQ.md b/docs/libcurl/opts/CURLINFO_RTSP_CLIENT_CSEQ.md index 0cfa84b34b..a16b5a1ab6 100644 --- a/docs/libcurl/opts/CURLINFO_RTSP_CLIENT_CSEQ.md +++ b/docs/libcurl/opts/CURLINFO_RTSP_CLIENT_CSEQ.md @@ -16,7 +16,7 @@ Added-in: 7.20.0 # NAME -CURLINFO_RTSP_CLIENT_CSEQ - get the next RTSP client CSeq +CURLINFO_RTSP_CLIENT_CSEQ - next RTSP client CSeq # SYNOPSIS @@ -41,10 +41,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "rtsp://rtsp.example.com"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { long cseq; curl_easy_getinfo(curl, CURLINFO_RTSP_CLIENT_CSEQ, &cseq); } diff --git a/docs/libcurl/opts/CURLINFO_RTSP_CSEQ_RECV.md b/docs/libcurl/opts/CURLINFO_RTSP_CSEQ_RECV.md index 9de26f0f76..98cf8a29aa 100644 --- a/docs/libcurl/opts/CURLINFO_RTSP_CSEQ_RECV.md +++ b/docs/libcurl/opts/CURLINFO_RTSP_CSEQ_RECV.md @@ -15,7 +15,7 @@ Added-in: 7.20.0 # NAME -CURLINFO_RTSP_CSEQ_RECV - get the recently received CSeq +CURLINFO_RTSP_CSEQ_RECV - last received RTSP CSeq # SYNOPSIS @@ -41,10 +41,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "rtsp://rtsp.example.com"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { long cseq; curl_easy_getinfo(curl, CURLINFO_RTSP_CSEQ_RECV, &cseq); } diff --git a/docs/libcurl/opts/CURLINFO_RTSP_SERVER_CSEQ.md b/docs/libcurl/opts/CURLINFO_RTSP_SERVER_CSEQ.md index 0f3c638f8f..d7fd2131e0 100644 --- a/docs/libcurl/opts/CURLINFO_RTSP_SERVER_CSEQ.md +++ b/docs/libcurl/opts/CURLINFO_RTSP_SERVER_CSEQ.md @@ -15,7 +15,7 @@ Added-in: 7.20.0 # NAME -CURLINFO_RTSP_SERVER_CSEQ - get the next RTSP server CSeq +CURLINFO_RTSP_SERVER_CSEQ - next RTSP server CSeq # SYNOPSIS @@ -45,10 +45,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "rtsp://rtsp.example.com"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { long cseq; curl_easy_getinfo(curl, CURLINFO_RTSP_SERVER_CSEQ, &cseq); } diff --git a/docs/libcurl/opts/CURLINFO_RTSP_SESSION_ID.md b/docs/libcurl/opts/CURLINFO_RTSP_SESSION_ID.md index c24f42b24d..4d75ef33e2 100644 --- a/docs/libcurl/opts/CURLINFO_RTSP_SESSION_ID.md +++ b/docs/libcurl/opts/CURLINFO_RTSP_SESSION_ID.md @@ -15,7 +15,7 @@ Added-in: 7.20.0 # NAME -CURLINFO_RTSP_SESSION_ID - get RTSP session ID +CURLINFO_RTSP_SESSION_ID - RTSP session ID # SYNOPSIS @@ -33,9 +33,9 @@ most recent RTSP Session ID. Applications wishing to resume an RTSP session on another connection should retrieve this info before closing the active connection. -The **id** pointer is NULL or points to private memory. You MUST NOT free - it -gets freed when you call curl_easy_cleanup(3) on the corresponding curl -handle. +The **id** pointer is NULL or points to private memory. You **must not** free +it. The memory gets freed automatically when you call curl_easy_cleanup(3) on +the corresponding curl handle. # %PROTOCOLS% @@ -46,10 +46,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "rtsp://rtsp.example.com"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { char *id; curl_easy_getinfo(curl, CURLINFO_RTSP_SESSION_ID, &id); } diff --git a/docs/libcurl/opts/CURLINFO_SCHEME.md b/docs/libcurl/opts/CURLINFO_SCHEME.md index 0089531d01..d453e2891d 100644 --- a/docs/libcurl/opts/CURLINFO_SCHEME.md +++ b/docs/libcurl/opts/CURLINFO_SCHEME.md @@ -17,7 +17,7 @@ Added-in: 7.52.0 # NAME -CURLINFO_SCHEME - get the URL scheme (sometimes called protocol) used in the connection +CURLINFO_SCHEME - URL scheme used in transfer # SYNOPSIS @@ -30,12 +30,13 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_SCHEME, char **scheme); # DESCRIPTION Pass a pointer to a char pointer to receive the pointer to a null-terminated -string holding the URL scheme used for the most recent connection done with -this CURL **handle**. +string holding the URL scheme used for the most recent transfer done with this +CURL **handle**. The scheme might also sometimes be referred to as the +protocol. -The **scheme** pointer is NULL or points to private memory. You MUST NOT -free - it gets freed when you call curl_easy_cleanup(3) on the corresponding -curl handle. +The **scheme** pointer is NULL or points to private memory. You **must not** +free it. The memory gets freed automatically when you call +curl_easy_cleanup(3) on the corresponding curl handle. The returned scheme might be upper or lowercase. Do comparisons case insensitively. @@ -49,10 +50,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(res == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { char *scheme = NULL; curl_easy_getinfo(curl, CURLINFO_SCHEME, &scheme); if(scheme) diff --git a/docs/libcurl/opts/CURLINFO_SIZE_DELIVERED.md b/docs/libcurl/opts/CURLINFO_SIZE_DELIVERED.md new file mode 100644 index 0000000000..db699c98a5 --- /dev/null +++ b/docs/libcurl/opts/CURLINFO_SIZE_DELIVERED.md @@ -0,0 +1,78 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Title: CURLINFO_SIZE_DELIVERED +Section: 3 +Source: libcurl +See-also: + - CURLINFO_SIZE_DOWNLOAD_T (3) + - CURLINFO_CONTENT_LENGTH_DOWNLOAD_T (3) + - CURLOPT_MAXFILESIZE (3) + - curl_easy_getinfo (3) + - curl_easy_setopt (3) +Protocol: + - All +Added-in: 8.20.0 +--- + +# NAME + +CURLINFO_SIZE_DELIVERED - number of delivered bytes + +# SYNOPSIS + +~~~c +#include + +CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_SIZE_DELIVERED, + curl_off_t *dlp); +~~~ + +# DESCRIPTION + +Pass a pointer to a *curl_off_t* to receive the total amount of bytes that +were passed on to the write callback in the download. The amount is only for +the latest transfer and gets reset again for each new transfer. This counts +actual payload data, what's also commonly called body. All meta and header +data is excluded from this amount (unless CURLOPT_HEADER(3) is set). + +The delivered size may differ from the size retrieved with +CURLINFO_SIZE_DOWNLOAD_T(3) when CURLOPT_ACCEPT_ENCODING(3) is used for +automatic data decompression, as this is then the size of the uncompressed +body while CURLINFO_SIZE_DOWNLOAD_T(3) returns the size of the download. + +# %PROTOCOLS% + +# EXAMPLE + +~~~c +int main(void) +{ + CURL *curl = curl_easy_init(); + if(curl) { + CURLcode result; + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); + + /* Perform the request */ + result = curl_easy_perform(curl); + + if(result == CURLE_OK) { + /* check the size */ + curl_off_t dl; + result = curl_easy_getinfo(curl, CURLINFO_SIZE_DELIVERED, &dl); + if(result == CURLE_OK) { + printf("Stored %" CURL_FORMAT_CURL_OFF_T " bytes\n", dl); + } + } + } +} +~~~ + +# %AVAILABILITY% + +# RETURN VALUE + +curl_easy_setopt(3) returns a CURLcode indicating success or error. + +CURLE_OK (0) means everything was OK, non-zero means an error occurred, see +libcurl-errors(3). diff --git a/docs/libcurl/opts/CURLINFO_SIZE_DOWNLOAD.md b/docs/libcurl/opts/CURLINFO_SIZE_DOWNLOAD.md index 30dc52b395..85efc6e2ff 100644 --- a/docs/libcurl/opts/CURLINFO_SIZE_DOWNLOAD.md +++ b/docs/libcurl/opts/CURLINFO_SIZE_DOWNLOAD.md @@ -17,7 +17,7 @@ Added-in: 7.4.1 # NAME -CURLINFO_SIZE_DOWNLOAD - get the number of downloaded bytes +CURLINFO_SIZE_DOWNLOAD - number of downloaded bytes # SYNOPSIS @@ -47,17 +47,17 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { /* check the size */ double dl; - res = curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &dl); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &dl); + if(result == CURLE_OK) { printf("Downloaded %.0f bytes\n", dl); } } diff --git a/docs/libcurl/opts/CURLINFO_SIZE_DOWNLOAD_T.md b/docs/libcurl/opts/CURLINFO_SIZE_DOWNLOAD_T.md index 5d181b12e0..00a1d83d3d 100644 --- a/docs/libcurl/opts/CURLINFO_SIZE_DOWNLOAD_T.md +++ b/docs/libcurl/opts/CURLINFO_SIZE_DOWNLOAD_T.md @@ -17,7 +17,7 @@ Added-in: 7.55.0 # NAME -CURLINFO_SIZE_DOWNLOAD_T - get the number of downloaded bytes +CURLINFO_SIZE_DOWNLOAD_T - number of downloaded bytes # SYNOPSIS @@ -44,17 +44,17 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { /* check the size */ curl_off_t dl; - res = curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD_T, &dl); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD_T, &dl); + if(result == CURLE_OK) { printf("Downloaded %" CURL_FORMAT_CURL_OFF_T " bytes\n", dl); } } diff --git a/docs/libcurl/opts/CURLINFO_SIZE_UPLOAD.md b/docs/libcurl/opts/CURLINFO_SIZE_UPLOAD.md index 6e1881271a..4f25a4526a 100644 --- a/docs/libcurl/opts/CURLINFO_SIZE_UPLOAD.md +++ b/docs/libcurl/opts/CURLINFO_SIZE_UPLOAD.md @@ -16,7 +16,7 @@ Added-in: 7.4.1 # NAME -CURLINFO_SIZE_UPLOAD - get the number of uploaded bytes +CURLINFO_SIZE_UPLOAD - number of uploaded bytes # SYNOPSIS @@ -44,16 +44,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { double ul; - res = curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD, &ul); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD, &ul); + if(result == CURLE_OK) { printf("Uploaded %.0f bytes\n", ul); } } diff --git a/docs/libcurl/opts/CURLINFO_SIZE_UPLOAD_T.md b/docs/libcurl/opts/CURLINFO_SIZE_UPLOAD_T.md index 939d66887a..c91e3e7c0d 100644 --- a/docs/libcurl/opts/CURLINFO_SIZE_UPLOAD_T.md +++ b/docs/libcurl/opts/CURLINFO_SIZE_UPLOAD_T.md @@ -16,7 +16,7 @@ Added-in: 7.55.0 # NAME -CURLINFO_SIZE_UPLOAD_T - get the number of uploaded bytes +CURLINFO_SIZE_UPLOAD_T - number of uploaded bytes # SYNOPSIS @@ -41,16 +41,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { curl_off_t ul; - res = curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD_T, &ul); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD_T, &ul); + if(result == CURLE_OK) { printf("Uploaded %" CURL_FORMAT_CURL_OFF_T " bytes\n", ul); } } diff --git a/docs/libcurl/opts/CURLINFO_SPEED_DOWNLOAD.md b/docs/libcurl/opts/CURLINFO_SPEED_DOWNLOAD.md index 53169e03fb..892f920d3b 100644 --- a/docs/libcurl/opts/CURLINFO_SPEED_DOWNLOAD.md +++ b/docs/libcurl/opts/CURLINFO_SPEED_DOWNLOAD.md @@ -16,7 +16,7 @@ Added-in: 7.4.1 # NAME -CURLINFO_SPEED_DOWNLOAD - get download speed +CURLINFO_SPEED_DOWNLOAD - download speed # SYNOPSIS @@ -44,16 +44,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { double speed; - res = curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD, &speed); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD, &speed); + if(result == CURLE_OK) { printf("Download speed %.0f bytes/sec\n", speed); } } diff --git a/docs/libcurl/opts/CURLINFO_SPEED_DOWNLOAD_T.md b/docs/libcurl/opts/CURLINFO_SPEED_DOWNLOAD_T.md index 60d759f844..94e5eea305 100644 --- a/docs/libcurl/opts/CURLINFO_SPEED_DOWNLOAD_T.md +++ b/docs/libcurl/opts/CURLINFO_SPEED_DOWNLOAD_T.md @@ -16,7 +16,7 @@ Added-in: 7.55.0 # NAME -CURLINFO_SPEED_DOWNLOAD_T - get download speed +CURLINFO_SPEED_DOWNLOAD_T - download speed # SYNOPSIS @@ -41,16 +41,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { curl_off_t speed; - res = curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD_T, &speed); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD_T, &speed); + if(result == CURLE_OK) { printf("Download speed %" CURL_FORMAT_CURL_OFF_T " bytes/sec\n", speed); } diff --git a/docs/libcurl/opts/CURLINFO_SPEED_UPLOAD.md b/docs/libcurl/opts/CURLINFO_SPEED_UPLOAD.md index a10b475b60..f2e1223675 100644 --- a/docs/libcurl/opts/CURLINFO_SPEED_UPLOAD.md +++ b/docs/libcurl/opts/CURLINFO_SPEED_UPLOAD.md @@ -15,7 +15,7 @@ Added-in: 7.4.1 # NAME -CURLINFO_SPEED_UPLOAD - get upload speed +CURLINFO_SPEED_UPLOAD - upload speed # SYNOPSIS @@ -42,16 +42,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { double speed; - res = curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &speed); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &speed); + if(result == CURLE_OK) { printf("Upload speed %.0f bytes/sec\n", speed); } } diff --git a/docs/libcurl/opts/CURLINFO_SPEED_UPLOAD_T.md b/docs/libcurl/opts/CURLINFO_SPEED_UPLOAD_T.md index 9f152131b2..b5165f4665 100644 --- a/docs/libcurl/opts/CURLINFO_SPEED_UPLOAD_T.md +++ b/docs/libcurl/opts/CURLINFO_SPEED_UPLOAD_T.md @@ -15,7 +15,7 @@ Added-in: 7.55.0 # NAME -CURLINFO_SPEED_UPLOAD_T - get upload speed +CURLINFO_SPEED_UPLOAD_T - upload speed # SYNOPSIS @@ -40,16 +40,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { curl_off_t speed; - res = curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD_T, &speed); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD_T, &speed); + if(result == CURLE_OK) { printf("Upload speed %" CURL_FORMAT_CURL_OFF_T " bytes/sec\n", speed); } } diff --git a/docs/libcurl/opts/CURLINFO_SSL_ENGINES.md b/docs/libcurl/opts/CURLINFO_SSL_ENGINES.md index 6c25f90161..da9582c01c 100644 --- a/docs/libcurl/opts/CURLINFO_SSL_ENGINES.md +++ b/docs/libcurl/opts/CURLINFO_SSL_ENGINES.md @@ -17,7 +17,7 @@ Added-in: 7.12.3 # NAME -CURLINFO_SSL_ENGINES - get an slist of OpenSSL crypto-engines +CURLINFO_SSL_ENGINES - an slist of OpenSSL crypto-engines # SYNOPSIS @@ -46,10 +46,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; struct curl_slist *engines; - res = curl_easy_getinfo(curl, CURLINFO_SSL_ENGINES, &engines); - if((res == CURLE_OK) && engines) { + result = curl_easy_getinfo(curl, CURLINFO_SSL_ENGINES, &engines); + if((result == CURLE_OK) && engines) { /* we have a list, free it when done using it */ curl_slist_free_all(engines); } diff --git a/docs/libcurl/opts/CURLINFO_SSL_VERIFYRESULT.md b/docs/libcurl/opts/CURLINFO_SSL_VERIFYRESULT.md index 15f9495d80..af1f911a09 100644 --- a/docs/libcurl/opts/CURLINFO_SSL_VERIFYRESULT.md +++ b/docs/libcurl/opts/CURLINFO_SSL_VERIFYRESULT.md @@ -18,7 +18,7 @@ Added-in: 7.5 # NAME -CURLINFO_SSL_VERIFYRESULT - get the result of the certificate verification +CURLINFO_SSL_VERIFYRESULT - result of the certificate verification # SYNOPSIS @@ -46,21 +46,21 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; long verifyresult; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(res) { - printf("error: %s\n", curl_easy_strerror(res)); + result = curl_easy_perform(curl); + if(result != CURLE_OK) { + printf("error: %s\n", curl_easy_strerror(result)); curl_easy_cleanup(curl); return 1; } - res = curl_easy_getinfo(curl, CURLINFO_SSL_VERIFYRESULT, - &verifyresult); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_SSL_VERIFYRESULT, + &verifyresult); + if(result == CURLE_OK) { printf("The peer verification said %s\n", (verifyresult ? "bad" : "fine")); } diff --git a/docs/libcurl/opts/CURLINFO_STARTTRANSFER_TIME.md b/docs/libcurl/opts/CURLINFO_STARTTRANSFER_TIME.md index 749930ce46..2424037ea1 100644 --- a/docs/libcurl/opts/CURLINFO_STARTTRANSFER_TIME.md +++ b/docs/libcurl/opts/CURLINFO_STARTTRANSFER_TIME.md @@ -16,7 +16,7 @@ Added-in: 7.9.2 # NAME -CURLINFO_STARTTRANSFER_TIME - get the time until the first byte is received +CURLINFO_STARTTRANSFER_TIME - time to first byte received # SYNOPSIS @@ -47,13 +47,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; double start; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &start); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME, &start); + if(result == CURLE_OK) { printf("Time: %.1f", start); } } diff --git a/docs/libcurl/opts/CURLINFO_STARTTRANSFER_TIME_T.md b/docs/libcurl/opts/CURLINFO_STARTTRANSFER_TIME_T.md index 2c0d0eac8c..6cbd0c3d34 100644 --- a/docs/libcurl/opts/CURLINFO_STARTTRANSFER_TIME_T.md +++ b/docs/libcurl/opts/CURLINFO_STARTTRANSFER_TIME_T.md @@ -16,7 +16,7 @@ Added-in: 7.61.0 # NAME -CURLINFO_STARTTRANSFER_TIME_T - get the time until the first byte is received +CURLINFO_STARTTRANSFER_TIME_T - time to first byte received # SYNOPSIS @@ -48,13 +48,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_off_t start; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME_T, &start); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME_T, &start); + if(result == CURLE_OK) { printf("Time: %" CURL_FORMAT_CURL_OFF_T ".%06ld", start / 1000000, (long)(start % 1000000)); } diff --git a/docs/libcurl/opts/CURLINFO_TLS_SESSION.md b/docs/libcurl/opts/CURLINFO_TLS_SESSION.md index 4fb5425f85..cbbd5c0298 100644 --- a/docs/libcurl/opts/CURLINFO_TLS_SESSION.md +++ b/docs/libcurl/opts/CURLINFO_TLS_SESSION.md @@ -18,7 +18,7 @@ Added-in: 7.34.0 # NAME -CURLINFO_TLS_SESSION - get TLS session info +CURLINFO_TLS_SESSION - TLS session info # SYNOPSIS @@ -31,16 +31,13 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_TLS_SESSION, # DESCRIPTION -**This option has been superseded** by CURLINFO_TLS_SSL_PTR(3) which -was added in 7.48.0. The only reason you would use this option instead is if -you could be using a version of libcurl earlier than 7.48.0. +**This option has been superseded** by CURLINFO_TLS_SSL_PTR(3). -This option is exactly the same as CURLINFO_TLS_SSL_PTR(3) except in the -case of OpenSSL and wolfSSL. If the session *backend* is -CURLSSLBACKEND_OPENSSL the session *internals* pointer varies depending -on the option: +This option is exactly the same as CURLINFO_TLS_SSL_PTR(3) except in the case +of OpenSSL and wolfSSL. If the session *backend* is CURLSSLBACKEND_OPENSSL the +session *internals* pointer varies depending on the option: -## OpenSSL: +## OpenSSL CURLINFO_TLS_SESSION(3) OpenSSL session *internals* is **SSL_CTX ***. @@ -71,12 +68,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; struct curl_tlssessioninfo *tls; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(res) - printf("error: %s\n", curl_easy_strerror(res)); + result = curl_easy_perform(curl); + if(result != CURLE_OK) + printf("error: %s\n", curl_easy_strerror(result)); curl_easy_getinfo(curl, CURLINFO_TLS_SESSION, &tls); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md b/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md index 2f87b1a9cd..c79b5e7ebd 100644 --- a/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md +++ b/docs/libcurl/opts/CURLINFO_TLS_SSL_PTR.md @@ -21,7 +21,7 @@ Added-in: 7.48.0 # NAME -CURLINFO_TLS_SESSION, CURLINFO_TLS_SSL_PTR - get TLS session info +CURLINFO_TLS_SSL_PTR - TLS session info # SYNOPSIS @@ -30,12 +30,6 @@ CURLINFO_TLS_SESSION, CURLINFO_TLS_SSL_PTR - get TLS session info CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_TLS_SSL_PTR, struct curl_tlssessioninfo **session); - -/* if you need compatibility with libcurl < 7.48.0 use - CURLINFO_TLS_SESSION instead: */ - -CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_TLS_SESSION, - struct curl_tlssessioninfo **session); ~~~ # DESCRIPTION @@ -60,7 +54,7 @@ The *backend* struct member is one of these defines: CURLSSLBACKEND_NONE (when built without TLS support), CURLSSLBACKEND_WOLFSSL, CURLSSLBACKEND_SECURETRANSPORT, CURLSSLBACKEND_GNUTLS, CURLSSLBACKEND_MBEDTLS, CURLSSLBACKEND_NSS, CURLSSLBACKEND_OPENSSL or CURLSSLBACKEND_SCHANNEL. (Note -that the OpenSSL forks are all reported as just OpenSSL here.) +that the OpenSSL forks are all reported as OpenSSL here.) The *internals* struct member points to a TLS library specific pointer for the active ("in use") SSL connection, with the following underlying types: @@ -74,8 +68,6 @@ the active ("in use") SSL connection, with the following underlying types: CURLINFO_TLS_SESSION(3): **SSL_CTX *** CURLINFO_TLS_SSL_PTR(3): **SSL *** -Since 7.48.0 the *internals* member can point to these other SSL backends -as well: ## mbedTLS @@ -136,11 +128,11 @@ https://github.com/curl/curl/issues/685 #include CURL *curl; -static size_t wf(void *ptr, size_t size, size_t nmemb, void *stream) +static size_t wf(char *ptr, size_t size, size_t nmemb, void *stream) { const struct curl_tlssessioninfo *info = NULL; - CURLcode res = curl_easy_getinfo(curl, CURLINFO_TLS_SSL_PTR, &info); - if(info && !res) { + CURLcode result = curl_easy_getinfo(curl, CURLINFO_TLS_SSL_PTR, &info); + if(info && !result) { if(CURLSSLBACKEND_OPENSSL == info->backend) { printf("OpenSSL ver. %s\n", SSL_get_version((SSL*)info->internals)); } @@ -150,15 +142,15 @@ static size_t wf(void *ptr, size_t size, size_t nmemb, void *stream) int main(int argc, char **argv) { - CURLcode res; + CURLcode result; curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, wf); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } - return res; + return result; } ~~~ @@ -167,6 +159,8 @@ int main(int argc, char **argv) This option supersedes CURLINFO_TLS_SESSION(3) which was added in 7.34.0. This option is exactly the same as that option except in the case of OpenSSL. +Non-OpenSSL support was added in 7.48.0. + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLINFO_TOTAL_TIME.md b/docs/libcurl/opts/CURLINFO_TOTAL_TIME.md index 201c4e4823..ab21ec2f09 100644 --- a/docs/libcurl/opts/CURLINFO_TOTAL_TIME.md +++ b/docs/libcurl/opts/CURLINFO_TOTAL_TIME.md @@ -16,7 +16,7 @@ Added-in: 7.4.1 # NAME -CURLINFO_TOTAL_TIME - get total time of previous transfer +CURLINFO_TOTAL_TIME - total time of previous transfer # SYNOPSIS @@ -45,13 +45,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; double total; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total); + if(result == CURLE_OK) { printf("Time: %.1f", total); } } diff --git a/docs/libcurl/opts/CURLINFO_TOTAL_TIME_T.md b/docs/libcurl/opts/CURLINFO_TOTAL_TIME_T.md index 8ae305d5af..0487acf239 100644 --- a/docs/libcurl/opts/CURLINFO_TOTAL_TIME_T.md +++ b/docs/libcurl/opts/CURLINFO_TOTAL_TIME_T.md @@ -15,7 +15,7 @@ Added-in: 7.61.0 --- # NAME -CURLINFO_TOTAL_TIME_T - get total time of previous transfer in microseconds +CURLINFO_TOTAL_TIME_T - total time of previous transfer # SYNOPSIS @@ -45,13 +45,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_off_t total; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { - res = curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME_T, &total); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { + result = curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME_T, &total); + if(result == CURLE_OK) { printf("Time: %" CURL_FORMAT_CURL_OFF_T ".%06ld", total / 1000000, (long)(total % 1000000)); } diff --git a/docs/libcurl/opts/CURLINFO_USED_PROXY.md b/docs/libcurl/opts/CURLINFO_USED_PROXY.md index 01ef3610db..34abe2d608 100644 --- a/docs/libcurl/opts/CURLINFO_USED_PROXY.md +++ b/docs/libcurl/opts/CURLINFO_USED_PROXY.md @@ -41,19 +41,19 @@ int main(int argc, char *argv[]) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, argv[1]); curl_easy_setopt(curl, CURLOPT_PROXY, "http://127.0.0.1:80"); curl_easy_setopt(curl, CURLOPT_NOPROXY, "example.com"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { /* extract the available proxy authentication types */ long used; - res = curl_easy_getinfo(curl, CURLINFO_USED_PROXY, &used); - if(!res) { - printf("The proxy was %sused\n", used ? "": "NOT "); + result = curl_easy_getinfo(curl, CURLINFO_USED_PROXY, &used); + if(result == CURLE_OK) { + printf("The proxy was %sused\n", used ? "" : "NOT "); } } curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLINFO_XFER_ID.md b/docs/libcurl/opts/CURLINFO_XFER_ID.md index a5f3dd01e7..34557b7f29 100644 --- a/docs/libcurl/opts/CURLINFO_XFER_ID.md +++ b/docs/libcurl/opts/CURLINFO_XFER_ID.md @@ -15,7 +15,7 @@ Added-in: 8.2.0 # NAME -CURLINFO_XFER_ID - get the ID of a transfer +CURLINFO_XFER_ID - ID of the transfer # SYNOPSIS @@ -45,16 +45,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { curl_off_t xfer_id; - res = curl_easy_getinfo(curl, CURLINFO_XFER_ID, &xfer_id); - if(!res) { + result = curl_easy_getinfo(curl, CURLINFO_XFER_ID, &xfer_id); + if(result == CURLE_OK) { printf("Transfer ID: %" CURL_FORMAT_CURL_OFF_T "\n", xfer_id); } } diff --git a/docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md b/docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md index 14934a9a40..b02029b59f 100644 --- a/docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md +++ b/docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md @@ -45,8 +45,8 @@ connections. Changing this value when there are transfers in progress is possible, and the new value is then used the next time checks are performed. Lowering the value -does not close down any active transfers, it simply does not allow new ones to -get made. +does not close down any active transfers, it prevents new ones from being +made. # DEFAULT diff --git a/docs/libcurl/opts/CURLMOPT_MAX_HOST_CONNECTIONS.md b/docs/libcurl/opts/CURLMOPT_MAX_HOST_CONNECTIONS.md index 26a0abd2aa..184830fd5d 100644 --- a/docs/libcurl/opts/CURLMOPT_MAX_HOST_CONNECTIONS.md +++ b/docs/libcurl/opts/CURLMOPT_MAX_HOST_CONNECTIONS.md @@ -50,7 +50,7 @@ making a new connection is permitted. Changing this value while there are transfers in progress is possible. The new value is then used the next time checks are performed. Lowering the value does -not close down any active transfers, it simply does not allow new ones to get +not close down any active transfers, it prevents new ones from being made. # DEFAULT diff --git a/docs/libcurl/opts/CURLMOPT_MAX_TOTAL_CONNECTIONS.md b/docs/libcurl/opts/CURLMOPT_MAX_TOTAL_CONNECTIONS.md index 7c4e5aa46a..5a8811da86 100644 --- a/docs/libcurl/opts/CURLMOPT_MAX_TOTAL_CONNECTIONS.md +++ b/docs/libcurl/opts/CURLMOPT_MAX_TOTAL_CONNECTIONS.md @@ -45,12 +45,12 @@ making a new connection is permitted. Changing this value while there are transfers in progress is possible. The new value is then used the next time checks are performed. Lowering the value does -not close down any active transfers, it simply does not allow new ones to get +not close down any active transfers, it prevents new ones from being made. # DEFAULT -0, which means that there is no limit. It is then simply controlled by the +0, which means that there is no limit. It is then controlled by the number of easy handles added concurrently and how much multiplexing is being done. diff --git a/docs/libcurl/opts/CURLMOPT_NETWORK_CHANGED.md b/docs/libcurl/opts/CURLMOPT_NETWORK_CHANGED.md index a38a9a83ba..a2a44c359d 100644 --- a/docs/libcurl/opts/CURLMOPT_NETWORK_CHANGED.md +++ b/docs/libcurl/opts/CURLMOPT_NETWORK_CHANGED.md @@ -27,30 +27,33 @@ CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_NETWORK_CHANGED, # DESCRIPTION -Pass a long with a bitmask to tell libcurl how the multi -handle should react. The following values in the mask are -defined. All bits not mentioned are reserved for future -extensions. +Pass a long with a bitmask to tell libcurl how the multi handle should react. +The following values in the mask are defined. All bits not mentioned are +reserved for future extensions. -This option can be set at any time and repeatedly. Each call only -affects the *currently* cached connections and DNS information. -Any connection created or DNS information added afterwards is -cached the usual way again. Phrasing it another way: the option is -not persisted but setting it serves as a "trigger" +This option can be set at any time and repeatedly. Each call only affects the +*currently* cached connections and DNS information. Any connection created or +DNS information added afterwards is cached the usual way again. Phrasing it +another way: the option is not persisted but setting it serves as a "trigger" to clear the caches. -The call affects only the connection and DNS cache of the multi handle -itself and not the ones owned by SHARE handles. +The call affects only the connection and DNS cache of the multi handle itself +and not the ones owned by SHARE handles. + +## CURLMNWC_CLEAR_ALL + +Clear everything. (Added in 8.20.0) ## CURLMNWC_CLEAR_CONNS -No longer reuse any existing connection in the multi handle's -connection cache. This closes all connections that are not in use. -Ongoing transfers continue on the connections they operate on. +No longer reuse any existing connection in the multi handle's connection +cache. This closes all connections that are not in use. Ongoing transfers +continue on the connections they operate on. ## CURLMNWC_CLEAR_DNS -Clear the multi handle's DNS cache. +Clear the multi handle's DNS cache. Ongoing transfers keep using their already +resolved addresses, but future name resolutions are performed again. # DEFAULT diff --git a/docs/libcurl/opts/CURLMOPT_NOTIFYDATA.md b/docs/libcurl/opts/CURLMOPT_NOTIFYDATA.md new file mode 100644 index 0000000000..ad57cbea14 --- /dev/null +++ b/docs/libcurl/opts/CURLMOPT_NOTIFYDATA.md @@ -0,0 +1,72 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Title: CURLMOPT_NOTIFYDATA +Section: 3 +Source: libcurl +See-also: + - CURLMOPT_NOTIFYFUNCTION (3) + - curl_multi_notify_disable (3) + - curl_multi_notify_enable (3) +Protocol: + - All +Added-in: 8.17.0 +--- + +# NAME + +CURLMOPT_NOTIFYDATA - custom pointer passed to the notification callback + +# SYNOPSIS + +~~~c +#include + +CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_NOTIFYDATA, void *pointer); +~~~ + +# DESCRIPTION + +A data *pointer* to pass to the notification callback set with the +CURLMOPT_NOTIFYFUNCTION(3) option. + +This pointer is not touched by libcurl but is only passed in as the +notification callback's **clientp** argument. + +# DEFAULT + +NULL + +# %PROTOCOLS% + +# EXAMPLE + +~~~c +struct priv { + void *ours; +}; + +static void notify_cb(CURLM *multi, unsigned int notification, + CURL *easy, void *notifyp) +{ + struct priv *p = notifyp; + printf("my ptr: %p\n", p->ours); + /* ... */ +} + +int main(void) +{ + struct priv setup; + CURLM *multi = curl_multi_init(); + /* ... use socket callback and custom pointer */ + curl_multi_setopt(multi, CURLMOPT_NOTIFYFUNCTION, notify_cb); + curl_multi_setopt(multi, CURLMOPT_NOTIFYDATA, &setup); + curl_multi_notify_enable(multi, CURLMNOTIFY_INFO_READ); +} +~~~ + +# %AVAILABILITY% + +# RETURN VALUE + +Returns CURLM_OK. diff --git a/docs/libcurl/opts/CURLMOPT_NOTIFYFUNCTION.md b/docs/libcurl/opts/CURLMOPT_NOTIFYFUNCTION.md new file mode 100644 index 0000000000..0e10aa9187 --- /dev/null +++ b/docs/libcurl/opts/CURLMOPT_NOTIFYFUNCTION.md @@ -0,0 +1,130 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Title: CURLMOPT_NOTIFYFUNCTION +Section: 3 +Source: libcurl +See-also: + - CURLMOPT_NOTIFYDATA (3) + - curl_multi_socket_action (3) + - curl_multi_notify_disable (3) + - curl_multi_notify_enable (3) +Protocol: + - All +Added-in: 8.17.0 +--- + +# NAME + +CURLMOPT_NOTIFYFUNCTION - callback receiving notifications + +# SYNOPSIS + +~~~c +#include + +void notify_callback(CURLM *multi, /* multi handle */ + unsigned int notification, /* notification type */ + CURL *easy, /* easy handle */ + void *notifyp); /* private notify pointer */ + +CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_NOTIFYFUNCTION, notify_callback); +~~~ + +# DESCRIPTION + +Pass a pointer to your callback function, which should match the prototype +shown above. + +When the multi handle processes transfers, changes can be observed +by receiving notifications about them. This can eliminate the need to +constantly interrogate the multi handle to observe such changes to +act on them. + +Notifications are collected and dispatched to the application's callback +function at an appropriate time. + +The notify callback is different from other callbacks in that it +can use more libcurl API functions. Apart from curl_multi_perform(3), +curl_multi_socket(3), curl_multi_socket_action(3), curl_multi_socket_all(3) +and curl_multi_cleanup(3) it may call all other methods on the +multi and easy handles. This includes adding and removing easy +handles to/from the multi handle. + +This callback may get invoked at any time when interacting with libcurl. +This may even happen after all transfers are done and *may also* +happen *during* a call to curl_multi_cleanup(3) when cached connections +are shut down. + +# CALLBACK ARGUMENTS + +*multi* identifies the multi handle that triggered the notification. + +**notification** is the type of notification, e.g. what happened. The +following types are available right now. In the future, new ones might be +added. + +## CURLMNOTIFY_INFO_READ + +When enabled via curl_multi_notify_enable(3), this informs the application +that there are new messages to be processed via curl_multi_info_read(3). + +This notification happens whenever a message is added to an empty +message stack in the multi handle and not for subsequent additions. The +notification callback is then expected to read all available message, +emptying the stack, so a subsequent addition triggers the notification +again. + +The *easy* handle passed is an internal handle. + +## CURLMNOTIFY_EASY_DONE + +When enabled via curl_multi_notify_enable(3), this notification is triggered +when an easy handle has finished. This happens both for successful and failed +transfers. + +The *easy* handle passed is the transfer that is done. This *may* be +an internal handle when DoH or other features are used. + +*easy* identifies the transfer involved. This may be one of the +application's own easy handle or an internal handle. + +**notifyp** is set with CURLMOPT_NOTIFYDATA(3). + +# DEFAULT + +NULL (no callback) + +# %PROTOCOLS% + +# EXAMPLE + +~~~c +struct priv { + void *ours; +}; + +static void notify_cb(CURLM *multi, unsigned int notification, + CURL *easy, void *notifyp) +{ + struct priv *p = notifyp; + printf("my ptr: %p\n", p->ours); + /* ... */ +} + +int main(void) +{ + struct priv setup; + CURLM *multi = curl_multi_init(); + /* ... use socket callback and custom pointer */ + curl_multi_setopt(multi, CURLMOPT_NOTIFYFUNCTION, notify_cb); + curl_multi_setopt(multi, CURLMOPT_NOTIFYDATA, &setup); + curl_multi_notify_enable(multi, CURLMNOTIFY_INFO_READ); +} +~~~ + +# %AVAILABILITY% + +# RETURN VALUE + +Returns CURLM_OK. diff --git a/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.md b/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.md index 3bbc517432..4bead2b94f 100644 --- a/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.md +++ b/docs/libcurl/opts/CURLMOPT_PUSHFUNCTION.md @@ -63,11 +63,11 @@ usual. If the callback returns CURL_PUSH_OK, the new easy handle is added to the multi handle, the callback must not do that by itself. -The callback can access PUSH_PROMISE headers with two accessor -functions. These functions can only be used from within this callback and they -can only access the PUSH_PROMISE headers: curl_pushheader_byname(3) and -curl_pushheader_bynum(3). The normal response headers are passed to the -header callback for pushed streams just as for normal streams. +The callback can access PUSH_PROMISE headers with two accessor functions. +These functions can only be used from within this callback and they can only +access the PUSH_PROMISE headers: curl_pushheader_byname(3) and +curl_pushheader_bynum(3). The normal response headers are passed to the header +callback for pushed streams like for normal streams. The header fields can also be accessed with curl_easy_header(3), introduced in later libcurl versions. diff --git a/docs/libcurl/opts/CURLMOPT_QUICK_EXIT.md b/docs/libcurl/opts/CURLMOPT_QUICK_EXIT.md new file mode 100644 index 0000000000..f932208363 --- /dev/null +++ b/docs/libcurl/opts/CURLMOPT_QUICK_EXIT.md @@ -0,0 +1,60 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Title: CURLMOPT_QUICK_EXIT +Section: 3 +Source: libcurl +See-also: + - CURLOPT_QUICK_EXIT (3) +Protocol: + - All +Added-in: 8.20.0 +--- + +# NAME + +CURLOPT_QUICK_EXIT - allow libcurl to exit quickly + +# SYNOPSIS + +~~~c +#include + +CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_QUICK_EXIT, + long value); +~~~ + +# DESCRIPTION + +Pass a long as a parameter, 1L meaning that when recovering from a timeout, +libcurl should skip lengthy cleanups that are intended to avoid all kinds of +leaks (threads etc.), as the caller program is about to call exit() anyway. +This allows for a swift termination after a DNS timeout for example, by +canceling and/or forgetting about a resolver thread, at the expense of a +possible (though short-lived) leak of associated resources. + +# DEFAULT + +20. + +# %PROTOCOLS% + +# EXAMPLE + +~~~c +int main(void) +{ + CURLM *m = curl_multi_init(); + /* do not join threads when cleaning up this multi handle */ + curl_multi_setopt(m, CURLMOPT_QUICK_EXIT, 1L); +} +~~~ + +# %AVAILABILITY% + +# RETURN VALUE + +curl_multi_setopt(3) returns a CURLMcode indicating success or error. + +CURLM_OK (0) means everything was OK, non-zero means an error occurred, see +libcurl-errors(3). diff --git a/docs/libcurl/opts/CURLMOPT_RESOLVE_THREADS_MAX.md b/docs/libcurl/opts/CURLMOPT_RESOLVE_THREADS_MAX.md new file mode 100644 index 0000000000..593ff7fa02 --- /dev/null +++ b/docs/libcurl/opts/CURLMOPT_RESOLVE_THREADS_MAX.md @@ -0,0 +1,75 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Title: CURLMOPT_RESOLVE_THREADS_MAX +Section: 3 +Source: libcurl +See-also: + - CURLOPT_IPRESOLVE (3) + - CURLOPT_RESOLVE (3) +Protocol: + - All +Added-in: 8.20.0 +--- + +# NAME + +CURLMOPT_RESOLVE_THREADS_MAX - max threads for threaded DNS resolver + +# SYNOPSIS + +~~~c +#include + +CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_RESOLVE_THREADS_MAX, + long amount); +~~~ + +# DESCRIPTION + +Pass a long for the **amount**. The set number is used as the maximum number +of threads to be used for the threaded DNS resolver. It has to be a +positive number in the range of 32 bits. + +When libcurl is built with a threaded resolver, which is the default on +many systems, it uses a thread pool to lookup addresses and other +properties of hostnames so other transfers are not blocked by this. + +Threads are started on demand to perform the resolving and shut down +again after a period of inactivity. When the maximum number of threads +is reached, outstanding resolves are held in a queue and served when +a thread becomes available. + +The default maximum is expected to work fine for many situations. Application +may override it using this option for the multi handle. + +Changing this value while there are resolves in progress is possible. +Increasing the value takes effect right away. Lowering the value does +not close down any resolves, but ends threads above the new maximum +once the resolving is done. + +# DEFAULT + +20. + +# %PROTOCOLS% + +# EXAMPLE + +~~~c +int main(void) +{ + CURLM *m = curl_multi_init(); + /* never use more than 5 threads for resolving */ + curl_multi_setopt(m, CURLMOPT_RESOLVE_THREADS_MAX, 5L); +} +~~~ + +# %AVAILABILITY% + +# RETURN VALUE + +curl_multi_setopt(3) returns a CURLMcode indicating success or error. + +CURLM_OK (0) means everything was OK, non-zero means an error occurred, see +libcurl-errors(3). diff --git a/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md b/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md index fae15d58a2..411b856e93 100644 --- a/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md +++ b/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md @@ -109,7 +109,7 @@ struct priv { static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) { - struct priv *p = sockp; + struct priv *p = cbp; printf("our ptr: %p\n", p->ours); if(what == CURL_POLL_REMOVE) { diff --git a/docs/libcurl/opts/CURLOPT_ACCEPTTIMEOUT_MS.md b/docs/libcurl/opts/CURLOPT_ACCEPTTIMEOUT_MS.md index 8fdbc96632..5dd19a708b 100644 --- a/docs/libcurl/opts/CURLOPT_ACCEPTTIMEOUT_MS.md +++ b/docs/libcurl/opts/CURLOPT_ACCEPTTIMEOUT_MS.md @@ -8,6 +8,7 @@ See-also: - CURLOPT_CONNECTTIMEOUT_MS (3) - CURLOPT_DEBUGFUNCTION (3) - CURLOPT_STDERR (3) + - CURLOPT_FTPPORT (3) Protocol: - FTP Added-in: 7.24.0 @@ -28,7 +29,11 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_ACCEPTTIMEOUT_MS, long ms); # DESCRIPTION Pass a long telling libcurl the maximum number of milliseconds to wait for a -server to connect back to libcurl when an active FTP connection is used. +server to connect back to libcurl when an active FTP connection is used. When +active FTP is used, the client (libcurl) tells the server to do a TCP connect +back to the client, instead of vice versa for passive FTP. + +This option has no purpose for passive FTP. # DEFAULT @@ -45,7 +50,7 @@ int main(void) if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/path/file"); - /* wait no more than 5 seconds for FTP server responses */ + /* wait no more than 5 seconds for the FTP server to connect */ curl_easy_setopt(curl, CURLOPT_ACCEPTTIMEOUT_MS, 5000L); curl_easy_perform(curl); diff --git a/docs/libcurl/opts/CURLOPT_ACCEPT_ENCODING.md b/docs/libcurl/opts/CURLOPT_ACCEPT_ENCODING.md index 05e7d19a35..6979b26804 100644 --- a/docs/libcurl/opts/CURLOPT_ACCEPT_ENCODING.md +++ b/docs/libcurl/opts/CURLOPT_ACCEPT_ENCODING.md @@ -29,15 +29,16 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_ACCEPT_ENCODING, char *enc); Pass a char pointer argument specifying what encoding you would like. -Sets the contents of the Accept-Encoding: header sent in an HTTP request, and -enables decoding of a response when a Content-Encoding: header is received. +Sets the contents of the `Accept-Encoding:` header sent in an HTTP request, +and enables decoding of a response when a `Content-Encoding:` header is +received. libcurl potentially supports several different compressed encodings depending on what support that has been built-in. To aid applications not having to bother about what specific algorithms this particular libcurl build supports, libcurl allows a zero-length string to be -set ("") to ask for an Accept-Encoding: header to be used that contains all +set ("") to ask for an `Accept-Encoding:` header to be used that contains all built-in supported encodings. Alternatively, you can specify exactly the encoding or list of encodings you @@ -49,25 +50,26 @@ is zstd. Provide them in the string as a comma-separated list of accepted encodings, like: **"br, gzip, deflate"**. Set CURLOPT_ACCEPT_ENCODING(3) to NULL to explicitly disable it, which makes -libcurl not send an Accept-Encoding: header and not decompress received +libcurl not send an `Accept-Encoding:` header and not decompress received contents automatically. -You can also opt to just include the Accept-Encoding: header in your request -with CURLOPT_HTTPHEADER(3) but then there is no automatic decompressing when +You can also opt to include the `Accept-Encoding:` header in your request with +CURLOPT_HTTPHEADER(3) but then there is no automatic decompressing when receiving data. -This is a request, not an order; the server may or may not do it. This option -must be set (to any non-NULL value) or else any unsolicited encoding done by -the server is ignored. +Setting this option is a request, not an order; the server may or may not do +it. It must be set (to any non-NULL value) or else any encoding done by the +server is ignored. -Servers might respond with Content-Encoding even without getting a -Accept-Encoding: in the request. Servers might respond with a different -Content-Encoding than what was asked for in the request. +Servers might respond with `Content-Encoding:` even without getting a +`Accept-Encoding:` in the request. Servers might respond with a different +content encoding than what was asked for in the request. -The Content-Length: servers send for a compressed response is supposed to -indicate the length of the compressed content so when auto decoding is enabled -it may not match the sum of bytes reported by the write callbacks (although, -sending the length of the non-compressed content is a common server mistake). +The `Content-Length:` header field servers send for a compressed response is +supposed to indicate the length of the compressed content so when auto +decoding is enabled it may not match the sum of bytes reported by the write +callbacks (although, sending the length of the non-compressed content is a +common server mistake). The application does not have to keep the string around after setting this option. @@ -75,6 +77,11 @@ option. Using this option multiple times makes the last set string override the previous ones. +**WARNING:** when decompressing data, even tiny transfers might be expanded +and generate a huge amount of bytes. You might want to limit using this option +to only known and trusted sites using secure protocols, perhaps in combination +with CURLOPT_MAXFILESIZE_LARGE(3). + # HISTORY This option was called CURLOPT_ENCODING before 7.21.6 diff --git a/docs/libcurl/opts/CURLOPT_ADDRESS_SCOPE.md b/docs/libcurl/opts/CURLOPT_ADDRESS_SCOPE.md index 38fa9542b3..e2880478fb 100644 --- a/docs/libcurl/opts/CURLOPT_ADDRESS_SCOPE.md +++ b/docs/libcurl/opts/CURLOPT_ADDRESS_SCOPE.md @@ -43,12 +43,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; long my_scope_id; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); my_scope_id = if_nametoindex("eth0"); curl_easy_setopt(curl, CURLOPT_ADDRESS_SCOPE, my_scope_id); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_ALTSVC.md b/docs/libcurl/opts/CURLOPT_ALTSVC.md index b74565790c..0b2a9cad72 100644 --- a/docs/libcurl/opts/CURLOPT_ALTSVC.md +++ b/docs/libcurl/opts/CURLOPT_ALTSVC.md @@ -47,6 +47,10 @@ libcurl cannot fully protect against attacks where an attacker has write access to the same directory where it is directed to save files. This is particularly sensitive if you save files using elevated privileges. +libcurl creates the file to store the alt-svc cache in using default file +permissions, meaning that on *nix systems you may need to restrict your umask +to prevent other users on the same system to access the file. + # DEFAULT NULL. The alt-svc cache is not read nor written to file. diff --git a/docs/libcurl/opts/CURLOPT_ALTSVC_CTRL.md b/docs/libcurl/opts/CURLOPT_ALTSVC_CTRL.md index 84e5d6657c..cdf9ec44a4 100644 --- a/docs/libcurl/opts/CURLOPT_ALTSVC_CTRL.md +++ b/docs/libcurl/opts/CURLOPT_ALTSVC_CTRL.md @@ -22,10 +22,10 @@ CURLOPT_ALTSVC_CTRL - control alt-svc behavior ~~~c #include -#define CURLALTSVC_READONLYFILE (1L<<2) -#define CURLALTSVC_H1 (1L<<3) -#define CURLALTSVC_H2 (1L<<4) -#define CURLALTSVC_H3 (1L<<5) +#define CURLALTSVC_READONLYFILE (1L << 2) +#define CURLALTSVC_H1 (1L << 3) +#define CURLALTSVC_H2 (1L << 4) +#define CURLALTSVC_H3 (1L << 5) CURLcode curl_easy_setopt(CURL *handle, CURLOPT_ALTSVC_CTRL, long bitmask); ~~~ diff --git a/docs/libcurl/opts/CURLOPT_AUTOREFERER.md b/docs/libcurl/opts/CURLOPT_AUTOREFERER.md index 0d4918a4da..8bc75d4666 100644 --- a/docs/libcurl/opts/CURLOPT_AUTOREFERER.md +++ b/docs/libcurl/opts/CURLOPT_AUTOREFERER.md @@ -53,7 +53,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); /* follow redirects */ @@ -62,7 +62,7 @@ int main(void) /* set Referer: automatically when following redirects */ curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_AWS_SIGV4.md b/docs/libcurl/opts/CURLOPT_AWS_SIGV4.md index cc38425cd7..286e9d5eea 100644 --- a/docs/libcurl/opts/CURLOPT_AWS_SIGV4.md +++ b/docs/libcurl/opts/CURLOPT_AWS_SIGV4.md @@ -62,8 +62,8 @@ Example with "Test:Try", when curl uses the algorithm, it generates for "date", **"test4_request"** for "request type", **"SignedHeaders=content-type;host;x-try-date"** for "signed headers" -If you use just "test", instead of "test:try", test is used for every -generated string. +If you use "test", instead of "test:try", test is used for every generated +string. Setting CURLOPT_HTTPAUTH(3) with the CURLAUTH_AWS_SIGV4 bit set is the same as setting this option with a **"aws:amz"** parameter. diff --git a/docs/libcurl/opts/CURLOPT_BUFFERSIZE.md b/docs/libcurl/opts/CURLOPT_BUFFERSIZE.md index 62212e88f7..e517a30bc4 100644 --- a/docs/libcurl/opts/CURLOPT_BUFFERSIZE.md +++ b/docs/libcurl/opts/CURLOPT_BUFFERSIZE.md @@ -33,7 +33,7 @@ in libcurl. The main point of this would be that the write callback gets called more often and with smaller chunks. Secondly, for some protocols, there is a benefit of having a larger buffer for performance. -This is just treated as a request, not an order. You cannot be guaranteed to +This is treated as a request, not an order. You cannot be guaranteed to actually get the given size. This buffer size is by default *CURL_MAX_WRITE_SIZE* (16kB). The maximum @@ -45,10 +45,10 @@ transfer as that may lead to unintended consequences. The maximum size was 512kB until 7.88.0. -Starting in libcurl 8.7.0, there is just a single transfer buffer allocated -per multi handle. This buffer is used by all easy handles added to a multi -handle no matter how many parallel transfers there are. The buffer remains -allocated as long as there are active transfers. +Starting in libcurl 8.7.0, there is a single transfer buffer allocated per +multi handle. This buffer is used by all easy handles added to a multi handle +no matter how many parallel transfers there are. The buffer remains allocated +as long as there are active transfers. # DEFAULT @@ -63,13 +63,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "sftp://example.com/foo.bin"); /* ask libcurl to allocate a larger receive buffer */ curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 120000L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.md b/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.md index 149c9b795f..ffced772b4 100644 --- a/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.md +++ b/docs/libcurl/opts/CURLOPT_CAINFO_BLOB.md @@ -13,8 +13,9 @@ See-also: - CURLOPT_SSL_VERIFYPEER (3) TLS-backend: - OpenSSL + - GnuTLS - mbedTLS - - rustls + - Rustls - wolfSSL - Schannel Added-in: 7.77.0 @@ -64,14 +65,14 @@ int main(void) char *strpem = "PEMDATA"; /* strpem must point to a PEM string */ CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; struct curl_blob blob; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); blob.data = strpem; blob.len = strlen(strpem); blob.flags = CURL_BLOB_COPY; curl_easy_setopt(curl, CURLOPT_CAINFO_BLOB, &blob); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } @@ -80,7 +81,7 @@ int main(void) # HISTORY This option is supported by the mbedTLS (since 7.81.0), Rustls (since 7.82.0), -wolfSSL (since 8.2.0), OpenSSL and Schannel backends. +wolfSSL (since 8.2.0), GnuTLS (since 8.18.0), OpenSSL and Schannel backends. # %AVAILABILITY% diff --git a/docs/libcurl/opts/CURLOPT_CAPATH.md b/docs/libcurl/opts/CURLOPT_CAPATH.md index 3dcba2d0a3..b82b40fe8e 100644 --- a/docs/libcurl/opts/CURLOPT_CAPATH.md +++ b/docs/libcurl/opts/CURLOPT_CAPATH.md @@ -63,10 +63,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_CAPATH, "/etc/cert-dir"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_CERTINFO.md b/docs/libcurl/opts/CURLOPT_CERTINFO.md index 7c6641a9fd..fdaa0d2132 100644 --- a/docs/libcurl/opts/CURLOPT_CERTINFO.md +++ b/docs/libcurl/opts/CURLOPT_CERTINFO.md @@ -16,7 +16,7 @@ TLS-backend: - OpenSSL - GnuTLS - Schannel - - rustls + - Rustls Added-in: 7.19.1 --- @@ -53,7 +53,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); /* connect to any HTTPS site, trusted or not */ @@ -62,13 +62,13 @@ int main(void) curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); - if(!res) { + if(result == CURLE_OK) { struct curl_certinfo *ci; - res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &ci); + result = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &ci); - if(!res) { + if(result == CURLE_OK) { int i; printf("%d certs!\n", ci->num_of_certs); diff --git a/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md b/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md index 8d729ee88a..838b2e5a5f 100644 --- a/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md @@ -92,7 +92,7 @@ NULL #include struct callback_data { - FILE *output; + FILE *output; }; static long file_is_coming(struct curl_fileinfo *finfo, diff --git a/docs/libcurl/opts/CURLOPT_CHUNK_DATA.md b/docs/libcurl/opts/CURLOPT_CHUNK_DATA.md index 9030cfeac3..fb1877e491 100644 --- a/docs/libcurl/opts/CURLOPT_CHUNK_DATA.md +++ b/docs/libcurl/opts/CURLOPT_CHUNK_DATA.md @@ -42,7 +42,7 @@ NULL #include struct callback_data { - FILE *output; + FILE *output; }; static long file_is_coming(struct curl_fileinfo *finfo, diff --git a/docs/libcurl/opts/CURLOPT_CHUNK_END_FUNCTION.md b/docs/libcurl/opts/CURLOPT_CHUNK_END_FUNCTION.md index d95da58876..82bf9ad5ba 100644 --- a/docs/libcurl/opts/CURLOPT_CHUNK_END_FUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_CHUNK_END_FUNCTION.md @@ -50,7 +50,7 @@ NULL #include struct callback_data { - FILE *output; + FILE *output; }; static long file_is_downloaded(void *ptr) diff --git a/docs/libcurl/opts/CURLOPT_CONNECT_ONLY.md b/docs/libcurl/opts/CURLOPT_CONNECT_ONLY.md index 0515a8ae35..19154bc844 100644 --- a/docs/libcurl/opts/CURLOPT_CONNECT_ONLY.md +++ b/docs/libcurl/opts/CURLOPT_CONNECT_ONLY.md @@ -32,16 +32,15 @@ Pass a long. If the parameter equals 1, it tells the library to perform all the required proxy authentication and connection setup, but no data transfer, and then return. -The option can be used to simply test a connection to a server, but is more +The option can be used to test a connection to a server, but is more useful when used with the CURLINFO_ACTIVESOCKET(3) option to curl_easy_getinfo(3) as the library can set up the connection and then the application can obtain the most recently used socket for special data transfers. -Since 7.86.0, this option can be set to '2' and if WebSocket is used, -libcurl performs the request and reads all response headers before handing -over control to the application. For other protocols the behavior of '2' -is undefined. +This option can be set to '2' and if WebSocket is used, libcurl performs the +request and reads all response headers before handing over control to the +application. For other protocols the behavior of '2' is undefined. Transfers marked connect only do not reuse any existing connections and connections marked connect only are not allowed to get reused. @@ -65,11 +64,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); - ret = curl_easy_perform(curl); - if(ret == CURLE_OK) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { /* only connected */ } } diff --git a/docs/libcurl/opts/CURLOPT_CONV_FROM_UTF8_FUNCTION.md b/docs/libcurl/opts/CURLOPT_CONV_FROM_UTF8_FUNCTION.md index 767092abe6..cb8fd354c7 100644 --- a/docs/libcurl/opts/CURLOPT_CONV_FROM_UTF8_FUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_CONV_FROM_UTF8_FUNCTION.md @@ -53,7 +53,7 @@ CURLE_CONV_REQD error code. If HAVE_ICONV is defined, CURL_ICONV_CODESET_OF_HOST must also be defined. For example: ~~~c - #define CURL_ICONV_CODESET_OF_HOST "IBM-1047" +#define CURL_ICONV_CODESET_OF_HOST "IBM-1047" ~~~ The iconv code in libcurl defaults the network and UTF8 codeset names as diff --git a/docs/libcurl/opts/CURLOPT_COOKIEFILE.md b/docs/libcurl/opts/CURLOPT_COOKIEFILE.md index 676d728552..bf2d374464 100644 --- a/docs/libcurl/opts/CURLOPT_COOKIEFILE.md +++ b/docs/libcurl/opts/CURLOPT_COOKIEFILE.md @@ -29,7 +29,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_COOKIEFILE, char *filename); Pass a pointer to a null-terminated string as parameter. It should point to the filename of your file holding cookie data to read. The cookie data can be -in either the old Netscape / Mozilla cookie data format or just regular HTTP +in either the old Netscape / Mozilla cookie data format or regular HTTP headers (Set-Cookie style) dumped to a file. It also enables the cookie engine, making libcurl parse and send cookies on @@ -37,7 +37,7 @@ subsequent requests with this handle. By passing the empty string ("") to this option, you enable the cookie engine without reading any initial cookies. If you tell libcurl the filename is "-" -(just a single minus sign), libcurl instead reads from stdin. +(a single minus sign), libcurl instead reads from stdin. This option only **reads** cookies. To make libcurl write cookies to file, see CURLOPT_COOKIEJAR(3). @@ -55,6 +55,11 @@ If you use this option multiple times, you add more files to read cookies from. Setting this option to NULL disables the cookie engine and clears the list of files to read cookies from. +The cookies are loaded from the specified file(s) when the transfer starts, +not when this option is set. + +libcurl ignores filenames which do not exist or point to a directory. + # SECURITY CONCERNS This document previously mentioned how specifying a non-existing file can also @@ -75,13 +80,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); /* get cookies from an existing file */ curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookies.txt"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_COOKIEJAR.md b/docs/libcurl/opts/CURLOPT_COOKIEJAR.md index 646972792e..ac3a7074d0 100644 --- a/docs/libcurl/opts/CURLOPT_COOKIEJAR.md +++ b/docs/libcurl/opts/CURLOPT_COOKIEJAR.md @@ -58,6 +58,10 @@ libcurl cannot fully protect against attacks where an attacker has write access to the same directory where it is directed to save files. This is particularly sensitive if you save files using elevated privileges. +libcurl creates the file to store cookies using default file permissions, +meaning that on *nix systems you may need to restrict your umask to prevent +other users on the same system to access the file. + # DEFAULT NULL @@ -71,13 +75,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); /* export cookies to this file when closing the handle */ curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "/tmp/cookies.txt"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* close the handle, write the cookies */ curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLOPT_COOKIELIST.md b/docs/libcurl/opts/CURLOPT_COOKIELIST.md index a2c43d8dfe..66c2429171 100644 --- a/docs/libcurl/opts/CURLOPT_COOKIELIST.md +++ b/docs/libcurl/opts/CURLOPT_COOKIELIST.md @@ -31,7 +31,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_COOKIELIST, Pass a char pointer to a *cookie* string. -Such a cookie can be either a single line in Netscape / Mozilla format or just +Such a cookie can be either a single line in Netscape / Mozilla format or regular HTTP-style header (`Set-Cookie:`) format. This option also enables the cookie engine. This adds that single cookie to the internal cookie store. @@ -77,7 +77,7 @@ NULL ~~~c /* an inline import of a cookie in Netscape format. */ -#define SEP "\t" /* Tab separates the fields */ +#define SEP "\t" /* Tab separates the fields */ int main(void) { @@ -98,7 +98,7 @@ int main(void) /* The list of cookies in cookies.txt are not be imported until right before a transfer is performed. Cookies in the list that have the same hostname, path and name as in my_cookie are skipped. That is because - libcurl has already imported my_cookie and it's considered a "live" + libcurl has already imported my_cookie and it is considered a "live" cookie. A live cookie is not replaced by one read from a file. */ curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "cookies.txt"); /* import */ diff --git a/docs/libcurl/opts/CURLOPT_COOKIESESSION.md b/docs/libcurl/opts/CURLOPT_COOKIESESSION.md index bda715af92..920b4b59fe 100644 --- a/docs/libcurl/opts/CURLOPT_COOKIESESSION.md +++ b/docs/libcurl/opts/CURLOPT_COOKIESESSION.md @@ -51,7 +51,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); /* new "session", do not load session cookies */ @@ -60,7 +60,7 @@ int main(void) /* get the (non session) cookies from this file */ curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookies.txt"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_COPYPOSTFIELDS.md b/docs/libcurl/opts/CURLOPT_COPYPOSTFIELDS.md index de3dd1b70b..837fd2de1a 100644 --- a/docs/libcurl/opts/CURLOPT_COPYPOSTFIELDS.md +++ b/docs/libcurl/opts/CURLOPT_COPYPOSTFIELDS.md @@ -11,6 +11,8 @@ See-also: - CURLOPT_UPLOAD (3) Protocol: - HTTP + - MQTT + - RTSP Added-in: 7.17.1 --- diff --git a/docs/libcurl/opts/CURLOPT_CRLF.md b/docs/libcurl/opts/CURLOPT_CRLF.md index 275f8bad3c..4ce1a165c1 100644 --- a/docs/libcurl/opts/CURLOPT_CRLF.md +++ b/docs/libcurl/opts/CURLOPT_CRLF.md @@ -45,10 +45,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/"); curl_easy_setopt(curl, CURLOPT_CRLF, 1L); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_CRLFILE.md b/docs/libcurl/opts/CURLOPT_CRLFILE.md index d5452db19b..62f5651090 100644 --- a/docs/libcurl/opts/CURLOPT_CRLFILE.md +++ b/docs/libcurl/opts/CURLOPT_CRLFILE.md @@ -14,7 +14,7 @@ TLS-backend: - GnuTLS - mbedTLS - OpenSSL - - rustls + - Rustls Added-in: 7.19.0 --- @@ -72,10 +72,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_CRLFILE, "/etc/certs/crl.pem"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_CURLU.md b/docs/libcurl/opts/CURLOPT_CURLU.md index 737e746a15..f9207b85db 100644 --- a/docs/libcurl/opts/CURLOPT_CURLU.md +++ b/docs/libcurl/opts/CURLOPT_CURLU.md @@ -57,13 +57,13 @@ int main(void) CURL *curl = curl_easy_init(); CURLU *urlp = curl_url(); if(curl) { - CURLcode res; + CURLcode result; CURLUcode ret; ret = curl_url_set(urlp, CURLUPART_URL, "https://example.com", 0); curl_easy_setopt(curl, CURLOPT_CURLU, urlp); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_url_cleanup(urlp); curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLOPT_CUSTOMREQUEST.md b/docs/libcurl/opts/CURLOPT_CUSTOMREQUEST.md index 060cfd9c77..9928eef9d6 100644 --- a/docs/libcurl/opts/CURLOPT_CUSTOMREQUEST.md +++ b/docs/libcurl/opts/CURLOPT_CUSTOMREQUEST.md @@ -119,13 +119,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); /* DELETE the given path */ curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_DEBUGDATA.md b/docs/libcurl/opts/CURLOPT_DEBUGDATA.md index ed84a27d12..d15a5576fb 100644 --- a/docs/libcurl/opts/CURLOPT_DEBUGDATA.md +++ b/docs/libcurl/opts/CURLOPT_DEBUGDATA.md @@ -57,7 +57,7 @@ static int my_trace(CURL *handle, curl_infotype type, int main(void) { CURL *curl; - CURLcode res; + CURLcode result; struct data my_tracedata; curl = curl_easy_init(); @@ -70,7 +70,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* always cleanup */ curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLOPT_DEBUGFUNCTION.md b/docs/libcurl/opts/CURLOPT_DEBUGFUNCTION.md index 4ecfb5327b..b1d0dd872c 100644 --- a/docs/libcurl/opts/CURLOPT_DEBUGFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_DEBUGFUNCTION.md @@ -56,7 +56,7 @@ specified in the *type* argument. This function must return 0. The *data* pointed to by the char * passed to this function is not null-terminated, but is exactly of the *size* as told by the *size* argument. -**WARNING** this callback may receive sensitive contents from headers and +**WARNING:** this callback may receive sensitive contents from headers and data, including information sent as **CURLINFO_TEXT**. The *clientp* argument is the pointer set with CURLOPT_DEBUGDATA(3). @@ -96,8 +96,8 @@ The data is SSL/TLS (binary) data received from the peer. ## -WARNING: This callback may be called with the curl *handle* set to an internal -handle. (Added in 8.4.0) +**WARNING:** This callback may be called with the curl *handle* set to an +internal handle. (Added in 8.4.0) If you need to distinguish your curl *handle* from internal handles then set CURLOPT_PRIVATE(3) on your handle. @@ -111,15 +111,14 @@ NULL # EXAMPLE ~~~c -static -void dump(const char *text, - FILE *stream, unsigned char *ptr, size_t size) +static void dump(const char *text, + FILE *stream, unsigned char *ptr, size_t size) { size_t i; size_t c; unsigned int width = 0x10; - fprintf(stream, "%s, %10.10ld bytes (0x%8.8lx)\n", + fprintf(stream, "%s, %lu bytes (0x%lx)\n", text, (long)size, (long)size); for(i = 0; i < size; i += width) { @@ -143,10 +142,9 @@ void dump(const char *text, } } -static -int my_trace(CURL *handle, curl_infotype type, - char *data, size_t size, - void *clientp) +static int my_trace(CURL *handle, curl_infotype type, + char *data, size_t size, + void *clientp) { const char *text; (void)handle; @@ -156,6 +154,7 @@ int my_trace(CURL *handle, curl_infotype type, case CURLINFO_TEXT: fputs("== Info: ", stderr); fwrite(data, size, 1, stderr); + return 0; default: /* in case a new one is introduced to shock us */ return 0; @@ -186,7 +185,7 @@ int my_trace(CURL *handle, curl_infotype type, int main(void) { CURL *curl; - CURLcode res; + CURLcode result; curl = curl_easy_init(); if(curl) { @@ -199,11 +198,11 @@ int main(void) curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* Check for errors */ - if(res != CURLE_OK) + if(result != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(res)); + curl_easy_strerror(result)); /* always cleanup */ curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLOPT_DIRLISTONLY.md b/docs/libcurl/opts/CURLOPT_DIRLISTONLY.md index d249fa6223..548d1f0bf6 100644 --- a/docs/libcurl/opts/CURLOPT_DIRLISTONLY.md +++ b/docs/libcurl/opts/CURLOPT_DIRLISTONLY.md @@ -63,13 +63,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/dir/"); /* list only */ curl_easy_setopt(curl, CURLOPT_DIRLISTONLY, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_DNS_CACHE_TIMEOUT.md b/docs/libcurl/opts/CURLOPT_DNS_CACHE_TIMEOUT.md index 4c40980cdb..5a9b7bbb9a 100644 --- a/docs/libcurl/opts/CURLOPT_DNS_CACHE_TIMEOUT.md +++ b/docs/libcurl/opts/CURLOPT_DNS_CACHE_TIMEOUT.md @@ -68,17 +68,17 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); /* only reuse addresses for a short time */ curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, 2L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* in this second request, the cache is not be used if more than two seconds have passed since the previous name resolve */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_DNS_INTERFACE.md b/docs/libcurl/opts/CURLOPT_DNS_INTERFACE.md index 015a77c236..729223d21b 100644 --- a/docs/libcurl/opts/CURLOPT_DNS_INTERFACE.md +++ b/docs/libcurl/opts/CURLOPT_DNS_INTERFACE.md @@ -52,10 +52,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_DNS_INTERFACE, "eth0"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_DNS_LOCAL_IP4.md b/docs/libcurl/opts/CURLOPT_DNS_LOCAL_IP4.md index f149475c4f..4613ff3e1d 100644 --- a/docs/libcurl/opts/CURLOPT_DNS_LOCAL_IP4.md +++ b/docs/libcurl/opts/CURLOPT_DNS_LOCAL_IP4.md @@ -51,10 +51,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_DNS_LOCAL_IP4, "192.168.0.14"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_DNS_LOCAL_IP6.md b/docs/libcurl/opts/CURLOPT_DNS_LOCAL_IP6.md index a0762b78ab..3a0fe9abe3 100644 --- a/docs/libcurl/opts/CURLOPT_DNS_LOCAL_IP6.md +++ b/docs/libcurl/opts/CURLOPT_DNS_LOCAL_IP6.md @@ -51,10 +51,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_DNS_LOCAL_IP6, "fe80::a9ff:fe46:b619"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_DNS_SERVERS.md b/docs/libcurl/opts/CURLOPT_DNS_SERVERS.md index a731b966ed..738714786f 100644 --- a/docs/libcurl/opts/CURLOPT_DNS_SERVERS.md +++ b/docs/libcurl/opts/CURLOPT_DNS_SERVERS.md @@ -55,11 +55,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_DNS_SERVERS, "192.168.1.100:53,192.168.1.101"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_DNS_USE_GLOBAL_CACHE.md b/docs/libcurl/opts/CURLOPT_DNS_USE_GLOBAL_CACHE.md index 67fba79142..05093eb2bd 100644 --- a/docs/libcurl/opts/CURLOPT_DNS_USE_GLOBAL_CACHE.md +++ b/docs/libcurl/opts/CURLOPT_DNS_USE_GLOBAL_CACHE.md @@ -49,11 +49,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - /* switch off the use of a global, thread unsafe, cache */ + /* switch off the use of a global, thread-unsafe, cache */ curl_easy_setopt(curl, CURLOPT_DNS_USE_GLOBAL_CACHE, 0L); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_DOH_SSL_VERIFYHOST.md b/docs/libcurl/opts/CURLOPT_DOH_SSL_VERIFYHOST.md index 1e307af313..3feee962e4 100644 --- a/docs/libcurl/opts/CURLOPT_DOH_SSL_VERIFYHOST.md +++ b/docs/libcurl/opts/CURLOPT_DOH_SSL_VERIFYHOST.md @@ -74,7 +74,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_DOH_URL, "https://cloudflare-dns.com/dns-query"); - /* Disable host name verification of the DoH server */ + /* Disable hostname verification of the DoH server */ curl_easy_setopt(curl, CURLOPT_DOH_SSL_VERIFYHOST, 0L); curl_easy_perform(curl); diff --git a/docs/libcurl/opts/CURLOPT_DOH_SSL_VERIFYPEER.md b/docs/libcurl/opts/CURLOPT_DOH_SSL_VERIFYPEER.md index dc192cfa0b..5c2860d57b 100644 --- a/docs/libcurl/opts/CURLOPT_DOH_SSL_VERIFYPEER.md +++ b/docs/libcurl/opts/CURLOPT_DOH_SSL_VERIFYPEER.md @@ -62,11 +62,11 @@ talking to. Use CURLOPT_DOH_SSL_VERIFYHOST(3) for that. The check that the hostname in the certificate is valid for the hostname you are connecting to is done independently of the CURLOPT_DOH_SSL_VERIFYPEER(3) option. -WARNING: disabling verification of the certificate allows bad guys to +**WARNING:** disabling verification of the certificate allows bad guys to man-in-the-middle the communication without you knowing it. Disabling -verification makes the communication insecure. Just having encryption on a -transfer is not enough as you cannot be sure that you are communicating with -the correct end-point. +verification makes the communication insecure. Having encryption on a transfer +is not enough as you cannot be sure that you are communicating with the +correct end-point. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_ECH.md b/docs/libcurl/opts/CURLOPT_ECH.md index 9ac65a73a0..0cb2d7e4fd 100644 --- a/docs/libcurl/opts/CURLOPT_ECH.md +++ b/docs/libcurl/opts/CURLOPT_ECH.md @@ -11,7 +11,7 @@ Protocol: TLS-backend: - OpenSSL - wolfSSL - - rustls + - Rustls Added-in: 8.8.0 --- @@ -33,7 +33,7 @@ ECH is only compatible with TLSv1.3. This experimental feature requires a special build of OpenSSL, as ECH is not yet supported in OpenSSL releases. In contrast ECH is supported by the latest -BoringSSL, wolfSSL and rustls-ffi releases. +BoringSSL, wolfSSL and Rustls-ffi releases. There is also a known issue with using wolfSSL which does not support ECH when the HelloRetryRequest mechanism is used. diff --git a/docs/libcurl/opts/CURLOPT_ERRORBUFFER.md b/docs/libcurl/opts/CURLOPT_ERRORBUFFER.md index 61bdab4d8a..6ba194aabb 100644 --- a/docs/libcurl/opts/CURLOPT_ERRORBUFFER.md +++ b/docs/libcurl/opts/CURLOPT_ERRORBUFFER.md @@ -31,19 +31,17 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_ERRORBUFFER, char *buf); # DESCRIPTION Pass a char pointer to a buffer that libcurl may use to store human readable -error messages on failures or problems. This may be more helpful than just the -return code from curl_easy_perform(3) and related functions. The buffer must -be at least **CURL_ERROR_SIZE** bytes big. +error messages on failures or problems. This may be more helpful than the +single return code from curl_easy_perform(3) and related functions. The buffer +must be at least **CURL_ERROR_SIZE** bytes big. You must keep the associated buffer available until libcurl no longer needs it. Failing to do so might cause odd behavior or even crashes. libcurl might need it until you call curl_easy_cleanup(3) or you set the same option again to use a different pointer. -Do not rely on the contents of the buffer unless an error code was returned. -Since 7.60.0 libcurl initializes the contents of the error buffer to an empty -string before performing the transfer. For earlier versions if an error code -was returned but there was no error detail then the buffer was untouched. +libcurl initializes the contents of the error buffer to an empty string before +performing a transfer. Do not attempt to set the contents of the buffer yourself, including in any callbacks you write that may be called by libcurl. The library may overwrite @@ -69,7 +67,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; char errbuf[CURL_ERROR_SIZE]; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); @@ -81,25 +79,30 @@ int main(void) errbuf[0] = 0; /* perform the request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* if the request did not complete correctly, show the error information. if no detailed error information was written to errbuf show the more generic information from curl_easy_strerror instead. */ - if(res != CURLE_OK) { + if(result != CURLE_OK) { size_t len = strlen(errbuf); - fprintf(stderr, "\nlibcurl: (%d) ", res); + fprintf(stderr, "\nlibcurl: (%d) ", result); if(len) fprintf(stderr, "%s%s", errbuf, ((errbuf[len - 1] != '\n') ? "\n" : "")); else - fprintf(stderr, "%s\n", curl_easy_strerror(res)); + fprintf(stderr, "%s\n", curl_easy_strerror(result)); } } } ~~~ +# HISTORY + +Before curl 7.60.0, if an error code was returned but there was no error +detail the buffer was untouched: not initialized. + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_FAILONERROR.md b/docs/libcurl/opts/CURLOPT_FAILONERROR.md index 24a3bdb7ed..29578978fc 100644 --- a/docs/libcurl/opts/CURLOPT_FAILONERROR.md +++ b/docs/libcurl/opts/CURLOPT_FAILONERROR.md @@ -55,11 +55,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); - ret = curl_easy_perform(curl); - if(ret == CURLE_HTTP_RETURNED_ERROR) { + result = curl_easy_perform(curl); + if(result == CURLE_HTTP_RETURNED_ERROR) { /* an HTTP response error problem */ } } diff --git a/docs/libcurl/opts/CURLOPT_FILETIME.md b/docs/libcurl/opts/CURLOPT_FILETIME.md index 65676e1380..ccf93af2e0 100644 --- a/docs/libcurl/opts/CURLOPT_FILETIME.md +++ b/docs/libcurl/opts/CURLOPT_FILETIME.md @@ -49,15 +49,15 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/path.html"); /* Ask for filetime */ curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); - res = curl_easy_perform(curl); - if(CURLE_OK == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OK) { long filetime; - res = curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime); - if((CURLE_OK == res) && (filetime >= 0)) { + result = curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime); + if((result == CURLE_OK) && (filetime >= 0)) { time_t file_time = (time_t)filetime; printf("filetime: %s", ctime(&file_time)); } diff --git a/docs/libcurl/opts/CURLOPT_FOLLOWLOCATION.md b/docs/libcurl/opts/CURLOPT_FOLLOWLOCATION.md index f3e3b99bd3..d8a1153ead 100644 --- a/docs/libcurl/opts/CURLOPT_FOLLOWLOCATION.md +++ b/docs/libcurl/opts/CURLOPT_FOLLOWLOCATION.md @@ -59,11 +59,11 @@ request the same way as the previous one; including the request body if one was provided. For users who think the existing location following is too naive, too simple -or just lacks features, it is easy to instead implement your own redirect -follow logic with the use of curl_easy_getinfo(3)'s CURLINFO_REDIRECT_URL(3) -option instead of using CURLOPT_FOLLOWLOCATION(3). +or lacking features, it is easy to instead implement your own redirect follow +logic with the use of curl_easy_getinfo(3)'s CURLINFO_REDIRECT_URL(3) option +instead of using CURLOPT_FOLLOWLOCATION(3). -By default, libcurl only sends `Authentication:` or explicitly set `Cookie:` +By default, libcurl only sends `Authorization:` or explicitly set `Cookie:` headers to the initial host given in the original URL, to avoid leaking username + password to other sites. CURLOPT_UNRESTRICTED_AUTH(3) is provided to change that behavior. @@ -77,9 +77,9 @@ Pick one of the following modes: ## CURLFOLLOW_ALL (1) -Before 8.13.0 this bit had no name and 1L was just the value to enable this -option. This makes a set custom method be used in all HTTP requests, even -after redirects. +Before 8.13.0 this bit had no name and 1L was the value to enable this option. +This makes a set custom method be used in all HTTP requests, even after +redirects. ## CURLFOLLOW_OBEYCODE (2) @@ -94,8 +94,9 @@ change PUT etc - and therefore also not when libcurl issues a custom PUT. A (except for HEAD). To control for which of the 301/302/303 status codes libcurl should *not* -switch back to GET for when doing a custom POST, and instead keep the custom -method, use CURLOPT_POSTREDIR(3). +switch back to GET for when doing a custom POST (a POST transfer using a +modified method), and instead keep the custom method, use +CURLOPT_POSTREDIR(3). If you prefer a custom POST method to be reset to exactly the method `POST`, use CURLFOLLOW_FIRSTONLY instead. diff --git a/docs/libcurl/opts/CURLOPT_FTPPORT.md b/docs/libcurl/opts/CURLOPT_FTPPORT.md index c5eee3c0ac..fe60393633 100644 --- a/docs/libcurl/opts/CURLOPT_FTPPORT.md +++ b/docs/libcurl/opts/CURLOPT_FTPPORT.md @@ -9,6 +9,7 @@ Protocol: See-also: - CURLOPT_FTP_USE_EPRT (3) - CURLOPT_FTP_USE_EPSV (3) + - CURLOPT_ACCEPTTIMEOUT_MS (3) Added-in: 7.1 --- @@ -32,9 +33,9 @@ IP address to use for the FTP PORT instruction. The PORT instruction tells the remote server to do a TCP connect to our specified IP address. The string may be a plain IP address, a hostname, a -network interface name (under Unix) or just a '-' symbol to let the library -use your system's default IP address. Default FTP operations are passive, and -does not use the PORT command. +network interface name (under Unix) or a '-' symbol to let the library use +your system's default IP address. Default FTP operations are passive, and does +not use the PORT command. The address can be followed by a ':' to specify a port, optionally followed by a '-' to specify a port range. If the port specified is 0, the operating @@ -79,11 +80,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/old-server/file.txt"); curl_easy_setopt(curl, CURLOPT_FTPPORT, "-"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_FTPSSLAUTH.md b/docs/libcurl/opts/CURLOPT_FTPSSLAUTH.md index a2021f830e..6360fb3908 100644 --- a/docs/libcurl/opts/CURLOPT_FTPSSLAUTH.md +++ b/docs/libcurl/opts/CURLOPT_FTPSSLAUTH.md @@ -57,12 +57,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/file.txt"); curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY); /* funny server, ask for SSL before TLS */ curl_easy_setopt(curl, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_SSL); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_FTP_ACCOUNT.md b/docs/libcurl/opts/CURLOPT_FTP_ACCOUNT.md index 3f60368653..97b843696c 100644 --- a/docs/libcurl/opts/CURLOPT_FTP_ACCOUNT.md +++ b/docs/libcurl/opts/CURLOPT_FTP_ACCOUNT.md @@ -49,12 +49,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_FTP_ACCOUNT, "human-resources"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_FTP_ALTERNATIVE_TO_USER.md b/docs/libcurl/opts/CURLOPT_FTP_ALTERNATIVE_TO_USER.md index 88824d08fc..d8ca86427c 100644 --- a/docs/libcurl/opts/CURLOPT_FTP_ALTERNATIVE_TO_USER.md +++ b/docs/libcurl/opts/CURLOPT_FTP_ALTERNATIVE_TO_USER.md @@ -51,10 +51,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER, "two users"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_FTP_CREATE_MISSING_DIRS.md b/docs/libcurl/opts/CURLOPT_FTP_CREATE_MISSING_DIRS.md index d775e1b826..7b1a766d08 100644 --- a/docs/libcurl/opts/CURLOPT_FTP_CREATE_MISSING_DIRS.md +++ b/docs/libcurl/opts/CURLOPT_FTP_CREATE_MISSING_DIRS.md @@ -63,13 +63,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/non-existing/new.txt"); curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, CURLFTP_CREATE_DIR_RETRY); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_FTP_FILEMETHOD.md b/docs/libcurl/opts/CURLOPT_FTP_FILEMETHOD.md index 847a491a27..dffec993d4 100644 --- a/docs/libcurl/opts/CURLOPT_FTP_FILEMETHOD.md +++ b/docs/libcurl/opts/CURLOPT_FTP_FILEMETHOD.md @@ -66,11 +66,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/1/2/3/4/new.txt"); curl_easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_FTP_SKIP_PASV_IP.md b/docs/libcurl/opts/CURLOPT_FTP_SKIP_PASV_IP.md index 7116b9536f..97e6ff6611 100644 --- a/docs/libcurl/opts/CURLOPT_FTP_SKIP_PASV_IP.md +++ b/docs/libcurl/opts/CURLOPT_FTP_SKIP_PASV_IP.md @@ -41,7 +41,7 @@ This option has no effect if PORT, EPRT or EPSV is used instead of PASV. # DEFAULT -1 since 7.74.0, was 0 before then. +1, enabled. # %PROTOCOLS% @@ -52,18 +52,22 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/file.txt"); /* please ignore the IP in the PASV response */ curl_easy_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } ~~~ +# HISTORY + +Before curl 7.74.0, this option was disabled by default. + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_FTP_SSL_CCC.md b/docs/libcurl/opts/CURLOPT_FTP_SSL_CCC.md index cce1616d41..78fc976bee 100644 --- a/docs/libcurl/opts/CURLOPT_FTP_SSL_CCC.md +++ b/docs/libcurl/opts/CURLOPT_FTP_SSL_CCC.md @@ -59,12 +59,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/file.txt"); curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_CONTROL); /* go back to clear-text FTP after authenticating */ curl_easy_setopt(curl, CURLOPT_FTP_SSL_CCC, CURLFTPSSL_CCC_ACTIVE); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_FTP_USE_EPRT.md b/docs/libcurl/opts/CURLOPT_FTP_USE_EPRT.md index 8f2cd82449..781a2c75de 100644 --- a/docs/libcurl/opts/CURLOPT_FTP_USE_EPRT.md +++ b/docs/libcurl/opts/CURLOPT_FTP_USE_EPRT.md @@ -51,7 +51,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/file.txt"); /* contact us back, aka "active" FTP */ @@ -60,7 +60,7 @@ int main(void) /* FTP the way the neanderthals did it */ curl_easy_setopt(curl, CURLOPT_FTP_USE_EPRT, 0L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_FTP_USE_EPSV.md b/docs/libcurl/opts/CURLOPT_FTP_USE_EPSV.md index 5bed94573d..48eb13641d 100644 --- a/docs/libcurl/opts/CURLOPT_FTP_USE_EPSV.md +++ b/docs/libcurl/opts/CURLOPT_FTP_USE_EPSV.md @@ -51,14 +51,14 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/old-server/file.txt"); /* let's shut off this modern feature */ curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 0L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_FTP_USE_PRET.md b/docs/libcurl/opts/CURLOPT_FTP_USE_PRET.md index 5439f2ca8c..9a26cf4504 100644 --- a/docs/libcurl/opts/CURLOPT_FTP_USE_PRET.md +++ b/docs/libcurl/opts/CURLOPT_FTP_USE_PRET.md @@ -44,14 +44,14 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/old-server/file.txt"); /* a drftpd server, do it */ curl_easy_setopt(curl, CURLOPT_FTP_USE_PRET, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_GSSAPI_DELEGATION.md b/docs/libcurl/opts/CURLOPT_GSSAPI_DELEGATION.md index bedceaabcf..2196f23984 100644 --- a/docs/libcurl/opts/CURLOPT_GSSAPI_DELEGATION.md +++ b/docs/libcurl/opts/CURLOPT_GSSAPI_DELEGATION.md @@ -47,12 +47,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* delegate if okayed by policy */ curl_easy_setopt(curl, CURLOPT_GSSAPI_DELEGATION, CURLGSSAPI_DELEGATION_POLICY_FLAG); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.md b/docs/libcurl/opts/CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.md index e38a7c3be5..c4b29d876f 100644 --- a/docs/libcurl/opts/CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.md +++ b/docs/libcurl/opts/CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.md @@ -65,6 +65,12 @@ anything back). That took 3 times the happy eyeballs timeout, so 600ms in the default setting. When any of those four report a success, that socket is used for the transfer and the other three are closed. +There is a limit on the number of sockets opened for connect attempts. When +that limit is reached and more addresses are available, the oldest +attempt is discarded. This limit is currently 6. With the default +happy eyeballs timeout of 200ms, this closes attempts after 1.2 seconds +*as long as there are more addresses to try*. + There are situations where connect attempts fail, but the failure is considered being inconclusive. The QUIC protocol may encounter this. When a QUIC server restarts, it may send replies indicating that it diff --git a/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.md b/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.md index 609d891064..77ef0e06f3 100644 --- a/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.md +++ b/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.md @@ -48,10 +48,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_HAPROXYPROTOCOL, 1L); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_HAPROXY_CLIENT_IP.md b/docs/libcurl/opts/CURLOPT_HAPROXY_CLIENT_IP.md index 6250101ee1..f26bda7cf1 100644 --- a/docs/libcurl/opts/CURLOPT_HAPROXY_CLIENT_IP.md +++ b/docs/libcurl/opts/CURLOPT_HAPROXY_CLIENT_IP.md @@ -27,9 +27,9 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HAPROXY_CLIENT_IP, # DESCRIPTION -When this parameter is set to a valid IPv4 or IPv6 numerical address, the -library sends this address as client address in the HAProxy PROXY protocol v1 -header at beginning of the connection. +When this parameter is set to a valid IPv4 or IPv6 numerical address in its +printable ASCII string version, the library sends this as the client address +in the HAProxy PROXY protocol v1 header at beginning of the connection. This option is an alternative to CURLOPT_HAPROXYPROTOCOL(3) as that one cannot use a specified address. @@ -40,6 +40,13 @@ previous ones. Set it to NULL to disable its use again. The application does not have to keep the string around after setting this option. +As with most libcurl options, the user of this option must make sure that the +*correct* data (address) is passed on. libcurl does little to no verification. + +Note that if you want to send a *different* HAProxy client IP in a subsequent +request, you need to make sure that it is done over a fresh connection as +libcurl does not send it again while reusing connections. + # DEFAULT NULL, no HAProxy header is sent @@ -53,10 +60,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_HAPROXY_CLIENT_IP, "1.1.1.1"); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_HEADERDATA.md b/docs/libcurl/opts/CURLOPT_HEADERDATA.md index bb3f2ed834..f767961cdc 100644 --- a/docs/libcurl/opts/CURLOPT_HEADERDATA.md +++ b/docs/libcurl/opts/CURLOPT_HEADERDATA.md @@ -36,9 +36,9 @@ If CURLOPT_WRITEFUNCTION(3) or CURLOPT_HEADERFUNCTION(3) is used, If neither of those options are set, *pointer* must be a valid FILE * and it is used by a plain fwrite() to write headers to. -If you are using libcurl as a Windows DLL, you **MUST** use a -CURLOPT_WRITEFUNCTION(3) or CURLOPT_HEADERFUNCTION(3) if you set -this option or you might experience crashes. +If you are using libcurl as a Windows DLL, you **must** use a +CURLOPT_WRITEFUNCTION(3) or CURLOPT_HEADERFUNCTION(3) if you set this option +or you might experience crashes. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_HEADERFUNCTION.md b/docs/libcurl/opts/CURLOPT_HEADERFUNCTION.md index 2f4c00a03c..d435e27f44 100644 --- a/docs/libcurl/opts/CURLOPT_HEADERFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_HEADERFUNCTION.md @@ -67,11 +67,11 @@ CURLOPT_WRITEFUNCTION(3), or if it is not specified or NULL - the default, stream-writing function. It is important to note that the callback is invoked for the headers of all -responses received after initiating a request and not just the final -response. This includes all responses which occur during authentication -negotiation. If you need to operate on only the headers from the final -response, you need to collect headers in the callback yourself and use HTTP -status lines, for example, to delimit response boundaries. +responses received after initiating a request and not the final response. This +includes all responses which occur during authentication negotiation. If you +need to operate on only the headers from the final response, you need to +collect headers in the callback yourself and use HTTP status lines, for +example, to delimit response boundaries. For an HTTP transfer, the status line and the blank line preceding the response body are both included as headers and passed to this function. @@ -95,7 +95,7 @@ curl_easy_header(3). libcurl does not unfold HTTP "folded headers" (deprecated since RFC 7230). A folded header is a header that continues on a subsequent line and starts with a whitespace. Such folds are passed to the header callback as separate ones, -although strictly they are just continuations of the previous lines. +although strictly they are continuations of the previous lines. # DEFAULT @@ -109,9 +109,9 @@ Nothing. static size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata) { - /* received header is nitems * size long in 'buffer' NOT ZERO TERMINATED */ + /* received header is 'nitems' bytes in 'buffer' NOT NULL-TERMINATED */ /* 'userdata' is set with CURLOPT_HEADERDATA */ - return nitems * size; + return nitems; } int main(void) diff --git a/docs/libcurl/opts/CURLOPT_HEADEROPT.md b/docs/libcurl/opts/CURLOPT_HEADEROPT.md index 5695d3e0b7..88d8194aba 100644 --- a/docs/libcurl/opts/CURLOPT_HEADEROPT.md +++ b/docs/libcurl/opts/CURLOPT_HEADEROPT.md @@ -54,7 +54,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; struct curl_slist *list; list = curl_slist_append(NULL, "Shoesize: 10"); list = curl_slist_append(list, "Accept:"); @@ -66,7 +66,7 @@ int main(void) libcurl to not send the custom headers to the proxy. Keep them separate. */ curl_easy_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_slist_free_all(list); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_HSTS.md b/docs/libcurl/opts/CURLOPT_HSTS.md index 79665d0a5c..1a9d811455 100644 --- a/docs/libcurl/opts/CURLOPT_HSTS.md +++ b/docs/libcurl/opts/CURLOPT_HSTS.md @@ -27,19 +27,22 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HSTS, char *filename); # DESCRIPTION -Make the *filename* point to a filename to load an existing HSTS cache -from, and to store the cache in when the easy handle is closed. Setting a file -name with this option also enables HSTS for this handle (the equivalent of -setting *CURLHSTS_ENABLE* with CURLOPT_HSTS_CTRL(3)). +Make the *filename* point to a filename to load an existing HSTS cache from, +and to store the cache in when the easy handle is closed. Setting a filename +with this option also enables HSTS for this handle (the equivalent of setting +*CURLHSTS_ENABLE* with CURLOPT_HSTS_CTRL(3)). If the given file does not exist or contains no HSTS entries at startup, the -HSTS cache simply starts empty. Setting the filename to NULL allows HSTS -without reading from or writing to any file. NULL also makes libcurl clear the -list of files to read HSTS data from, if any such were previously set. +HSTS cache starts empty. Setting the filename to NULL allows HSTS without +reading from or writing to any file. NULL also makes libcurl clear the list of +files to read HSTS data from, if any such were previously set. If this option is set multiple times, libcurl loads cache entries from each given file but only stores the last used name for later writing. +Since libcurl 8.20.0, each in-memory HSTS cache (per easy handle or shared +cache) holds no more than the most recently added 10,000 HSTS hostnames. + # FILE FORMAT The HSTS cache is saved to and loaded from a text file with one entry per @@ -63,10 +66,17 @@ NULL, no filename # SECURITY CONCERNS +We strongly urge users to stick to `HTTPS://` URLs, which makes this option +unnecessary. + libcurl cannot fully protect against attacks where an attacker has write access to the same directory where it is directed to save files. This is particularly sensitive if you save files using elevated privileges. +libcurl creates the file to store HSTS data in using default file permissions, +meaning that on *nix systems you may need to restrict your umask to prevent +other users on the same system to access the file. + # %PROTOCOLS% # EXAMPLE diff --git a/docs/libcurl/opts/CURLOPT_HSTSREADFUNCTION.md b/docs/libcurl/opts/CURLOPT_HSTSREADFUNCTION.md index 5d03607fa1..b5a685b73b 100644 --- a/docs/libcurl/opts/CURLOPT_HSTSREADFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_HSTSREADFUNCTION.md @@ -42,23 +42,26 @@ Pass a pointer to your callback function, as the prototype shows above. This callback function gets called by libcurl repeatedly when it populates the in-memory HSTS cache. -Set the *clientp* argument with the CURLOPT_HSTSREADDATA(3) option -or it is NULL. +Set the *clientp* argument with the CURLOPT_HSTSREADDATA(3) option or it is +NULL. -When this callback is invoked, the *sts* pointer points to a populated -struct: Copy the hostname to *name* (no longer than *namelen* -bytes). Make it null-terminated. Set *includeSubDomains* to TRUE or -FALSE. Set *expire* to a date stamp or a zero length string for *forever* -(wrong date stamp format might cause the name to not get accepted) +When this callback is invoked, the *sts* pointer points to a populated struct: +Copy the hostname to *name* (no longer than *namelen* bytes). Make it +null-terminated. Set *includeSubDomains* to TRUE or FALSE. Set *expire* to a +date stamp or a zero length string for *forever* (wrong date stamp format +might cause the name to not get accepted) -The callback should return *CURLSTS_OK* if it returns a name and is -prepared to be called again (for another host) or *CURLSTS_DONE* if it has -no entry to return. It can also return *CURLSTS_FAIL* to signal -error. Returning *CURLSTS_FAIL* stops the transfer from being performed -and make *CURLE_ABORTED_BY_CALLBACK* get returned. +The callback should return *CURLSTS_OK* if it returns a name and is prepared +to be called again (for another host) or *CURLSTS_DONE* if it has no entry to +return. It can also return *CURLSTS_FAIL* to signal error. Returning +*CURLSTS_FAIL* stops the transfer from being performed and make +*CURLE_ABORTED_BY_CALLBACK* get returned. -This option does not enable HSTS, you need to use CURLOPT_HSTS_CTRL(3) to -do that. +This option does not enable HSTS, you need to use CURLOPT_HSTS_CTRL(3) to do +that. + +The hostname provided to libcurl *should not* have a trailing dot nor leading +dot. # DEFAULT @@ -85,7 +88,7 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { struct priv my_stuff; - CURLcode res; + CURLcode result; /* set HSTS read callback */ curl_easy_setopt(curl, CURLOPT_HSTSREADFUNCTION, hsts_cb); @@ -93,7 +96,7 @@ int main(void) /* pass in suitable argument to the callback */ curl_easy_setopt(curl, CURLOPT_HSTSREADDATA, &my_stuff); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_HSTSWRITEFUNCTION.md b/docs/libcurl/opts/CURLOPT_HSTSWRITEFUNCTION.md index 621268ea52..cdbdbe5d26 100644 --- a/docs/libcurl/opts/CURLOPT_HSTSWRITEFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_HSTSWRITEFUNCTION.md @@ -89,7 +89,7 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { struct priv my_stuff; - CURLcode res; + CURLcode result; /* set HSTS read callback */ curl_easy_setopt(curl, CURLOPT_HSTSWRITEFUNCTION, hswr_cb); @@ -97,7 +97,7 @@ int main(void) /* pass in suitable argument to the callback */ curl_easy_setopt(curl, CURLOPT_HSTSWRITEDATA, &my_stuff); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_HSTS_CTRL.md b/docs/libcurl/opts/CURLOPT_HSTS_CTRL.md index 9026dddc15..e432fc4edc 100644 --- a/docs/libcurl/opts/CURLOPT_HSTS_CTRL.md +++ b/docs/libcurl/opts/CURLOPT_HSTS_CTRL.md @@ -23,8 +23,8 @@ CURLOPT_HSTS_CTRL - control HSTS behavior ~~~c #include -#define CURLHSTS_ENABLE (1L<<0) -#define CURLHSTS_READONLYFILE (1L<<1) +#define CURLHSTS_ENABLE (1L << 0) +#define CURLHSTS_READONLYFILE (1L << 1) CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HSTS_CTRL, long bitmask); ~~~ diff --git a/docs/libcurl/opts/CURLOPT_HTTP09_ALLOWED.md b/docs/libcurl/opts/CURLOPT_HTTP09_ALLOWED.md index 585faa275a..400348f4ca 100644 --- a/docs/libcurl/opts/CURLOPT_HTTP09_ALLOWED.md +++ b/docs/libcurl/opts/CURLOPT_HTTP09_ALLOWED.md @@ -45,10 +45,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_HTTP09_ALLOWED, 1L); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_HTTPAUTH.md b/docs/libcurl/opts/CURLOPT_HTTPAUTH.md index b21647eda2..e05c84183a 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPAUTH.md +++ b/docs/libcurl/opts/CURLOPT_HTTPAUTH.md @@ -56,7 +56,7 @@ regular old-fashioned Basic method. HTTP Digest authentication with an IE flavor. Digest authentication is defined in RFC 2617 and is a more secure way to do authentication over public networks -than the regular old-fashioned Basic method. The IE flavor is simply that +than the regular old-fashioned Basic method. The IE flavor means that libcurl uses a special "quirk" that IE is known to have used before version 7 and that some servers require the client to use. @@ -134,12 +134,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* allow whatever auth the server speaks */ curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_easy_setopt(curl, CURLOPT_USERPWD, "james:bond"); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_HTTPHEADER.md b/docs/libcurl/opts/CURLOPT_HTTPHEADER.md index 4440b4ec4b..c7a705bedc 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPHEADER.md +++ b/docs/libcurl/opts/CURLOPT_HTTPHEADER.md @@ -55,6 +55,10 @@ without content (no data on the right side of the colon) as in `Accept:`, the internally used header is removed. To forcibly add a header without content (nothing after the colon), use the form `name;` (using a trailing semicolon). +There are exceptions when suppressing headers. The `Connection:` header in +HTTP/1.1 cannot be overridden. You can provide values for it, but should a +request require specific ones, they are always added to your own. + The headers included in the linked list **must not** be CRLF-terminated, since libcurl adds CRLF after each header item itself. Failure to comply with this might result in strange behavior. libcurl passes on the verbatim strings you diff --git a/docs/libcurl/opts/CURLOPT_HTTPPOST.md b/docs/libcurl/opts/CURLOPT_HTTPPOST.md index e45538b762..526f1d9e41 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPPOST.md +++ b/docs/libcurl/opts/CURLOPT_HTTPPOST.md @@ -61,7 +61,7 @@ int main(void) struct curl_httppost *lastptr; /* Fill in the file upload field. This makes libcurl load data from - the given file name when curl_easy_perform() is called. */ + the given filename when curl_easy_perform() is called. */ curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "sendfile", diff --git a/docs/libcurl/opts/CURLOPT_HTTPPROXYTUNNEL.md b/docs/libcurl/opts/CURLOPT_HTTPPROXYTUNNEL.md index 5a3b7390ae..75815c3cb5 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPPROXYTUNNEL.md +++ b/docs/libcurl/opts/CURLOPT_HTTPPROXYTUNNEL.md @@ -33,8 +33,8 @@ difference between using a proxy and to tunnel through it. Tunneling means that an HTTP CONNECT request is sent to the proxy, asking it to connect to a remote host on a specific port number and then the traffic is -just passed through the proxy. Proxies tend to white-list specific port numbers -it allows CONNECT requests to and often only port 80 and 443 are allowed. +passed through the proxy. Proxies tend to white-list specific port numbers it +allows CONNECT requests to and often only port 80 and 443 are allowed. To suppress proxy CONNECT response headers from user callbacks use CURLOPT_SUPPRESS_CONNECT_HEADERS(3). diff --git a/docs/libcurl/opts/CURLOPT_HTTP_CONTENT_DECODING.md b/docs/libcurl/opts/CURLOPT_HTTP_CONTENT_DECODING.md index 10ca0c5100..f56e55f184 100644 --- a/docs/libcurl/opts/CURLOPT_HTTP_CONTENT_DECODING.md +++ b/docs/libcurl/opts/CURLOPT_HTTP_CONTENT_DECODING.md @@ -46,10 +46,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, 0L); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_HTTP_TRANSFER_DECODING.md b/docs/libcurl/opts/CURLOPT_HTTP_TRANSFER_DECODING.md index ea59ad2c16..15444a2e96 100644 --- a/docs/libcurl/opts/CURLOPT_HTTP_TRANSFER_DECODING.md +++ b/docs/libcurl/opts/CURLOPT_HTTP_TRANSFER_DECODING.md @@ -44,10 +44,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_HTTP_TRANSFER_DECODING, 0L); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_HTTP_VERSION.md b/docs/libcurl/opts/CURLOPT_HTTP_VERSION.md index dd791b81cb..f865955d48 100644 --- a/docs/libcurl/opts/CURLOPT_HTTP_VERSION.md +++ b/docs/libcurl/opts/CURLOPT_HTTP_VERSION.md @@ -31,9 +31,9 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HTTP_VERSION, long version); Pass *version* a long, set to one of the values described below. They ask libcurl to use the specific HTTP versions. -Note that the HTTP version is just a request. libcurl still prioritizes to -reuse existing connections so it might then reuse a connection using an HTTP -version you have not asked for. +Note that the HTTP version is a request. libcurl still prioritizes to reuse +existing connections so it might then reuse a connection using an HTTP version +you have not asked for. ## CURL_HTTP_VERSION_NONE @@ -51,27 +51,27 @@ Enforce HTTP 1.1 requests. ## CURL_HTTP_VERSION_2_0 Attempt HTTP 2 requests. libcurl falls back to HTTP 1.1 if HTTP 2 cannot be -negotiated with the server. (Added in 7.33.0) +negotiated with the server. When libcurl uses HTTP/2 over HTTPS, it does not itself insist on TLS 1.2 or higher even though that is required by the specification. A user can add this version requirement with CURLOPT_SSLVERSION(3). -The alias *CURL_HTTP_VERSION_2* was added in 7.43.0 to better reflect the -actual protocol name. +The alias *CURL_HTTP_VERSION_2* was added to better reflect the actual +protocol name. ## CURL_HTTP_VERSION_2TLS Attempt HTTP 2 over TLS (HTTPS) only. libcurl falls back to HTTP 1.1 if HTTP 2 cannot be negotiated with the HTTPS server. For clear text HTTP servers, -libcurl uses 1.1. (Added in 7.47.0) +libcurl uses 1.1. ## CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE Issue non-TLS HTTP requests using HTTP/2 without HTTP/1.1 Upgrade. It requires prior knowledge that the server supports HTTP/2 straight away. HTTPS requests still do HTTP/2 the standard way with negotiated protocol version in the TLS -handshake. (Added in 7.49.0) +handshake. Since 8.10.0 if this option is set for an HTTPS request then the application layer protocol version (ALPN) offered to the server is only HTTP/2. Prior to @@ -105,11 +105,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS); - ret = curl_easy_perform(curl); - if(ret == CURLE_HTTP_RETURNED_ERROR) { + result = curl_easy_perform(curl); + if(result == CURLE_HTTP_RETURNED_ERROR) { /* an HTTP response error problem */ } } diff --git a/docs/libcurl/opts/CURLOPT_INTERFACE.md b/docs/libcurl/opts/CURLOPT_INTERFACE.md index d58398383b..dc966aad19 100644 --- a/docs/libcurl/opts/CURLOPT_INTERFACE.md +++ b/docs/libcurl/opts/CURLOPT_INTERFACE.md @@ -67,12 +67,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_INTERFACE, "eth0"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_INTERLEAVEDATA.md b/docs/libcurl/opts/CURLOPT_INTERLEAVEDATA.md index 27d8bf8c06..9bd1739202 100644 --- a/docs/libcurl/opts/CURLOPT_INTERLEAVEDATA.md +++ b/docs/libcurl/opts/CURLOPT_INTERLEAVEDATA.md @@ -60,7 +60,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_INTERLEAVEDATA, &rtp_data); curl_easy_perform(curl); - } + } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_IOCTLFUNCTION.md b/docs/libcurl/opts/CURLOPT_IOCTLFUNCTION.md index 29dc8bb4ff..939161bee0 100644 --- a/docs/libcurl/opts/CURLOPT_IOCTLFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_IOCTLFUNCTION.md @@ -28,7 +28,7 @@ typedef enum { CURLIOE_LAST /* never use */ } curlioerr; -typedef enum { +typedef enum { CURLIOCMD_NOP, /* no operation */ CURLIOCMD_RESTARTREAD, /* restart the read stream from start */ CURLIOCMD_LAST /* never use */ @@ -50,15 +50,15 @@ rewinding the read data stream is the only action it can request. The rewinding of the read data stream may be necessary when doing an HTTP PUT or POST with a multi-pass authentication method. -The callback MUST return *CURLIOE_UNKNOWNCMD* if the input *cmd* is -not *CURLIOCMD_RESTARTREAD*. +The callback **must** return *CURLIOE_UNKNOWNCMD* if the input *cmd* is not +*CURLIOCMD_RESTARTREAD*. -The *clientp* argument to the callback is set with the -CURLOPT_IOCTLDATA(3) option. +The *clientp* argument to the callback is set with the CURLOPT_IOCTLDATA(3) +option. **This option is deprecated**. Do not use it. Use CURLOPT_SEEKFUNCTION(3) -instead to provide seeking. If CURLOPT_SEEKFUNCTION(3) is set, this -parameter is ignored when seeking. +instead to provide seeking. If CURLOPT_SEEKFUNCTION(3) is set, this parameter +is ignored when seeking. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_IPRESOLVE.md b/docs/libcurl/opts/CURLOPT_IPRESOLVE.md index 9d7bee3150..180546a9df 100644 --- a/docs/libcurl/opts/CURLOPT_IPRESOLVE.md +++ b/docs/libcurl/opts/CURLOPT_IPRESOLVE.md @@ -62,13 +62,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); /* of all addresses example.com resolves to, only IPv6 ones are used */ curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_ISSUERCERT.md b/docs/libcurl/opts/CURLOPT_ISSUERCERT.md index 0dc4c2243c..1e10b1bbb2 100644 --- a/docs/libcurl/opts/CURLOPT_ISSUERCERT.md +++ b/docs/libcurl/opts/CURLOPT_ISSUERCERT.md @@ -43,8 +43,8 @@ not considered as failure. A specific error code (CURLE_SSL_ISSUER_ERROR) is defined with the option, which is returned if the setup of the SSL/TLS session has failed due to a -mismatch with the issuer of peer certificate (CURLOPT_SSL_VERIFYPEER(3) -has to be set too for the check to fail). (Added in 7.19.0) +mismatch with the issuer of peer certificate (CURLOPT_SSL_VERIFYPEER(3) has to +be set too for the check to fail). Using this option multiple times makes the last set string override the previous ones. Set it to NULL to disable its use again. @@ -65,10 +65,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_ISSUERCERT, "/etc/certs/cacert.pem"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_ISSUERCERT_BLOB.md b/docs/libcurl/opts/CURLOPT_ISSUERCERT_BLOB.md index e347f6ca9f..c000353619 100644 --- a/docs/libcurl/opts/CURLOPT_ISSUERCERT_BLOB.md +++ b/docs/libcurl/opts/CURLOPT_ISSUERCERT_BLOB.md @@ -72,14 +72,14 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; struct curl_blob blob; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); blob.data = certificateData; blob.len = filesize; blob.flags = CURL_BLOB_COPY; curl_easy_setopt(curl, CURLOPT_ISSUERCERT_BLOB, &blob); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_KEEP_SENDING_ON_ERROR.md b/docs/libcurl/opts/CURLOPT_KEEP_SENDING_ON_ERROR.md index 5b43545e0f..eeb4704dd2 100644 --- a/docs/libcurl/opts/CURLOPT_KEEP_SENDING_ON_ERROR.md +++ b/docs/libcurl/opts/CURLOPT_KEEP_SENDING_ON_ERROR.md @@ -51,11 +51,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "sending data"); curl_easy_setopt(curl, CURLOPT_KEEP_SENDING_ON_ERROR, 1L); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_KEYPASSWD.md b/docs/libcurl/opts/CURLOPT_KEYPASSWD.md index 0b7ded9c70..d97a947164 100644 --- a/docs/libcurl/opts/CURLOPT_KEYPASSWD.md +++ b/docs/libcurl/opts/CURLOPT_KEYPASSWD.md @@ -55,12 +55,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_SSLCERT, "client.pem"); curl_easy_setopt(curl, CURLOPT_SSLKEY, "key.pem"); curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "superman"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_KRBLEVEL.md b/docs/libcurl/opts/CURLOPT_KRBLEVEL.md index bdea064600..cfc728ccd6 100644 --- a/docs/libcurl/opts/CURLOPT_KRBLEVEL.md +++ b/docs/libcurl/opts/CURLOPT_KRBLEVEL.md @@ -5,7 +5,6 @@ Title: CURLOPT_KRBLEVEL Section: 3 Source: libcurl See-also: - - CURLOPT_KRBLEVEL (3) - CURLOPT_USE_SSL (3) Protocol: - FTP @@ -26,11 +25,13 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_KRBLEVEL, char *level); # DESCRIPTION +Deprecated. It serves no purpose anymore. + Pass a char pointer as parameter. Set the kerberos security level for FTP; this also enables kerberos awareness. This is a string that should match one -of the following: &'clear', &'safe', &'confidential' or &'private'. If the -string is set but does not match one of these, 'private' is used. Set the -string to NULL to disable kerberos support for FTP. +of the following: `clear`, `safe`, `confidential` or `private`. If the string +is set but does not match one of these, `private` is used. Set the string to +NULL to disable kerberos support for FTP. The application does not have to keep the string around after setting this option. @@ -51,10 +52,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_KRBLEVEL, "private"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } @@ -62,8 +63,14 @@ int main(void) # HISTORY +Functionality removed in 8.17.0 + This option was known as CURLOPT_KRB4LEVEL up to 7.16.3 +# DEPRECATED + +Deprecated since 8.17.0 + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_LOCALPORT.md b/docs/libcurl/opts/CURLOPT_LOCALPORT.md index ef71d5373b..806de6e9ac 100644 --- a/docs/libcurl/opts/CURLOPT_LOCALPORT.md +++ b/docs/libcurl/opts/CURLOPT_LOCALPORT.md @@ -45,12 +45,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_LOCALPORT, 49152L); /* and try 20 more ports following that */ curl_easy_setopt(curl, CURLOPT_LOCALPORTRANGE, 20L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_LOCALPORTRANGE.md b/docs/libcurl/opts/CURLOPT_LOCALPORTRANGE.md index a3a33ff108..da3ef5ae1f 100644 --- a/docs/libcurl/opts/CURLOPT_LOCALPORTRANGE.md +++ b/docs/libcurl/opts/CURLOPT_LOCALPORTRANGE.md @@ -48,12 +48,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_LOCALPORT, 49152L); /* and try 20 more ports following that */ curl_easy_setopt(curl, CURLOPT_LOCALPORTRANGE, 20L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_LOGIN_OPTIONS.md b/docs/libcurl/opts/CURLOPT_LOGIN_OPTIONS.md index 9d41a3fa74..80dabd7027 100644 --- a/docs/libcurl/opts/CURLOPT_LOGIN_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_LOGIN_OPTIONS.md @@ -51,6 +51,15 @@ option. Using this option multiple times makes the last set string override the previous ones. Set it to NULL to disable its use again. +CURLOPT_LOGIN_OPTIONS(3) is a *login property*, it does not change the +security context. This means that this option changes how the login happens +when a connection is created, but it does not affect which connections libcurl +can reuse. libcurl may reuse a connection that was set up with a different +options string; a different options string does not by itself prevent reuse. +Connection reuse still depends on other connection properties matching, such +as the protocol, hostname, port number, credentials and other settings that +affect the connection. + # DEFAULT NULL @@ -64,10 +73,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "smtp://example.com/"); curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, "AUTH=*"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_LOW_SPEED_LIMIT.md b/docs/libcurl/opts/CURLOPT_LOW_SPEED_LIMIT.md index 97c2654604..7a5f094b77 100644 --- a/docs/libcurl/opts/CURLOPT_LOW_SPEED_LIMIT.md +++ b/docs/libcurl/opts/CURLOPT_LOW_SPEED_LIMIT.md @@ -47,13 +47,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* abort if slower than 30 bytes/sec during 60 seconds */ curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 60L); curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 30L); - res = curl_easy_perform(curl); - if(CURLE_OPERATION_TIMEDOUT == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OPERATION_TIMEDOUT) { printf("Timeout.\n"); } /* always cleanup */ diff --git a/docs/libcurl/opts/CURLOPT_LOW_SPEED_TIME.md b/docs/libcurl/opts/CURLOPT_LOW_SPEED_TIME.md index d478e753ce..a27e2f0c5d 100644 --- a/docs/libcurl/opts/CURLOPT_LOW_SPEED_TIME.md +++ b/docs/libcurl/opts/CURLOPT_LOW_SPEED_TIME.md @@ -44,13 +44,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* abort if slower than 30 bytes/sec during 60 seconds */ curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 60L); curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 30L); - res = curl_easy_perform(curl); - if(CURLE_OPERATION_TIMEDOUT == res) { + result = curl_easy_perform(curl); + if(result == CURLE_OPERATION_TIMEDOUT) { printf("Timeout.\n"); } /* always cleanup */ diff --git a/docs/libcurl/opts/CURLOPT_MAIL_AUTH.md b/docs/libcurl/opts/CURLOPT_MAIL_AUTH.md index 8643c892e3..3a0263a177 100644 --- a/docs/libcurl/opts/CURLOPT_MAIL_AUTH.md +++ b/docs/libcurl/opts/CURLOPT_MAIL_AUTH.md @@ -38,9 +38,8 @@ and the AUTH address is not known or is invalid, then an empty string should be used for this parameter. Unlike CURLOPT_MAIL_FROM(3) and CURLOPT_MAIL_RCPT(3), the address should not -be specified within a pair of angled brackets (\<\>). However, if an empty -string is used then a pair of brackets are sent by libcurl as required by RFC -2554. +be specified within a pair of angled brackets (\<\>). If an empty string is +used, a pair of brackets is sent by libcurl as required by RFC 2554. The application does not have to keep the string around after setting this option. @@ -61,10 +60,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "smtp://example.com/"); curl_easy_setopt(curl, CURLOPT_MAIL_AUTH, ""); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_MAIL_FROM.md b/docs/libcurl/opts/CURLOPT_MAIL_FROM.md index aa5656e413..fe6968f368 100644 --- a/docs/libcurl/opts/CURLOPT_MAIL_FROM.md +++ b/docs/libcurl/opts/CURLOPT_MAIL_FROM.md @@ -57,10 +57,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "smtp://example.com/"); curl_easy_setopt(curl, CURLOPT_MAIL_FROM, "president@example.com"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_MAIL_RCPT.md b/docs/libcurl/opts/CURLOPT_MAIL_RCPT.md index 99ee600f58..49dd618fae 100644 --- a/docs/libcurl/opts/CURLOPT_MAIL_RCPT.md +++ b/docs/libcurl/opts/CURLOPT_MAIL_RCPT.md @@ -36,8 +36,8 @@ libcurl does not copy the list, it needs to be kept around until after the transfer has completed. When performing a mail transfer, each recipient should be specified within a -pair of angled brackets (\<\>), however, should you not use an angled bracket -as the first character libcurl assumes you provided a single email address and +pair of angled brackets (\<\>). Should you not use an angled bracket as the +first character, libcurl assumes you provided a single email address and encloses that address within brackets for you. In order to specify DSN parameters (as per RFC 3461), the address has to be @@ -67,14 +67,14 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; struct curl_slist *list; list = curl_slist_append(NULL, "root@localhost"); list = curl_slist_append(list, "person@example.com"); list = curl_slist_append(list, " NOTIFY=SUCCESS"); curl_easy_setopt(curl, CURLOPT_URL, "smtp://example.com/"); curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, list); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_slist_free_all(list); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_MAIL_RCPT_ALLOWFAILS.md b/docs/libcurl/opts/CURLOPT_MAIL_RCPT_ALLOWFAILS.md index eae7c3f51c..d87ba09bd7 100644 --- a/docs/libcurl/opts/CURLOPT_MAIL_RCPT_ALLOWFAILS.md +++ b/docs/libcurl/opts/CURLOPT_MAIL_RCPT_ALLOWFAILS.md @@ -55,7 +55,7 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { struct curl_slist *list; - CURLcode res; + CURLcode result; /* Adding one valid and one invalid email address */ list = curl_slist_append(NULL, "person@example.com"); @@ -64,7 +64,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "smtp://example.com/"); curl_easy_setopt(curl, CURLOPT_MAIL_RCPT_ALLOWFAILS, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_slist_free_all(list); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_MAXCONNECTS.md b/docs/libcurl/opts/CURLOPT_MAXCONNECTS.md index 80a4f8b326..96df2614a1 100644 --- a/docs/libcurl/opts/CURLOPT_MAXCONNECTS.md +++ b/docs/libcurl/opts/CURLOPT_MAXCONNECTS.md @@ -58,11 +58,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* limit the connection cache for this handle to no more than 3 */ curl_easy_setopt(curl, CURLOPT_MAXCONNECTS, 3L); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md b/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md index 0dd9d7922a..17c4c9f4e1 100644 --- a/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md +++ b/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md @@ -41,6 +41,9 @@ If you want a limit above 2GB, use CURLOPT_MAXFILESIZE_LARGE(3). Since 8.4.0, this option also stops ongoing transfers if they reach this threshold. +Since 8.20.0, this option also stops ongoing transfers that would reach this +threshold due to automatic decompression using CURLOPT_ACCEPT_ENCODING(3). + # DEFAULT 0, meaning disabled. @@ -54,11 +57,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* refuse to download if larger than 1000 bytes */ curl_easy_setopt(curl, CURLOPT_MAXFILESIZE, 1000L); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.md b/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.md index e58b8195e6..791b25862f 100644 --- a/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.md +++ b/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.md @@ -42,6 +42,9 @@ ends up being larger than this given limit. Since 8.4.0, this option also stops ongoing transfers if they reach this threshold. +Since 8.20.0, this option also stops ongoing transfers that would reach this +threshold due to automatic decompression using CURLOPT_ACCEPT_ENCODING(3). + # DEFAULT 0, meaning disabled. @@ -55,12 +58,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_off_t ridiculous = (curl_off_t)1 << 48; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* refuse to download if larger than ridiculous */ curl_easy_setopt(curl, CURLOPT_MAXFILESIZE_LARGE, ridiculous); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_MAXLIFETIME_CONN.md b/docs/libcurl/opts/CURLOPT_MAXLIFETIME_CONN.md index b29e61a2c3..8c1fbf82f8 100644 --- a/docs/libcurl/opts/CURLOPT_MAXLIFETIME_CONN.md +++ b/docs/libcurl/opts/CURLOPT_MAXLIFETIME_CONN.md @@ -35,16 +35,18 @@ connection to have to be considered for reuse for this request. libcurl features a connection cache that holds previously used connections. When a new request is to be done, libcurl considers any connection that -matches for reuse. The CURLOPT_MAXLIFETIME_CONN(3) limit prevents -libcurl from trying too old connections for reuse. This can be used for -client-side load balancing. If a connection is found in the cache that is -older than this set *maxlifetime*, it is instead marked for closure. +matches for reuse. The CURLOPT_MAXLIFETIME_CONN(3) limit prevents libcurl from +trying too old connections for reuse. This can be used for client-side load +balancing. If a connection is found in the cache that is older than this set +*maxlifetime*, it is instead marked for closure. -If set to 0, this behavior is disabled: all connections are eligible for reuse. +If set to 0, this behavior is disabled: all connections are eligible for +reuse. # DEFAULT -0 seconds (i.e., disabled) +24 hours (since 8.17.0). Before that, the default was 0 seconds (i.e., +disabled) # %PROTOCOLS% diff --git a/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.md b/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.md index 2ad3d49b96..9a9117669d 100644 --- a/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.md +++ b/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.md @@ -53,11 +53,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* cap the download speed to 31415 bytes/sec */ curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, (curl_off_t)31415); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md b/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md index 545fec9a0d..34566ece0d 100644 --- a/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md +++ b/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md @@ -54,12 +54,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* cap the upload speed to 1000 bytes/sec */ curl_easy_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE, (curl_off_t)1000); /* (set some upload options as well) */ - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_NETRC.md b/docs/libcurl/opts/CURLOPT_NETRC.md index aba4ed2ad9..1e3dbb1dde 100644 --- a/docs/libcurl/opts/CURLOPT_NETRC.md +++ b/docs/libcurl/opts/CURLOPT_NETRC.md @@ -50,6 +50,11 @@ and similar things are not supported). The netrc file provides credentials for a hostname independent of which protocol and port number that are used. +When providing a username in the URL and a *.netrc* file, libcurl looks for +and uses the password for that specific user for the given host if such an +entry appears in the file before a "generic" `machine` entry without `login` +specified. + libcurl does not verify that the file has the correct properties set (as the standard Unix ftp client does). It should only be readable by user. @@ -131,10 +136,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/"); curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_NETRC_FILE.md b/docs/libcurl/opts/CURLOPT_NETRC_FILE.md index 8863415910..dba6f85007 100644 --- a/docs/libcurl/opts/CURLOPT_NETRC_FILE.md +++ b/docs/libcurl/opts/CURLOPT_NETRC_FILE.md @@ -51,11 +51,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/"); curl_easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); curl_easy_setopt(curl, CURLOPT_NETRC_FILE, "/tmp/magic-netrc"); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_NEW_DIRECTORY_PERMS.md b/docs/libcurl/opts/CURLOPT_NEW_DIRECTORY_PERMS.md index 7c2c13fd00..cb6731912c 100644 --- a/docs/libcurl/opts/CURLOPT_NEW_DIRECTORY_PERMS.md +++ b/docs/libcurl/opts/CURLOPT_NEW_DIRECTORY_PERMS.md @@ -48,12 +48,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "sftp://upload.example.com/newdir/file.zip"); curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L); curl_easy_setopt(curl, CURLOPT_NEW_DIRECTORY_PERMS, 0644L); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_NEW_FILE_PERMS.md b/docs/libcurl/opts/CURLOPT_NEW_FILE_PERMS.md index 7252ac5956..4c9579c826 100644 --- a/docs/libcurl/opts/CURLOPT_NEW_FILE_PERMS.md +++ b/docs/libcurl/opts/CURLOPT_NEW_FILE_PERMS.md @@ -46,10 +46,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "sftp://upload.example.com/file.txt"); curl_easy_setopt(curl, CURLOPT_NEW_FILE_PERMS, 0664L); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_NOBODY.md b/docs/libcurl/opts/CURLOPT_NOBODY.md index 8e772a026f..0f4c2cb990 100644 --- a/docs/libcurl/opts/CURLOPT_NOBODY.md +++ b/docs/libcurl/opts/CURLOPT_NOBODY.md @@ -31,8 +31,8 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_NOBODY, long opt); A long parameter set to 1 tells libcurl to not include the body-part in the output when doing what would otherwise be a download. For HTTP(S), this makes -libcurl do a HEAD request. For most other protocols it means just not asking -to transfer the body data. +libcurl do a HEAD request. For most other protocols it means not asking to +transfer the body data. For HTTP operations when CURLOPT_NOBODY(3) has been set, disabling this option (with 0) makes it a GET again - only if the method is still set to be diff --git a/docs/libcurl/opts/CURLOPT_NOPROXY.md b/docs/libcurl/opts/CURLOPT_NOPROXY.md index 117cf09be8..087a49f5b9 100644 --- a/docs/libcurl/opts/CURLOPT_NOPROXY.md +++ b/docs/libcurl/opts/CURLOPT_NOPROXY.md @@ -43,10 +43,13 @@ brackets: "example.com,::1,localhost" -Since 7.86.0, IP addresses specified to this option can be provided using CIDR -notation: an appended slash and number specifies the number of "network bits" -out of the address to use in the comparison. For example "192.168.0.0/16" -would match all addresses starting with "192.168". +IP addresses specified to this option can be provided using CIDR notation: an +appended slash and number specifies the number of "network bits" out of the +address to use in the comparison. For example "192.168.0.0/16" would match all +addresses starting with "192.168". + +To use international hostnames in this list, add the punycode version of the +hostname. The application does not have to keep the string around after setting this option. @@ -76,14 +79,18 @@ int main(void) /* accept various URLs */ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* use this proxy */ - curl_easy_setopt(curl, CURLOPT_PROXY, "http://proxy:80"); - /* ... but make sure this host name is not proxied */ + curl_easy_setopt(curl, CURLOPT_PROXY, "http://proxy.example:80"); + /* ... but make sure this hostname is not proxied */ curl_easy_setopt(curl, CURLOPT_NOPROXY, "www.example.com"); curl_easy_perform(curl); } } ~~~ +# HISTORY + +CIDR format support was added in 7.86.0. + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_NOSIGNAL.md b/docs/libcurl/opts/CURLOPT_NOSIGNAL.md index 27886ff65e..c89b01465b 100644 --- a/docs/libcurl/opts/CURLOPT_NOSIGNAL.md +++ b/docs/libcurl/opts/CURLOPT_NOSIGNAL.md @@ -56,12 +56,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_OPENSOCKETDATA.md b/docs/libcurl/opts/CURLOPT_OPENSOCKETDATA.md index 2962994b2a..2bcdee3b66 100644 --- a/docs/libcurl/opts/CURLOPT_OPENSOCKETDATA.md +++ b/docs/libcurl/opts/CURLOPT_OPENSOCKETDATA.md @@ -56,7 +56,6 @@ static curl_socket_t opensocket(void *clientp, static int sockopt_callback(void *clientp, curl_socket_t curlfd, curlsocktype purpose) { - /* This return code was added in libcurl 7.21.5 */ return CURL_SOCKOPT_ALREADY_CONNECTED; } @@ -64,7 +63,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; extern int sockfd; /* the already connected one */ /* libcurl thinks that you connect to the host @@ -77,7 +76,7 @@ int main(void) /* call this function to set options for the socket */ curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_callback); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_OPENSOCKETFUNCTION.md b/docs/libcurl/opts/CURLOPT_OPENSOCKETFUNCTION.md index b8517c4f32..568fb11b94 100644 --- a/docs/libcurl/opts/CURLOPT_OPENSOCKETFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_OPENSOCKETFUNCTION.md @@ -22,7 +22,7 @@ CURLOPT_OPENSOCKETFUNCTION - callback for opening socket ~~~c #include -typedef enum { +typedef enum { CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ } curlsocktype; @@ -80,7 +80,7 @@ signal that it already is connected. The equivalent of this: ~~~c - return socket(addr->family, addr->socktype, addr->protocol); + return socket(addr->family, addr->socktype, addr->protocol); ~~~ # %PROTOCOLS% @@ -104,7 +104,6 @@ static curl_socket_t opensocket(void *clientp, static int sockopt_callback(void *clientp, curl_socket_t curlfd, curlsocktype purpose) { - /* This return code was added in libcurl 7.21.5 */ return CURL_SOCKOPT_ALREADY_CONNECTED; } @@ -112,7 +111,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; extern int sockfd; /* the already connected one */ /* libcurl thinks that you connect to the host * and port that you specify in the URL option. */ @@ -124,7 +123,7 @@ int main(void) /* call this function to set options for the socket */ curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_callback); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_PASSWORD.md b/docs/libcurl/opts/CURLOPT_PASSWORD.md index 26bbf32d39..f8b9ee752d 100644 --- a/docs/libcurl/opts/CURLOPT_PASSWORD.md +++ b/docs/libcurl/opts/CURLOPT_PASSWORD.md @@ -53,12 +53,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "qwerty"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md b/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md index c868ed86e6..6eb5d0ee3e 100644 --- a/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md +++ b/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md @@ -93,7 +93,7 @@ server's certificate. # Windows-specific: # - Use NUL instead of /dev/null. # - OpenSSL may wait for input instead of disconnecting. Hit enter. -# - If you do not have sed, then just copy the certificate into a file: +# - If you do not have sed, then copy the certificate into a file: # Lines from -----BEGIN CERTIFICATE----- to -----END CERTIFICATE-----. # openssl s_client -servername www.example.com -connect www.example.com:443 \ diff --git a/docs/libcurl/opts/CURLOPT_PORT.md b/docs/libcurl/opts/CURLOPT_PORT.md index e452cb54f7..334f01b3d7 100644 --- a/docs/libcurl/opts/CURLOPT_PORT.md +++ b/docs/libcurl/opts/CURLOPT_PORT.md @@ -34,10 +34,10 @@ This option sets *number* to be the remote port number to connect to, instead of the one specified in the URL or the default port for the used protocol. -Usually, you just let the URL decide which port to use but this allows the +Usually, you let the URL decide which port to use but this allows the application to override that. -While this option accepts a 'long', a port number is an unsigned 16 bit number +While this option accepts a 'long', a port number is an unsigned 16-bit number and therefore using a port number lower than zero or over 65535 causes a **CURLE_BAD_FUNCTION_ARGUMENT** error. @@ -55,10 +55,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_PORT, 8080L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_POST.md b/docs/libcurl/opts/CURLOPT_POST.md index c7850f3aed..1bc634a04f 100644 --- a/docs/libcurl/opts/CURLOPT_POST.md +++ b/docs/libcurl/opts/CURLOPT_POST.md @@ -41,7 +41,7 @@ you must make sure to not set CURLOPT_POSTFIELDS(3) to anything but NULL. When providing data with a callback, you must transmit it using chunked transfer-encoding or you must set the size of the data with the CURLOPT_POSTFIELDSIZE(3) or CURLOPT_POSTFIELDSIZE_LARGE(3) -options. To enable chunked encoding, you simply pass in the appropriate +options. To enable chunked encoding, pass in the appropriate Transfer-Encoding header, see the post-callback.c example. You can override the default POST Content-Type: header by setting your own @@ -52,10 +52,10 @@ You can disable this header with CURLOPT_HTTPHEADER(3) as usual. If you use POST to an HTTP 1.1 server, you can send data without knowing the size before starting the POST if you use chunked encoding. You enable this by -adding a header like "Transfer-Encoding: chunked" with -CURLOPT_HTTPHEADER(3). With HTTP 1.0 or without chunked transfer, you -must specify the size in the request. (Since 7.66.0, libcurl automatically -uses chunked encoding for POSTs if the size is unknown.) +adding a header like "Transfer-Encoding: chunked" with CURLOPT_HTTPHEADER(3). +With HTTP 1.0 or without chunked transfer, you must specify the size in the +request. libcurl automatically uses chunked encoding for POSTs if the size is +unknown. When setting CURLOPT_POST(3) to 1, libcurl automatically sets CURLOPT_NOBODY(3) and CURLOPT_HTTPGET(3) to 0. @@ -81,13 +81,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_POST, 1L); /* set up the read callback with CURLOPT_READFUNCTION */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_POSTFIELDSIZE.md b/docs/libcurl/opts/CURLOPT_POSTFIELDSIZE.md index 5871e91d9b..10414e7eea 100644 --- a/docs/libcurl/opts/CURLOPT_POSTFIELDSIZE.md +++ b/docs/libcurl/opts/CURLOPT_POSTFIELDSIZE.md @@ -9,6 +9,8 @@ See-also: - CURLOPT_POSTFIELDSIZE_LARGE (3) Protocol: - HTTP + - MQTT + - RTSP Added-in: 7.2 --- diff --git a/docs/libcurl/opts/CURLOPT_POSTFIELDSIZE_LARGE.md b/docs/libcurl/opts/CURLOPT_POSTFIELDSIZE_LARGE.md index 16e874b667..e3d983e253 100644 --- a/docs/libcurl/opts/CURLOPT_POSTFIELDSIZE_LARGE.md +++ b/docs/libcurl/opts/CURLOPT_POSTFIELDSIZE_LARGE.md @@ -10,6 +10,8 @@ See-also: - CURLOPT_POSTFIELDSIZE (3) Protocol: - HTTP + - MQTT + - RTSP Added-in: 7.11.1 --- diff --git a/docs/libcurl/opts/CURLOPT_POSTQUOTE.md b/docs/libcurl/opts/CURLOPT_POSTQUOTE.md index 0457f490db..55ef81aabc 100644 --- a/docs/libcurl/opts/CURLOPT_POSTQUOTE.md +++ b/docs/libcurl/opts/CURLOPT_POSTQUOTE.md @@ -56,13 +56,13 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/foo.bin"); /* pass in the FTP commands to run after the transfer */ curl_easy_setopt(curl, CURLOPT_POSTQUOTE, cmdlist); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_POSTREDIR.md b/docs/libcurl/opts/CURLOPT_POSTREDIR.md index efabddcefd..521d770a5e 100644 --- a/docs/libcurl/opts/CURLOPT_POSTREDIR.md +++ b/docs/libcurl/opts/CURLOPT_POSTREDIR.md @@ -32,18 +32,21 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_POSTREDIR, Pass a bitmask to control how libcurl acts on redirects after POSTs that get a 301, 302 or 303 response back. A parameter with bit 0 set (value -**CURL_REDIR_POST_301**) tells the library to respect RFC 7231 (section -6.4.2 to 6.4.4) and not convert POST requests into GET requests when following -a 301 redirection. Setting bit 1 (value **CURL_REDIR_POST_302**) makes -libcurl maintain the request method after a 302 redirect whilst setting bit 2 -(value **CURL_REDIR_POST_303**) makes libcurl maintain the request method -after a 303 redirect. The value **CURL_REDIR_POST_ALL** is a convenience -define that sets all three bits. +**CURL_REDIR_POST_301**) tells the library to not convert POST requests into +GET requests when following a 301 redirection. Setting bit 1 (value +**CURL_REDIR_POST_302**) makes libcurl maintain the request method after a 302 +redirect whilst setting bit 2 (value **CURL_REDIR_POST_303**) makes libcurl +maintain the request method after a 303 redirect. The value +**CURL_REDIR_POST_ALL** is a convenience define that sets all three bits. The non-RFC behavior is ubiquitous in web browsers, so the library does the -conversion by default to maintain consistency. However, a server may require a -POST to remain a POST after such a redirection. This option is meaningful only -when setting CURLOPT_FOLLOWLOCATION(3). +conversion by default to maintain consistency. A server may require a POST to +remain a POST after such a redirection. This option is meaningful only when +setting CURLOPT_FOLLOWLOCATION(3). + +This option affects transfers where libcurl has been told to use HTTP POST +using for example CURLOPT_POST(3) or CURLOPT_MIMEPOST(3) and not if the +method has merely been modified with CURLOPT_CUSTOMREQUEST(3). # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_PREQUOTE.md b/docs/libcurl/opts/CURLOPT_PREQUOTE.md index c570e57731..79cf9fee80 100644 --- a/docs/libcurl/opts/CURLOPT_PREQUOTE.md +++ b/docs/libcurl/opts/CURLOPT_PREQUOTE.md @@ -57,13 +57,13 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/foo.bin"); /* pass in the FTP commands to run */ curl_easy_setopt(curl, CURLOPT_PREQUOTE, cmdlist); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_PROGRESSDATA.md b/docs/libcurl/opts/CURLOPT_PROGRESSDATA.md index d86210b3e2..c8f59321ca 100644 --- a/docs/libcurl/opts/CURLOPT_PROGRESSDATA.md +++ b/docs/libcurl/opts/CURLOPT_PROGRESSDATA.md @@ -63,7 +63,7 @@ int main(void) if(curl) { struct progress data; - /* pass struct to callback */ + /* pass struct to callback */ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &data); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback); diff --git a/docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md b/docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md index 5334edbe22..47718002da 100644 --- a/docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_PROGRESSFUNCTION.md @@ -109,7 +109,7 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { - /* pass struct to callback */ + /* pass struct to callback */ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &data); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback); diff --git a/docs/libcurl/opts/CURLOPT_PROTOCOLS.md b/docs/libcurl/opts/CURLOPT_PROTOCOLS.md index 37d1e5fe3e..0d5fca78af 100644 --- a/docs/libcurl/opts/CURLOPT_PROTOCOLS.md +++ b/docs/libcurl/opts/CURLOPT_PROTOCOLS.md @@ -35,8 +35,10 @@ Pass a long that holds a bitmask of protocol bits. If used, this bitmask limits what protocols libcurl may use in the transfer. This allows you to have a libcurl built to support a wide range of protocols but still limit specific transfers to only be allowed to use a subset of them. By default libcurl -accepts all protocols it supports (*CURLPROTO_ALL*). See also -CURLOPT_REDIR_PROTOCOLS(3). +accepts all protocols it supports. See also CURLOPT_REDIR_PROTOCOLS(3). + +Note that the old define *CURLPROTO_ALL* no longer enables all protocols a +modern curl might support! These are the available protocol defines: ~~~c @@ -51,6 +53,7 @@ CURLPROTO_IMAP CURLPROTO_IMAPS CURLPROTO_LDAP CURLPROTO_LDAPS +CURLPROTO_MQTT CURLPROTO_POP3 CURLPROTO_POP3S CURLPROTO_RTMP diff --git a/docs/libcurl/opts/CURLOPT_PROTOCOLS_STR.md b/docs/libcurl/opts/CURLOPT_PROTOCOLS_STR.md index 8545208510..7044dc8d81 100644 --- a/docs/libcurl/opts/CURLOPT_PROTOCOLS_STR.md +++ b/docs/libcurl/opts/CURLOPT_PROTOCOLS_STR.md @@ -42,8 +42,8 @@ set, it returns error. These are the available protocols: DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, -MQTT, POP3, POP3S, RTMP, RTMPE, RTMPS, RTMPT, RTMPTE, RTMPTS, RTSP, SCP, SFTP, -SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS, WSS +MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, +TFTP, WS, WSS You can set "ALL" as a short-cut to enable all protocols. Note that by setting all, you may enable protocols that were not supported the day you write this @@ -81,6 +81,10 @@ int main(int argc, char **argv) } ~~~ +# HISTORY + +RTMP and its related protocol schemes are not supported since curl 8.20.0 + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_PROXY.md b/docs/libcurl/opts/CURLOPT_PROXY.md index 310e123e3d..dd42878deb 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY.md +++ b/docs/libcurl/opts/CURLOPT_PROXY.md @@ -53,8 +53,7 @@ HTTP Proxy. Default when no scheme or proxy type is specified. ## https:// -HTTPS Proxy. (Added in 7.52.0 for OpenSSL and GnuTLS Since 7.87.0, it also -works for mbedTLS, Rustls, Schannel and wolfSSL.) +HTTPS Proxy. (with OpenSSL, GnuTLS, mbedTLS, Rustls, Schannel or wolfSSL.) This uses HTTP/1 by default. Setting CURLOPT_PROXYTYPE(3) to **CURLPROXY_HTTPS2** allows libcurl to negotiate using HTTP/2 with proxy. @@ -89,8 +88,9 @@ proxy. Such tunneling is activated with CURLOPT_HTTPPROXYTUNNEL(3). Setting the proxy string to "" (an empty string) explicitly disables the use of a proxy, even if there is an environment variable set for it. -Unix domain sockets are supported for socks proxies since 7.84.0. Set -localhost for the host part. e.g. socks5h://localhost/path/to/socket.sock +Unix domain sockets are supported for SOCKS proxies. Set `localhost` for the +host part and append the absolute path to the domain socket. For example: +`socks5h://localhost/path/to/socket.sock` When you set a hostname to use, do not assume that there is any particular single port number used widely for proxies. Specify it. @@ -137,7 +137,7 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/file.txt"); - curl_easy_setopt(curl, CURLOPT_PROXY, "http://proxy:80"); + curl_easy_setopt(curl, CURLOPT_PROXY, "http://proxy.example:80"); curl_easy_perform(curl); } } @@ -153,6 +153,11 @@ Since 7.21.7 the proxy string supports the socks protocols as "schemes". Since 7.50.2, unsupported schemes in proxy strings cause libcurl to return error. +Since 7.52.0, it supports HTTPS proxy for OpenSSL. + +Since 7.87.0, it supports HTTPS proxy for GnuTLS, for mbedTLS, Rustls, +Schannel and wolfSSL. + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_PROXYAUTH.md b/docs/libcurl/opts/CURLOPT_PROXYAUTH.md index c010c8fd0c..459fd429c3 100644 --- a/docs/libcurl/opts/CURLOPT_PROXYAUTH.md +++ b/docs/libcurl/opts/CURLOPT_PROXYAUTH.md @@ -53,7 +53,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* use this proxy */ curl_easy_setopt(curl, CURLOPT_PROXY, "http://local.example.com:1080"); @@ -61,7 +61,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY); /* set the proxy credentials */ curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, "james:007"); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXYPASSWORD.md b/docs/libcurl/opts/CURLOPT_PROXYPASSWORD.md index 20e3a687a6..c4664b6631 100644 --- a/docs/libcurl/opts/CURLOPT_PROXYPASSWORD.md +++ b/docs/libcurl/opts/CURLOPT_PROXYPASSWORD.md @@ -53,12 +53,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_PROXY, "http://localhost:8080"); curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, "mrsmith"); curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, "qwerty"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXYPORT.md b/docs/libcurl/opts/CURLOPT_PROXYPORT.md index 31abbf9cbf..faab053f59 100644 --- a/docs/libcurl/opts/CURLOPT_PROXYPORT.md +++ b/docs/libcurl/opts/CURLOPT_PROXYPORT.md @@ -38,7 +38,7 @@ Disabling this option, setting it to zero, makes it not specified which makes libcurl use the default proxy port number or the port number specified in the proxy URL string. -While this accepts a 'long', the port number is 16 bit so it cannot be larger +While this accepts a 'long', the port number is 16-bit so it cannot be larger than 65535. # DEFAULT @@ -54,11 +54,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_PROXY, "localhost"); curl_easy_setopt(curl, CURLOPT_PROXYPORT, 8080L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXYTYPE.md b/docs/libcurl/opts/CURLOPT_PROXYTYPE.md index d7870efc57..6000d10b00 100644 --- a/docs/libcurl/opts/CURLOPT_PROXYTYPE.md +++ b/docs/libcurl/opts/CURLOPT_PROXYTYPE.md @@ -81,12 +81,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_PROXY, "local.example.com:1080"); /* set the proxy type */ curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXYUSERNAME.md b/docs/libcurl/opts/CURLOPT_PROXYUSERNAME.md index 0bf170f4e8..be196eed13 100644 --- a/docs/libcurl/opts/CURLOPT_PROXYUSERNAME.md +++ b/docs/libcurl/opts/CURLOPT_PROXYUSERNAME.md @@ -53,12 +53,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_PROXY, "http://localhost:8080"); curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, "mrsmith"); curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, "qwerty"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXYUSERPWD.md b/docs/libcurl/opts/CURLOPT_PROXYUSERPWD.md index 3fed4a3d55..87dac82a13 100644 --- a/docs/libcurl/opts/CURLOPT_PROXYUSERPWD.md +++ b/docs/libcurl/opts/CURLOPT_PROXYUSERPWD.md @@ -55,11 +55,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_PROXY, "http://localhost:8080"); curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, "clark%20kent:superman"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_CAINFO.md b/docs/libcurl/opts/CURLOPT_PROXY_CAINFO.md index 6be10774db..3eb5af912a 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_CAINFO.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_CAINFO.md @@ -69,12 +69,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* using an HTTPS proxy */ - curl_easy_setopt(curl, CURLOPT_PROXY, "https://localhost:443"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example:443"); curl_easy_setopt(curl, CURLOPT_PROXY_CAINFO, "/etc/certs/cabundle.pem"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_CAINFO_BLOB.md b/docs/libcurl/opts/CURLOPT_PROXY_CAINFO_BLOB.md index a352886e06..36666edf21 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_CAINFO_BLOB.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_CAINFO_BLOB.md @@ -18,7 +18,7 @@ Protocol: - TLS TLS-backend: - OpenSSL - - rustls + - Rustls - Schannel Added-in: 7.77.0 --- @@ -69,16 +69,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; struct curl_blob blob; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* using an HTTPS proxy */ - curl_easy_setopt(curl, CURLOPT_PROXY, "https://localhost:443"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example:443"); blob.data = strpem; blob.len = strlen(strpem); blob.flags = CURL_BLOB_COPY; curl_easy_setopt(curl, CURLOPT_PROXY_CAINFO_BLOB, &blob); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_CAPATH.md b/docs/libcurl/opts/CURLOPT_PROXY_CAPATH.md index d0fce53cae..0bd3770f91 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_CAPATH.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_CAPATH.md @@ -61,12 +61,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* using an HTTPS proxy */ - curl_easy_setopt(curl, CURLOPT_PROXY, "https://localhost:443"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example:443"); curl_easy_setopt(curl, CURLOPT_PROXY_CAPATH, "/etc/cert-dir"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_CRLFILE.md b/docs/libcurl/opts/CURLOPT_PROXY_CRLFILE.md index 76c9612515..2dda8235c6 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_CRLFILE.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_CRLFILE.md @@ -71,11 +71,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_PROXY, "https://localhost:80"); curl_easy_setopt(curl, CURLOPT_PROXY_CRLFILE, "/etc/certs/crl.pem"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_ISSUERCERT.md b/docs/libcurl/opts/CURLOPT_PROXY_ISSUERCERT.md index 88af6f8ed9..c3687a46fe 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_ISSUERCERT.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_ISSUERCERT.md @@ -67,12 +67,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* using an HTTPS proxy */ - curl_easy_setopt(curl, CURLOPT_PROXY, "https://localhost:443"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example:443"); curl_easy_setopt(curl, CURLOPT_PROXY_ISSUERCERT, "/etc/certs/cacert.pem"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_ISSUERCERT_BLOB.md b/docs/libcurl/opts/CURLOPT_PROXY_ISSUERCERT_BLOB.md index a697751397..180a7b1808 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_ISSUERCERT_BLOB.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_ISSUERCERT_BLOB.md @@ -73,16 +73,16 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; struct curl_blob blob; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* using an HTTPS proxy */ - curl_easy_setopt(curl, CURLOPT_PROXY, "https://localhost:443"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example:443"); blob.data = certificateData; blob.len = filesize; blob.flags = CURL_BLOB_COPY; curl_easy_setopt(curl, CURLOPT_PROXY_ISSUERCERT_BLOB, &blob); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_KEYPASSWD.md b/docs/libcurl/opts/CURLOPT_PROXY_KEYPASSWD.md index 4d82870953..98f3683eef 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_KEYPASSWD.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_KEYPASSWD.md @@ -59,11 +59,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); - curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy:443"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example:443"); curl_easy_setopt(curl, CURLOPT_PROXY_KEYPASSWD, "superman"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_PINNEDPUBLICKEY.md b/docs/libcurl/opts/CURLOPT_PROXY_PINNEDPUBLICKEY.md index 49c28a7b6e..85a404459f 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_PINNEDPUBLICKEY.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_PINNEDPUBLICKEY.md @@ -67,7 +67,7 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy:443"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example:443"); curl_easy_setopt(curl, CURLOPT_PROXY_PINNEDPUBLICKEY, "sha256//YhKJKSzoTt2b5FP18fvpHo7fJYqQCjA" "a3HWY3tvRMwE=;sha256//t62CeU2tQiqkexU74" @@ -91,7 +91,7 @@ from the https proxy server's certificate. # Windows-specific: # - Use NUL instead of /dev/null. # - OpenSSL may wait for input instead of disconnecting. Hit enter. -# - If you do not have sed, then just copy the certificate into a file: +# - If you do not have sed, then copy the certificate into a file: # Lines from -----BEGIN CERTIFICATE----- to -----END CERTIFICATE-----. # openssl s_client -servername www.example.com -connect www.example.com:443 \ diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SERVICE_NAME.md b/docs/libcurl/opts/CURLOPT_PROXY_SERVICE_NAME.md index 59cd1aaea2..9bdb4af26a 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SERVICE_NAME.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SERVICE_NAME.md @@ -51,10 +51,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_PROXY_SERVICE_NAME, "custom"); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSLCERT.md b/docs/libcurl/opts/CURLOPT_PROXY_SSLCERT.md index f54832839f..8ba84add1f 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSLCERT.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSLCERT.md @@ -62,13 +62,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example"); curl_easy_setopt(curl, CURLOPT_PROXY_SSLCERT, "client.pem"); curl_easy_setopt(curl, CURLOPT_PROXY_SSLKEY, "key.pem"); curl_easy_setopt(curl, CURLOPT_PROXY_KEYPASSWD, "s3cret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSLCERTTYPE.md b/docs/libcurl/opts/CURLOPT_PROXY_SSLCERTTYPE.md index f616c75e40..c7c7b728a5 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSLCERTTYPE.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSLCERTTYPE.md @@ -58,14 +58,14 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example"); curl_easy_setopt(curl, CURLOPT_PROXY_SSLCERT, "client.pem"); curl_easy_setopt(curl, CURLOPT_PROXY_SSLCERTTYPE, "PEM"); curl_easy_setopt(curl, CURLOPT_PROXY_SSLKEY, "key.pem"); curl_easy_setopt(curl, CURLOPT_PROXY_KEYPASSWD, "s3cret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSLCERT_BLOB.md b/docs/libcurl/opts/CURLOPT_PROXY_SSLCERT_BLOB.md index e3b947b146..5be7b134a0 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSLCERT_BLOB.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSLCERT_BLOB.md @@ -62,17 +62,17 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; struct curl_blob blob; blob.data = certificateData; blob.len = filesize; blob.flags = CURL_BLOB_COPY; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example"); curl_easy_setopt(curl, CURLOPT_PROXY_SSLKEY, "key.pem"); curl_easy_setopt(curl, CURLOPT_PROXY_KEYPASSWD, "s3cret"); curl_easy_setopt(curl, CURLOPT_PROXY_SSLCERT_BLOB, &blob); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSLKEY.md b/docs/libcurl/opts/CURLOPT_PROXY_SSLKEY.md index 74b431a6b0..9b8d293735 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSLKEY.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSLKEY.md @@ -62,13 +62,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example"); curl_easy_setopt(curl, CURLOPT_PROXY_SSLCERT, "client.pem"); curl_easy_setopt(curl, CURLOPT_PROXY_SSLKEY, "key.pem"); curl_easy_setopt(curl, CURLOPT_PROXY_KEYPASSWD, "s3cret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSLKEYTYPE.md b/docs/libcurl/opts/CURLOPT_PROXY_SSLKEYTYPE.md index df14f081f5..8513dffced 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSLKEYTYPE.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSLKEYTYPE.md @@ -51,14 +51,14 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example"); curl_easy_setopt(curl, CURLOPT_PROXY_SSLCERT, "client.pem"); curl_easy_setopt(curl, CURLOPT_PROXY_SSLKEY, "key.pem"); curl_easy_setopt(curl, CURLOPT_PROXY_SSLKEYTYPE, "PEM"); curl_easy_setopt(curl, CURLOPT_PROXY_KEYPASSWD, "s3cret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSLKEY_BLOB.md b/docs/libcurl/opts/CURLOPT_PROXY_SSLKEY_BLOB.md index 91e9146849..65f940b861 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSLKEY_BLOB.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSLKEY_BLOB.md @@ -59,10 +59,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; struct curl_blob blob; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example"); blob.data = certificateData; blob.len = filesize; blob.flags = CURL_BLOB_COPY; @@ -73,7 +73,7 @@ int main(void) blob.len = privateKeySize; curl_easy_setopt(curl, CURLOPT_PROXY_SSLKEY_BLOB, &blob); curl_easy_setopt(curl, CURLOPT_PROXY_KEYPASSWD, "s3cret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSLVERSION.md b/docs/libcurl/opts/CURLOPT_PROXY_SSLVERSION.md index 7e1f6f1400..bb35bda1ae 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSLVERSION.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSLVERSION.md @@ -72,33 +72,22 @@ supported for wolfSSL. The flag defines the maximum supported TLS version as TLSv1.2, or the default value from the SSL library. -(Added in 7.54.0) ## CURL_SSLVERSION_MAX_TLSv1_0 The flag defines maximum supported TLS version as TLSv1.0. -(Added in 7.54.0) ## CURL_SSLVERSION_MAX_TLSv1_1 The flag defines maximum supported TLS version as TLSv1.1. -(Added in 7.54.0) ## CURL_SSLVERSION_MAX_TLSv1_2 The flag defines maximum supported TLS version as TLSv1.2. -(Added in 7.54.0) ## CURL_SSLVERSION_MAX_TLSv1_3 The flag defines maximum supported TLS version as TLSv1.3. -(Added in 7.54.0) - -## - -In versions of curl prior to 7.54 the CURL_SSLVERSION_TLS options were -documented to allow *only* the specified TLS version, but behavior was -inconsistent depending on the TLS library. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSL_CIPHER_LIST.md b/docs/libcurl/opts/CURLOPT_PROXY_SSL_CIPHER_LIST.md index 239ce03c73..ce1f63aa3b 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSL_CIPHER_LIST.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSL_CIPHER_LIST.md @@ -17,7 +17,7 @@ TLS-backend: - Schannel - wolfSSL - mbedTLS - - rustls + - Rustls Added-in: 7.52.0 --- @@ -75,13 +75,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_PROXY, "https://localhost"); curl_easy_setopt(curl, CURLOPT_PROXY_SSL_CIPHER_LIST, "ECDHE-ECDSA-CHACHA20-POLY1305:" "ECDHE-RSA-CHACHA20-POLY1305"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md b/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md index 0729518a0d..ab0e366b0d 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md @@ -40,17 +40,17 @@ Tells libcurl to not attempt to use any workarounds for a security flaw in the SSL3 and TLS1.0 protocols. If this option is not used or this bit is set to 0, the SSL layer libcurl uses may use a work-around for this flaw although it might cause interoperability problems with some (older) SSL implementations. -WARNING: avoiding this work-around lessens the security, and by setting this -option to 1 you ask for exactly that. This option is only supported for Secure -Transport and OpenSSL. + +**WARNING:** avoiding this work-around lessens the security, and by setting +this option to 1 you ask for exactly that. This option is only supported for +Secure Transport and OpenSSL. ## CURLSSLOPT_NO_REVOKE Tells libcurl to disable certificate revocation checks for those SSL backends where such behavior is present. This option is only supported for Schannel (the native Windows SSL library), with an exception in the case of Windows' -Untrusted Publishers block list which it seems cannot be bypassed. (Added in -7.44.0) +Untrusted Publishers block list which it seems cannot be bypassed. ## CURLSSLOPT_NO_PARTIALCHAIN @@ -105,13 +105,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example"); /* weaken TLS only for use with silly proxies */ curl_easy_setopt(curl, CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST | CURLSSLOPT_NO_REVOKE); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSL_VERIFYPEER.md b/docs/libcurl/opts/CURLOPT_PROXY_SSL_VERIFYPEER.md index 5904821dee..ef3f83d47e 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSL_VERIFYPEER.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSL_VERIFYPEER.md @@ -57,11 +57,11 @@ talking to. Use CURLOPT_PROXY_SSL_VERIFYHOST(3) for that. The check that the hostname in the certificate is valid for the hostname you are connecting to is done independently of the CURLOPT_PROXY_SSL_VERIFYPEER(3) option. -WARNING: disabling verification of the certificate allows bad guys to +**WARNING:** disabling verification of the certificate allows bad guys to man-in-the-middle the communication without you knowing it. Disabling -verification makes the communication insecure. Just having encryption on a -transfer is not enough as you cannot be sure that you are communicating with -the correct end-point. +verification makes the communication insecure. Having encryption on a transfer +is not enough as you cannot be sure that you are communicating with the +correct end-point. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_PROXY_TLS13_CIPHERS.md b/docs/libcurl/opts/CURLOPT_PROXY_TLS13_CIPHERS.md index ee17d27e99..999a111c98 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_TLS13_CIPHERS.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_TLS13_CIPHERS.md @@ -16,7 +16,7 @@ TLS-backend: - OpenSSL - wolfSSL - mbedTLS - - rustls + - Rustls Added-in: 7.61.0 --- @@ -70,11 +70,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_PROXY_TLS13_CIPHERS, "TLS_CHACHA20_POLY1305_SHA256"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_PASSWORD.md b/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_PASSWORD.md index 91f2110716..17acca6761 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_PASSWORD.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_PASSWORD.md @@ -56,13 +56,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example"); curl_easy_setopt(curl, CURLOPT_PROXY_TLSAUTH_TYPE, "SRP"); curl_easy_setopt(curl, CURLOPT_PROXY_TLSAUTH_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PROXY_TLSAUTH_PASSWORD, "secret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_TYPE.md b/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_TYPE.md index 4c7cfd700f..d1badf4be5 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_TYPE.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_TYPE.md @@ -63,13 +63,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example"); curl_easy_setopt(curl, CURLOPT_PROXY_TLSAUTH_TYPE, "SRP"); curl_easy_setopt(curl, CURLOPT_PROXY_TLSAUTH_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PROXY_TLSAUTH_PASSWORD, "secret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_USERNAME.md b/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_USERNAME.md index 44e32299ee..c4db7aa815 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_USERNAME.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_TLSAUTH_USERNAME.md @@ -56,13 +56,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy"); + curl_easy_setopt(curl, CURLOPT_PROXY, "https://proxy.example"); curl_easy_setopt(curl, CURLOPT_PROXY_TLSAUTH_TYPE, "SRP"); curl_easy_setopt(curl, CURLOPT_PROXY_TLSAUTH_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_PROXY_TLSAUTH_PASSWORD, "secret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_PROXY_TRANSFER_MODE.md b/docs/libcurl/opts/CURLOPT_PROXY_TRANSFER_MODE.md index f424a27ef5..93b483802e 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_TRANSFER_MODE.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_TRANSFER_MODE.md @@ -48,13 +48,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/old-server/file.txt"); curl_easy_setopt(curl, CURLOPT_PROXY, "http://localhost:80"); curl_easy_setopt(curl, CURLOPT_PROXY_TRANSFER_MODE, 1L); curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_QUICK_EXIT.md b/docs/libcurl/opts/CURLOPT_QUICK_EXIT.md index 7027159e31..d6505244cb 100644 --- a/docs/libcurl/opts/CURLOPT_QUICK_EXIT.md +++ b/docs/libcurl/opts/CURLOPT_QUICK_EXIT.md @@ -47,9 +47,9 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_QUICK_EXIT, 1L); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_QUOTE.md b/docs/libcurl/opts/CURLOPT_QUOTE.md index 8687f527a9..d4365f7b41 100644 --- a/docs/libcurl/opts/CURLOPT_QUOTE.md +++ b/docs/libcurl/opts/CURLOPT_QUOTE.md @@ -52,11 +52,15 @@ libcurl does not inspect, parse or "understand" the commands passed to the server using this option. If you change connection state, working directory or similar using quote commands, libcurl does not know about it. -The path arguments for FTP or SFTP can use single or double quotes to -distinguish a space from being the parameter separator or being a part of the -path. e.g. rename with sftp using a quote command like this: +The path arguments for FTP or SFTP should use double quotes to distinguish a +space from being the parameter separator or being a part of the path. For +example, rename with sftp using a quote command like this: - "rename 'test/_upload.txt' 'test/Hello World.txt'" + rename "test/_upload.txt" "test/Hello World.txt" + +For SFTP, filenames must be provided within double quotes to embed spaces, +backslashes, quotes or double quotes. Within double quotes the following +escape sequences are available for that purpose: \\, \", and \'. # SFTP commands @@ -120,7 +124,7 @@ operand, provided it is empty. ## statvfs file The statvfs command returns statistics on the file system in which specified -file resides. (Added in 7.49.0) +file resides. ## symlink source_file target_file @@ -143,13 +147,13 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/foo.bin"); /* pass in the FTP commands to run before the transfer */ curl_easy_setopt(curl, CURLOPT_QUOTE, cmdlist); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_READFUNCTION.md b/docs/libcurl/opts/CURLOPT_READFUNCTION.md index 71ad2e9ca0..0046ded635 100644 --- a/docs/libcurl/opts/CURLOPT_READFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_READFUNCTION.md @@ -36,9 +36,8 @@ Pass a pointer to your callback function, as the prototype shows above. This callback function gets called by libcurl as soon as it needs to read data in order to send it to the peer - like if you ask it to upload or post data to -the server. The data area pointed at by the pointer *buffer* should be -filled up with at most *size* multiplied with *nitems* number of bytes -by your function. *size* is always 1. +the server. The data area pointed at by the pointer *buffer* should be filled +up with at most *nitems* number of bytes by your function. *size* is always 1. Set the *userdata* argument with the CURLOPT_READDATA(3) option. diff --git a/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md b/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md index d76bd0cf0a..f8c45519fb 100644 --- a/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md +++ b/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md @@ -58,6 +58,7 @@ CURLPROTO_IMAP CURLPROTO_IMAPS CURLPROTO_LDAP CURLPROTO_LDAPS +CURLPROTO_MQTT CURLPROTO_POP3 CURLPROTO_POP3S CURLPROTO_RTMP @@ -79,10 +80,7 @@ CURLPROTO_TFTP # DEFAULT -HTTP, HTTPS, FTP and FTPS (Added in 7.65.2). - -Older versions defaulted to all protocols except FILE, SCP and since 7.40.0 -SMB and SMBS. +HTTP, HTTPS, FTP and FTPS # %PROTOCOLS% diff --git a/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS_STR.md b/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS_STR.md index 2f14b04d00..88d3620306 100644 --- a/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS_STR.md +++ b/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS_STR.md @@ -46,8 +46,8 @@ By default libcurl allows HTTP, HTTPS, FTP and FTPS on redirects (since These are the available protocols: DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, -MQTT, POP3, POP3S, RTMP, RTMPE, RTMPS, RTMPT, RTMPTE, RTMPTS, RTSP, SCP, SFTP, -SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS, WSS +MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, +TFTP, WS, WSS You can set "ALL" as a short-cut to enable all protocols. Note that by setting all, you may enable protocols that were not supported the day you write this @@ -64,10 +64,7 @@ option. # DEFAULT -HTTP, HTTPS, FTP and FTPS (Added in 7.65.2). - -Older versions defaulted to all protocols except FILE, SCP and since 7.40.0 -SMB and SMBS. +HTTP, HTTPS, FTP and FTPS # %PROTOCOLS% diff --git a/docs/libcurl/opts/CURLOPT_RESOLVE.md b/docs/libcurl/opts/CURLOPT_RESOLVE.md index 952ff824e7..e923c46752 100644 --- a/docs/libcurl/opts/CURLOPT_RESOLVE.md +++ b/docs/libcurl/opts/CURLOPT_RESOLVE.md @@ -57,7 +57,7 @@ use your provided ADDRESS. The optional leading plus (`+`) specifies that the new entry should timeout. Entries added without the leading plus character never times out whereas -entries added with `+HOST:...` times out just like ordinary DNS cache entries. +entries added with `+HOST:...` times out like ordinary DNS cache entries. If the DNS cache already has an entry for the given host+port pair, the new entry overrides the former one. diff --git a/docs/libcurl/opts/CURLOPT_RTSP_CLIENT_CSEQ.md b/docs/libcurl/opts/CURLOPT_RTSP_CLIENT_CSEQ.md index d7b13a04ab..27da0f3b83 100644 --- a/docs/libcurl/opts/CURLOPT_RTSP_CLIENT_CSEQ.md +++ b/docs/libcurl/opts/CURLOPT_RTSP_CLIENT_CSEQ.md @@ -45,10 +45,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "rtsp://example.com/"); curl_easy_setopt(curl, CURLOPT_RTSP_CLIENT_CSEQ, 1234L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_RTSP_REQUEST.md b/docs/libcurl/opts/CURLOPT_RTSP_REQUEST.md index 89e303d4ab..c9c00a4b10 100644 --- a/docs/libcurl/opts/CURLOPT_RTSP_REQUEST.md +++ b/docs/libcurl/opts/CURLOPT_RTSP_REQUEST.md @@ -49,7 +49,7 @@ option is used. (The session ID is not needed for this method) When sent by a client, this method changes the description of the session. For example, if a client is using the server to record a meeting, the client can use Announce to inform the server of all the meta-information about the -session. ANNOUNCE acts like an HTTP PUT or POST just like +session. ANNOUNCE acts like an HTTP PUT or POST like *CURL_RTSPREQ_SET_PARAMETER* ## CURL_RTSPREQ_SETUP @@ -74,7 +74,7 @@ halted. (e.g. *npt=25*) ## CURL_RTSPREQ_TEARDOWN -This command terminates an RTSP session. Simply closing a connection does not +This command terminates an RTSP session. Closing a connection does not terminate the RTSP session since it is valid to control an RTSP session over different connections. @@ -82,7 +82,7 @@ different connections. Retrieve a parameter from the server. By default, libcurl adds a *Content-Type: text/parameters* header on all non-empty requests unless a -custom one is set. GET_PARAMETER acts just like an HTTP PUT or POST (see +custom one is set. GET_PARAMETER acts like an HTTP PUT or POST (see *CURL_RTSPREQ_SET_PARAMETER*). Applications wishing to send a heartbeat message (e.g. in the presence of a server-specified timeout) should send use an empty GET_PARAMETER request. @@ -121,11 +121,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "rtsp://example.com/"); /* ask for options */ curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_RTSP_SERVER_CSEQ.md b/docs/libcurl/opts/CURLOPT_RTSP_SERVER_CSEQ.md index d054be7a55..f8672f1bf4 100644 --- a/docs/libcurl/opts/CURLOPT_RTSP_SERVER_CSEQ.md +++ b/docs/libcurl/opts/CURLOPT_RTSP_SERVER_CSEQ.md @@ -44,10 +44,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "rtsp://example.com/"); curl_easy_setopt(curl, CURLOPT_RTSP_SERVER_CSEQ, 1234L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_RTSP_SESSION_ID.md b/docs/libcurl/opts/CURLOPT_RTSP_SESSION_ID.md index 0d76e60186..e60438eecb 100644 --- a/docs/libcurl/opts/CURLOPT_RTSP_SESSION_ID.md +++ b/docs/libcurl/opts/CURLOPT_RTSP_SESSION_ID.md @@ -52,11 +52,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; char *prev_id = "old"; /* saved from before somehow */ curl_easy_setopt(curl, CURLOPT_URL, "rtsp://example.com/"); curl_easy_setopt(curl, CURLOPT_RTSP_SESSION_ID, prev_id); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_RTSP_STREAM_URI.md b/docs/libcurl/opts/CURLOPT_RTSP_STREAM_URI.md index eb745da043..619096bf1a 100644 --- a/docs/libcurl/opts/CURLOPT_RTSP_STREAM_URI.md +++ b/docs/libcurl/opts/CURLOPT_RTSP_STREAM_URI.md @@ -56,11 +56,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "rtsp://example.com/"); curl_easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, "rtsp://foo.example.com/twister/video"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_RTSP_TRANSPORT.md b/docs/libcurl/opts/CURLOPT_RTSP_TRANSPORT.md index cba9b4ad53..44633cd71d 100644 --- a/docs/libcurl/opts/CURLOPT_RTSP_TRANSPORT.md +++ b/docs/libcurl/opts/CURLOPT_RTSP_TRANSPORT.md @@ -48,12 +48,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "rtsp://example.com/"); curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); curl_easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, "RTP/AVP;unicast;client_port=4588-4589"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SASL_AUTHZID.md b/docs/libcurl/opts/CURLOPT_SASL_AUTHZID.md index 7f627f5a61..e32380d65e 100644 --- a/docs/libcurl/opts/CURLOPT_SASL_AUTHZID.md +++ b/docs/libcurl/opts/CURLOPT_SASL_AUTHZID.md @@ -59,12 +59,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "imap://example.com/"); curl_easy_setopt(curl, CURLOPT_USERNAME, "Kurt"); curl_easy_setopt(curl, CURLOPT_PASSWORD, "xipj3plmq"); curl_easy_setopt(curl, CURLOPT_SASL_AUTHZID, "Ursel"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SASL_IR.md b/docs/libcurl/opts/CURLOPT_SASL_IR.md index 6a3b50ca35..c99239db5b 100644 --- a/docs/libcurl/opts/CURLOPT_SASL_IR.md +++ b/docs/libcurl/opts/CURLOPT_SASL_IR.md @@ -56,10 +56,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "smtp://example.com/"); curl_easy_setopt(curl, CURLOPT_SASL_IR, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SERVER_RESPONSE_TIMEOUT.md b/docs/libcurl/opts/CURLOPT_SERVER_RESPONSE_TIMEOUT.md index 3d6ba1f9aa..68e27529dc 100644 --- a/docs/libcurl/opts/CURLOPT_SERVER_RESPONSE_TIMEOUT.md +++ b/docs/libcurl/opts/CURLOPT_SERVER_RESPONSE_TIMEOUT.md @@ -5,6 +5,7 @@ Title: CURLOPT_SERVER_RESPONSE_TIMEOUT Section: 3 Source: libcurl See-also: + - CURLOPT_SERVER_RESPONSE_TIMEOUT_MS (3) - CURLOPT_CONNECTTIMEOUT (3) - CURLOPT_LOW_SPEED_LIMIT (3) - CURLOPT_TIMEOUT (3) @@ -33,19 +34,16 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SERVER_RESPONSE_TIMEOUT, # DESCRIPTION -Pass a long. Causes libcurl to set a *timeout* period (in seconds) on the -amount of time that the server is allowed to take in order to send a response -message for a command before the session is considered dead. While libcurl is -waiting for a response, this value overrides CURLOPT_TIMEOUT(3). It is -recommended that if used in conjunction with CURLOPT_TIMEOUT(3), you set -CURLOPT_SERVER_RESPONSE_TIMEOUT(3) to a value smaller than -CURLOPT_TIMEOUT(3). +Pass a long. It tells libcurl to wait no longer than *timeout* seconds for +responses on sent commands. If no response is received within this period, the +connection is considered dead and the transfer fails. -This option was formerly known as CURLOPT_FTP_RESPONSE_TIMEOUT. +It is recommended that if used in conjunction with CURLOPT_TIMEOUT(3), you set +CURLOPT_SERVER_RESPONSE_TIMEOUT(3) to a value smaller than CURLOPT_TIMEOUT(3). # DEFAULT -None +60 seconds # %PROTOCOLS% @@ -56,17 +54,21 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/slow.txt"); /* wait no more than 23 seconds */ curl_easy_setopt(curl, CURLOPT_SERVER_RESPONSE_TIMEOUT, 23L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } ~~~ +# HISTORY + +This option was formerly known as CURLOPT_FTP_RESPONSE_TIMEOUT. + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_SERVER_RESPONSE_TIMEOUT_MS.md b/docs/libcurl/opts/CURLOPT_SERVER_RESPONSE_TIMEOUT_MS.md index 7acbd105ec..cb57bf5d4a 100644 --- a/docs/libcurl/opts/CURLOPT_SERVER_RESPONSE_TIMEOUT_MS.md +++ b/docs/libcurl/opts/CURLOPT_SERVER_RESPONSE_TIMEOUT_MS.md @@ -5,6 +5,7 @@ Title: CURLOPT_SERVER_RESPONSE_TIMEOUT_MS Section: 3 Source: libcurl See-also: + - CURLOPT_SERVER_RESPONSE_TIMEOUT (3) - CURLOPT_CONNECTTIMEOUT (3) - CURLOPT_LOW_SPEED_LIMIT (3) - CURLOPT_TIMEOUT (3) @@ -33,11 +34,11 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, # DESCRIPTION -Pass a long. Causes libcurl to set a *timeout* period (in milliseconds) on the -amount of time that the server is allowed to take in order to send a response -message for a command before the session is considered dead. While libcurl is -waiting for a response, this value overrides CURLOPT_TIMEOUT(3). It is -recommended that if used in conjunction with CURLOPT_TIMEOUT(3), you set +Pass a long. It tells libcurl to wait no longer than *timeout* milliseconds +for responses on sent commands. If no response is received within this period, +the connection is considered dead and the transfer fails. + +It is recommended that if used in conjunction with CURLOPT_TIMEOUT(3), you set CURLOPT_SERVER_RESPONSE_TIMEOUT_MS(3) to a value smaller than CURLOPT_TIMEOUT(3). @@ -47,7 +48,7 @@ This is the millisecond version of CURLOPT_SERVER_RESPONSE_TIMEOUT(3). # DEFAULT -None +60000 milliseconds # %PROTOCOLS% @@ -58,11 +59,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/slow.txt"); /* wait no more than 237 milliseconds */ curl_easy_setopt(curl, CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, 237L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_SERVICE_NAME.md b/docs/libcurl/opts/CURLOPT_SERVICE_NAME.md index 56d7029cd2..f6efb393f2 100644 --- a/docs/libcurl/opts/CURLOPT_SERVICE_NAME.md +++ b/docs/libcurl/opts/CURLOPT_SERVICE_NAME.md @@ -56,10 +56,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode ret; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_SERVICE_NAME, "custom"); - ret = curl_easy_perform(curl); + result = curl_easy_perform(curl); } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_SHARE.md b/docs/libcurl/opts/CURLOPT_SHARE.md index c4488086a7..2c6a496593 100644 --- a/docs/libcurl/opts/CURLOPT_SHARE.md +++ b/docs/libcurl/opts/CURLOPT_SHARE.md @@ -26,13 +26,12 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SHARE, CURLSH *share); # DESCRIPTION -Pass a *share* handle as a parameter. The share handle must have been -created by a previous call to curl_share_init(3). Setting this option, -makes this curl handle use the data from the shared handle instead of keeping -the data to itself. This enables several curl handles to share data. If the -curl handles are used simultaneously in multiple threads, you **MUST** use -the locking methods in the share handle. See curl_share_setopt(3) for -details. +Pass a *share* handle as a parameter. The share handle must have been created +by a previous call to curl_share_init(3). Setting this option, makes this curl +handle use the data from the shared handle instead of keeping the data to +itself. This enables several curl handles to share data. If the curl handles +are used simultaneously in multiple threads, you **must** use the locking +methods in the share handle. See curl_share_setopt(3) for details. If you add a share that is set to share cookies, your easy handle uses that cookie cache and get the cookie engine enabled. If you stop sharing an object @@ -58,21 +57,21 @@ int main(void) CURL *curl = curl_easy_init(); CURL *curl2 = curl_easy_init(); /* a second handle */ if(curl) { - CURLcode res; + CURLcode result; CURLSH *shobject = curl_share_init(); curl_share_setopt(shobject, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); curl_easy_setopt(curl, CURLOPT_SHARE, shobject); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); /* the second handle shares cookies from the first */ curl_easy_setopt(curl2, CURLOPT_URL, "https://example.com/second"); curl_easy_setopt(curl2, CURLOPT_COOKIEFILE, ""); curl_easy_setopt(curl2, CURLOPT_SHARE, shobject); - res = curl_easy_perform(curl2); + result = curl_easy_perform(curl2); curl_easy_cleanup(curl2); curl_share_cleanup(shobject); diff --git a/docs/libcurl/opts/CURLOPT_SOCKOPTDATA.md b/docs/libcurl/opts/CURLOPT_SOCKOPTDATA.md index dbf75da3bc..be913df029 100644 --- a/docs/libcurl/opts/CURLOPT_SOCKOPTDATA.md +++ b/docs/libcurl/opts/CURLOPT_SOCKOPTDATA.md @@ -51,7 +51,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; int recvbuffersize = 256 * 1024; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); @@ -60,7 +60,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_callback); curl_easy_setopt(curl, CURLOPT_SOCKOPTDATA, &recvbuffersize); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_SOCKOPTFUNCTION.md b/docs/libcurl/opts/CURLOPT_SOCKOPTFUNCTION.md index 0efdd0b3be..2080aa9483 100644 --- a/docs/libcurl/opts/CURLOPT_SOCKOPTFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_SOCKOPTFUNCTION.md @@ -22,7 +22,7 @@ CURLOPT_SOCKOPTFUNCTION - callback for setting socket options ~~~c #include -typedef enum { +typedef enum { CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */ CURLSOCKTYPE_LAST /* never use */ @@ -50,9 +50,9 @@ been created, but before the connect call to allow applications to change specific socket options. The callback's *purpose* argument identifies the exact purpose for this particular socket: -*CURLSOCKTYPE_IPCXN* for actively created connections or since 7.28.0 -*CURLSOCKTYPE_ACCEPT* for FTP when the connection was setup with PORT/EPSV -(in earlier versions these sockets were not passed to this callback). +*CURLSOCKTYPE_IPCXN* for actively created connections or *CURLSOCKTYPE_ACCEPT* +for FTP when the connection was setup with PORT/EPSV (in earlier versions +these sockets were not passed to this callback). Future versions of libcurl may support more purposes. libcurl passes the newly created socket descriptor to the callback in the *curlfd* parameter so @@ -64,12 +64,14 @@ CURLOPT_SOCKOPTDATA(3) function. Return *CURL_SOCKOPT_OK* from the callback on success. Return *CURL_SOCKOPT_ERROR* from the callback function to signal an unrecoverable error to the library and it closes the socket and returns -*CURLE_COULDNT_CONNECT*. Alternatively, the callback function can return -*CURL_SOCKOPT_ALREADY_CONNECTED*, to tell libcurl that the socket is -already connected and then libcurl does no attempt to connect. This allows an -application to pass in an already connected socket with -CURLOPT_OPENSOCKETFUNCTION(3) and then have this function make libcurl -not attempt to connect (again). +*CURLE_COULDNT_CONNECT* for *CURLSOCKTYPE_IPCXN* and +*CURLE_ABORTED_BY_CALLBACK* for *CURLSOCKTYPE_ACCEPT*. + +The callback function may return *CURL_SOCKOPT_ALREADY_CONNECTED* for +*CURLSOCKTYPE_IPCXN*, to tell libcurl that the socket is already connected and +then libcurl does no attempt to connect. This allows an application to pass in +an already connected socket with CURLOPT_OPENSOCKETFUNCTION(3) and then have +this function make libcurl not attempt to connect (again). # DEFAULT @@ -96,7 +98,6 @@ static curl_socket_t opensocket(void *clientp, static int sockopt_callback(void *clientp, curl_socket_t curlfd, curlsocktype purpose) { - /* This return code was added in libcurl 7.21.5 */ return CURL_SOCKOPT_ALREADY_CONNECTED; } @@ -104,7 +105,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; int sockfd; /* our custom file descriptor */ /* libcurl thinks that you connect to the host * and port that you specify in the URL option. */ @@ -116,7 +117,7 @@ int main(void) /* call this function to set options for the socket */ curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_callback); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_SOCKS5_AUTH.md b/docs/libcurl/opts/CURLOPT_SOCKS5_AUTH.md index a9adf6b77d..7ff97484df 100644 --- a/docs/libcurl/opts/CURLOPT_SOCKS5_AUTH.md +++ b/docs/libcurl/opts/CURLOPT_SOCKS5_AUTH.md @@ -33,9 +33,18 @@ authentication, *CURLAUTH_GSSAPI*, which allows GSS-API authentication, and *CURLAUTH_NONE*, which allows no authentication. Set the actual username and password with the CURLOPT_PROXYUSERPWD(3) option. +The specific socks authentication method is an *access property*, it does not +change the security context. This means that this option changes how the +connection and access to the proxy happens when a connection is setup, but it +does not affect which proxy connections libcurl can reuse. libcurl may reuse a +connection that was set up with a different socks authentication method. Proxy +connection reuse still depends on other properties matching, such as the +protocol, proxy hostname, port number, credentials and other settings that +affect the connection. + # DEFAULT -CURLAUTH_BASIC|CURLAUTH_GSSAPI +CURLAUTH_BASIC | CURLAUTH_GSSAPI # %PROTOCOLS% diff --git a/docs/libcurl/opts/CURLOPT_SOCKS5_GSSAPI_NEC.md b/docs/libcurl/opts/CURLOPT_SOCKS5_GSSAPI_NEC.md index f873c6a5cc..38f5004adc 100644 --- a/docs/libcurl/opts/CURLOPT_SOCKS5_GSSAPI_NEC.md +++ b/docs/libcurl/opts/CURLOPT_SOCKS5_GSSAPI_NEC.md @@ -45,11 +45,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_PROXY, "socks5://proxy"); curl_easy_setopt(curl, CURLOPT_SOCKS5_GSSAPI_NEC, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SOCKS5_GSSAPI_SERVICE.md b/docs/libcurl/opts/CURLOPT_SOCKS5_GSSAPI_SERVICE.md index b47c2ca226..51ae347a2d 100644 --- a/docs/libcurl/opts/CURLOPT_SOCKS5_GSSAPI_SERVICE.md +++ b/docs/libcurl/opts/CURLOPT_SOCKS5_GSSAPI_SERVICE.md @@ -27,7 +27,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SOCKS5_GSSAPI_SERVICE, # DESCRIPTION -Deprecated since 7.49.0. Use CURLOPT_PROXY_SERVICE_NAME(3) instead. +Deprecated. Use CURLOPT_PROXY_SERVICE_NAME(3) instead. Pass a char pointer as parameter to a string holding the *name* of the service. The default service name for a SOCKS5 server is *rcmd*. This option @@ -49,11 +49,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_PROXY, "socks5://proxy"); curl_easy_setopt(curl, CURLOPT_SOCKS5_GSSAPI_SERVICE, "rcmd-special"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSH_AUTH_TYPES.md b/docs/libcurl/opts/CURLOPT_SSH_AUTH_TYPES.md index 60a6025c8c..3b9fb989d1 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_AUTH_TYPES.md +++ b/docs/libcurl/opts/CURLOPT_SSH_AUTH_TYPES.md @@ -50,11 +50,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "sftp://example.com/file"); curl_easy_setopt(curl, CURLOPT_SSH_AUTH_TYPES, CURLSSH_AUTH_PUBLICKEY | CURLSSH_AUTH_KEYBOARD); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md b/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md index 89422e4ef6..0f7e9996bd 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md @@ -44,7 +44,7 @@ says what type it is, from the **CURLKHTYPE_*** series in the **clientp** is a custom pointer set with CURLOPT_SSH_HOSTKEYDATA(3). -The callback MUST return one of the following return codes to tell libcurl how +The callback must return one of the following return codes to tell libcurl how to act: ## CURLKHMATCH_OK diff --git a/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_MD5.md b/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_MD5.md index 09810f22ee..777b25c4a1 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_MD5.md +++ b/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_MD5.md @@ -31,7 +31,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, # DESCRIPTION Pass a char pointer pointing to a string containing 32 hexadecimal digits. The -string should be the 128 bit MD5 checksum of the remote host's public key, and +string should be the 128-bit MD5 checksum of the remote host's public key, and libcurl aborts the connection to the host unless the MD5 checksum match. MD5 is a weak algorithm. We strongly recommend using @@ -43,6 +43,20 @@ option. Using this option multiple times makes the last set string override the previous ones. Set it to NULL to disable its use again. +This option is only applied when libcurl creates a new SSH connection. Once a +connection has been created and successfully verified with this MD5 check, it +is deemed vetted and may be reused by libcurl without performing the MD5 +verification again, even if you later change or disable this option or switch +to other verification mechanisms such as CURLOPT_SSH_KNOWNHOSTS(3) or +CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256(3). Any such changes only affect future new +connections, not already established ones. + +When MD5 verification is enabled for a connection via this option, libcurl +uses that MD5-based check instead of the known hosts/host key callback +verification path for that connection, so you must not assume that both the +MD5 check and the known hosts/host key callback verification are performed for +the same connection. + # DEFAULT NULL @@ -56,11 +70,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "sftp://example.com/file"); curl_easy_setopt(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, "afe17cd62a0f3b61f1ab9cb22ba269a7"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.md b/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.md index 3e6861bc35..43a6d9e708 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.md +++ b/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.md @@ -39,6 +39,17 @@ option. Using this option multiple times makes the last set string override the previous ones. Set it to NULL to disable its use again. +This option is used to verify a new connection only. The SHA256 hash check is +performed when libcurl establishes a new SSH connection; once that connection +has been successfully verified, it is deemed vetted and may be reused without +performing the SHA256 (or any other host key) verification again, even if you +subsequently change SSH verification-related options. When this SHA256-based +verification is enabled for a new connection, libcurl does not additionally +consult CURLOPT_SSH_KNOWNHOSTS(3) or SSH host key callbacks (including +CURLOPT_SSH_HOST_PUBLIC_KEY_MD5(3)) for that connection, so you should not +expect multiple host verification methods to be applied to the same new +connection. + # DEFAULT NULL @@ -52,11 +63,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "sftp://example.com/file"); curl_easy_setopt(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, "NDVkMTQxMGQ1ODdmMjQ3MjczYjAyOTY5MmRkMjVmNDQ="); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSH_KEYFUNCTION.md b/docs/libcurl/opts/CURLOPT_SSH_KEYFUNCTION.md index 636b7a3609..85801b8af7 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_KEYFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_SSH_KEYFUNCTION.md @@ -68,7 +68,7 @@ called if CURLOPT_SSH_KNOWNHOSTS(3) is also set. This callback function gets passed the curl handle, the key from the known_hosts file *knownkey*, the key from the remote site *foundkey*, info from libcurl on the matching status and a custom pointer (set with -CURLOPT_SSH_KEYDATA(3)). It MUST return one of the following return codes to +CURLOPT_SSH_KEYDATA(3)). It must return one of the following return codes to tell libcurl how to act: ## CURLKHSTAT_FINE_REPLACE @@ -141,7 +141,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_SSH_KNOWNHOSTS, "/home/user/known_hosts"); curl_easy_perform(curl); -} + } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_SSH_KNOWNHOSTS.md b/docs/libcurl/opts/CURLOPT_SSH_KNOWNHOSTS.md index e4443a10b6..fc7e607771 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_KNOWNHOSTS.md +++ b/docs/libcurl/opts/CURLOPT_SSH_KNOWNHOSTS.md @@ -45,6 +45,18 @@ option. Using this option multiple times makes the last set string override the previous ones. Set it to NULL to disable its use again. +This option is only consulted when libcurl establishes a new connection. Once +a connection has been created and its host key verified against the known +hosts file, it is deemed vetted and may be reused by libcurl without +re-running the known hosts check, even if you later change SSH host +verification options (including setting this option to NULL or using +CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256(3) or CURLOPT_SSH_HOST_PUBLIC_KEY_MD5(3)). +Such changes only affect subsequently created connections; existing cached +connections can continue to be reused with the verification that was in effect +when they were first established. If you need to force re-verification with +the new settings, use CURLOPT_FRESH_CONNECT(3) or CURLOPT_FORBID_REUSE(3) to +avoid reusing the old connection. + # DEFAULT NULL @@ -58,11 +70,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "sftp://example.com/file"); curl_easy_setopt(curl, CURLOPT_SSH_KNOWNHOSTS, "/home/clarkkent/.ssh/known_hosts"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSH_PRIVATE_KEYFILE.md b/docs/libcurl/opts/CURLOPT_SSH_PRIVATE_KEYFILE.md index a92f744d59..2a1f79845d 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_PRIVATE_KEYFILE.md +++ b/docs/libcurl/opts/CURLOPT_SSH_PRIVATE_KEYFILE.md @@ -57,12 +57,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "sftp://example.com/file"); curl_easy_setopt(curl, CURLOPT_SSH_PRIVATE_KEYFILE, "/home/clarkkent/.ssh/id_rsa"); curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "password"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSH_PUBLIC_KEYFILE.md b/docs/libcurl/opts/CURLOPT_SSH_PUBLIC_KEYFILE.md index d7878e8626..161daf4ecf 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_PUBLIC_KEYFILE.md +++ b/docs/libcurl/opts/CURLOPT_SSH_PUBLIC_KEYFILE.md @@ -30,7 +30,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSH_PUBLIC_KEYFILE, Pass a char pointer pointing to a *filename* for your public key. If not used, libcurl defaults to **$HOME/.ssh/id_dsa.pub** if the HOME environment variable -is set, and just "id_dsa.pub" in the current directory if HOME is not set. +is set, and "id_dsa.pub" in the current directory if HOME is not set. If NULL (or an empty string) is passed to this option, libcurl passes no public key to the SSH library, which then rather derives it from the private @@ -53,11 +53,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "sftp://example.com/file"); curl_easy_setopt(curl, CURLOPT_SSH_PUBLIC_KEYFILE, "/home/clarkkent/.ssh/id_rsa.pub"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSLCERT.md b/docs/libcurl/opts/CURLOPT_SSLCERT.md index 20e7d1a176..d3669e90a4 100644 --- a/docs/libcurl/opts/CURLOPT_SSLCERT.md +++ b/docs/libcurl/opts/CURLOPT_SSLCERT.md @@ -11,11 +11,7 @@ See-also: Protocol: - TLS TLS-backend: - - OpenSSL - - GnuTLS - - mbedTLS - - Schannel - - wolfSSL + - All Added-in: 7.1 --- @@ -70,12 +66,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_SSLCERT, "client.pem"); curl_easy_setopt(curl, CURLOPT_SSLKEY, "key.pem"); curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "s3cret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSLCERTTYPE.md b/docs/libcurl/opts/CURLOPT_SSLCERTTYPE.md index acf875dfb5..8497016083 100644 --- a/docs/libcurl/opts/CURLOPT_SSLCERTTYPE.md +++ b/docs/libcurl/opts/CURLOPT_SSLCERTTYPE.md @@ -58,13 +58,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_SSLCERT, "client.pem"); curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); curl_easy_setopt(curl, CURLOPT_SSLKEY, "key.pem"); curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "s3cret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSLCERT_BLOB.md b/docs/libcurl/opts/CURLOPT_SSLCERT_BLOB.md index 5d121de2de..aa69fbbc89 100644 --- a/docs/libcurl/opts/CURLOPT_SSLCERT_BLOB.md +++ b/docs/libcurl/opts/CURLOPT_SSLCERT_BLOB.md @@ -62,7 +62,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; struct curl_blob stblob; stblob.data = certificateData; stblob.len = filesize; @@ -71,7 +71,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_SSLCERT_BLOB, &stblob); curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "P12"); curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "s3cret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSLENGINE.md b/docs/libcurl/opts/CURLOPT_SSLENGINE.md index 0983e93a51..85aa7753ec 100644 --- a/docs/libcurl/opts/CURLOPT_SSLENGINE.md +++ b/docs/libcurl/opts/CURLOPT_SSLENGINE.md @@ -56,10 +56,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_SSLENGINE, "dynamic"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSLENGINE_DEFAULT.md b/docs/libcurl/opts/CURLOPT_SSLENGINE_DEFAULT.md index 72e908b76e..87aab0c2bb 100644 --- a/docs/libcurl/opts/CURLOPT_SSLENGINE_DEFAULT.md +++ b/docs/libcurl/opts/CURLOPT_SSLENGINE_DEFAULT.md @@ -46,11 +46,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_SSLENGINE, "dynamic"); curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSLKEY.md b/docs/libcurl/opts/CURLOPT_SSLKEY.md index 7d311aeec8..f590e16e9e 100644 --- a/docs/libcurl/opts/CURLOPT_SSLKEY.md +++ b/docs/libcurl/opts/CURLOPT_SSLKEY.md @@ -15,6 +15,7 @@ TLS-backend: - mbedTLS - Schannel - wolfSSL + - Rustls Added-in: 7.9.3 --- @@ -59,12 +60,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_SSLCERT, "client.pem"); curl_easy_setopt(curl, CURLOPT_SSLKEY, "key.pem"); curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "s3cret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSLKEYTYPE.md b/docs/libcurl/opts/CURLOPT_SSLKEYTYPE.md index 0a996fc054..aef7fd5f8d 100644 --- a/docs/libcurl/opts/CURLOPT_SSLKEYTYPE.md +++ b/docs/libcurl/opts/CURLOPT_SSLKEYTYPE.md @@ -63,13 +63,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_SSLCERT, "client.pem"); curl_easy_setopt(curl, CURLOPT_SSLKEY, "key.pem"); curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM"); curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "s3cret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSLKEY_BLOB.md b/docs/libcurl/opts/CURLOPT_SSLKEY_BLOB.md index 515fa91b38..a062d39deb 100644 --- a/docs/libcurl/opts/CURLOPT_SSLKEY_BLOB.md +++ b/docs/libcurl/opts/CURLOPT_SSLKEY_BLOB.md @@ -61,7 +61,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; struct curl_blob blob; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); blob.data = certificateData; @@ -75,7 +75,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_SSLKEY_BLOB, &blob); curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "s3cret"); curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSLVERSION.md b/docs/libcurl/opts/CURLOPT_SSLVERSION.md index 366edc0a7e..6aa641def9 100644 --- a/docs/libcurl/opts/CURLOPT_SSLVERSION.md +++ b/docs/libcurl/opts/CURLOPT_SSLVERSION.md @@ -58,58 +58,48 @@ SSL v3 - refused ## CURL_SSLVERSION_TLSv1_0 -TLS v1.0 or later (Added in 7.34.0) +TLS v1.0 or later ## CURL_SSLVERSION_TLSv1_1 -TLS v1.1 or later (Added in 7.34.0) +TLS v1.1 or later ## CURL_SSLVERSION_TLSv1_2 -TLS v1.2 or later (Added in 7.34.0) +TLS v1.2 or later ## CURL_SSLVERSION_TLSv1_3 -TLS v1.3 or later (Added in 7.52.0) +TLS v1.3 or later ## -The maximum TLS version can be set by using *one* of the -CURL_SSLVERSION_MAX_ macros below. It is also possible to OR *one* of the -CURL_SSLVERSION_ macros with *one* of the CURL_SSLVERSION_MAX_ macros. +The maximum TLS version can be set by using *one* of the CURL_SSLVERSION_MAX_ +macros below. It is also possible to OR *one* of the CURL_SSLVERSION_ macros +with *one* of the CURL_SSLVERSION_MAX_ macros. ## CURL_SSLVERSION_MAX_DEFAULT The flag defines the maximum supported TLS version by libcurl, or the default value from the SSL library is used. libcurl uses a sensible default maximum, which was TLS v1.2 up to before 7.61.0 and is TLS v1.3 since then - assuming -the TLS library support it. (Added in 7.54.0) +the TLS library support it. ## CURL_SSLVERSION_MAX_TLSv1_0 The flag defines maximum supported TLS version as TLS v1.0. -(Added in 7.54.0) ## CURL_SSLVERSION_MAX_TLSv1_1 The flag defines maximum supported TLS version as TLS v1.1. -(Added in 7.54.0) ## CURL_SSLVERSION_MAX_TLSv1_2 The flag defines maximum supported TLS version as TLS v1.2. -(Added in 7.54.0) ## CURL_SSLVERSION_MAX_TLSv1_3 The flag defines maximum supported TLS version as TLS v1.3. -(Added in 7.54.0) - -## - -In versions of curl prior to 7.54 the CURL_SSLVERSION_TLS options were -documented to allow *only* the specified TLS version, but behavior was -inconsistent depending on the TLS library. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_SSL_CIPHER_LIST.md b/docs/libcurl/opts/CURLOPT_SSL_CIPHER_LIST.md index c71a58d685..1bbbf6e8c5 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_CIPHER_LIST.md +++ b/docs/libcurl/opts/CURLOPT_SSL_CIPHER_LIST.md @@ -17,7 +17,7 @@ TLS-backend: - Schannel - wolfSSL - mbedTLS - - rustls + - Rustls - GnuTLS Added-in: 7.9 --- @@ -80,12 +80,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, "ECDHE-ECDSA-CHACHA20-POLY1305:" "ECDHE-RSA-CHACHA20-POLY1305"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md b/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md index 6dc81a0867..a2132ee5b3 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md +++ b/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md @@ -81,9 +81,10 @@ static CURLcode sslctx_function(CURL *curl, void *sslctx, void *pointer) int main(void) { - CURL *ch; - CURLcode rv; - char *mypem = /* CA cert in PEM format, replace the XXXs */ + CURL *curl; + CURLcode result; + /* CA cert in PEM format, replace the XXXs */ + char *mypem = "-----BEGIN CERTIFICATE-----\n" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" @@ -94,23 +95,23 @@ int main(void) "-----END CERTIFICATE-----\n"; curl_global_init(CURL_GLOBAL_ALL); - ch = curl_easy_init(); + curl = curl_easy_init(); - curl_easy_setopt(ch, CURLOPT_SSLCERTTYPE, "PEM"); - curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, 1L); - curl_easy_setopt(ch, CURLOPT_URL, "https://www.example.com/"); + curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); - curl_easy_setopt(ch, CURLOPT_SSL_CTX_FUNCTION, *sslctx_function); - curl_easy_setopt(ch, CURLOPT_SSL_CTX_DATA, mypem); - rv = curl_easy_perform(ch); - if(!rv) + curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, *sslctx_function); + curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, mypem); + result = curl_easy_perform(curl); + if(result == CURLE_OK) printf("*** transfer succeeded ***\n"); else printf("*** transfer failed ***\n"); - curl_easy_cleanup(ch); + curl_easy_cleanup(curl); curl_global_cleanup(); - return rv; + return (int)result; } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md b/docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md index 75e1dc8edb..f0460e5357 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_SSL_CTX_FUNCTION.md @@ -40,13 +40,13 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSL_CTX_FUNCTION, Pass a pointer to your callback function, which should match the prototype shown above. -This callback function gets called by libcurl just before the initialization -of an SSL connection after having processed all other SSL related options to -give a last chance to an application to modify the behavior of the SSL -initialization. The *ssl_ctx* parameter is a pointer to the SSL library's -*SSL_CTX* for OpenSSL or wolfSSL, a pointer to *mbedtls_ssl_config* for -mbedTLS. If an error is returned from the callback no attempt to establish a -connection is made and the perform operation returns the callback's error +This callback function gets called by libcurl immediately before the +initialization of an SSL connection after having processed all other SSL +related options to give a last chance to an application to modify the behavior +of the SSL initialization. The *ssl_ctx* parameter is a pointer to the SSL +library's *SSL_CTX* for OpenSSL or wolfSSL, a pointer to *mbedtls_ssl_config* +for mbedTLS. If an error is returned from the callback no attempt to establish +a connection is made and the perform operation returns the callback's error code. Set the *clientp* argument passed in to this callback with the CURLOPT_SSL_CTX_DATA(3) option. @@ -70,6 +70,13 @@ anything about it, which then subsequently can lead to libcurl unknowingly reusing SSL connections with different properties. To remedy this you may set CURLOPT_FORBID_REUSE(3) from the callback function. +A connection that is set up with this callback can be put in the connection +pool by libcurl and then reused in following transfers without the callback +being called. The connection may even be selected from the pool to be used for +transfers not using this callback. If the callback should only be valid for +the specific transfer the callback verifies, it should be marked unsuitable +for reuse with CURLOPT_FORBID_REUSE(3). + If you are using DNS-over-HTTPS (DoH) via CURLOPT_DOH_URL(3) then this callback is also called for those transfers and the curl handle is set to an internal handle. **This behavior is subject to change.** We recommend setting @@ -134,9 +141,10 @@ static CURLcode sslctx_function(CURL *curl, void *sslctx, void *pointer) int main(void) { - CURL *ch; - CURLcode rv; - char *mypem = /* CA cert in PEM format, replace the XXXs */ + CURL *curl; + CURLcode result; + /* CA cert in PEM format, replace the XXXs */ + char *mypem = "-----BEGIN CERTIFICATE-----\n" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" @@ -147,23 +155,23 @@ int main(void) "-----END CERTIFICATE-----\n"; curl_global_init(CURL_GLOBAL_ALL); - ch = curl_easy_init(); + curl = curl_easy_init(); - curl_easy_setopt(ch, CURLOPT_SSLCERTTYPE, "PEM"); - curl_easy_setopt(ch, CURLOPT_SSL_VERIFYPEER, 1L); - curl_easy_setopt(ch, CURLOPT_URL, "https://www.example.com/"); + curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com/"); - curl_easy_setopt(ch, CURLOPT_SSL_CTX_FUNCTION, *sslctx_function); - curl_easy_setopt(ch, CURLOPT_SSL_CTX_DATA, mypem); - rv = curl_easy_perform(ch); - if(!rv) + curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, *sslctx_function); + curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, mypem); + result = curl_easy_perform(curl); + if(result == CURLE_OK) printf("*** transfer succeeded ***\n"); else printf("*** transfer failed ***\n"); - curl_easy_cleanup(ch); + curl_easy_cleanup(curl); curl_global_cleanup(); - return rv; + return (int)result; } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_SSL_EC_CURVES.md b/docs/libcurl/opts/CURLOPT_SSL_EC_CURVES.md index fcdef1d5b4..674e1ac6d5 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_EC_CURVES.md +++ b/docs/libcurl/opts/CURLOPT_SSL_EC_CURVES.md @@ -53,10 +53,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_SSL_EC_CURVES, "X25519:P-521"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSL_ENABLE_ALPN.md b/docs/libcurl/opts/CURLOPT_SSL_ENABLE_ALPN.md index a9e20a68d3..2e0399d0fa 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_ENABLE_ALPN.md +++ b/docs/libcurl/opts/CURLOPT_SSL_ENABLE_ALPN.md @@ -45,10 +45,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, 0L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSL_ENABLE_NPN.md b/docs/libcurl/opts/CURLOPT_SSL_ENABLE_NPN.md index e8a6649bfd..849791e9e3 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_ENABLE_NPN.md +++ b/docs/libcurl/opts/CURLOPT_SSL_ENABLE_NPN.md @@ -28,7 +28,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSL_ENABLE_NPN, long npn); # DESCRIPTION -Deprecated since 7.86.0 Setting this option has no function. +Deprecated. Setting this option has no function. Pass a long as parameter, 0 or 1 where 1 is for enable and 0 for disable. This option enables/disables NPN in the SSL handshake (if the SSL backend libcurl @@ -47,10 +47,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_SSL_ENABLE_NPN, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md b/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md index 89a1d430f7..2fdf8ee15f 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md @@ -38,17 +38,17 @@ Tells libcurl to not attempt to use any workarounds for a security flaw in the SSL3 and TLS1.0 protocols. If this option is not used or this bit is set to 0, the SSL layer libcurl uses may use a work-around for this flaw although it might cause interoperability problems with some (older) SSL implementations. -WARNING: avoiding this work-around lessens the security, and by setting this -option to 1 you ask for exactly that. This option is only supported for Secure -Transport and OpenSSL. + +**WARNING:** avoiding this work-around lessens the security, and by setting +this option to 1 you ask for exactly that. This option is only supported for +Secure Transport and OpenSSL. ## CURLSSLOPT_NO_REVOKE Tells libcurl to disable certificate revocation checks for those SSL backends where such behavior is present. This option is only supported for Schannel (the native Windows SSL library), with an exception in the case of Windows' -Untrusted Publishers block list which it seems cannot be bypassed. (Added in -7.44.0) +Untrusted Publishers block list which it seems cannot be bypassed. ## CURLSSLOPT_NO_PARTIALCHAIN @@ -80,8 +80,8 @@ Works with wolfSSL on Windows, Linux (Debian, Ubuntu, Gentoo, Fedora, RHEL), macOS, Android and iOS (added in 8.3.0); with GnuTLS (added in 8.5.0) and with OpenSSL and its forks (LibreSSL, BoringSSL, etc) on Windows (Added in 7.71.0). -This works with rustls on Windows, macOS, Android and iOS. On Linux it is -equivalent to using the Mozilla CA certificate bundle. When used with rustls +This works with Rustls on Windows, macOS, Android and iOS. On Linux it is +equivalent to using the Mozilla CA certificate bundle. When used with Rustls _only_ the native CA store is consulted, not other locations set at run time or build time. (Added in 8.13.0) @@ -101,7 +101,7 @@ Tell libcurl to try sending application data as TLS1.3 early data. This option is supported for GnuTLS, wolfSSL, quictls and OpenSSL (but not BoringSSL or AWS-LC). It works on TCP and QUIC connections using ngtcp2. This option works on a best effort basis, -in cases when it wasn't possible to send early data the request is resent +in cases when it was not possible to send early data the request is resent normally post-handshake. This option does not work when using QUIC. (Added in 8.11.0 for GnuTLS and 8.13.0 for wolfSSL, quictls and OpenSSL) @@ -119,12 +119,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* weaken TLS only for use with silly servers */ curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST | CURLSSLOPT_NO_REVOKE); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSL_SESSIONID_CACHE.md b/docs/libcurl/opts/CURLOPT_SSL_SESSIONID_CACHE.md index 6f0d527b83..2e0c7c6fc0 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_SESSIONID_CACHE.md +++ b/docs/libcurl/opts/CURLOPT_SSL_SESSIONID_CACHE.md @@ -50,11 +50,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* switch off session-id use */ curl_easy_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, 0L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSL_SIGNATURE_ALGORITHMS.md b/docs/libcurl/opts/CURLOPT_SSL_SIGNATURE_ALGORITHMS.md index 4fce470bf8..4e7644719c 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_SIGNATURE_ALGORITHMS.md +++ b/docs/libcurl/opts/CURLOPT_SSL_SIGNATURE_ALGORITHMS.md @@ -60,11 +60,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_SSL_SIGNATURE_ALGORITHMS, "DSA+SHA256:rsa_pss_pss_sha256"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SSL_VERIFYHOST.md b/docs/libcurl/opts/CURLOPT_SSL_VERIFYHOST.md index 9db4d4dfd3..2672f7846a 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_VERIFYHOST.md +++ b/docs/libcurl/opts/CURLOPT_SSL_VERIFYHOST.md @@ -8,6 +8,8 @@ See-also: - CURLOPT_CAINFO (3) - CURLOPT_PINNEDPUBLICKEY (3) - CURLOPT_SSL_VERIFYPEER (3) + - CURLOPT_PROXY_SSL_VERIFYHOST (3) + - CURLOPT_DOH_SSL_VERIFYHOST (3) Protocol: - TLS TLS-backend: @@ -37,25 +39,25 @@ its identity. When CURLOPT_SSL_VERIFYHOST(3) is set to 1 or 2, the server certificate must indicate that it was made for the hostname or address curl connects to, or the -connection fails. Simply put, it means it has to have the same name in the -certificate as is used in the URL you operate against. +connection fails. The certificate has to have the same name as is used in the +URL you operate against. curl considers the server the intended one when the Common Name field or a Subject Alternate Name field in the certificate matches the hostname in the URL to which you told curl to connect. When the *verify* value is 0, the connection succeeds regardless of the names -in the certificate. Use that ability with caution, +in the certificate. Use that ability with caution. This option controls checking the server's certificate's claimed identity. The separate CURLOPT_SSL_VERIFYPEER(3) options enables/disables verification that the certificate is signed by a trusted Certificate Authority. -WARNING: disabling verification of the certificate allows bad guys to +**WARNING:** disabling verification of the certificate allows bad guys to man-in-the-middle the communication without you knowing it. Disabling -verification makes the communication insecure. Just having encryption on a -transfer is not enough as you cannot be sure that you are communicating with -the correct end-point. +verification makes the communication insecure. Having encryption on a transfer +is not enough as you cannot be sure that you are communicating with the +correct end-point. When libcurl uses secure protocols it trusts responses and allows for example HSTS and Alt-Svc information to be stored and used subsequently. Disabling diff --git a/docs/libcurl/opts/CURLOPT_SSL_VERIFYPEER.md b/docs/libcurl/opts/CURLOPT_SSL_VERIFYPEER.md index 40a6f75eaf..23a7b6947f 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_VERIFYPEER.md +++ b/docs/libcurl/opts/CURLOPT_SSL_VERIFYPEER.md @@ -50,7 +50,7 @@ When CURLOPT_SSL_VERIFYPEER(3) is enabled, and the verification fails to prove that the certificate is signed by a CA, the connection fails. When this option is disabled (set to zero), the CA certificates are not loaded -and the peer certificate verification is simply skipped. +and the peer certificate verification is skipped. Authenticating the certificate is not enough to be sure about the server. You typically also want to ensure that the server is the server you mean to be @@ -58,11 +58,11 @@ talking to. Use CURLOPT_SSL_VERIFYHOST(3) for that. The check that the host name in the certificate is valid for the hostname you are connecting to is done independently of the CURLOPT_SSL_VERIFYPEER(3) option. -WARNING: disabling verification of the certificate allows bad guys to +**WARNING:** disabling verification of the certificate allows bad guys to man-in-the-middle the communication without you knowing it. Disabling -verification makes the communication insecure. Just having encryption on a -transfer is not enough as you cannot be sure that you are communicating with -the correct end-point. +verification makes the communication insecure. Having encryption on a transfer +is not enough as you cannot be sure that you are communicating with the +correct end-point. When libcurl uses secure protocols it trusts responses and allows for example HSTS and Alt-Svc information to be stored and used subsequently. Disabling diff --git a/docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.md b/docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.md index 14e1ab08b0..b1d373eef6 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.md +++ b/docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.md @@ -51,11 +51,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* ask for OCSP stapling */ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_SUPPRESS_CONNECT_HEADERS.md b/docs/libcurl/opts/CURLOPT_SUPPRESS_CONNECT_HEADERS.md index b05824630b..e86be358ed 100644 --- a/docs/libcurl/opts/CURLOPT_SUPPRESS_CONNECT_HEADERS.md +++ b/docs/libcurl/opts/CURLOPT_SUPPRESS_CONNECT_HEADERS.md @@ -83,7 +83,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); curl_easy_setopt(curl, CURLOPT_HEADER, 1L); - curl_easy_setopt(curl, CURLOPT_PROXY, "http://foo:3128"); + curl_easy_setopt(curl, CURLOPT_PROXY, "http://proxy.example:3128"); curl_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); curl_easy_setopt(curl, CURLOPT_SUPPRESS_CONNECT_HEADERS, 1L); diff --git a/docs/libcurl/opts/CURLOPT_TCP_KEEPINTVL.md b/docs/libcurl/opts/CURLOPT_TCP_KEEPINTVL.md index 9ac65c42b4..2d664439a2 100644 --- a/docs/libcurl/opts/CURLOPT_TCP_KEEPINTVL.md +++ b/docs/libcurl/opts/CURLOPT_TCP_KEEPINTVL.md @@ -28,7 +28,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_TCP_KEEPINTVL, long interval); # DESCRIPTION Pass a long. Sets the interval, in seconds, to wait between sending keepalive -probes. Not all operating systems support this option. (Added in 7.25.0) +probes. Not all operating systems support this option. The maximum value this accepts is 2147483648. Any larger value is capped to this amount. diff --git a/docs/libcurl/opts/CURLOPT_TCP_NODELAY.md b/docs/libcurl/opts/CURLOPT_TCP_NODELAY.md index d6c2269620..50c961890e 100644 --- a/docs/libcurl/opts/CURLOPT_TCP_NODELAY.md +++ b/docs/libcurl/opts/CURLOPT_TCP_NODELAY.md @@ -37,10 +37,9 @@ small packets on the network (where "small packets" means TCP segments less than the Maximum Segment Size for the network). Maximizing the amount of data sent per TCP segment is good because it -amortizes the overhead of the send. However, in some cases small segments may -need to be sent without delay. This is less efficient than sending larger -amounts of data at a time, and can contribute to congestion on the network if -overdone. +amortizes the overhead of the send. In some cases small segments may need to +be sent without delay. This is less efficient than sending larger amounts of +data at a time, and can contribute to congestion on the network if overdone. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_TELNETOPTIONS.md b/docs/libcurl/opts/CURLOPT_TELNETOPTIONS.md index f306c64fa2..8cab6200bc 100644 --- a/docs/libcurl/opts/CURLOPT_TELNETOPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_TELNETOPTIONS.md @@ -51,13 +51,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; struct curl_slist *options; options = curl_slist_append(NULL, "TTTYPE=vt100"); options = curl_slist_append(options, "USER=foobar"); curl_easy_setopt(curl, CURLOPT_URL, "telnet://example.com/"); curl_easy_setopt(curl, CURLOPT_TELNETOPTIONS, options); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); curl_slist_free_all(options); } diff --git a/docs/libcurl/opts/CURLOPT_TFTP_BLKSIZE.md b/docs/libcurl/opts/CURLOPT_TFTP_BLKSIZE.md index dd299ec922..d2de3c19c7 100644 --- a/docs/libcurl/opts/CURLOPT_TFTP_BLKSIZE.md +++ b/docs/libcurl/opts/CURLOPT_TFTP_BLKSIZE.md @@ -45,11 +45,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "tftp://example.com/bootimage"); /* try using larger blocks */ curl_easy_setopt(curl, CURLOPT_TFTP_BLKSIZE, 2048L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_TIMECONDITION.md b/docs/libcurl/opts/CURLOPT_TIMECONDITION.md index d17174c254..f03473f608 100644 --- a/docs/libcurl/opts/CURLOPT_TIMECONDITION.md +++ b/docs/libcurl/opts/CURLOPT_TIMECONDITION.md @@ -9,6 +9,8 @@ See-also: - CURLOPT_TIMEVALUE (3) Protocol: - HTTP + - FILE + - FTP Added-in: 7.1 --- @@ -26,15 +28,15 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_TIMECONDITION, long cond); # DESCRIPTION -Pass a long as parameter. This defines how the CURLOPT_TIMEVALUE(3) time -value is treated. You can set this parameter to *CURL_TIMECOND_IFMODSINCE* -or *CURL_TIMECOND_IFUNMODSINCE*. +Pass a long as parameter. This defines how the CURLOPT_TIMEVALUE(3) time value +is treated. You can set this parameter to *CURL_TIMECOND_IFMODSINCE* or +*CURL_TIMECOND_IFUNMODSINCE*. The last modification time of a file is not always known and in such instances this feature has no effect even if the given time condition would not have -been met. curl_easy_getinfo(3) with the *CURLINFO_CONDITION_UNMET* -option can be used after a transfer to learn if a zero-byte successful -"transfer" was due to this condition not matching. +been met. curl_easy_getinfo(3) with the *CURLINFO_CONDITION_UNMET* option can +be used after a transfer to learn if a zero-byte successful "transfer" was due +to this condition not matching. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.md b/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.md index 62f734f963..07dcc79c64 100644 --- a/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.md +++ b/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.md @@ -17,7 +17,7 @@ TLS-backend: - OpenSSL - wolfSSL - mbedTLS - - rustls + - Rustls Added-in: 7.61.0 --- @@ -70,11 +70,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_TLS13_CIPHERS, "TLS_CHACHA20_POLY1305_SHA256"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_TLSAUTH_PASSWORD.md b/docs/libcurl/opts/CURLOPT_TLSAUTH_PASSWORD.md index d6c871118c..89dd79ebc9 100644 --- a/docs/libcurl/opts/CURLOPT_TLSAUTH_PASSWORD.md +++ b/docs/libcurl/opts/CURLOPT_TLSAUTH_PASSWORD.md @@ -41,7 +41,7 @@ option. Using this option multiple times makes the last set string override the previous ones. Set it to NULL to disable its use again. -This feature relies on TLS SRP which does not work with TLS 1.3. +This feature relies on TLS-SRP which does not work with TLS 1.3. # DEFAULT @@ -56,12 +56,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_TLSAUTH_TYPE, "SRP"); curl_easy_setopt(curl, CURLOPT_TLSAUTH_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_TLSAUTH_PASSWORD, "secret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_TLSAUTH_TYPE.md b/docs/libcurl/opts/CURLOPT_TLSAUTH_TYPE.md index 64ce85e5db..35ae6bf467 100644 --- a/docs/libcurl/opts/CURLOPT_TLSAUTH_TYPE.md +++ b/docs/libcurl/opts/CURLOPT_TLSAUTH_TYPE.md @@ -45,7 +45,7 @@ defined in RFC 5054 and provides mutual authentication if both sides have a shared secret. To use TLS-SRP, you must also set the CURLOPT_TLSAUTH_USERNAME(3) and CURLOPT_TLSAUTH_PASSWORD(3) options. -TLS SRP does not work with TLS 1.3. +TLS-SRP does not work with TLS 1.3. # DEFAULT @@ -60,12 +60,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_TLSAUTH_TYPE, "SRP"); curl_easy_setopt(curl, CURLOPT_TLSAUTH_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_TLSAUTH_PASSWORD, "secret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_TLSAUTH_USERNAME.md b/docs/libcurl/opts/CURLOPT_TLSAUTH_USERNAME.md index 230c3babee..b1d352e80e 100644 --- a/docs/libcurl/opts/CURLOPT_TLSAUTH_USERNAME.md +++ b/docs/libcurl/opts/CURLOPT_TLSAUTH_USERNAME.md @@ -40,7 +40,7 @@ option. Using this option multiple times makes the last set string override the previous ones. Set it to NULL to disable its use again. -This feature relies on TLS SRP which does not work with TLS 1.3. +This feature relies on TLS-SRP which does not work with TLS 1.3. # DEFAULT @@ -55,12 +55,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_TLSAUTH_TYPE, "SRP"); curl_easy_setopt(curl, CURLOPT_TLSAUTH_USERNAME, "user"); curl_easy_setopt(curl, CURLOPT_TLSAUTH_PASSWORD, "secret"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_TRAILERFUNCTION.md b/docs/libcurl/opts/CURLOPT_TRAILERFUNCTION.md index 0636233765..716967d88a 100644 --- a/docs/libcurl/opts/CURLOPT_TRAILERFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_TRAILERFUNCTION.md @@ -78,7 +78,7 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; /* Set the URL of the request */ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); @@ -91,13 +91,13 @@ int main(void) struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Trailer: My-super-awesome-trailer"); - res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + result = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); /* Set the trailers filling callback */ curl_easy_setopt(curl, CURLOPT_TRAILERFUNCTION, trailer_cb); /* Perform the transfer */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLOPT_TRANSFERTEXT.md b/docs/libcurl/opts/CURLOPT_TRANSFERTEXT.md index 4923f33d8f..8e742abb1a 100644 --- a/docs/libcurl/opts/CURLOPT_TRANSFERTEXT.md +++ b/docs/libcurl/opts/CURLOPT_TRANSFERTEXT.md @@ -33,7 +33,7 @@ or similar. libcurl does not do a complete ASCII conversion when doing ASCII transfers over FTP. This is a known limitation/flaw that nobody has rectified. libcurl -simply sets the mode to ASCII and performs a standard transfer. +only sets the mode to ASCII and performs a standard transfer. # DEFAULT @@ -48,10 +48,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/textfile"); curl_easy_setopt(curl, CURLOPT_TRANSFERTEXT, 1L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_TRANSFER_ENCODING.md b/docs/libcurl/opts/CURLOPT_TRANSFER_ENCODING.md index 262c089794..07e7266da5 100644 --- a/docs/libcurl/opts/CURLOPT_TRANSFER_ENCODING.md +++ b/docs/libcurl/opts/CURLOPT_TRANSFER_ENCODING.md @@ -35,8 +35,8 @@ HTTP response sent using a compressed Transfer-Encoding that is automatically uncompressed by libcurl on reception. Transfer-Encoding differs slightly from the Content-Encoding you ask for with -CURLOPT_ACCEPT_ENCODING(3) in that a Transfer-Encoding is strictly meant -to be for the transfer and thus MUST be decoded before the data arrives in the +CURLOPT_ACCEPT_ENCODING(3) in that a Transfer-Encoding is strictly meant to be +for the transfer and thus must be decoded before the data arrives in the client. Traditionally, Transfer-Encoding has been much less used and supported by both HTTP clients and HTTP servers. diff --git a/docs/libcurl/opts/CURLOPT_UPLOAD_BUFFERSIZE.md b/docs/libcurl/opts/CURLOPT_UPLOAD_BUFFERSIZE.md index 209ea32e4e..425fe23deb 100644 --- a/docs/libcurl/opts/CURLOPT_UPLOAD_BUFFERSIZE.md +++ b/docs/libcurl/opts/CURLOPT_UPLOAD_BUFFERSIZE.md @@ -33,7 +33,7 @@ the next layer in the stack to get sent off. In some setups and for some protocols, there is a huge performance benefit of having a larger upload buffer. -This is just treated as a request, not an order. You cannot be guaranteed to +This is treated as a request, not an order. You cannot be guaranteed to actually get the given size. The upload buffer size is by default 64 kilobytes. The maximum buffer size @@ -59,13 +59,13 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "sftp://example.com/foo.bin"); /* ask libcurl to allocate a larger upload buffer */ curl_easy_setopt(curl, CURLOPT_UPLOAD_BUFFERSIZE, 120000L); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_UPLOAD_FLAGS.md b/docs/libcurl/opts/CURLOPT_UPLOAD_FLAGS.md index 973af24b80..0faf3a9944 100644 --- a/docs/libcurl/opts/CURLOPT_UPLOAD_FLAGS.md +++ b/docs/libcurl/opts/CURLOPT_UPLOAD_FLAGS.md @@ -27,12 +27,34 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_UPLOAD_FLAGS, long bitmask); # DESCRIPTION Pass a long as parameter, which is set to a bitmask, to tell libcurl which -flags to send the server relating to uploaded files. The current supported -flags are **CURLULFLAG_ANSWERED**, which sets the **Answered** flag for IMAP -uploads, **CURLULFLAG_DELETED**, which sets the **Deleted** flag for IMAP -uploads, **CURLULFLAG_DRAFT**, which sets the **Draft** flag for IMAP uploads, -**CURLULFLAG_FLAGGED**, which sets the **Flagged** flag for IMAP uploads, and -**CURLULFLAG_SEEN**, which sets the **Seen** flag for IMAP uploads. +flags to send the server relating to uploaded files. The currently supported +flags are: + +## `CURLULFLAG_ANSWERED` + +Sets the **Answered** flag for IMAP uploads. Indicates that the message has +been replied to. + +## `CURLULFLAG_DELETED` + +Sets the **Deleted** flag for IMAP uploads. Marks the message for deletion +rather than immediately removing it. + +## `CURLULFLAG_DRAFT` + +Sets the **Draft** flag for IMAP uploads. Marks the message as an uncompleted +composition. + +## `CURLULFLAG_FLAGGED` + +Sets the **Flagged** flag for IMAP uploads. Marks the message for special +attention. + +## `CURLULFLAG_SEEN` + +Sets the **Seen** flag for IMAP uploads. Marks the message as read. + +## # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_URL.md b/docs/libcurl/opts/CURLOPT_URL.md index d167fa972e..cc1f5366c9 100644 --- a/docs/libcurl/opts/CURLOPT_URL.md +++ b/docs/libcurl/opts/CURLOPT_URL.md @@ -33,23 +33,22 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_URL, char *URL); # DESCRIPTION -Pass in a pointer to the *URL* to work with. The parameter should be a -char * to a null-terminated string which must be URL-encoded in the following -format: +Pass in a pointer to the *URL* to work with. The parameter should be a char * +to a null-terminated string which must be URL-encoded in the following format: scheme://host:port/path For a greater explanation of the format please see RFC 3986. libcurl does not validate the syntax or use the URL until the transfer is -started. Even if you set a crazy value here, curl_easy_setopt(3) might -still return *CURLE_OK*. +started. Even if you set a crazy value here, curl_easy_setopt(3) might still +return *CURLE_OK*. If the given URL is missing a scheme name (such as "http://" or "ftp://" etc) then libcurl guesses based on the host. If the outermost subdomain name matches DICT, FTP, IMAP, LDAP, POP3 or SMTP then that protocol gets used, -otherwise HTTP is used. Since 7.45.0 guessing can be disabled by setting a -default protocol, see CURLOPT_DEFAULT_PROTOCOL(3) for details. +otherwise HTTP is used. Scheme guessing can be disabled by setting a default +protocol, see CURLOPT_DEFAULT_PROTOCOL(3) for details. Should the protocol, either as specified by the URL scheme or deduced by libcurl from the hostname, not be supported by libcurl then @@ -58,15 +57,15 @@ or curl_multi_perform(3) functions when you call them. Use curl_version_info(3) for detailed information of which protocols are supported by the build of libcurl you are using. -CURLOPT_PROTOCOLS_STR(3) can be used to limit what protocols libcurl may -use for this transfer, independent of what libcurl has been compiled to -support. That may be useful if you accept the URL from an external source and -want to limit the accessibility. +CURLOPT_PROTOCOLS_STR(3) can be used to limit what protocols libcurl may use +for this transfer, independent of what libcurl has been compiled to support. +That may be useful if you accept the URL from an external source and want to +limit the accessibility. The CURLOPT_URL(3) string is ignored if CURLOPT_CURLU(3) is set. -Either CURLOPT_URL(3) or CURLOPT_CURLU(3) must be set before a -transfer is started. +Either CURLOPT_URL(3) or CURLOPT_CURLU(3) must be set before a transfer is +started. The application does not have to keep the string around after setting this option. @@ -75,13 +74,13 @@ Using this option multiple times makes the last set string override the previous ones. Set it to NULL to disable its use again. Note however that libcurl needs a URL set to be able to performed a transfer. -The parser used for handling the URL set with CURLOPT_URL(3) is the same -that curl_url_set(3) uses. +The parser used for handling the URL set with CURLOPT_URL(3) is the same that +curl_url_set(3) uses. # ENCODING -The string pointed to in the CURLOPT_URL(3) argument is generally -expected to be a sequence of characters using an ASCII compatible encoding. +The string pointed to in the CURLOPT_URL(3) argument is generally expected to +be a sequence of characters using an ASCII compatible encoding. If libcurl is built with IDN support, the server name part of the URL can use an "international name" by using the current encoding (according to locale) or diff --git a/docs/libcurl/opts/CURLOPT_USERNAME.md b/docs/libcurl/opts/CURLOPT_USERNAME.md index 736c80cf81..faf7031949 100644 --- a/docs/libcurl/opts/CURLOPT_USERNAME.md +++ b/docs/libcurl/opts/CURLOPT_USERNAME.md @@ -41,8 +41,8 @@ include the domain name in order for the server to successfully obtain a Kerberos Ticket. If you do not then the initial part of the authentication handshake may fail. -When using NTLM, the username can be specified simply as the username without -the domain name should the server be part of a single domain and forest. +When using NTLM, the username can be specified without the domain name +should the server be part of a single domain and forest. To include the domain name use either Down-Level Logon Name or UPN (User Principal Name) formats. For example, **EXAMPLE\user** and @@ -70,12 +70,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_USERNAME, "clark"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_USERPWD.md b/docs/libcurl/opts/CURLOPT_USERPWD.md index ba54949bee..d42a83e582 100644 --- a/docs/libcurl/opts/CURLOPT_USERPWD.md +++ b/docs/libcurl/opts/CURLOPT_USERPWD.md @@ -35,8 +35,8 @@ specify the username part with the domain name in order for the server to successfully obtain a Kerberos Ticket. If you do not then the initial part of the authentication handshake may fail. -When using NTLM, the username can be specified simply as the username without -the domain name should the server be part of a single domain and forest. +When using NTLM, the username can be specified without the domain name +should the server be part of a single domain and forest. To specify the domain name use either Down-Level Logon Name or UPN (User Principal Name) formats. For example **EXAMPLE\user** and **user@example.com** @@ -79,12 +79,12 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/foo.bin"); curl_easy_setopt(curl, CURLOPT_USERPWD, "clark:kent"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLOPT_USE_SSL.md b/docs/libcurl/opts/CURLOPT_USE_SSL.md index 9a2d2abe5e..0729f6e84e 100644 --- a/docs/libcurl/opts/CURLOPT_USE_SSL.md +++ b/docs/libcurl/opts/CURLOPT_USE_SSL.md @@ -45,7 +45,7 @@ do not attempt to use SSL. ## CURLUSESSL_TRY Try using SSL, proceed as normal otherwise. Note that server may close the -connection if the negotiation does not succeed. +connection if the negotiation fails. ## CURLUSESSL_CONTROL diff --git a/docs/libcurl/opts/CURLOPT_VERBOSE.md b/docs/libcurl/opts/CURLOPT_VERBOSE.md index 3718003de4..cc33851ac9 100644 --- a/docs/libcurl/opts/CURLOPT_VERBOSE.md +++ b/docs/libcurl/opts/CURLOPT_VERBOSE.md @@ -39,7 +39,7 @@ this used when you debug/report problems. To also get all the protocol data sent and received, consider using the CURLOPT_DEBUGFUNCTION(3). -**WARNING** this may show sensitive contents from headers and data. +**WARNING:** this may show sensitive contents from headers and data. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_WRITEDATA.md b/docs/libcurl/opts/CURLOPT_WRITEDATA.md index 5983de5d27..f6c6a2eeb9 100644 --- a/docs/libcurl/opts/CURLOPT_WRITEDATA.md +++ b/docs/libcurl/opts/CURLOPT_WRITEDATA.md @@ -36,7 +36,7 @@ to *fwrite(3)* when writing data. The internal CURLOPT_WRITEFUNCTION(3) writes the data to the FILE * given with this option, or to stdout if this option has not been set. -If you are using libcurl as a Windows DLL, you **MUST** use a +If you are using libcurl as a Windows DLL, you must also use CURLOPT_WRITEFUNCTION(3) if you set this option or you might experience crashes. diff --git a/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md b/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md index 3ee11d8e47..407484debb 100644 --- a/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md @@ -40,13 +40,12 @@ delivered data, and the size of that data is *nmemb*; *size* is always 1. The data passed to this function is not null-terminated. The callback function is passed as much data as possible in all invokes, but -you must not make any assumptions. It may be one byte, it may be -thousands. The maximum amount of body data that is passed to the write -callback is defined in the curl.h header file: *CURL_MAX_WRITE_SIZE* (the -usual default is 16K). If CURLOPT_HEADER(3) is enabled, which makes header -data get passed to the write callback, you can get up to -*CURL_MAX_HTTP_HEADER* bytes of header data passed into it. This usually means -100K. +you must not make any assumptions. It may be one byte, it may be thousands. +The maximum amount of body data that is passed to the write callback is +defined in the curl.h header file: *CURL_MAX_WRITE_SIZE* (the usual default is +16K). If CURLOPT_HEADER(3) is enabled, which makes header data get passed to +the write callback, you can get up to *CURL_MAX_HTTP_HEADER* bytes of header +data passed into it. This usually means 100K. This function may be called with zero bytes data if the transferred file is empty. @@ -90,7 +89,7 @@ struct memory { static size_t cb(char *data, size_t size, size_t nmemb, void *clientp) { - size_t realsize = size * nmemb; + size_t realsize = nmemb; struct memory *mem = (struct memory *)clientp; char *ptr = realloc(mem->response, mem->size + realsize + 1); @@ -107,18 +106,18 @@ static size_t cb(char *data, size_t size, size_t nmemb, void *clientp) int main(void) { - struct memory chunk = {0}; - CURLcode res; + struct memory chunk = { 0 }; + CURLcode result; CURL *curl = curl_easy_init(); if(curl) { - /* send all data to this function */ + /* send all data to this function */ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb); /* we pass our 'chunk' struct to the callback function */ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); /* send a request */ - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); /* remember to free the buffer */ free(chunk.response); diff --git a/docs/libcurl/opts/CURLOPT_WS_OPTIONS.md b/docs/libcurl/opts/CURLOPT_WS_OPTIONS.md index d1488a762b..965183b38f 100644 --- a/docs/libcurl/opts/CURLOPT_WS_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_WS_OPTIONS.md @@ -64,11 +64,11 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "ws://example.com/"); - /* tell curl we deal with all the WebSocket magic ourselves */ + /* tell curl we deal with all the WebSocket logic ourselves */ curl_easy_setopt(curl, CURLOPT_WS_OPTIONS, CURLWS_RAW_MODE); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_XFERINFODATA.md b/docs/libcurl/opts/CURLOPT_XFERINFODATA.md index 3cc0ddd1ca..5099e0a469 100644 --- a/docs/libcurl/opts/CURLOPT_XFERINFODATA.md +++ b/docs/libcurl/opts/CURLOPT_XFERINFODATA.md @@ -65,7 +65,7 @@ int main(void) if(curl) { struct progress data; - /* pass struct to callback */ + /* pass struct to callback */ curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &data); curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_cb); } diff --git a/docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md b/docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md index 67f48afcb2..161be8fd4b 100644 --- a/docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_XFERINFOFUNCTION.md @@ -105,7 +105,7 @@ int main(void) if(curl) { struct progress data; - /* pass struct to callback */ + /* pass struct to callback */ curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &data); /* enable progress callback getting called */ diff --git a/docs/libcurl/opts/CURLOPT_XOAUTH2_BEARER.md b/docs/libcurl/opts/CURLOPT_XOAUTH2_BEARER.md index d80e38ed78..894c1384a5 100644 --- a/docs/libcurl/opts/CURLOPT_XOAUTH2_BEARER.md +++ b/docs/libcurl/opts/CURLOPT_XOAUTH2_BEARER.md @@ -56,10 +56,10 @@ int main(void) { CURL *curl = curl_easy_init(); if(curl) { - CURLcode res; + CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "pop3://example.com/"); curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, "1ab9cb22bf269a7"); - res = curl_easy_perform(curl); + result = curl_easy_perform(curl); curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLSHOPT_SHARE.md b/docs/libcurl/opts/CURLSHOPT_SHARE.md index 4171430a79..60e1405bb2 100644 --- a/docs/libcurl/opts/CURLSHOPT_SHARE.md +++ b/docs/libcurl/opts/CURLSHOPT_SHARE.md @@ -23,7 +23,7 @@ CURLSHOPT_SHARE - add data to share ~~~c #include -CURLSHcode curl_share_setopt(CURLSH *share, CURLSHOPT_SHARE, long type); +CURLSHcode curl_share_setopt(CURLSH *share, CURLSHOPT_SHARE, int type); ~~~ # DESCRIPTION @@ -54,10 +54,9 @@ the same multi handle share the DNS cache by default without using this option. ## CURL_LOCK_DATA_SSL_SESSION -SSL sessions are shared across the easy handles using this shared -object. This reduces the time spent in the SSL handshake when reconnecting to -the same server. This symbol was added in 7.10.3 but was not implemented until -7.23.0. +SSL sessions are shared across the easy handles using this shared object. This +reduces the time spent in the SSL handshake when reconnecting to the same +server. Note that when you use the multi interface, all easy handles added to the same multi handle share the SSL session cache by default without using this option. @@ -74,9 +73,6 @@ additional transfers added to them if the existing connection is held by the same multi or easy handle. libcurl does not support doing multiplexed streams in different threads using a shared connection. -Support for **CURL_LOCK_DATA_CONNECT** was added in 7.57.0, but the symbol -existed before this. - Note that when you use the multi interface, all easy handles added to the same multi handle share the connection cache by default without using this option. diff --git a/docs/libcurl/opts/CURLSHOPT_UNSHARE.md b/docs/libcurl/opts/CURLSHOPT_UNSHARE.md index 43edb4d256..45ee27dec0 100644 --- a/docs/libcurl/opts/CURLSHOPT_UNSHARE.md +++ b/docs/libcurl/opts/CURLSHOPT_UNSHARE.md @@ -23,7 +23,7 @@ CURLSHOPT_UNSHARE - remove data to share ~~~c #include -CURLSHcode curl_share_setopt(CURLSH *share, CURLSHOPT_UNSHARE, long type); +CURLSHcode curl_share_setopt(CURLSH *share, CURLSHOPT_UNSHARE, int type); ~~~ # DESCRIPTION diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc index 98691cac3c..1eb628b800 100644 --- a/docs/libcurl/opts/Makefile.inc +++ b/docs/libcurl/opts/Makefile.inc @@ -83,6 +83,7 @@ man_MANS = \ CURLINFO_RTSP_SERVER_CSEQ.3 \ CURLINFO_RTSP_SESSION_ID.3 \ CURLINFO_SCHEME.3 \ + CURLINFO_SIZE_DELIVERED.3 \ CURLINFO_SIZE_DOWNLOAD.3 \ CURLINFO_SIZE_DOWNLOAD_T.3 \ CURLINFO_SIZE_UPLOAD.3 \ @@ -114,11 +115,15 @@ man_MANS = \ CURLMOPT_MAX_TOTAL_CONNECTIONS.3 \ CURLMOPT_MAXCONNECTS.3 \ CURLMOPT_NETWORK_CHANGED.3 \ + CURLMOPT_NOTIFYDATA.3 \ + CURLMOPT_NOTIFYFUNCTION.3 \ CURLMOPT_PIPELINING.3 \ CURLMOPT_PIPELINING_SERVER_BL.3 \ CURLMOPT_PIPELINING_SITE_BL.3 \ CURLMOPT_PUSHDATA.3 \ CURLMOPT_PUSHFUNCTION.3 \ + CURLMOPT_QUICK_EXIT.3 \ + CURLMOPT_RESOLVE_THREADS_MAX.3 \ CURLMOPT_SOCKETDATA.3 \ CURLMOPT_SOCKETFUNCTION.3 \ CURLMOPT_TIMERDATA.3 \ diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index bfcf357bf6..0d775fa655 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -161,7 +161,7 @@ CURL_VERSION_ASYNCHDNS 7.10.7 CURL_VERSION_BITS 7.43.0 CURL_VERSION_BROTLI 7.57.0 CURL_VERSION_CONV 7.15.4 -CURL_VERSION_CURLDEBUG 7.19.6 +CURL_VERSION_CURLDEBUG 7.19.6 8.19.0 CURL_VERSION_DEBUG 7.10.6 CURL_VERSION_GSASL 7.76.0 CURL_VERSION_GSSAPI 7.38.0 @@ -234,6 +234,7 @@ CURLE_CONV_REQD 7.15.4 7.82.0 CURLE_COULDNT_CONNECT 7.1 CURLE_COULDNT_RESOLVE_HOST 7.1 CURLE_COULDNT_RESOLVE_PROXY 7.1 +CURLE_ECH_REQUIRED 8.8.0 CURLE_FAILED_INIT 7.1 CURLE_FILE_COULDNT_READ_FILE 7.1 CURLE_FILESIZE_EXCEEDED 7.10.8 @@ -337,7 +338,6 @@ CURLE_UNRECOVERABLE_POLL 7.84.0 CURLE_UNSUPPORTED_PROTOCOL 7.1 CURLE_UPLOAD_FAILED 7.16.3 CURLE_URL_MALFORMAT 7.1 -CURLE_ECH_REQUIRED 8.8.0 CURLE_URL_MALFORMAT_USER 7.1 7.17.0 CURLE_USE_SSL_FAILED 7.17.0 CURLE_WEIRD_SERVER_REPLY 7.51.0 @@ -360,8 +360,8 @@ CURLFINFOFLAG_KNOWN_SIZE 7.21.0 CURLFINFOFLAG_KNOWN_TIME 7.21.0 CURLFINFOFLAG_KNOWN_UID 7.21.0 CURLFOLLOW_ALL 8.13.0 -CURLFOLLOW_OBEYCODE 8.13.0 CURLFOLLOW_FIRSTONLY 8.13.0 +CURLFOLLOW_OBEYCODE 8.13.0 CURLFORM_ARRAY 7.9.1 7.56.0 CURLFORM_ARRAY_END 7.9.1 7.9.5 7.9.6 CURLFORM_ARRAY_START 7.9.1 7.9.5 7.9.6 @@ -466,9 +466,9 @@ CURLINFO_NONE 7.4.1 CURLINFO_NUM_CONNECTS 7.12.3 CURLINFO_OFF_T 7.55.0 CURLINFO_OS_ERRNO 7.12.2 +CURLINFO_POSTTRANSFER_TIME_T 8.10.0 CURLINFO_PRETRANSFER_TIME 7.4.1 CURLINFO_PRETRANSFER_TIME_T 7.61.0 -CURLINFO_POSTTRANSFER_TIME_T 8.10.0 CURLINFO_PRIMARY_IP 7.19.0 CURLINFO_PRIMARY_PORT 7.21.0 CURLINFO_PRIVATE 7.10.3 @@ -492,6 +492,7 @@ CURLINFO_RTSP_CSEQ_RECV 7.20.0 CURLINFO_RTSP_SERVER_CSEQ 7.20.0 CURLINFO_RTSP_SESSION_ID 7.20.0 CURLINFO_SCHEME 7.52.0 +CURLINFO_SIZE_DELIVERED 8.20.0 CURLINFO_SIZE_DOWNLOAD 7.4.1 7.55.0 CURLINFO_SIZE_DOWNLOAD_T 7.55.0 CURLINFO_SIZE_UPLOAD 7.4.1 7.55.0 @@ -558,6 +559,9 @@ CURLMINFO_XFERS_CURRENT 8.16.0 CURLMINFO_XFERS_DONE 8.16.0 CURLMINFO_XFERS_PENDING 8.16.0 CURLMINFO_XFERS_RUNNING 8.16.0 +CURLMNOTIFY_EASY_DONE 8.17.0 +CURLMNOTIFY_INFO_READ 8.17.0 +CURLMNWC_CLEAR_ALL 8.20.0 CURLMNWC_CLEAR_CONNS 8.16.0 CURLMNWC_CLEAR_DNS 8.16.0 CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE 7.30.0 @@ -568,11 +572,15 @@ CURLMOPT_MAX_PIPELINE_LENGTH 7.30.0 CURLMOPT_MAX_TOTAL_CONNECTIONS 7.30.0 CURLMOPT_MAXCONNECTS 7.16.3 CURLMOPT_NETWORK_CHANGED 8.16.0 +CURLMOPT_NOTIFYDATA 8.17.0 +CURLMOPT_NOTIFYFUNCTION 8.17.0 CURLMOPT_PIPELINING 7.16.0 CURLMOPT_PIPELINING_SERVER_BL 7.30.0 CURLMOPT_PIPELINING_SITE_BL 7.30.0 CURLMOPT_PUSHDATA 7.44.0 CURLMOPT_PUSHFUNCTION 7.44.0 +CURLMOPT_QUICK_EXIT 8.20.0 +CURLMOPT_RESOLVE_THREADS_MAX 8.20.0 CURLMOPT_SOCKETDATA 7.15.4 CURLMOPT_SOCKETFUNCTION 7.15.4 CURLMOPT_TIMERDATA 7.16.0 @@ -590,10 +598,10 @@ CURLOPT_APPEND 7.17.0 CURLOPT_AUTOREFERER 7.1 CURLOPT_AWS_SIGV4 7.75.0 CURLOPT_BUFFERSIZE 7.10 +CURLOPT_CA_CACHE_TIMEOUT 7.87.0 CURLOPT_CAINFO 7.4.2 CURLOPT_CAINFO_BLOB 7.77.0 CURLOPT_CAPATH 7.9.8 -CURLOPT_CA_CACHE_TIMEOUT 7.87.0 CURLOPT_CERTINFO 7.19.1 CURLOPT_CHUNK_BGN_FUNCTION 7.21.0 CURLOPT_CHUNK_DATA 7.21.0 @@ -666,8 +674,8 @@ CURLOPT_FTPPORT 7.1 CURLOPT_FTPSSLAUTH 7.12.2 CURLOPT_GSSAPI_DELEGATION 7.22.0 CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS 7.59.0 -CURLOPT_HAPROXYPROTOCOL 7.60.0 CURLOPT_HAPROXY_CLIENT_IP 8.2.0 +CURLOPT_HAPROXYPROTOCOL 7.60.0 CURLOPT_HEADER 7.1 CURLOPT_HEADERDATA 7.10 CURLOPT_HEADERFUNCTION 7.7.2 @@ -704,7 +712,7 @@ CURLOPT_ISSUERCERT_BLOB 7.71.0 CURLOPT_KEEP_SENDING_ON_ERROR 7.51.0 CURLOPT_KEYPASSWD 7.17.0 CURLOPT_KRB4LEVEL 7.3 7.17.0 -CURLOPT_KRBLEVEL 7.16.4 +CURLOPT_KRBLEVEL 7.16.4 8.17.0 CURLOPT_LOCALPORT 7.15.2 CURLOPT_LOCALPORTRANGE 7.15.2 CURLOPT_LOGIN_OPTIONS 7.34.0 @@ -715,7 +723,6 @@ CURLOPT_MAIL_FROM 7.20.0 CURLOPT_MAIL_RCPT 7.20.0 CURLOPT_MAIL_RCPT_ALLLOWFAILS 7.69.0 8.2.0 CURLOPT_MAIL_RCPT_ALLOWFAILS 8.2.0 -CURLOPT_QUICK_EXIT 7.87.0 CURLOPT_MAX_RECV_SPEED_LARGE 7.15.5 CURLOPT_MAX_SEND_SPEED_LARGE 7.15.5 CURLOPT_MAXAGE_CONN 7.65.0 @@ -796,6 +803,7 @@ CURLOPT_PROXYTYPE 7.10 CURLOPT_PROXYUSERNAME 7.19.1 CURLOPT_PROXYUSERPWD 7.1 CURLOPT_PUT 7.1 7.12.1 +CURLOPT_QUICK_EXIT 7.87.0 CURLOPT_QUOTE 7.1 CURLOPT_RANDOM_FILE 7.7 7.84.0 CURLOPT_RANGE 7.1 @@ -880,9 +888,9 @@ CURLOPT_STREAM_WEIGHT 7.46.0 CURLOPT_SUPPRESS_CONNECT_HEADERS 7.54.0 CURLOPT_TCP_FASTOPEN 7.49.0 CURLOPT_TCP_KEEPALIVE 7.25.0 +CURLOPT_TCP_KEEPCNT 8.9.0 CURLOPT_TCP_KEEPIDLE 7.25.0 CURLOPT_TCP_KEEPINTVL 7.25.0 -CURLOPT_TCP_KEEPCNT 8.9.0 CURLOPT_TCP_NODELAY 7.11.2 CURLOPT_TELNETOPTIONS 7.7 CURLOPT_TFTP_BLKSIZE 7.19.4 @@ -966,12 +974,12 @@ CURLPROTO_LDAPS 7.19.4 CURLPROTO_MQTT 7.71.0 CURLPROTO_POP3 7.20.0 CURLPROTO_POP3S 7.20.0 -CURLPROTO_RTMP 7.21.0 -CURLPROTO_RTMPE 7.21.0 -CURLPROTO_RTMPS 7.21.0 -CURLPROTO_RTMPT 7.21.0 -CURLPROTO_RTMPTE 7.21.0 -CURLPROTO_RTMPTS 7.21.0 +CURLPROTO_RTMP 7.21.0 8.20.0 +CURLPROTO_RTMPE 7.21.0 8.20.0 +CURLPROTO_RTMPS 7.21.0 8.20.0 +CURLPROTO_RTMPT 7.21.0 8.20.0 +CURLPROTO_RTMPTE 7.21.0 8.20.0 +CURLPROTO_RTMPTS 7.21.0 8.20.0 CURLPROTO_RTSP 7.20.0 CURLPROTO_SCP 7.19.4 CURLPROTO_SFTP 7.19.4 @@ -1068,11 +1076,11 @@ CURLSSLBACKEND_SECURETRANSPORT 7.64.1 8.15.0 CURLSSLBACKEND_WOLFSSL 7.49.0 CURLSSLOPT_ALLOW_BEAST 7.25.0 CURLSSLOPT_AUTO_CLIENT_CERT 7.77.0 +CURLSSLOPT_EARLYDATA 8.11.0 CURLSSLOPT_NATIVE_CA 7.71.0 CURLSSLOPT_NO_PARTIALCHAIN 7.68.0 CURLSSLOPT_NO_REVOKE 7.44.0 CURLSSLOPT_REVOKE_BEST_EFFORT 7.70.0 -CURLSSLOPT_EARLYDATA 8.11.0 CURLSSLSET_NO_BACKENDS 7.56.0 CURLSSLSET_OK 7.56.0 CURLSSLSET_TOO_LATE 7.56.0 @@ -1128,6 +1136,11 @@ CURLUE_UNKNOWN_PART 7.62.0 CURLUE_UNSUPPORTED_SCHEME 7.62.0 CURLUE_URLDECODE 7.62.0 CURLUE_USER_NOT_ALLOWED 7.62.0 +CURLULFLAG_ANSWERED 8.13.0 +CURLULFLAG_DELETED 8.13.0 +CURLULFLAG_DRAFT 8.13.0 +CURLULFLAG_FLAGGED 8.13.0 +CURLULFLAG_SEEN 8.13.0 CURLUPART_FRAGMENT 7.62.0 CURLUPART_HOST 7.62.0 CURLUPART_OPTIONS 7.62.0 @@ -1139,11 +1152,6 @@ CURLUPART_SCHEME 7.62.0 CURLUPART_URL 7.62.0 CURLUPART_USER 7.62.0 CURLUPART_ZONEID 7.65.0 -CURLULFLAG_ANSWERED 8.13.0 -CURLULFLAG_DELETED 8.13.0 -CURLULFLAG_DRAFT 8.13.0 -CURLULFLAG_FLAGGED 8.13.0 -CURLULFLAG_SEEN 8.13.0 CURLUSESSL_ALL 7.17.0 CURLUSESSL_CONTROL 7.17.0 CURLUSESSL_NONE 7.17.0 diff --git a/docs/libcurl/symbols.pl b/docs/libcurl/symbols.pl index 1751a4887d..4126fe54e1 100755 --- a/docs/libcurl/symbols.pl +++ b/docs/libcurl/symbols.pl @@ -52,7 +52,7 @@ open F, "`) or shell script style (beginning with `#`) and must appear on their own -lines and not alongside actual test data. Most test data files are -syntactically valid XML, although a few files are not (lack of support for -character entities and the preservation of CR/LF characters at the end of -lines are the biggest differences). +lines and not alongside actual test data. Test data files are syntactically +valid XML; lack of support for character entities is a big difference but macros +like %CR fill that particular role here. Each test case source exists as a file matching the format `tests/data/testNUM`, where `NUM` is the unique test number, and must begin @@ -76,6 +75,26 @@ For example, to insert the word hello 100 times: %repeat[100 x hello]% +## Whitespace + +To force CRLF newline, add this macro to the end of the line: + + %CR - carriage return + +To add significant whitespace characters at the end of the line, or to empty +lines: + + %SP - space + %TAB - horizontal tab + +## Special characters + +Macros to help keep data files XML-compliant: + + %AMP - Ampersand: `&` + %GT - Greater-than sign: `>` + %LT - Less-than sign: `<` + ## Insert capped epoch days Mostly to test capped cookie expire dates: `%days[NUM]` inserts the number of @@ -94,6 +113,12 @@ the include instruction: %include filename% +Or, a variant of the above where the file is loaded as a newline-agnostic +text file, and whitespace, special character macros and variables expanded +after inclusion: + + %includetext filename% + ## Conditional lines Lines in the test file can be made to appear conditionally on a specific @@ -140,7 +165,7 @@ Available substitute variables include: - `%FTP6PORT` - IPv6 port number of the FTP server - `%FTPPORT` - Port number of the FTP server - `%FTPSPORT` - Port number of the FTPS server -- `%FTPTIME2` - Timeout in seconds that should be just sufficient to receive a +- `%FTPTIME2` - Timeout in seconds that should be sufficient to receive a response from the test FTP server - `%GOPHER6PORT` - IPv6 port number of the Gopher server - `%GOPHERPORT` - Port number of the Gopher server @@ -159,6 +184,7 @@ Available substitute variables include: - `%IMAPPORT` - Port number of the IMAP server - `%LOGDIR` - Log directory relative to %PWD - `%MQTTPORT` - Port number of the MQTT server +- `%MQTTSPORT` - Port number of the MQTTS server - `%NOLISTENPORT` - Port number where no service is listening - `%POP36PORT` - IPv6 port number of the POP3 server - `%POP3PORT` - Port number of the POP3 server @@ -176,6 +202,7 @@ Available substitute variables include: - `%SRCDIR` - Full path to the source dir - `%SCP_PWD` - Current directory friendly for the SSH server for the scp:// protocol - `%SFTP_PWD` - Current directory friendly for the SSH server for the sftp:// protocol +- `%SSHKEYALGO` - SSH host and client key algorithm, e.g. `ssh-rsa` or `ssh-ed25519` - `%SSHPORT` - Port number of the SCP/SFTP server - `%SSHSRVMD5` - MD5 of SSH server's public key - `%SSHSRVSHA256` - SHA256 of SSH server's public key @@ -222,7 +249,7 @@ When running a unit test and the keywords include `unittest`, the `` section can be left empty to use the standard unit test tool name `unitN` where `N` is the test number. -The `text-ci` make target automatically skips test with the `flaky` keyword. +The `test-ci` make target automatically skips test with the `flaky` keyword. Tests that have strict timing dependencies have the `timing-dependent` keyword. These are intended to eventually be treated specially on CI builds which are @@ -233,7 +260,7 @@ similar. ## `` -### `` +### `` data to be sent to the client on its request and later verified that it arrived safely. Set `nocheck="yes"` to prevent the test script from verifying @@ -248,7 +275,7 @@ the HTTP server overrides the part number response returned for a subsequent request made by the same test to `previous part number + 1`. For example, if a test makes a request which causes the server to return `` that contains keyword `swsbounce` then for the next response it ignores the requested part -number and instead returns ``. And if `` contains keyword +number and instead returns ``. If `` contains keyword `swsbounce` then the next response is `` and so on. This is useful for auth tests and similar. @@ -261,27 +288,30 @@ used as "raw" data. `nonewline=yes` means that the last byte (the trailing newline character) should be cut off from the data before sending or comparing it. -`crlf=yes` forces *header* newlines to become CRLF even if not written so in -the source file. Note that this makes runtests.pl parse and "guess" what is a -header and what is not in order to apply the CRLF line endings appropriately. +`crlf=yes` forces the newlines to become CRLF even if not written so in the +test. + +`crlf=headers` forces *header* newlines to become CRLF even if not written so +in the source file. Note that this makes runtests.pl parse and "guess" what is +a header and what is not in order to apply the CRLF line endings appropriately. For FTP file listings, the `` section is be used *only* if you make sure that there has been a CWD done first to a directory named `test-[NUM]` where `NUM` is the test case number. Otherwise the ftp server cannot know from which test file to load the list content. -### `` +### `` Send back this contents instead of the `` one. The `NUM` is set by: - - The test number in the request line is >10000 and this is the remainder - of [test case number]%10000. - - The request was HTTP and included digest details, which adds 1000 to `NUM` - - If an HTTP request is NTLM type-1, it adds 1001 to `NUM` - - If an HTTP request is NTLM type-3, it adds 1002 to `NUM` - - If an HTTP request is Basic and `NUM` is already >=1000, it adds 1 to `NUM` - - If an HTTP request is Negotiate, `NUM` gets incremented by one for each - request with Negotiate authorization header on the same test case. +- The test number in the request line is >10000 and this is the remainder of + [test case number]%10000. +- The request was HTTP and included digest details, which adds 1000 to `NUM` +- If an HTTP request is NTLM type-1, it adds 1001 to `NUM` +- If an HTTP request is NTLM type-3, it adds 1002 to `NUM` +- If an HTTP request is Basic and `NUM` is already >=1000, it adds 1 to `NUM` +- If an HTTP request is Negotiate, `NUM` gets incremented by one for each + request with Negotiate authorization header on the same test case. Dynamically changing `NUM` in this way allows the test harness to be used to test authentication negotiation where several different requests must be sent @@ -289,15 +319,22 @@ to complete a transfer. The response to each request is found in its own data section. Validating the entire negotiation sequence can be done by specifying a `datacheck` section. -### `` +### `` The connect section is used instead of the 'data' for all CONNECT requests. The remainder of the rules for the data section then apply but with a connect prefix. +`crlf=yes` forces the newlines to become CRLF even if not written so in the +test. + +`crlf=headers` forces *header* newlines to become CRLF even if not written so +in the source file. Note that this makes runtests.pl parse and "guess" what is +a header and what is not in order to apply the CRLF line endings appropriately. + ### `` Address type and address details as logged by the SOCKS proxy. -### `` +### `` if the data is sent but this is what should be checked afterwards. If `nonewline=yes` is set, runtests cuts off the trailing newline from the data before comparing with the one actually received by the client. @@ -305,7 +342,7 @@ before comparing with the one actually received by the client. Use the `mode="text"` attribute if the output is in text mode on platforms that have a text/binary difference. -### `` +### `` The contents of numbered `datacheck` sections are appended to the non-numbered one. @@ -365,7 +402,7 @@ issue. - `auth_required` if this is set and a POST/PUT is made without auth, the server does NOT wait for the full request body to get sent - `delay: [msecs]` - delay this amount after connection -- `idle` - do nothing after receiving the request, just "sit idle" +- `idle` - do nothing after receiving the request, "sit idle" - `stream` - continuously send data to the client, never-ending - `writedelay: [msecs]` delay this amount between reply packets - `skip: [num]` - instructs the server to ignore reading this many bytes from @@ -429,10 +466,12 @@ What server(s) this test case requires/uses. Available servers: - `telnet` - `tftp` -Give only one per line. This subsection is mandatory (use `none` if no servers -are required). Servers that require a special server certificate can have the -PEM certificate filename (found in the `certs` directory) appended to the -server name separated by a space. +Give only one per line. If a test does not require any servers, the `` +subsection should be omitted. + +Servers that require a special server certificate can +have the PEM certificate filename (found in the `certs` directory) appended to +the server name separated by a space. ### `` A list of features that MUST be present in the client/library for this test to @@ -477,7 +516,7 @@ Features testable here are: - `large-size` (size_t is larger than 32-bit) - `libssh2` - `libssh` -- `oldlibssh` (versions before 0.9.4) +- `badlibssh` (libssh configuration incompatible with the test suite) - `libz` - `local-http`. The HTTP server runs on 127.0.0.1 - `manual` @@ -513,7 +552,6 @@ Features testable here are: - `wakeup` - `win32` - `WinIDN` -- `wolfssh` - `wolfssl` - `xattr` - `zstd` @@ -552,23 +590,13 @@ Set the given environment variables to the specified value before the actual command is run. They are restored back to their former values again after the command has been run. -If the variable name has no assignment, no `=`, then that variable is just -deleted. +If the variable name has no assignment, no `=`, then that variable is deleted. -### `` +### `` Command line to run. -Note that the URL that gets passed to the server actually controls what data -that is returned. The last slash in the URL must be followed by a number. That -number (N) is used by the test-server to load test case N and return the data -that is defined within the `` section. - -If there is no test number found above, the HTTP test server uses the number -following the last dot in the given hostname (made so that a CONNECT can still -pass on test number) so that "foo.bar.123" gets treated as test case -123. Alternatively, if an IPv6 address is provided to CONNECT, the last -hexadecimal group in the address is used as the test number. For example the -address "[1234::ff]" would be treated as test case 255. +If the command spans multiple lines, they are concatenated with a space added +between them. Set `type="perl"` to write the test case as a perl script. It implies that there is no memory debugging and valgrind gets shut off for this test. @@ -586,6 +614,9 @@ otherwise written to verify stdout. Set `option="no-include"` to prevent the test script to slap on the `--include` argument. +Set `option="no-memdebug"` to make the test run without the memdebug tracking +system. For tests that are incompatible - multi-threaded for example. + Set `option="no-q"` avoid using `-q` as the first argument in the curl command line. @@ -606,13 +637,19 @@ parameter is the not negative integer number of seconds for the delay. This 'delay' attribute is intended for specific test cases, and normally not needed. -### `` +### `` This creates the named file with this content before the test case is run, which is useful if the test case needs a file to act on. If `nonewline="yes"` is used, the created file gets the final newline stripped off. +`crlf=yes` forces the newlines to become CRLF even if not written so in the +test. + +`mode="text"` normalizes the line endings to make them compare as text on all +platforms. + ### `` 1 to 4 can be appended to 'file' to create more files. @@ -622,12 +659,15 @@ off. ### `` -### `` +### `` Pass this given data on stdin to the tool. If `nonewline` is set, we cut off the trailing newline of this given data before comparing with the one actually received by the client +`crlf=yes` forces the newlines to become CRLF even if not written so in the +test. + ## `` If `test-duphandle` is a listed item here, this is not run when @@ -656,7 +696,7 @@ command exists with a non-zero status code, the test is considered failed. A list of directory entries that are checked for after the test has completed and that must not exist. A listed entry existing causes the test to fail. -### `` +### `` the protocol dump curl should transmit, if `nonewline` is set, we cut off the trailing newline of this given data before comparing with the one actually @@ -666,26 +706,38 @@ comparisons are made. `crlf=yes` forces the newlines to become CRLF even if not written so in the test. -### `` +`crlf=headers` forces *header* newlines to become CRLF even if not written so +in the source file. Note that this makes runtests.pl parse and "guess" what is +a header and what is not in order to apply the CRLF line endings appropriately. + +### `` The protocol dump curl should transmit to an HTTP proxy (when the http-proxy server is used), if `nonewline` is set, we cut off the trailing newline of this given data before comparing with the one actually sent by the client The `` and `` rules are applied before comparisons are made. -### `` +### `` This verifies that this data was passed to stderr. Use the mode="text" attribute if the output is in text mode on platforms that have a text/binary difference. +Use the mode="warn" attribute for curl warning output, as it then makes the +check without newlines and the prefix to better handle that the lines may wrap +at different points depending on the lengths of the lines and terminal width. + `crlf=yes` forces the newlines to become CRLF even if not written so in the test. +`crlf=headers` forces *header* newlines to become CRLF even if not written so +in the source file. Note that this makes runtests.pl parse and "guess" what is +a header and what is not in order to apply the CRLF line endings appropriately. + If `nonewline` is set, we cut off the trailing newline of this given data before comparing with the one actually received by the client -### `` +### `` This verifies that this data was passed to stdout. Use the mode="text" attribute if the output is in text mode on platforms that @@ -697,6 +749,10 @@ before comparing with the one actually received by the client `crlf=yes` forces the newlines to become CRLF even if not written so in the test. +`crlf=headers` forces *header* newlines to become CRLF even if not written so +in the source file. Note that this makes runtests.pl parse and "guess" what is +a header and what is not in order to apply the CRLF line endings appropriately. + `loadfile="filename"` makes loading the data from an external file. ### `` @@ -707,11 +763,18 @@ that the set limits are not exceeded. Supported limits: Allocations: [number of allocation calls] Maximum allocated: [maximum concurrent memory allocated] -### `` +### `` The file's contents must be identical to this after the test is complete. Use the mode="text" attribute if the output is in text mode on platforms that have a text/binary difference. +`crlf=yes` forces the newlines to become CRLF even if not written so in the +test. + +`crlf=headers` forces *header* newlines to become CRLF even if not written so +in the source file. Note that this makes runtests.pl parse and "guess" what is +a header and what is not in order to apply the CRLF line endings appropriately. + ### `` 1 to 4 can be appended to 'file' to compare more files. diff --git a/docs/tests/HTTP.md b/docs/tests/HTTP.md index afbfe9e5bd..88fb9a0c4b 100644 --- a/docs/tests/HTTP.md +++ b/docs/tests/HTTP.md @@ -6,7 +6,9 @@ SPDX-License-Identifier: curl # The curl HTTP Test Suite -This is an additional test suite using a combination of Apache httpd and nghttpx servers to perform various tests beyond the capabilities of the standard curl test suite. +This is an additional test suite using a combination of Apache httpd and +nghttpx servers to perform various tests beyond the capabilities of the +standard curl test suite. # Usage @@ -14,8 +16,8 @@ The test cases and necessary files are in `tests/http`. You can invoke `pytest` from there or from the top level curl checkout and it finds all tests. -``` -curl> pytest test/http +```sh +curl> pytest tests/http platform darwin -- Python 3.9.15, pytest-6.2.0, py-1.10.0, pluggy-0.13.1 rootdir: /Users/sei/projects/curl collected 5 items @@ -23,13 +25,17 @@ collected 5 items tests/http/test_01_basic.py ..... ``` -Pytest takes arguments. `-v` increases its verbosity and can be used several times. `-k ` can be used to run only matching test cases. The `expr` can be something resembling a python test or just a string that needs to match test cases in their names. +Pytest takes arguments. `-v` increases its verbosity and can be used several +times. `-k ` can be used to run only matching test cases. The `expr` can +be something resembling a python test or a string that needs to match test +cases in their names. -``` +```sh curl/tests/http> pytest -vv -k test_01_02 ``` -runs all test cases that have `test_01_02` in their name. This does not have to be the start of the name. +runs all test cases that have `test_01_02` in their name. This does not have +to be the start of the name. Depending on your setup, some test cases may be skipped and appear as `s` in the output. If you run pytest verbose, it also gives you the reason for @@ -39,44 +45,65 @@ skipping. You need: -1. a recent Python, the `cryptography` module and, of course, `pytest` -2. an apache httpd development version. On Debian/Ubuntu, the package `apache2-dev` has this -3. a local `curl` project build -3. optionally, a `nghttpx` with HTTP/3 enabled or h3 test cases are skipped +1. a recent Python, `pytest` and the other modules listed in + `tests/http/requirements.txt` +2. Apache httpd and its development files. On Debian/Ubuntu, the packages + `apache2-bin` and `apache2-dev` have these. +3. the Apache `mod_ssl`, `mod_http2` and `mod_proxy` modules. On Debian/Ubuntu, these + modules are part of the `apache2-bin` package, but other distributions may + package them separately. +4. a local `curl` project build +5. optionally, `nghttpx` with HTTP/3 enabled or h3 test cases are skipped ### Configuration Via curl's `configure` script you may specify: - * `--with-test-nghttpx=` if you have nghttpx to use somewhere outside your `$PATH`. - * `--with-test-httpd=` if you have an Apache httpd installed somewhere else. On Debian/Ubuntu it will otherwise look into `/usr/bin` and `/usr/sbin` to find those. - * `--with-test-caddy=` if you have a Caddy web server installed somewhere else. - * `--with-test-vsftpd=` if you have a vsftpd ftp server installed somewhere else. + * `--with-test-nghttpx=` if you have nghttpx to use + somewhere outside your `$PATH`. + + * `--with-test-httpd=` if you have an Apache httpd + installed somewhere else. On Debian/Ubuntu it otherwise looks into + `/usr/bin` and `/usr/sbin` to find those. + + * `--with-test-caddy=` if you have a Caddy web server + installed somewhere else. + + * `--with-test-vsftpd=` if you have a vsftpd ftp + server installed somewhere else. + * `--with-test-danted=` if you have `dante-server` installed ## Usage Tips -Several test cases are parameterized, for example with the HTTP version to use. If you want to run a test with a particular protocol only, use a command line like: +Several test cases are parameterized, for example with the HTTP version to +use. If you want to run a test with a particular protocol only, use a command +line like: -``` +```sh curl/tests/http> pytest -k "test_02_06 and h2" ``` -Test cases can be repeated, with the `pytest-repeat` module (`pip install pytest-repeat`). Like in: +Test cases can be repeated, with the `pytest-repeat` module (`pip install +pytest-repeat`). Like in: -``` +```sh curl/tests/http> pytest -k "test_02_06 and h2" --count=100 ``` -which then runs this test case a hundred times. In case of flaky tests, you can make pytest stop on the first one with: +which then runs this test case a hundred times. In case of flaky tests, you +can make pytest stop on the first one with: -``` +```sh curl/tests/http> pytest -k "test_02_06 and h2" --count=100 --maxfail=1 ``` -which allow you to inspect output and log files for the failed run. Speaking of log files, the verbosity of pytest is also used to collect curl trace output. If you specify `-v` three times, the `curl` command is started with `--trace`: +which allow you to inspect output and log files for the failed run. Speaking +of log files, the verbosity of pytest is also used to collect curl trace +output. If you specify `-v` three times, the `curl` command is started with +`--trace`: -``` +```sh curl/tests/http> pytest -vvv -k "test_02_06 and h2" --count=100 --maxfail=1 ``` @@ -84,7 +111,10 @@ all of curl's output and trace file are found in `tests/http/gen/curl`. ## Writing Tests -There is a lot of [`pytest` documentation](https://docs.pytest.org/) with examples. No use in repeating that here. Assuming you are somewhat familiar with it, it is useful how *this* general test suite is setup. Especially if you want to add test cases. +There is a lot of [`pytest` documentation](https://docs.pytest.org/) with +examples. No use in repeating that here. Assuming you are somewhat familiar +with it, it is useful how *this* general test suite is setup. Especially if +you want to add test cases. ### Servers @@ -110,22 +140,44 @@ left behind. ### Test Cases -Tests making use of these fixtures have them in their parameter list. This tells pytest that a particular test needs them, so it has to create them. Since one can invoke pytest for just a single test, it is important that a test references the ones it needs. +Tests making use of these fixtures have them in their parameter list. This +tells pytest that a particular test needs them, so it has to create them. +Since one can invoke pytest for a single test, it is important that a test +references the ones it needs. -All test cases start with `test_` in their name. We use a double number scheme to group them. This makes it ease to run only specific tests and also give a short mnemonic to communicate trouble with others in the project. Otherwise you are free to name test cases as you think fitting. +All test cases start with `test_` in their name. We use a double number scheme +to group them. This makes it ease to run only specific tests and also give a +short mnemonic to communicate trouble with others in the project. Otherwise +you are free to name test cases as you think fitting. -Tests are grouped thematically in a file with a single Python test class. This is convenient if you need a special "fixture" for several tests. "fixtures" can have "class" scope. +Tests are grouped thematically in a file with a single Python test class. This +is convenient if you need a special "fixture" for several tests. "fixtures" +can have "class" scope. -There is a curl helper class that knows how to invoke curl and interpret its output. Among other things, it does add the local CA to the command line, so that SSL connections to the test servers are verified. Nothing prevents anyone from running curl directly, for specific uses not covered by the `CurlClient` class. +There is a curl helper class that knows how to invoke curl and interpret its +output. Among other things, it does add the local CA to the command line, so +that SSL connections to the test servers are verified. Nothing prevents anyone +from running curl directly, for specific uses not covered by the `CurlClient` +class. ### mod_curltest -The module source code is found in `testenv/mod_curltest`. It is compiled using the `apxs` command, commonly provided via the `apache2-dev` package. Compilation is quick and done once at the start of a test run. +The module source code is found in `testenv/mod_curltest`. It is compiled +using the `apxs` command, commonly provided via the `apache2-dev` package. +Compilation is quick and done once at the start of a test run. -The module adds 2 "handlers" to the Apache server (right now). Handler are pieces of code that receive HTTP requests and generate the response. Those handlers are: +The module adds 2 "handlers" to the Apache server (right now). Handler are +pieces of code that receive HTTP requests and generate the response. Those +handlers are: + +* `curltest-echo`: hooked up on the path `/curltest/echo`. This one echoes + a request and copies all data from the request body to the response body. + Useful for simulating upload and checking that the data arrived as intended. + +* `curltest-tweak`: hooked up on the path `/curltest/tweak`. This handler is + more of a Swiss army knife. It interprets parameters from the URL query + string to drive its behavior. -* `curltest-echo`: hooked up on the path `/curltest/echo`. This one echoes a request and copies all data from the request body to the response body. Useful for simulating upload and checking that the data arrived as intended. -* `curltest-tweak`: hooked up on the path `/curltest/tweak`. This handler is more of a Swiss army knife. It interprets parameters from the URL query string to drive its behavior. * `status=nnn`: generate a response with HTTP status code `nnn`. * `chunks=n`: generate `n` chunks of data in the response body, defaults to 3. * `chunk_size=nnn`: each chunk should contain `nnn` bytes of data. Maximum is 16KB right now. diff --git a/docs/tests/TEST-SUITE.md b/docs/tests/TEST-SUITE.md index 2110cfb362..0c053b96d5 100644 --- a/docs/tests/TEST-SUITE.md +++ b/docs/tests/TEST-SUITE.md @@ -8,313 +8,315 @@ SPDX-License-Identifier: curl # Running - See the "Requires to run" section for prerequisites. +See the "Requires to run" section for prerequisites. - In the root of the curl repository: +In the root of the curl repository: ./configure && make && make test - To run a specific set of tests (e.g. 303 and 410): +To run a specific set of tests (e.g. 303 and 410): make test TFLAGS="303 410" - To run the tests faster, pass the -j (parallelism) flag: +To run the tests faster, pass the -j (parallelism) flag: make test TFLAGS="-j10" - "make test" builds the test suite support code and invokes the 'runtests.pl' - perl script to run all the tests. The value of `TFLAGS` is passed - directly to 'runtests.pl'. +"make test" builds the test suite support code and invokes the 'runtests.pl' +perl script to run all the tests. The value of `TFLAGS` is passed directly +to 'runtests.pl'. - When you run tests via make, the flags `-a` and `-s` are passed, meaning - to continue running tests even after one fails, and to emit short output. +When you run tests via make, the flags `-a` and `-s` are passed, meaning to +continue running tests even after one fails, and to emit short output. - If you would like to not use those flags, you can run 'runtests.pl' directly. - You must `chdir` into the tests directory, then you can run it like so: +If you would like to not use those flags, you can run 'runtests.pl' +directly. You must `chdir` into the tests directory, then you can run it +like so: ./runtests.pl 303 410 - You must have run `make test` at least once first to build the support code. +You must have run `make test` at least once first to build the support code. - To see what flags are available for runtests.pl, and what output it emits, run: +To see what flags are available for runtests.pl, and what output it emits, +run: - man ./tests/runtests.1 + man ./docs/runtests.1 - After a test fails, examine the tests/log directory for stdout, stderr, and - output from the servers used in the test. +After a test fails, examine the tests/log directory for stdout, stderr, and +output from the servers used in the test. ## Requires to run - - `perl` (and a Unix-style shell) - - `python` (and a Unix-style shell, for SMB and TELNET tests) - - `python-impacket` (for SMB tests) - - `diff` (when a test fails, a diff is shown) - - `stunnel` (for HTTPS and FTPS tests) - - `openssl` (the command line tool, for generating test server certificates) - - `openssh` or `SunSSH` (for SCP and SFTP tests) - - `nghttpx` (for HTTP/2 and HTTP/3 tests) +- `perl` (and a Unix-style shell) +- `python` (and a Unix-style shell, for SMB and TELNET tests) +- `python-impacket` (for SMB tests) +- `diff` (when a test fails, a diff is shown) +- `stunnel` (for HTTPS and FTPS tests) +- `openssl` (the command line tool, for generating test server certificates) +- `openssh` or `SunSSH` (for SCP and SFTP tests) +- `nghttpx` (for HTTP/2 and HTTP/3 tests) ### Installation of impacket - The Python-based test servers support Python 3. +The Python-based test servers support Python 3. - Please install python-impacket in the correct Python environment. - You can use pip or your OS' package manager to install 'impacket'. +Please install python-impacket in the correct Python environment. You can +use pip or your OS' package manager to install 'impacket'. - On Debian/Ubuntu the package name is 'python3-impacket' +On Debian/Ubuntu the package name is 'python3-impacket' - On FreeBSD the package name is 'py311-impacket' +On FreeBSD the package name is 'py311-impacket' - On any system where pip is available: 'python3 -m pip install impacket' +On any system where pip is available: 'python3 -m pip install impacket' - You may also need to manually install the Python package 'six' - as that may be a missing requirement for impacket. +You may also need to manually install the Python package 'six' as that may +be a missing requirement for impacket. ## Event-based - If curl is built with `Debug` enabled (see below), then the `runtests.pl` - script offers a `-e` option (or `--test-event`) that makes it perform - *event-based*. Such tests invokes the curl tool with `--test-event`, a - debug-only option made for this purpose. +If curl is built with `Debug` enabled (see below), then the `runtests.pl` +script offers a `-e` option (or `--test-event`) that makes it perform +*event-based*. Such tests invokes the curl tool with `--test-event`, a +debug-only option made for this purpose. - Performing event-based means that the curl tool uses the - `curl_multi_socket_action()` API call to drive the transfer(s), instead of - the otherwise "normal" functions it would use. This allows us to test drive - the socket_action API. Transfers done this way should work exactly the same - as with the non-event based API. +Performing event-based means that the curl tool uses the +`curl_multi_socket_action()` API call to drive the transfer(s), instead of +the otherwise "normal" functions it would use. This allows us to test drive +the socket_action API. Transfers done this way should work exactly the same +as with the non-event based API. - To be able to use `--test-event` together with `--parallel`, curl requires - *libuv* to be present and enabled in the build: `configure --enable-libuv` +To be able to use `--test-event` together with `--parallel`, curl requires +*libuv* to be present and enabled in the build: `configure --enable-libuv` ## Duplicated handles - If curl is built with `Debug` enabled (see below), then the `runtests.pl` - script offers a `--test-duphandle` option. When enabled, curl always - duplicates the easy handle and does its transfers using the new one instead - of the original. This is done entirely for testing purpose to verify that - everything works exactly the same when this is done; confirming that the - `curl_easy_duphandle()` function duplicates everything that it should. +If curl is built with `Debug` enabled (see below), then the `runtests.pl` +script offers a `--test-duphandle` option. When enabled, curl always +duplicates the easy handle and does its transfers using the new one instead +of the original. This is done entirely for testing purpose to verify that +everything works exactly the same when this is done; confirming that the +`curl_easy_duphandle()` function duplicates everything that it should. ### Port numbers used by test servers - All test servers run on "random" port numbers. All tests must be written to - use the suitable variables instead of fixed port numbers so that test cases - continue to work independently of what port numbers the test servers - actually use. +All test servers run on "random" port numbers. All tests must be written to +use the suitable variables instead of fixed port numbers so that test cases +continue to work independently of what port numbers the test servers +actually use. - See [`FILEFORMAT`](FILEFORMAT.md) for the port number variables. +See [`FILEFORMAT`](FILEFORMAT.md) for the port number variables. ### Test servers - The test suite runs stand-alone servers on random ports to which it makes - requests. For SSL tests, it runs stunnel to handle encryption to the regular - servers. For SSH, it runs a standard OpenSSH server. +The test suite runs stand-alone servers on random ports to which it makes +requests. For SSL tests, it runs stunnel to handle encryption to the regular +servers. For SSH, it runs a standard OpenSSH server. - The listen port numbers for the test servers are picked randomly to allow - users to run multiple test cases concurrently and to not collide with other - existing services that might listen to ports on the machine. +The listen port numbers for the test servers are picked randomly to allow +users to run multiple test cases concurrently and to not collide with other +existing services that might listen to ports on the machine. - The HTTP server supports listening on a Unix domain socket, the default - location is 'http.sock'. +The HTTP server supports listening on a Unix domain socket, the default +location is 'http.sock'. - For HTTP/2 and HTTP/3 testing an installed `nghttpx` is used. HTTP/3 tests - check if nghttpx supports the protocol. To override the nghttpx used, set - the environment variable `NGHTTPX`. The default can also be changed by - specifying `--with-test-nghttpx=` as argument to `configure`. +For HTTP/2 and HTTP/3 testing an installed `nghttpx` is used. HTTP/3 tests +check if nghttpx supports the protocol. To override the nghttpx used, set +the environment variable `NGHTTPX`. The default can also be changed by +specifying `--with-test-nghttpx=` as argument to `configure`. ### DNS server - There is a test DNS server to allow tests to resolve hostnames to verify - those code paths. This server is started like all the other servers within - the `` section. +There is a test DNS server to allow tests to resolve hostnames to verify +those code paths. This server is started like all the other servers within +the `` section. - To make a curl build actually use the test DNS server requires a debug - build. When such a test runs, the environment variable `CURL_DNS_SERVER` is - set to identify the IP address and port number of the DNS server to use. +To make a curl build actually use the test DNS server requires a debug +build. When such a test runs, the environment variable `CURL_DNS_SERVER` is +set to identify the IP address and port number of the DNS server to use. - - curl built to use c-ares for resolving automatically asks that server for - host information +- curl built to use c-ares for resolving automatically asks that server for + host information - - curl built to use `getaddrinfo()` for resolving *and* is built with c-ares - 1.26.0 or later, gets a special work-around. In such builds, when the - environment variable is set, curl instead invokes a getaddrinfo wrapper - that emulates the function and acknowledges the DNS server environment - variable. This way, the getaddrinfo-using code paths in curl are verified, - and yet the custom responses from the test DNS server are used. +- curl built to use `getaddrinfo()` for resolving *and* is built with c-ares + 1.26.0 or later, gets a special work-around. In such builds, when the + environment variable is set, curl instead invokes a getaddrinfo wrapper + that emulates the function and acknowledges the DNS server environment + variable. This way, the getaddrinfo-using code paths in curl are verified, + and yet the custom responses from the test DNS server are used. - curl that is built to support a custom DNS server in a test gets the - `override-dns` feature set. +curl that is built to support a custom DNS server in a test gets the +`override-dns` feature set. - When curl ask for HTTPS-RR, c-ares is always used and in debug builds such - asks respects the dns server environment variable as well. +When curl ask for HTTPS-RR, c-ares is always used and in debug builds such +asks respects the dns server environment variable as well. - The test DNS server only has a few limited responses. When asked for +The test DNS server only has a few limited responses. When asked for - - type `A` response, it returns the address `127.0.0.1` three times - - type `AAAA` response, it returns the address `::1` three times - - other types, it returns a blank response without answers +- type `A` response, it returns the address `127.0.0.1` three times +- type `AAAA` response, it returns the address `::1` three times +- other types, it returns a blank response without answers ### Shell startup scripts - Tests which use the ssh test server, SCP/SFTP tests, might be badly - influenced by the output of system wide or user specific shell startup - scripts, .bashrc, .profile, /etc/csh.cshrc, .login, /etc/bashrc, etc. which - output text messages or escape sequences on user login. When these shell - startup messages or escape sequences are output they might corrupt the - expected stream of data which flows to the sftp-server or from the ssh - client which can result in bad test behavior or even prevent the test server - from running. +Tests which use the ssh test server, SCP/SFTP tests, might be badly +influenced by the output of system wide or user specific shell startup +scripts, .bashrc, .profile, /etc/csh.cshrc, .login, /etc/bashrc, etc. which +output text messages or escape sequences on user login. When these shell +startup messages or escape sequences are output they might corrupt the +expected stream of data which flows to the sftp-server or from the ssh +client which can result in bad test behavior or even prevent the test server +from running. - If the test suite ssh or sftp server fails to start up and logs the message - 'Received message too long' then you are certainly suffering the unwanted - output of a shell startup script. Locate, cleanup or adjust the shell - script. +If the test suite ssh or sftp server fails to start up and logs the message +'Received message too long' then you are certainly suffering the unwanted +output of a shell startup script. Locate, cleanup or adjust the shell +script. ### Memory test - The test script checks that all allocated memory is freed properly IF curl - has been built with the `CURLDEBUG` define set. The script automatically - detects if that is the case, and it uses the `memanalyze.pl` script to - analyze the memory debugging output. +The test script checks that all allocated memory is freed properly IF curl +has been built with the `DEBUGBUILD` define set. The script automatically +detects if that is the case, and it uses the `memanalyze.pl` script to +analyze the memory debugging output. - Also, if you run tests on a machine where valgrind is found, the script uses - valgrind to run the test with (unless you use `-n`) to further verify - correctness. +Also, if you run tests on a machine where valgrind is found, the script uses +valgrind to run the test with (unless you use `-n`) to further verify +correctness. - The `runtests.pl` `-t` option enables torture testing mode. It runs each - test many times and makes each different memory allocation fail on each - successive run. This tests the out of memory error handling code to ensure - that memory leaks do not occur even in those situations. It can help to - compile curl with `CPPFLAGS=-DMEMDEBUG_LOG_SYNC` when using this option, to - ensure that the memory log file is properly written even if curl crashes. +The `runtests.pl` `-t` option enables torture testing mode. It runs each +test many times and makes each different memory allocation fail on each +successive run. This tests the out of memory error handling code to ensure +that memory leaks do not occur even in those situations. It can help to +compile curl with `CPPFLAGS=-DMEMDEBUG_LOG_SYNC` when using this option, to +ensure that the memory log file is properly written even if curl crashes. ### Debug - If a test case fails, you can conveniently get the script to invoke the - debugger (gdb) for you with the server running and the same command line - parameters that failed. Just invoke `runtests.pl -g` and then - just type 'run' in the debugger to perform the command through the debugger. +If a test case fails, you can conveniently get the script to invoke the +debugger (gdb) for you with the server running and the same command line +parameters that failed. Invoke `runtests.pl -g` and then +type 'run' in the debugger to perform the command through the debugger. ### Logs - All logs are generated in the log/ subdirectory (it is emptied first in the - runtests.pl script). They remain in there after a test run. +All logs are generated in the log/ subdirectory (it is emptied first in the +runtests.pl script). They remain in there after a test run. ### Log Verbosity - A curl build with `--enable-debug` offers more verbose output in the logs. - This applies not only for test cases, but also when running it standalone - with `curl -v`. While a curl debug built is - ***not suitable for production***, it is often helpful in tracking down - problems. +A curl build with `--enable-debug` offers more verbose output in the logs. +This applies not only for test cases, but also when running it standalone +with `curl -v`. While a curl debug built is +***not suitable for production***, it is often helpful in tracking down +problems. - Sometimes, one needs detailed logging of operations, but does not want - to drown in output. The newly introduced *connection filters* allows one to - dynamically increase log verbosity for a particular *filter type*. Example: +Sometimes, one needs detailed logging of operations, but does not want +to drown in output. The newly introduced *connection filters* allows one to +dynamically increase log verbosity for a particular *filter type*. Example: - CURL_DEBUG=ssl curl -v https://curl.se + CURL_DEBUG=ssl curl -v https://curl.se/ - makes the `ssl` connection filter log more details. One may do that for - every filter type and also use a combination of names, separated by `,` or - space. +makes the `ssl` connection filter log more details. One may do that for +every filter type and also use a combination of names, separated by `,` or +space. - CURL_DEBUG=ssl,http/2 curl -v https://curl.se + CURL_DEBUG=ssl,http/2 curl -v https://curl.se/ - The order of filter type names is not relevant. Names used here are - case insensitive. Note that these names are implementation internals and - subject to change. +The order of filter type names is not relevant. Names used here are +case insensitive. Note that these names are implementation internals and +subject to change. - Some, likely stable names are `tcp`, `ssl`, `http/2`. For a current list, - one may search the sources for `struct Curl_cftype` definitions and find - the names there. Also, some filters are only available with certain build - options, of course. +Some, likely stable names are `tcp`, `ssl`, `http/2`. For a current list, +one may search the sources for `struct Curl_cftype` definitions and find +the names there. Also, some filters are only available with certain build +options, of course. ### Test input files - All test cases are put in the `data/` subdirectory. Each test is stored in - the file named according to the test number. +All test cases are put in the `data/` subdirectory. Each test is stored in +the file named according to the test number. - See [`FILEFORMAT`](FILEFORMAT.md) for a description of the test case file - format. +See [`FILEFORMAT`](FILEFORMAT.md) for a description of the test case file +format. ### Code coverage - gcc provides a tool that can determine the code coverage figures for the - test suite. To use it, configure curl with `CFLAGS='-fprofile-arcs - -ftest-coverage -g -O0'`. Make sure you run the normal and torture tests to - get more full coverage, i.e. do: +gcc provides a tool that can determine the code coverage figures for the +test suite. To use it, configure curl with `CFLAGS='-fprofile-arcs +-ftest-coverage -g -O0'`. Make sure you run the normal and torture tests to +get more full coverage, i.e. do: make test make test-torture - The graphical tool `ggcov` can be used to browse the source and create - coverage reports on \*nix hosts: +The graphical tool `ggcov` can be used to browse the source and create +coverage reports on \*nix hosts: ggcov -r lib src - The text mode tool `gcov` may also be used, but it does not handle object - files in more than one directory correctly. +The text mode tool `gcov` may also be used, but it does not handle object +files in more than one directory correctly. ### Remote testing - The runtests.pl script provides some hooks to allow curl to be tested on a - machine where perl can not be run. The test framework in this case runs on - a workstation where perl is available, while curl itself is run on a remote - system using ssh or some other remote execution method. See the comments at - the beginning of runtests.pl for details. +The runtests.pl script provides some hooks to allow curl to be tested on a +machine where perl can not be run. The test framework in this case runs on +a workstation where perl is available, while curl itself is run on a remote +system using ssh or some other remote execution method. See the comments at +the beginning of runtests.pl for details. ## Test case numbering - Test cases used to be numbered by category ranges, but the ranges filled - up. Subsets of tests can now be selected by passing keywords to the - runtests.pl script via the make `TFLAGS` variable. +Test cases used to be numbered by category ranges, but the ranges filled +up. Subsets of tests can now be selected by passing keywords to the +runtests.pl script via the make `TFLAGS` variable. - New tests are added by finding a free number in `tests/data/Makefile.am`. +New tests are added by finding a free number in `tests/data/Makefile.am`. ## Write tests - Here's a quick description on writing test cases. We basically have three - kinds of tests: the ones that test the curl tool, the ones that build small - applications and test libcurl directly and the unit tests that test - individual (possibly internal) functions. +Here's a quick description on writing test cases. We have three kinds of +tests: the ones that test the curl tool, the ones that build small +applications and test libcurl directly and the unit tests that test individual +(possibly internal) functions. ### test data - Each test has a master file that controls all the test data. What to read, - what the protocol exchange should look like, what exit code to expect and - what command line arguments to use etc. +Each test has a master file that controls all the test data. What to read, +what the protocol exchange should look like, what exit code to expect and +what command line arguments to use etc. - These files are `tests/data/test[num]` where `[num]` is just a unique - identifier described above, and the XML-like file format of them is - described in the separate [`FILEFORMAT`](FILEFORMAT.md) document. +These files are `tests/data/test[num]` where `[num]` is a unique identifier +described above, and the XML-like file format of them is described in the +separate [`FILEFORMAT`](FILEFORMAT.md) document. ### curl tests - A test case that runs the curl tool and verifies that it gets the correct - data, it sends the correct data, it uses the correct protocol primitives - etc. +A test case that runs the curl tool and verifies that it gets the correct +data, it sends the correct data, it uses the correct protocol primitives +etc. ### libcurl tests - The libcurl tests are identical to the curl ones, except that they use a - specific and dedicated custom-built program to run instead of "curl". This - tool is built from source code placed in `tests/libtest` and if you want to - make a new libcurl test that is where you add your code. +The libcurl tests are identical to the curl ones, except that they use a +specific and dedicated custom-built program to run instead of "curl". This +tool is built from source code placed in `tests/libtest` and if you want to +make a new libcurl test that is where you add your code. ### unit tests - Unit tests are placed in `tests/unit`. There is a tests/unit/README - describing the specific set of checks and macros that may be used when - writing tests that verify behaviors of specific individual functions. +Unit tests are placed in `tests/unit`. There is a tests/unit/README +describing the specific set of checks and macros that may be used when +writing tests that verify behaviors of specific individual functions. - The unit tests depend on curl being built with debug enabled. +The unit tests depend on curl being built with debug enabled. ### test bundles - Individual tests are bundled into single executables, one for libtests, one - for unit tests and one for servers. The executables' first argument is - the name of libtest, unit test or server respectively. - In these executables, the build process automatically renames the entry point - to a unique symbol. `test` becomes `test_`, e.g. `test_lib1598` or - `test_unit1305`. For servers `main` becomes `main_sws` for the `sws` server, - and so on. Other common symbols may also be suffixed the same way. +Individual tests are bundled into single executables, one for libtests, one +for unit tests and one for servers. The executables' first argument is +the name of libtest, unit test or server respectively. +In these executables, the build process automatically renames the entry point +to a unique symbol. `test` becomes `test_`, e.g. `test_lib1598` or +`test_unit1305`. For servers `main` becomes `main_sws` for the `sws` server, +and so on. Other common symbols may also be suffixed the same way. diff --git a/docs/wcurl.md b/docs/wcurl.md index 4111af5226..f0ed42baf2 100644 --- a/docs/wcurl.md +++ b/docs/wcurl.md @@ -31,26 +31,29 @@ Added-in: n/a **wcurl** is a simple curl wrapper which lets you use curl to download files without having to remember any parameters. -Simply call **wcurl** with a list of URLs you want to download and **wcurl** +Call **wcurl** with a list of URLs you want to download and **wcurl** picks sane defaults. If you need anything more complex, you can provide any of curl's supported -parameters via the **--curl-options** option. Just beware that you likely -should be using curl directly if your use case is not covered. +parameters via the **--curl-options** option. Beware that you likely should be +using curl directly if your use case is not covered. By default, **wcurl** does: -## * Percent-encode whitespaces in URLs; +## * Percent-encode whitespace in URLs; ## * Download multiple URLs in parallel if the installed curl's version is \>= 7.66.0 (--parallel); +## * Use a total number of 5 parallel connections to the same protocol + hostname + port number target + if the installed curl's version is \>= 8.16.0 (--parallel-max-host); + ## * Follow redirects; ## * Automatically choose a filename as output; ## * Avoid overwriting files - if the installed curl's version is \>= 7.83.0 (--no-clobber); + if the installed curl's version is \>= 7.83.0 (--no-clobber); ## * Perform retries; @@ -84,12 +87,12 @@ last value is considered. ## --no-decode-filename -Don't percent-decode the output filename, even if the percent-encoding in the -URL was done by **wcurl**, e.g.: The URL contained whitespaces. +Do not percent-decode the output filename, even if the percent-encoding in the +URL was done by **wcurl**, e.g.: The URL contained whitespace. ## --dry-run -Do not actually execute curl, just print what would be invoked. +Do not actually execute curl, print what would be invoked. ## -V, \--version @@ -107,7 +110,7 @@ is instead forwarded to the curl invocation. # URL URL to be downloaded. Anything that is not a parameter is considered -an URL. Whitespaces are percent-encoded and the URL is passed to curl, which +an URL. Whitespace is percent-encoded and the URL is passed to curl, which then performs the parsing. May be specified more than once. # EXAMPLES @@ -124,10 +127,16 @@ Download a file passing the **--progress-bar** and **--http2** flags to curl: **wcurl --curl-options="--progress-bar --http2" example.com/filename.txt** -Resume from an interrupted download (if more options are used, this needs to -be the last one in the list): +Resume from an interrupted download. The options necessary to resume the download +(`--clobber --continue-at -`) must be the **last** options specified in `--curl-options`. +Note that the only way to resume interrupted downloads is to allow wcurl to overwrite +the destination file: -**wcurl --curl-options="--continue-at -" example.com/filename.txt** +**wcurl --curl-options="--clobber --continue-at -" example.com/filename.txt** + +Download multiple files without a limit of concurrent connections per host (the default limit is 5): + +**wcurl --curl-options="--parallel-max-host 0" example.com/filename1.txt example.com/filename2.txt** # AUTHORS @@ -138,7 +147,7 @@ be the last one in the list): # REPORTING BUGS If you experience any problems with **wcurl** that you do not experience with -curl, submit an issue on Github: https://github.com/curl/wcurl +curl, submit an issue on GitHub: https://github.com/curl/wcurl # COPYRIGHT diff --git a/include/curl/Makefile.am b/include/curl/Makefile.am index f664f6ab7e..f2810fd307 100644 --- a/include/curl/Makefile.am +++ b/include/curl/Makefile.am @@ -25,7 +25,7 @@ pkginclude_HEADERS = \ curl.h curlver.h easy.h mprintf.h stdcheaders.h multi.h \ typecheck-gcc.h system.h urlapi.h options.h header.h websockets.h -pkgincludedir= $(includedir)/curl +pkgincludedir = $(includedir)/curl CHECKSRC = $(CS_$(V)) CS_0 = @echo " RUN " $@; diff --git a/include/curl/curl.h b/include/curl/curl.h index 6f4aa90f13..6961a6c4c1 100644 --- a/include/curl/curl.h +++ b/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 */ @@ -202,26 +202,26 @@ struct curl_httppost { long flags; /* as defined below */ /* specified content is a filename */ -#define CURL_HTTPPOST_FILENAME (1<<0) +#define CURL_HTTPPOST_FILENAME (1 << 0) /* specified content is a filename */ -#define CURL_HTTPPOST_READFILE (1<<1) +#define CURL_HTTPPOST_READFILE (1 << 1) /* name is only stored pointer do not free in formfree */ -#define CURL_HTTPPOST_PTRNAME (1<<2) +#define CURL_HTTPPOST_PTRNAME (1 << 2) /* contents is only stored pointer do not free in formfree */ -#define CURL_HTTPPOST_PTRCONTENTS (1<<3) +#define CURL_HTTPPOST_PTRCONTENTS (1 << 3) /* upload file from buffer */ -#define CURL_HTTPPOST_BUFFER (1<<4) +#define CURL_HTTPPOST_BUFFER (1 << 4) /* upload file from pointer contents */ -#define CURL_HTTPPOST_PTRBUFFER (1<<5) +#define CURL_HTTPPOST_PTRBUFFER (1 << 5) /* upload file contents by using the regular read callback to get the data and pass the given pointer as custom pointer */ -#define CURL_HTTPPOST_CALLBACK (1<<6) +#define CURL_HTTPPOST_CALLBACK (1 << 6) /* use size in 'contentlen', added in 7.46.0 */ -#define CURL_HTTPPOST_LARGE (1<<7) +#define CURL_HTTPPOST_LARGE (1 << 7) char *showfilename; /* The filename to show. If not set, the - actual filename will be used (if this - is a file part) */ + actual filename is used (if this is + a file part) */ void *userp; /* custom pointer used for HTTPPOST_CALLBACK posts */ curl_off_t contentlen; /* alternative length of contents @@ -229,9 +229,8 @@ struct curl_httppost { set. Added in 7.46.0 */ }; - -/* This is a return code for the progress callback that, when returned, will - signal libcurl to continue executing the default progress function */ +/* This is a return code for the progress callback that, when returned, + signals libcurl to continue executing the default progress function */ #define CURL_PROGRESSFUNC_CONTINUE 0x10000001 /* This is the CURLOPT_PROGRESSFUNCTION callback prototype. It is now @@ -268,17 +267,17 @@ typedef int (*curl_xferinfo_callback)(void *clientp, #ifndef CURL_MAX_HTTP_HEADER /* The only reason to have a max limit for this is to avoid the risk of a bad - server feeding libcurl with a never-ending header that will cause reallocs + server feeding libcurl with a never-ending header that causes reallocs infinitely */ #define CURL_MAX_HTTP_HEADER (100*1024) #endif /* This is a magic return code for the write callback that, when returned, - will signal libcurl to pause receiving on the current transfer. */ + signals libcurl to pause receiving on the current transfer. */ #define CURL_WRITEFUNC_PAUSE 0x10000001 /* This is a magic return code for the write callback that, when returned, - will signal an error from the callback. */ + signals an error from the callback. */ #define CURL_WRITEFUNC_ERROR 0xFFFFFFFF typedef size_t (*curl_write_callback)(char *buffer, @@ -286,7 +285,7 @@ typedef size_t (*curl_write_callback)(char *buffer, size_t nitems, void *outstream); -/* This callback will be called when a new resolver request is made */ +/* This callback is called when a new resolver request is made */ typedef int (*curl_resolver_start_callback)(void *resolver_state, void *reserved, void *userdata); @@ -304,14 +303,14 @@ typedef enum { CURLFILETYPE_UNKNOWN /* should never occur */ } curlfiletype; -#define CURLFINFOFLAG_KNOWN_FILENAME (1<<0) -#define CURLFINFOFLAG_KNOWN_FILETYPE (1<<1) -#define CURLFINFOFLAG_KNOWN_TIME (1<<2) -#define CURLFINFOFLAG_KNOWN_PERM (1<<3) -#define CURLFINFOFLAG_KNOWN_UID (1<<4) -#define CURLFINFOFLAG_KNOWN_GID (1<<5) -#define CURLFINFOFLAG_KNOWN_SIZE (1<<6) -#define CURLFINFOFLAG_KNOWN_HLINKCOUNT (1<<7) +#define CURLFINFOFLAG_KNOWN_FILENAME (1 << 0) +#define CURLFINFOFLAG_KNOWN_FILETYPE (1 << 1) +#define CURLFINFOFLAG_KNOWN_TIME (1 << 2) +#define CURLFINFOFLAG_KNOWN_PERM (1 << 3) +#define CURLFINFOFLAG_KNOWN_UID (1 << 4) +#define CURLFINFOFLAG_KNOWN_GID (1 << 5) +#define CURLFINFOFLAG_KNOWN_SIZE (1 << 6) +#define CURLFINFOFLAG_KNOWN_HLINKCOUNT (1 << 7) /* Information about a single file, used when doing FTP wildcard matching */ struct curl_fileinfo { @@ -386,11 +385,11 @@ typedef int (*curl_seek_callback)(void *instream, curl_off_t offset, int origin); /* 'whence' */ -/* This is a return code for the read callback that, when returned, will - signal libcurl to immediately abort the current transfer. */ +/* This is a return code for the read callback that, when returned, + signals libcurl to immediately abort the current transfer. */ #define CURL_READFUNC_ABORT 0x10000000 -/* This is a return code for the read callback that, when returned, will - signal libcurl to pause sending data on the current transfer. */ +/* This is a return code for the read callback that, when returned, + signals libcurl to pause sending data on the current transfer. */ #define CURL_READFUNC_PAUSE 0x10000001 /* Return code for when the trailing headers' callback has terminated @@ -401,12 +400,12 @@ typedef int (*curl_seek_callback)(void *instream, #define CURL_TRAILERFUNC_ABORT 1 typedef size_t (*curl_read_callback)(char *buffer, - size_t size, - size_t nitems, - void *instream); + size_t size, + size_t nitems, + void *instream); typedef int (*curl_trailer_callback)(struct curl_slist **list, - void *userdata); + void *userdata); typedef enum { CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ @@ -610,7 +609,7 @@ typedef enum { or wrong format */ CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ CURLE_SSH, /* 79 - error from the SSH layer, somewhat - generic so the error message will be of + generic so the error message is of interest when this has happened */ CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL @@ -628,7 +627,7 @@ typedef enum { CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the - session will be queued */ + session is queued */ CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not match */ CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ @@ -821,40 +820,43 @@ 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 */ #define CURLAUTH_NONE ((unsigned long)0) -#define CURLAUTH_BASIC (((unsigned long)1)<<0) -#define CURLAUTH_DIGEST (((unsigned long)1)<<1) -#define CURLAUTH_NEGOTIATE (((unsigned long)1)<<2) +#define CURLAUTH_BASIC (((unsigned long)1) << 0) +#define CURLAUTH_DIGEST (((unsigned long)1) << 1) +#define CURLAUTH_NEGOTIATE (((unsigned long)1) << 2) /* Deprecated since the advent of CURLAUTH_NEGOTIATE */ #define CURLAUTH_GSSNEGOTIATE CURLAUTH_NEGOTIATE /* Used for CURLOPT_SOCKS5_AUTH to stay terminologically correct */ #define CURLAUTH_GSSAPI CURLAUTH_NEGOTIATE -#define CURLAUTH_NTLM (((unsigned long)1)<<3) -#define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) +#define CURLAUTH_NTLM (((unsigned long)1) << 3) +#define CURLAUTH_DIGEST_IE (((unsigned long)1) << 4) #ifndef CURL_NO_OLDIES /* functionality removed since 8.8.0 */ -#define CURLAUTH_NTLM_WB (((unsigned long)1)<<5) +#define CURLAUTH_NTLM_WB (((unsigned long)1) << 5) #endif -#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_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) & \ + ((unsigned long)0xffffffff)) +#define CURLAUTH_ANYSAFE ((~(CURLAUTH_BASIC | CURLAUTH_DIGEST_IE)) & \ + ((unsigned long)0xffffffff)) -#define CURLSSH_AUTH_ANY ~0L /* all types supported by the server */ -#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 */ -#define CURLSSH_AUTH_HOST (1L<<2) /* host key files */ -#define CURLSSH_AUTH_KEYBOARD (1L<<3) /* keyboard interactive */ -#define CURLSSH_AUTH_AGENT (1L<<4) /* agent (ssh-agent, pageant...) */ -#define CURLSSH_AUTH_GSSAPI (1L<<5) /* gssapi (kerberos, ...) */ -#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY +/* 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 */ +#define CURLSSH_AUTH_HOST (1L << 2) /* host key files */ +#define CURLSSH_AUTH_KEYBOARD (1L << 3) /* keyboard interactive */ +#define CURLSSH_AUTH_AGENT (1L << 4) /* agent (ssh-agent, pageant...) */ +#define CURLSSH_AUTH_GSSAPI (1L << 5) /* gssapi (kerberos, ...) */ +#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY #define CURLGSSAPI_DELEGATION_NONE 0L /* no delegation (default) */ #define CURLGSSAPI_DELEGATION_POLICY_FLAG (1L<<0) /* if permitted by policy */ @@ -886,7 +888,7 @@ enum curl_khstat { CURLKHSTAT_REJECT, /* reject the connection, return an error */ CURLKHSTAT_DEFER, /* do not accept it, but we cannot answer right now. Causes a CURLE_PEER_FAILED_VERIFICATION error but the - connection will be left intact etc */ + connection is left intact etc */ CURLKHSTAT_FINE_REPLACE, /* accept and replace the wrong key */ CURLKHSTAT_LAST /* not for use, only a marker for last-in-list */ }; @@ -916,7 +918,6 @@ typedef int /* return CURLE_OK to accept */ /* or something else to refuse */ - /* parameter for the CURLOPT_USE_SSL option */ #define CURLUSESSL_NONE 0L /* do not attempt to use SSL */ #define CURLUSESSL_TRY 1L /* try using SSL, proceed anyway otherwise */ @@ -934,31 +935,31 @@ typedef enum { have introduced work-arounds for this flaw but those work-arounds sometimes make the SSL communication fail. To regain functionality with those broken servers, a user can this way allow the vulnerability back. */ -#define CURLSSLOPT_ALLOW_BEAST (1L<<0) +#define CURLSSLOPT_ALLOW_BEAST (1L << 0) /* - NO_REVOKE tells libcurl to disable certificate revocation checks for those SSL backends where such behavior is present. */ -#define CURLSSLOPT_NO_REVOKE (1L<<1) +#define CURLSSLOPT_NO_REVOKE (1L << 1) /* - NO_PARTIALCHAIN tells libcurl to *NOT* accept a partial certificate chain if possible. The OpenSSL backend has this ability. */ -#define CURLSSLOPT_NO_PARTIALCHAIN (1L<<2) +#define CURLSSLOPT_NO_PARTIALCHAIN (1L << 2) /* - REVOKE_BEST_EFFORT tells libcurl to ignore certificate revocation offline checks and ignore missing revocation list for those SSL backends where such behavior is present. */ -#define CURLSSLOPT_REVOKE_BEST_EFFORT (1L<<3) +#define CURLSSLOPT_REVOKE_BEST_EFFORT (1L << 3) /* - CURLSSLOPT_NATIVE_CA tells libcurl to use standard certificate store of operating system. Currently implemented under MS-Windows. */ -#define CURLSSLOPT_NATIVE_CA (1L<<4) +#define CURLSSLOPT_NATIVE_CA (1L << 4) /* - CURLSSLOPT_AUTO_CLIENT_CERT tells libcurl to automatically locate and use a client certificate for authentication. (Schannel) */ -#define CURLSSLOPT_AUTO_CLIENT_CERT (1L<<5) +#define CURLSSLOPT_AUTO_CLIENT_CERT (1L << 5) /* If possible, send data using TLS 1.3 early data */ -#define CURLSSLOPT_EARLYDATA (1L<<6) +#define CURLSSLOPT_EARLYDATA (1L << 6) /* The default connection attempt delay in milliseconds for happy eyeballs. CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 and happy-eyeballs-timeout-ms.d document @@ -1025,20 +1026,20 @@ typedef enum { /* bitmask defines for CURLOPT_HEADEROPT */ #define CURLHEADER_UNIFIED 0L -#define CURLHEADER_SEPARATE (1L<<0) +#define CURLHEADER_SEPARATE (1L << 0) /* CURLALTSVC_* are bits for the CURLOPT_ALTSVC_CTRL option */ -#define CURLALTSVC_READONLYFILE (1L<<2) -#define CURLALTSVC_H1 (1L<<3) -#define CURLALTSVC_H2 (1L<<4) -#define CURLALTSVC_H3 (1L<<5) +#define CURLALTSVC_READONLYFILE (1L << 2) +#define CURLALTSVC_H1 (1L << 3) +#define CURLALTSVC_H2 (1L << 4) +#define CURLALTSVC_H3 (1L << 5) /* bitmask values for CURLOPT_UPLOAD_FLAGS */ -#define CURLULFLAG_ANSWERED (1L<<0) -#define CURLULFLAG_DELETED (1L<<1) -#define CURLULFLAG_DRAFT (1L<<2) -#define CURLULFLAG_FLAGGED (1L<<3) -#define CURLULFLAG_SEEN (1L<<4) +#define CURLULFLAG_ANSWERED (1L << 0) +#define CURLULFLAG_DELETED (1L << 1) +#define CURLULFLAG_DRAFT (1L << 2) +#define CURLULFLAG_FLAGGED (1L << 3) +#define CURLULFLAG_SEEN (1L << 4) struct curl_hstsentry { char *name; @@ -1067,42 +1068,43 @@ typedef CURLSTScode (*curl_hstswrite_callback)(CURL *easy, void *userp); /* CURLHSTS_* are bits for the CURLOPT_HSTS option */ -#define CURLHSTS_ENABLE (1L<<0) -#define CURLHSTS_READONLYFILE (1L<<1) +#define CURLHSTS_ENABLE (1L << 0) +#define CURLHSTS_READONLYFILE (1L << 1) /* The CURLPROTO_ defines below are for the **deprecated** CURLOPT_*PROTOCOLS options. Do not use. */ -#define CURLPROTO_HTTP (1L<<0) -#define CURLPROTO_HTTPS (1L<<1) -#define CURLPROTO_FTP (1L<<2) -#define CURLPROTO_FTPS (1L<<3) -#define CURLPROTO_SCP (1L<<4) -#define CURLPROTO_SFTP (1L<<5) -#define CURLPROTO_TELNET (1L<<6) -#define CURLPROTO_LDAP (1L<<7) -#define CURLPROTO_LDAPS (1L<<8) -#define CURLPROTO_DICT (1L<<9) -#define CURLPROTO_FILE (1L<<10) -#define CURLPROTO_TFTP (1L<<11) -#define CURLPROTO_IMAP (1L<<12) -#define CURLPROTO_IMAPS (1L<<13) -#define CURLPROTO_POP3 (1L<<14) -#define CURLPROTO_POP3S (1L<<15) -#define CURLPROTO_SMTP (1L<<16) -#define CURLPROTO_SMTPS (1L<<17) -#define CURLPROTO_RTSP (1L<<18) -#define CURLPROTO_RTMP (1L<<19) -#define CURLPROTO_RTMPT (1L<<20) -#define CURLPROTO_RTMPE (1L<<21) -#define CURLPROTO_RTMPTE (1L<<22) -#define CURLPROTO_RTMPS (1L<<23) -#define CURLPROTO_RTMPTS (1L<<24) -#define CURLPROTO_GOPHER (1L<<25) -#define CURLPROTO_SMB (1L<<26) -#define CURLPROTO_SMBS (1L<<27) -#define CURLPROTO_MQTT (1L<<28) -#define CURLPROTO_GOPHERS (1L<<29) -#define CURLPROTO_ALL (~0L) /* enable everything */ +#define CURLPROTO_HTTP (1L << 0) +#define CURLPROTO_HTTPS (1L << 1) +#define CURLPROTO_FTP (1L << 2) +#define CURLPROTO_FTPS (1L << 3) +#define CURLPROTO_SCP (1L << 4) +#define CURLPROTO_SFTP (1L << 5) +#define CURLPROTO_TELNET (1L << 6) +#define CURLPROTO_LDAP (1L << 7) +#define CURLPROTO_LDAPS (1L << 8) +#define CURLPROTO_DICT (1L << 9) +#define CURLPROTO_FILE (1L << 10) +#define CURLPROTO_TFTP (1L << 11) +#define CURLPROTO_IMAP (1L << 12) +#define CURLPROTO_IMAPS (1L << 13) +#define CURLPROTO_POP3 (1L << 14) +#define CURLPROTO_POP3S (1L << 15) +#define CURLPROTO_SMTP (1L << 16) +#define CURLPROTO_SMTPS (1L << 17) +#define CURLPROTO_RTSP (1L << 18) +#define CURLPROTO_RTMP (1L << 19) +#define CURLPROTO_RTMPT (1L << 20) +#define CURLPROTO_RTMPE (1L << 21) +#define CURLPROTO_RTMPTE (1L << 22) +#define CURLPROTO_RTMPS (1L << 23) +#define CURLPROTO_RTMPTS (1L << 24) +#define CURLPROTO_GOPHER (1L << 25) +#define CURLPROTO_SMB (1L << 26) +#define CURLPROTO_SMBS (1L << 27) +#define CURLPROTO_MQTT (1L << 28) +#define CURLPROTO_GOPHERS (1L << 29) +#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 */ @@ -1115,9 +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 */ @@ -1168,12 +1170,12 @@ typedef enum { * bytes big. */ CURLOPT(CURLOPT_ERRORBUFFER, CURLOPTTYPE_OBJECTPOINT, 10), - /* Function that will be called to store the output (instead of fwrite). The - * parameters will use fwrite() syntax, make sure to follow them. */ + /* Function that is called to store the output (instead of fwrite). The + * parameters use fwrite() syntax, make sure to follow them. */ CURLOPT(CURLOPT_WRITEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 11), - /* Function that will be called to read the input (instead of fread). The - * parameters will use fread() syntax, make sure to follow them. */ + /* Function that is called to read the input (instead of fread). The + * parameters use fread() syntax, make sure to follow them. */ CURLOPT(CURLOPT_READFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 12), /* Time-out the read operation after this amount of seconds */ @@ -1247,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 @@ -1327,7 +1329,7 @@ typedef enum { /* 55 = OBSOLETE */ /* DEPRECATED - * Function that will be called instead of the internal progress display + * Function that is called instead of the internal progress display * function. This function should be defined as the curl_progress_callback * prototype defines. */ CURLOPTDEPRECATED(CURLOPT_PROGRESSFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 56, @@ -1356,8 +1358,9 @@ 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. */ - CURLOPT(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63), + * is set but does not match one of these, 'private' is used. */ + CURLOPTDEPRECATED(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63, + 8.17.0, "removed"), /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ CURLOPT(CURLOPT_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 64), @@ -1408,8 +1411,8 @@ typedef enum { OK within this time, then fine... This only aborts the connect phase. */ CURLOPT(CURLOPT_CONNECTTIMEOUT, CURLOPTTYPE_LONG, 78), - /* Function that will be called to store headers (instead of fwrite). The - * parameters will use fwrite() syntax, make sure to follow them. */ + /* Function that is called to store headers (instead of fwrite). The + * parameters use fwrite() syntax, make sure to follow them. */ CURLOPT(CURLOPT_HEADERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 79), /* Set this to force the HTTP request to get back to GET. Only really usable @@ -1434,7 +1437,7 @@ typedef enum { CURLOPT(CURLOPT_HTTP_VERSION, CURLOPTTYPE_VALUES, 84), /* Specifically switch on or off the FTP engine's use of the EPSV command. By - default, that one will always be attempted before the more traditional + default, that one is always attempted before the more traditional PASV command. */ CURLOPT(CURLOPT_FTP_USE_EPSV, CURLOPTTYPE_LONG, 85), @@ -1512,7 +1515,7 @@ typedef enum { CURLOPT(CURLOPT_UNRESTRICTED_AUTH, CURLOPTTYPE_LONG, 105), /* Specifically switch on or off the FTP engine's use of the EPRT command ( - it also disables the LPRT attempt). By default, those ones will always be + it also disables the LPRT attempt). By default, those ones are always attempted before the good old traditional PORT command. */ CURLOPT(CURLOPT_FTP_USE_EPRT, CURLOPTTYPE_LONG, 106), @@ -1552,7 +1555,7 @@ typedef enum { systems with support for more than one, i.e IPv4 _and_ IPv6. */ CURLOPT(CURLOPT_IPRESOLVE, CURLOPTTYPE_VALUES, 113), - /* Set this option to limit the size of a file that will be downloaded from + /* Set this option to limit the size of a file that is to be downloaded from an HTTP or FTP server. Note there is also _LARGE version which adds large file support for @@ -1569,13 +1572,13 @@ typedef enum { */ CURLOPT(CURLOPT_RESUME_FROM_LARGE, CURLOPTTYPE_OFF_T, 116), - /* Sets the maximum size of data that will be downloaded from + /* Sets the maximum size of data that is to be downloaded from * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. */ CURLOPT(CURLOPT_MAXFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 117), /* Set this option to the filename of your .netrc file you want libcurl - to parse (using the CURLOPT_NETRC option). If not set, libcurl will do + to parse (using the CURLOPT_NETRC option). If not set, libcurl does a poor attempt to find the user's home directory and check for a .netrc file in there. */ CURLOPT(CURLOPT_NETRC_FILE, CURLOPTTYPE_STRINGPOINT, 118), @@ -1633,7 +1636,7 @@ typedef enum { /* Set to non-zero to skip the IP address received in a 227 PASV FTP server response. Typically used for FTP-SSL purposes but is not restricted to - that. libcurl will then instead use the same IP address it used for the + that. libcurl is then instead using the same IP address it used for the control connection. */ CURLOPT(CURLOPT_FTP_SKIP_PASV_IP, CURLOPTTYPE_LONG, 137), @@ -1645,7 +1648,7 @@ typedef enum { CURLOPT(CURLOPT_LOCALPORT, CURLOPTTYPE_LONG, 139), /* Number of ports to try, including the first one set with LOCALPORT. - Thus, setting it to 1 will make no additional attempts but the first. + Thus, setting it to 1 makes no additional attempts but the first. */ CURLOPT(CURLOPT_LOCALPORTRANGE, CURLOPTTYPE_LONG, 140), @@ -1653,19 +1656,19 @@ typedef enum { extracting it with CURLINFO_LASTSOCKET */ CURLOPT(CURLOPT_CONNECT_ONLY, CURLOPTTYPE_LONG, 141), - /* Function that will be called to convert from the + /* Function that is called to convert from the network encoding (instead of using the iconv calls in libcurl) */ CURLOPTDEPRECATED(CURLOPT_CONV_FROM_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 142, 7.82.0, "Serves no purpose anymore"), - /* Function that will be called to convert to the + /* Function that is called to convert to the network encoding (instead of using the iconv calls in libcurl) */ CURLOPTDEPRECATED(CURLOPT_CONV_TO_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 143, 7.82.0, "Serves no purpose anymore"), - /* Function that will be called to convert from UTF8 + /* Function that is called to convert from UTF8 (instead of using the iconv calls in libcurl) Note that this is used only for SSL certificate processing */ CURLOPTDEPRECATED(CURLOPT_CONV_FROM_UTF8_FUNCTION, @@ -1759,9 +1762,9 @@ typedef enum { /* Comma separated list of hostnames defining no-proxy zones. These should match both hostnames directly, and hostnames within a domain. For - example, local.com will match local.com and www.local.com, but NOT + example, local.com matches local.com and www.local.com, but NOT notlocal.com or www.notlocal.com. For compatibility with other - implementations of this, .local.com will be considered to be the same as + implementations of this, .local.com is considered to be the same as local.com. A single * is the only valid wildcard, and effectively disables the use of proxy. */ CURLOPT(CURLOPT_NOPROXY, CURLOPTTYPE_STRINGPOINT, 177), @@ -1871,11 +1874,9 @@ typedef enum { in outgoing requests. The current default is 0, but it might change in a future libcurl release. - libcurl will ask for the compressed methods it knows of, and if that - is not any, it will not ask for transfer-encoding at all even if this - option is set to 1. - - */ + libcurl asks for the compressed methods it knows of, and if that + is not any, it does not ask for transfer-encoding at all even if this + option is set to 1. */ CURLOPT(CURLOPT_TRANSFER_ENCODING, CURLOPTTYPE_LONG, 207), /* Callback function for closing socket (instead of close(2)). The callback @@ -1910,7 +1911,7 @@ typedef enum { /* Enable/disable SASL initial response */ CURLOPT(CURLOPT_SASL_IR, CURLOPTTYPE_LONG, 218), - /* Function that will be called instead of the internal progress display + /* Function that is called instead of the internal progress display * function. This function should be defined as the curl_xferinfo_callback * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */ CURLOPT(CURLOPT_XFERINFOFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 219), @@ -1952,8 +1953,7 @@ typedef enum { /* Pass in a bitmask of "header options" */ CURLOPT(CURLOPT_HEADEROPT, CURLOPTTYPE_VALUES, 229), - /* The public key in DER form used to validate the peer public key - this option is used only if SSL_VERIFYPEER is true */ + /* The public key used to validate the peer public key */ CURLOPT(CURLOPT_PINNEDPUBLICKEY, CURLOPTTYPE_STRINGPOINT, 230), /* Path to Unix domain socket */ @@ -2093,7 +2093,7 @@ typedef enum { /* Head start in milliseconds to give happy eyeballs. */ CURLOPT(CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, CURLOPTTYPE_LONG, 271), - /* Function that will be called before a resolver request is made */ + /* Function that is called before a resolver request is made */ CURLOPT(CURLOPT_RESOLVER_START_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 272), /* User data to pass to the resolver start callback. */ @@ -2124,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 */ @@ -2202,8 +2202,8 @@ typedef enum { /* used by scp/sftp to verify the host's public key */ 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). */ + /* Function that is called immediately before the initial request + is made on a connection (after any protocol negotiation step). */ CURLOPT(CURLOPT_PREREQFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 312), /* Data passed to the CURLOPT_PREREQFUNCTION callback */ @@ -2283,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 */ @@ -2294,10 +2293,9 @@ typedef enum { #undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */ #endif - - /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host - name resolves addresses using more than one IP protocol version, this - option might be handy to force libcurl to use a specific IP version. */ +/* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host + name resolves addresses using more than one IP protocol version, this + option might be handy to force libcurl to use a specific IP version. */ #define CURL_IPRESOLVE_WHATEVER 0L /* default, uses addresses to all IP versions that your system allows */ #define CURL_IPRESOLVE_V4 1L /* uses only IPv4 addresses/connections */ @@ -2350,16 +2348,16 @@ typedef enum { #define CURL_RTSPREQ_LAST 12L /* not used */ /* These enums are for use with the CURLOPT_NETRC option. */ -#define CURL_NETRC_IGNORED 0L /* The .netrc will never be read. +#define CURL_NETRC_IGNORED 0L /* The .netrc is never read. This is the default. */ -#define CURL_NETRC_OPTIONAL 1L /* A user:password in the URL will be preferred +#define CURL_NETRC_OPTIONAL 1L /* A user:password in the URL is preferred to one in the .netrc. */ -#define CURL_NETRC_REQUIRED 2L /* A user:password in the URL will be ignored. +#define CURL_NETRC_REQUIRED 2L /* A user:password in the URL is ignored. Unless one is set programmatically, the - .netrc will be queried. */ + .netrc is 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 }; @@ -2381,14 +2379,14 @@ enum CURL_NETRC_OPTION { #define CURL_SSLVERSION_MAX_TLSv1_2 (CURL_SSLVERSION_TLSv1_2 << 16) #define CURL_SSLVERSION_MAX_TLSv1_3 (CURL_SSLVERSION_TLSv1_3 << 16) - /* never use, keep last */ +/* never use, keep last */ #define CURL_SSLVERSION_MAX_LAST (CURL_SSLVERSION_LAST << 16) #define CURL_TLSAUTH_NONE 0L #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 }; @@ -2403,7 +2401,7 @@ enum CURL_TLSAUTH { #define CURL_REDIR_POST_302 2L #define CURL_REDIR_POST_303 4L #define CURL_REDIR_POST_ALL \ - (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303) + (CURL_REDIR_POST_301 | CURL_REDIR_POST_302 | CURL_REDIR_POST_303) #define CURL_TIMECOND_NONE 0L #define CURL_TIMECOND_IFMODSINCE 1L @@ -2411,14 +2409,14 @@ 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 } curl_TimeCond; /* Special size_t value signaling a null-terminated string. */ -#define CURL_ZERO_TERMINATED ((size_t) -1) +#define CURL_ZERO_TERMINATED ((size_t)-1) /* curl_strequal() and curl_strnequal() are subject for removal in a future release */ @@ -2430,7 +2428,7 @@ typedef struct curl_mime curl_mime; /* Mime context. */ typedef struct curl_mimepart curl_mimepart; /* Mime part context. */ /* CURLMIMEOPT_ defines are for the CURLOPT_MIME_OPTIONS option. */ -#define CURLMIMEOPT_FORMESCAPE (1L<<0) /* Use backslash-escaping for forms. */ +#define CURLMIMEOPT_FORMESCAPE (1L << 0) /* Use backslash-escaping for forms */ /* * NAME curl_mime_init() @@ -2637,7 +2635,7 @@ curl_formadd(struct curl_httppost **httppost, /* * callback function for curl_formget() - * The void *arg pointer will be the one passed as second argument to + * The void *arg pointer is the one passed as second argument to * curl_formget(). * The character buffer passed to it must not be freed. * Should return the buffer length passed to it as the argument "len" on @@ -2652,7 +2650,7 @@ typedef size_t (*curl_formget_callback)(void *arg, const char *buf, * DESCRIPTION * * Serialize a curl_httppost struct built with curl_formadd(). - * Accepts a void pointer as second argument which will be passed to + * Accepts a void pointer as second argument which is passed to * the curl_formget_callback function. * Returns 0 on success. */ @@ -2697,7 +2695,7 @@ CURL_EXTERN char *curl_version(void); * %XX versions). This function returns a new allocated string or NULL if an * error occurred. */ -CURL_EXTERN char *curl_easy_escape(CURL *handle, +CURL_EXTERN char *curl_easy_escape(CURL *curl, const char *string, int length); @@ -2705,21 +2703,20 @@ CURL_EXTERN char *curl_easy_escape(CURL *handle, CURL_EXTERN char *curl_escape(const char *string, int length); - /* * NAME curl_easy_unescape() * * 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 * converted into the host encoding. */ -CURL_EXTERN char *curl_easy_unescape(CURL *handle, +CURL_EXTERN char *curl_easy_unescape(CURL *curl, const char *string, - int length, + int inlength, int *outlength); /* the previous version */ @@ -2743,10 +2740,9 @@ CURL_EXTERN void curl_free(void *p); * * curl_global_init() should be invoked exactly once for each application that * uses libcurl and before any call of other libcurl functions. - + * * This function is thread-safe if CURL_VERSION_THREADSAFE is set in the * curl_version_info_data.features flag (fetch by curl_version_info()). - */ CURL_EXTERN CURLcode curl_global_init(long flags); @@ -2760,7 +2756,7 @@ CURL_EXTERN CURLcode curl_global_init(long flags); * initialize libcurl and set user defined memory management callback * functions. Users can implement memory management routines to check for * memory leaks, check for misuse of the curl library etc. User registered - * callback routines will be invoked by this library instead of the system + * callback routines is invoked by this library instead of the system * memory management routines like malloc, free etc. */ CURL_EXTERN CURLcode curl_global_init_mem(long flags, @@ -2787,10 +2783,9 @@ CURL_EXTERN void curl_global_cleanup(void); * * curl_global_trace() can be invoked at application start to * configure which components in curl should participate in tracing. - + * * This function is thread-safe if CURL_VERSION_THREADSAFE is set in the * curl_version_info_data.features flag (fetch by curl_version_info()). - */ CURL_EXTERN CURLcode curl_global_trace(const char *config); @@ -2811,8 +2806,8 @@ struct curl_slist { * * The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The * backend can also be specified via the name parameter (passing -1 as id). If - * both id and name are specified, the name will be ignored. If neither id nor - * name are specified, the function will fail with CURLSSLSET_UNKNOWN_BACKEND + * both id and name are specified, the name is ignored. If neither id nor + * name are specified, the function fails with CURLSSLSET_UNKNOWN_BACKEND * and set the "avail" pointer to the NULL-terminated list of available * backends. * @@ -2823,7 +2818,7 @@ struct curl_slist { * NULL-terminated list of available SSL backends. * * The SSL backend can be set only once. If it has already been set, a - * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE. + * subsequent attempt to change it results in a CURLSSLSET_TOO_LATE. */ struct curl_ssl_backend { @@ -2847,8 +2842,8 @@ CURL_EXTERN CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, * * DESCRIPTION * - * Appends a string to a linked list. If no list exists, it will be created - * first. Returns the new list, after appending. + * Appends a string to a linked list. If no list exists, it is created first. + * Returns the new list, after appending. */ CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *list, const char *data); @@ -2996,7 +2991,8 @@ typedef enum { CURLINFO_EARLYDATA_SENT_T = CURLINFO_OFF_T + 68, CURLINFO_HTTPAUTH_USED = CURLINFO_LONG + 69, CURLINFO_PROXYAUTH_USED = CURLINFO_LONG + 70, - CURLINFO_LASTONE = 70 + CURLINFO_SIZE_DELIVERED = CURLINFO_OFF_T + 71, + CURLINFO_LASTONE = 71 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as @@ -3015,13 +3011,12 @@ typedef enum { CURLCLOSEPOLICY_LAST /* last, never use this */ } curl_closepolicy; -#define CURL_GLOBAL_SSL (1<<0) /* no purpose since 7.57.0 */ -#define CURL_GLOBAL_WIN32 (1<<1) -#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_SSL (1 << 0) /* no purpose since 7.57.0 */ +#define CURL_GLOBAL_WIN32 (1 << 1) +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL | CURL_GLOBAL_WIN32) #define CURL_GLOBAL_NOTHING 0 #define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL -#define CURL_GLOBAL_ACK_EINTR (1<<2) - +#define CURL_GLOBAL_ACK_EINTR (1 << 2) /***************************************************************************** * Setup defines, protos etc for the sharing stuff. @@ -3030,9 +3025,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, @@ -3060,7 +3054,6 @@ typedef void (*curl_unlock_function)(CURL *handle, curl_lock_data data, void *userptr); - typedef enum { CURLSHE_OK, /* all is fine */ CURLSHE_BAD_OPTION, /* 1 */ @@ -3083,9 +3076,8 @@ typedef enum { } CURLSHoption; CURL_EXTERN CURLSH *curl_share_init(void); -CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *share, CURLSHoption option, - ...); -CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *share); +CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...); +CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *sh); /**************************************************************************** * Structures for querying information about the curl library at runtime. @@ -3107,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 { @@ -3194,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 */ @@ -3223,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() @@ -3234,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() @@ -3245,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() @@ -3253,19 +3245,19 @@ CURL_EXTERN const char *curl_share_strerror(CURLSHcode); * DESCRIPTION * * The curl_easy_pause function pauses or unpauses transfers. Select the new - * state by setting the bitmask, use the convenience defines below. + * state by setting the action bitmask, use the convenience defines below. * */ -CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); +CURL_EXTERN CURLcode curl_easy_pause(CURL *curl, int action); -#define CURLPAUSE_RECV (1<<0) -#define CURLPAUSE_RECV_CONT (0) +#define CURLPAUSE_RECV (1 << 0) +#define CURLPAUSE_RECV_CONT 0 -#define CURLPAUSE_SEND (1<<2) -#define CURLPAUSE_SEND_CONT (0) +#define CURLPAUSE_SEND (1 << 2) +#define CURLPAUSE_SEND_CONT 0 -#define CURLPAUSE_ALL (CURLPAUSE_RECV|CURLPAUSE_SEND) -#define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT) +#define CURLPAUSE_ALL (CURLPAUSE_RECV | CURLPAUSE_SEND) +#define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT | CURLPAUSE_SEND_CONT) /* * NAME curl_easy_ssls_import() @@ -3275,7 +3267,7 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); * The curl_easy_ssls_import function adds a previously exported SSL session * to the SSL session cache of the easy handle (or the underlying share). */ -CURL_EXTERN CURLcode curl_easy_ssls_import(CURL *handle, +CURL_EXTERN CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key, const unsigned char *shmac, size_t shmac_len, @@ -3284,7 +3276,7 @@ CURL_EXTERN CURLcode curl_easy_ssls_import(CURL *handle, /* This is the curl_ssls_export_cb callback prototype. It * is passed to curl_easy_ssls_export() to extract SSL sessions/tickets. */ -typedef CURLcode curl_ssls_export_cb(CURL *handle, +typedef CURLcode curl_ssls_export_cb(CURL *curl, void *userptr, const char *session_key, const unsigned char *shmac, @@ -3306,17 +3298,16 @@ typedef CURLcode curl_ssls_export_cb(CURL *handle, * callback. * */ -CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *handle, +CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *curl, curl_ssls_export_cb *export_fn, void *userptr); - #ifdef __cplusplus } /* end of extern "C" */ #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" @@ -3333,13 +3324,19 @@ CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *handle, #include "typecheck-gcc.h" #else #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) +/* This preprocessor magic ensures that application authors pass exactly three + arguments to these functions. For compatibility with C++ global namespace + '::' and reusing these symbols as method names, while also avoiding + recursive macros, use a two-stage solution. */ +#define curl_exactly_three_arguments(a, b, c) (a, b, c) +#define curl_easy_setopt(handle, opt, param) \ + curl_easy_setopt curl_exactly_three_arguments(handle, opt, param) +#define curl_easy_getinfo(handle, info, arg) \ + curl_easy_getinfo curl_exactly_three_arguments(handle, info, arg) +#define curl_share_setopt(share, opt, param) \ + curl_share_setopt curl_exactly_three_arguments(share, opt, param) +#define curl_multi_setopt(handle, opt, param) \ + curl_multi_setopt curl_exactly_three_arguments(handle, opt, param) #endif /* __STDC__ >= 1 */ #endif /* gcc >= 4.3 && !__cplusplus && !CURL_DISABLE_TYPECHECK */ diff --git a/include/curl/curlver.h b/include/curl/curlver.h index 48fb81df81..144f5fea17 100644 --- a/include/curl/curlver.h +++ b/include/curl/curlver.h @@ -32,16 +32,16 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.16.0-DEV" +#define LIBCURL_VERSION "8.20.0-DEV" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 16 +#define LIBCURL_VERSION_MINOR 20 #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 - always follow this syntax: + parsing and comparisons by programs. The LIBCURL_VERSION_NUM define always + follows this syntax: 0xXXYYZZ @@ -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 0x081000 +#define LIBCURL_VERSION_NUM 0x081400 /* * This is the date and time when the full source package was created. The @@ -71,8 +71,8 @@ */ #define LIBCURL_TIMESTAMP "[unreleased]" -#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z)) -#define CURL_AT_LEAST_VERSION(x,y,z) \ +#define CURL_VERSION_BITS(x, y, z) ((x) << 16 | (y) << 8 | (z)) +#define CURL_AT_LEAST_VERSION(x, y, z) \ (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z)) #endif /* CURLINC_CURLVER_H */ diff --git a/include/curl/easy.h b/include/curl/easy.h index fa13564937..197e6e7b92 100644 --- a/include/curl/easy.h +++ b/include/curl/easy.h @@ -50,15 +50,14 @@ CURL_EXTERN void curl_easy_cleanup(CURL *curl); * * Request internal information from the curl session with this function. * The third argument MUST be pointing to the specific type of the used option - * which is documented in each manpage of the option. The data pointed to - * will be filled in accordingly and can be relied upon only if the function + * which is documented in each man page of the option. The data pointed to + * is filled in accordingly and can be relied upon only if the function * returns CURLE_OK. This function is intended to get used *AFTER* a performed * transfer, all results from this function are undefined until the transfer * is completed. */ CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); - /* * NAME curl_easy_duphandle() * @@ -67,7 +66,7 @@ CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); * Creates a new curl session handle with the same options set for the handle * passed in. Duplicating a handle could only be a matter of cloning data and * options, internal state info and things like persistent connections cannot - * be transferred. It is useful in multithreaded applications when you can run + * be transferred. It is useful in multi-threaded applications when you can run * curl_easy_duphandle() for each new thread to avoid a series of identical * curl_easy_setopt() invokes in every thread. */ @@ -79,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. @@ -108,7 +107,6 @@ CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, size_t *n); - /* * NAME curl_easy_upkeep() * diff --git a/include/curl/header.h b/include/curl/header.h index 7465274b9c..5c3281a531 100644 --- a/include/curl/header.h +++ b/include/curl/header.h @@ -31,18 +31,18 @@ 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 */ }; /* 'origin' bits */ -#define CURLH_HEADER (1<<0) /* plain server header */ -#define CURLH_TRAILER (1<<1) /* trailers */ -#define CURLH_CONNECT (1<<2) /* CONNECT headers */ -#define CURLH_1XX (1<<3) /* 1xx headers */ -#define CURLH_PSEUDO (1<<4) /* pseudo headers */ +#define CURLH_HEADER (1 << 0) /* plain server header */ +#define CURLH_TRAILER (1 << 1) /* trailers */ +#define CURLH_CONNECT (1 << 2) /* CONNECT headers */ +#define CURLH_1XX (1 << 3) /* 1xx headers */ +#define CURLH_PSEUDO (1 << 4) /* pseudo headers */ typedef enum { CURLHE_OK, @@ -55,14 +55,14 @@ typedef enum { CURLHE_NOT_BUILT_IN /* if API was disabled in the build */ } CURLHcode; -CURL_EXTERN CURLHcode curl_easy_header(CURL *easy, +CURL_EXTERN CURLHcode curl_easy_header(CURL *curl, const char *name, - size_t index, + size_t nameindex, unsigned int origin, int request, struct curl_header **hout); -CURL_EXTERN struct curl_header *curl_easy_nextheader(CURL *easy, +CURL_EXTERN struct curl_header *curl_easy_nextheader(CURL *curl, unsigned int origin, int request, struct curl_header *prev); diff --git a/include/curl/multi.h b/include/curl/multi.h index 782541f1ab..060b73eeec 100644 --- a/include/curl/multi.h +++ b/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 */ @@ -133,29 +132,29 @@ CURL_EXTERN CURLM *curl_multi_init(void); * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, - CURL *curl_handle); +CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *m, + CURL *curl); - /* - * Name: curl_multi_remove_handle() - * - * Desc: removes a curl handle from the multi stack again - * - * Returns: CURLMcode type, general multi error code. - */ -CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, - CURL *curl_handle); +/* + * Name: curl_multi_remove_handle() + * + * Desc: removes a curl handle from the multi stack again + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *m, + CURL *curl); - /* - * Name: curl_multi_fdset() - * - * Desc: Ask curl for its fd_set sets. The app can use these to select() or - * poll() on. We want curl_multi_perform() called as soon as one of - * them are ready. - * - * Returns: CURLMcode type, general multi error code. - */ -CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, +/* + * Name: curl_multi_fdset() + * + * Desc: Ask curl for its fd_set sets. The app can use these to select() or + * poll() on. We want curl_multi_perform() called as soon as one of + * them are ready. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *m, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, @@ -169,7 +168,7 @@ CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_wait(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, @@ -183,7 +182,7 @@ CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_poll(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, @@ -196,60 +195,60 @@ CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle, * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle); +CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *m); - /* - * Name: curl_multi_perform() - * - * 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. - * - * Returns: CURLMcode type, general multi error code. *NOTE* that this only - * returns errors etc regarding the whole multi stack. There might - * still have occurred problems on individual transfers even when - * this returns OK. - */ -CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, +/* + * Name: curl_multi_perform() + * + * 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. 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 + * still have occurred problems on individual transfers even when + * this returns OK. + */ +CURL_EXTERN CURLMcode curl_multi_perform(CURLM *m, int *running_handles); - /* - * Name: curl_multi_cleanup() - * - * Desc: Cleans up and removes a whole multi stack. It does not free or - * touch any individual easy handles in any way. We need to define - * in what state those handles will be if this function is called - * in the middle of a transfer. - * - * Returns: CURLMcode type, general multi error code. - */ -CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); +/* + * Name: curl_multi_cleanup() + * + * Desc: Cleans up and removes a whole multi stack. It does not free or + * touch any individual easy handles in any way. We need to define + * in what state those handles are going to be if this function is + * called in the middle of a transfer. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *m); /* * Name: curl_multi_info_read() * * 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 - * time, until a special "end of msgs" struct is returned as a signal - * that there is no more to get at this point. + * Repeated calls to this function return a new struct each time, + * until a special "end of msgs" struct is returned as a signal that + * there is no more to get at this point. * - * The data the returned pointer points to will not survive calling + * The data the returned pointer points to does not survive calling * curl_multi_cleanup(). * * The 'CURLMsg' struct is meant to be simple and only contain basic - * information. If more involved information is wanted, we will - * provide the particular "transfer handle" in that struct and that + * information. If more involved information is wanted, we provide + * the particular "transfer handle" in that struct and that * should/could/would be used in subsequent curl_easy_getinfo() calls * (or similar). The point being that we must never expose complex - * structs to applications, as then we will undoubtably get backwards + * structs to applications, as then we undoubtably get backwards * compatibility problems in the future. * * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out @@ -257,7 +256,7 @@ CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); * queue (after this read) in the integer the second argument points * to. */ -CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, +CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *m, int *msgs_in_queue); /* @@ -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 @@ -278,7 +277,7 @@ CURL_EXTERN const char *curl_multi_strerror(CURLMcode); * Desc: An alternative version of curl_multi_perform() that allows the * application to pass in one of the file descriptors that have been * detected to have "action" on them and let libcurl perform. - * See manpage for details. + * See man page for details. */ #define CURL_POLL_NONE 0 #define CURL_POLL_IN 1 @@ -309,27 +308,27 @@ typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */ * * Returns: The callback should return zero. */ -typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */ +typedef int (*curl_multi_timer_callback)(CURLM *m, /* multi handle */ long timeout_ms, /* see above */ void *userp); /* private callback pointer */ CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()") -curl_multi_socket(CURLM *multi_handle, curl_socket_t s, int *running_handles); +curl_multi_socket(CURLM *m, curl_socket_t s, int *running_handles); -CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *m, curl_socket_t s, int ev_bitmask, int *running_handles); CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()") -curl_multi_socket_all(CURLM *multi_handle, int *running_handles); +curl_multi_socket_all(CURLM *m, int *running_handles); #ifndef CURL_ALLOW_OLD_MULTI_SOCKET /* 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) + * 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) #endif /* @@ -341,8 +340,8 @@ curl_multi_socket_all(CURLM *multi_handle, int *running_handles); * * Returns: CURLM error code. */ -CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, - long *milliseconds); +CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *m, + long *timeout_ms); typedef enum { /* This is the socket callback function pointer */ @@ -351,10 +350,10 @@ typedef enum { /* This is the argument passed to the socket callback */ CURLOPT(CURLMOPT_SOCKETDATA, CURLOPTTYPE_OBJECTPOINT, 2), - /* set to 1 to enable pipelining for this multi handle */ + /* set to 1 to enable pipelining for this multi handle */ CURLOPT(CURLMOPT_PIPELINING, CURLOPTTYPE_LONG, 3), - /* This is the timer callback function pointer */ + /* This is the timer callback function pointer */ CURLOPT(CURLMOPT_TIMERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 4), /* This is the argument passed to the timer callback */ @@ -370,11 +369,11 @@ typedef enum { CURLOPT(CURLMOPT_MAX_PIPELINE_LENGTH, CURLOPTTYPE_LONG, 8), /* a connection with a content-length longer than this - will not be considered for pipelining */ + is not considered for pipelining */ CURLOPT(CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 9), /* a connection with a chunk length longer than this - will not be considered for pipelining */ + is not considered for pipelining */ CURLOPT(CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 10), /* a list of site names(+port) that are blocked from pipelining */ @@ -398,20 +397,36 @@ typedef enum { /* network has changed, adjust caches/connection reuse */ CURLOPT(CURLMOPT_NETWORK_CHANGED, CURLOPTTYPE_LONG, 17), + /* This is the notify callback function pointer */ + CURLOPT(CURLMOPT_NOTIFYFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 18), + + /* This is the argument passed to the notify callback */ + CURLOPT(CURLMOPT_NOTIFYDATA, CURLOPTTYPE_OBJECTPOINT, 19), + + /* maximum number of threads used with threaded DNS resolver */ + CURLOPT(CURLMOPT_RESOLVE_THREADS_MAX, CURLOPTTYPE_LONG, 20), + + /* set to 1L for not joining threads when multi is cleaned up */ + CURLOPT(CURLMOPT_QUICK_EXIT, CURLOPTTYPE_LONG, 21), + CURLMOPT_LASTENTRY /* the last unused */ } CURLMoption; /* Definition of bits for the CURLMOPT_NETWORK_CHANGED argument: */ -/* - CURLMNWC_CLEAR_CONNS tells libcurl to prevent further reuse of existing - connections. Connections that are idle will be closed. Ongoing transfers - will continue with the connection they have. */ -#define CURLMNWC_CLEAR_CONNS (1L<<0) +/* - CURLMNWC_CLEAR_ALL tells libcurl to clear "everything" that could be + associated with this network, including both connections and DNS data. */ +#define CURLMNWC_CLEAR_ALL (1L << 0) -/* - CURLMNWC_CLEAR_DNS tells libcurl to prevent further reuse of existing - connections. Connections that are idle will be closed. Ongoing transfers - will continue with the connection they have. */ -#define CURLMNWC_CLEAR_DNS (1L<<0) +/* - CURLMNWC_CLEAR_CONNS tells libcurl to prevent further reuse of existing + connections. Connections that are idle are closed. Ongoing transfers do + continue with the connection they have. */ +#define CURLMNWC_CLEAR_CONNS (1L << 1) + +/* - CURLMNWC_CLEAR_DNS tells libcurl to clear the DNS cache associated with + this multi handle. Ongoing transfers keep using their already resolved + addresses, but future name resolutions are performed again. */ +#define CURLMNWC_CLEAR_DNS (1L << 2) /* * Name: curl_multi_setopt() @@ -420,10 +435,9 @@ typedef enum { * * Returns: CURLM error code. */ -CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...); - /* * Name: curl_multi_assign() * @@ -433,7 +447,7 @@ CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, * * Returns: CURLM error code. */ -CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_assign(CURLM *m, curl_socket_t sockfd, void *sockp); /* @@ -446,8 +460,7 @@ CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, * * Returns: NULL on failure, otherwise a CURL **array pointer */ -CURL_EXTERN CURL **curl_multi_get_handles(CURLM *multi_handle); - +CURL_EXTERN CURL **curl_multi_get_handles(CURLM *m); typedef enum { CURLMINFO_NONE, /* first, never use this */ @@ -464,7 +477,9 @@ typedef enum { * be read via `curl_multi_info_read()`. */ CURLMINFO_XFERS_DONE = 4, /* The total number of easy handles added to the multi handle, ever. */ - CURLMINFO_XFERS_ADDED = 5 + CURLMINFO_XFERS_ADDED = 5, + + CURLMINFO_LASTENTRY /* the last unused */ } CURLMinfo_offt; /* @@ -474,7 +489,7 @@ typedef enum { * * Returns: CULRM_OK or error when value could not be obtained. */ -CURL_EXTERN CURLMcode curl_multi_get_offt(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_get_offt(CURLM *m, CURLMinfo_offt info, curl_off_t *pvalue); @@ -509,15 +524,35 @@ 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. */ -CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *multi, +CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *m, struct curl_waitfd *ufds, unsigned int size, unsigned int *fd_count); +/* + * Notifications dispatched by a multi handle, when enabled. + */ +#define CURLMNOTIFY_INFO_READ 0 +#define CURLMNOTIFY_EASY_DONE 1 + +/* + * Callback to install via CURLMOPT_NOTIFYFUNCTION. + */ +typedef void (*curl_notify_callback)(CURLM *m, + unsigned int notification, + CURL *easy, + void *user_data); + +CURL_EXTERN CURLMcode curl_multi_notify_disable(CURLM *m, + unsigned int notification); + +CURL_EXTERN CURLMcode curl_multi_notify_enable(CURLM *m, + unsigned int notification); + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/include/curl/options.h b/include/curl/options.h index 77cab7768f..835a722e91 100644 --- a/include/curl/options.h +++ b/include/curl/options.h @@ -44,7 +44,7 @@ typedef enum { /* "alias" means it is provided for old programs to remain functional, we prefer another name */ -#define CURLOT_FLAG_ALIAS (1<<0) +#define CURLOT_FLAG_ALIAS (1 << 0) /* The CURLOPTTYPE_* id ranges can still be used to figure out what type/size to use for curl_easy_setopt() for the given id */ diff --git a/include/curl/system.h b/include/curl/system.h index 62ed2b0f43..c2dbab56e0 100644 --- a/include/curl/system.h +++ b/include/curl/system.h @@ -45,7 +45,7 @@ * size of off_t is independent of large file support settings. Keep your * build on the safe side avoiding an off_t gating. If you have a 64-bit * off_t then take for sure that another 64-bit data type exists, dig deeper - * and you will find it. + * to find it. * */ @@ -135,21 +135,12 @@ # endif #elif defined(UNDER_CE) -# ifdef __MINGW32CE__ -# define CURL_TYPEOF_CURL_OFF_T long long -# define CURL_FORMAT_CURL_OFF_T "lld" -# define CURL_FORMAT_CURL_OFF_TU "llu" -# define CURL_SUFFIX_CURL_OFF_T LL -# define CURL_SUFFIX_CURL_OFF_TU ULL -# define CURL_TYPEOF_CURL_SOCKLEN_T int -# else -# define CURL_TYPEOF_CURL_OFF_T __int64 -# define CURL_FORMAT_CURL_OFF_T "I64d" -# define CURL_FORMAT_CURL_OFF_TU "I64u" -# define CURL_SUFFIX_CURL_OFF_T i64 -# define CURL_SUFFIX_CURL_OFF_TU ui64 -# define CURL_TYPEOF_CURL_SOCKLEN_T int -# endif +# define CURL_TYPEOF_CURL_OFF_T __int64 +# define CURL_FORMAT_CURL_OFF_T "I64d" +# define CURL_FORMAT_CURL_OFF_TU "I64u" +# define CURL_SUFFIX_CURL_OFF_T i64 +# define CURL_SUFFIX_CURL_OFF_TU ui64 +# define CURL_TYPEOF_CURL_SOCKLEN_T int #elif defined(__MINGW32__) # include @@ -376,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/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h index a0b41aeb24..d600c73cdc 100644 --- a/include/curl/typecheck-gcc.h +++ b/include/curl/typecheck-gcc.h @@ -24,207 +24,211 @@ * ***************************************************************************/ -/* wraps curl_easy_setopt() with typechecking */ +/* wraps curl_easy_setopt() with type checking */ /* To add a new kind of warning, add an * if(curlcheck_sometype_option(_curl_opt)) * if(!curlcheck_sometype(value)) - * _curl_easy_setopt_err_sometype(); + * Wcurl_easy_setopt_err_sometype(); * block and define curlcheck_sometype_option, curlcheck_sometype and - * _curl_easy_setopt_err_sometype below + * Wcurl_easy_setopt_err_sometype below * * NOTE: We use two nested 'if' statements here instead of the && operator, in * order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x * 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 + * To add an option that uses the same type as an existing option, you 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)) \ - _curl_easy_setopt_err_long(); \ - if(curlcheck_off_t_option(option)) \ - if(!curlcheck_off_t(value)) \ - _curl_easy_setopt_err_curl_off_t(); \ - if(curlcheck_string_option(option)) \ - if(!curlcheck_string(value)) \ - _curl_easy_setopt_err_string(); \ - if((option) == CURLOPT_PRIVATE) { } \ - if(curlcheck_write_cb_option(option)) \ - if(!curlcheck_write_cb(value)) \ - _curl_easy_setopt_err_write_callback(); \ - if(curlcheck_curl_option(option)) \ - if(!curlcheck_curl(value)) \ - _curl_easy_setopt_err_curl(); \ - if((option) == CURLOPT_RESOLVER_START_FUNCTION) \ - if(!curlcheck_resolver_start_callback(value)) \ - _curl_easy_setopt_err_resolver_start_callback(); \ - if((option) == CURLOPT_READFUNCTION) \ - if(!curlcheck_read_cb(value)) \ - _curl_easy_setopt_err_read_cb(); \ - if((option) == CURLOPT_IOCTLFUNCTION) \ - if(!curlcheck_ioctl_cb(value)) \ - _curl_easy_setopt_err_ioctl_cb(); \ - if((option) == CURLOPT_SOCKOPTFUNCTION) \ - if(!curlcheck_sockopt_cb(value)) \ - _curl_easy_setopt_err_sockopt_cb(); \ - if((option) == CURLOPT_OPENSOCKETFUNCTION) \ - if(!curlcheck_opensocket_cb(value)) \ - _curl_easy_setopt_err_opensocket_cb(); \ - if((option) == CURLOPT_PROGRESSFUNCTION) \ - if(!curlcheck_progress_cb(value)) \ - _curl_easy_setopt_err_progress_cb(); \ - if((option) == CURLOPT_XFERINFOFUNCTION) \ - if(!curlcheck_xferinfo_cb(value)) \ - _curl_easy_setopt_err_xferinfo_cb(); \ - if((option) == CURLOPT_DEBUGFUNCTION) \ - if(!curlcheck_debug_cb(value)) \ - _curl_easy_setopt_err_debug_cb(); \ - if((option) == CURLOPT_SSL_CTX_FUNCTION) \ - if(!curlcheck_ssl_ctx_cb(value)) \ - _curl_easy_setopt_err_ssl_ctx_cb(); \ - if(curlcheck_conv_cb_option(option)) \ - if(!curlcheck_conv_cb(value)) \ - _curl_easy_setopt_err_conv_cb(); \ - if((option) == CURLOPT_SEEKFUNCTION) \ - if(!curlcheck_seek_cb(value)) \ - _curl_easy_setopt_err_seek_cb(); \ - if((option) == CURLOPT_CHUNK_BGN_FUNCTION) \ - if(!curlcheck_chunk_bgn_cb(value)) \ - _curl_easy_setopt_err_chunk_bgn_cb(); \ - if((option) == CURLOPT_CHUNK_END_FUNCTION) \ - if(!curlcheck_chunk_end_cb(value)) \ - _curl_easy_setopt_err_chunk_end_cb(); \ - if((option) == CURLOPT_CLOSESOCKETFUNCTION) \ - if(!curlcheck_close_socket_cb(value)) \ - _curl_easy_setopt_err_close_socket_cb(); \ - if((option) == CURLOPT_FNMATCH_FUNCTION) \ - if(!curlcheck_fnmatch_cb(value)) \ - _curl_easy_setopt_err_fnmatch_cb(); \ - if((option) == CURLOPT_HSTSREADFUNCTION) \ - if(!curlcheck_hstsread_cb(value)) \ - _curl_easy_setopt_err_hstsread_cb(); \ - if((option) == CURLOPT_HSTSWRITEFUNCTION) \ - if(!curlcheck_hstswrite_cb(value)) \ - _curl_easy_setopt_err_hstswrite_cb(); \ - if((option) == CURLOPT_SSH_HOSTKEYFUNCTION) \ - if(!curlcheck_ssh_hostkey_cb(value)) \ - _curl_easy_setopt_err_ssh_hostkey_cb(); \ - if((option) == CURLOPT_SSH_KEYFUNCTION) \ - if(!curlcheck_ssh_key_cb(value)) \ - _curl_easy_setopt_err_ssh_key_cb(); \ - if((option) == CURLOPT_INTERLEAVEFUNCTION) \ - if(!curlcheck_interleave_cb(value)) \ - _curl_easy_setopt_err_interleave_cb(); \ - if((option) == CURLOPT_PREREQFUNCTION) \ - if(!curlcheck_prereq_cb(value)) \ - _curl_easy_setopt_err_prereq_cb(); \ - if((option) == CURLOPT_TRAILERFUNCTION) \ - if(!curlcheck_trailer_cb(value)) \ - _curl_easy_setopt_err_trailer_cb(); \ - if(curlcheck_cb_data_option(option)) \ - if(!curlcheck_cb_data(value)) \ - _curl_easy_setopt_err_cb_data(); \ - if((option) == CURLOPT_ERRORBUFFER) \ - if(!curlcheck_error_buffer(value)) \ - _curl_easy_setopt_err_error_buffer(); \ - if((option) == CURLOPT_CURLU) \ - if(!curlcheck_ptr((value), CURLU)) \ - _curl_easy_setopt_err_curlu(); \ - if((option) == CURLOPT_STDERR) \ - if(!curlcheck_FILE(value)) \ - _curl_easy_setopt_err_FILE(); \ - if(curlcheck_postfields_option(option)) \ - if(!curlcheck_postfields(value)) \ - _curl_easy_setopt_err_postfields(); \ - if((option) == CURLOPT_HTTPPOST) \ - if(!curlcheck_arr((value), struct curl_httppost)) \ - _curl_easy_setopt_err_curl_httpost(); \ - if((option) == CURLOPT_MIMEPOST) \ - if(!curlcheck_ptr((value), curl_mime)) \ - _curl_easy_setopt_err_curl_mimepost(); \ - if(curlcheck_slist_option(option)) \ - if(!curlcheck_arr((value), struct curl_slist)) \ - _curl_easy_setopt_err_curl_slist(); \ - if((option) == CURLOPT_SHARE) \ - if(!curlcheck_ptr((value), CURLSH)) \ - _curl_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 typechecking */ +/* 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 *)) \ - _curl_easy_getinfo_err_string(); \ - if(curlcheck_long_info(info)) \ - if(!curlcheck_arr((arg), long)) \ - _curl_easy_getinfo_err_long(); \ - if(curlcheck_double_info(info)) \ - if(!curlcheck_arr((arg), double)) \ - _curl_easy_getinfo_err_double(); \ - if(curlcheck_slist_info(info)) \ - if(!curlcheck_arr((arg), struct curl_slist *)) \ - _curl_easy_getinfo_err_curl_slist(); \ - if(curlcheck_tlssessioninfo_info(info)) \ - if(!curlcheck_arr((arg), struct curl_tlssessioninfo *)) \ - _curl_easy_getinfo_err_curl_tlssessioninfo(); \ - if(curlcheck_certinfo_info(info)) \ - if(!curlcheck_arr((arg), struct curl_certinfo *)) \ - _curl_easy_getinfo_err_curl_certinfo(); \ - if(curlcheck_socket_info(info)) \ - if(!curlcheck_arr((arg), curl_socket_t)) \ - _curl_easy_getinfo_err_curl_socket(); \ - if(curlcheck_off_t_info(info)) \ - if(!curlcheck_arr((arg), curl_off_t)) \ - _curl_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)) \ - _curl_multi_setopt_err_long(); \ - if(curlcheck_off_t_option(option)) \ - if(!curlcheck_off_t(value)) \ - _curl_multi_setopt_err_curl_off_t(); \ - if(curlcheck_multicb_data_option(option)) \ - if(!curlcheck_cb_data(value)) \ - _curl_multi_setopt_err_cb_data(); \ - if(curlcheck_charpp_option(option)) \ - if(!curlcheck_ptrptr(value, char)) \ - _curl_multi_setopt_err_charpp(); \ - if((option) == CURLMOPT_PUSHFUNCTION) \ - if(!curlcheck_multipush_cb(value)) \ - _curl_multi_setopt_err_pushcb(); \ - if((option) == CURLMOPT_SOCKETFUNCTION) \ - if(!curlcheck_multisocket_cb(value)) \ - _curl_multi_setopt_err_socketcb(); \ - if((option) == CURLMOPT_TIMERFUNCTION) \ - if(!curlcheck_multitimer_cb(value)) \ - _curl_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 */ #define curlcheck_multicb_data_option(option) \ - ((option) == CURLMOPT_PUSHDATA || \ + ((option) == CURLMOPT_NOTIFYDATA || \ + (option) == CURLMOPT_PUSHDATA || \ (option) == CURLMOPT_SOCKETDATA || \ (option) == CURLMOPT_TIMERDATA || \ 0) @@ -246,17 +250,22 @@ curlcheck_cb_compatible((expr), curl_socket_callback)) /* evaluates to true if expr is of type curl_push_callback */ -#define curlcheck_multipush_cb(expr) \ - (curlcheck_NULL(expr) || \ +#define curlcheck_multipush_cb(expr) \ + (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_push_callback)) +/* evaluates to true if expr is of type curl_push_callback */ +#define curlcheck_multinotify_cb(expr) \ + (curlcheck_NULL(expr) || \ + 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 _curl_easy_setopt_err* +/* the actual warnings, triggered by calling the Wcurl_easy_setopt_err* * functions */ /* To define a new warning, use _CURL_WARNING(identifier, "message") */ @@ -265,132 +274,134 @@ __attribute__((__unused__)) __attribute__((__noinline__)) \ id(void) { __asm__(""); } -CURLWARNING(_curl_multi_setopt_err_long, +CURLWARNING(Wcurl_multi_setopt_err_long, "curl_multi_setopt expects a long argument") -CURLWARNING(_curl_multi_setopt_err_curl_off_t, +CURLWARNING(Wcurl_multi_setopt_err_curl_off_t, "curl_multi_setopt expects a curl_off_t argument") -CURLWARNING(_curl_multi_setopt_err_cb_data, +CURLWARNING(Wcurl_multi_setopt_err_cb_data, "curl_multi_setopt expects a 'void *' argument") -CURLWARNING(_curl_multi_setopt_err_charpp, +CURLWARNING(Wcurl_multi_setopt_err_charpp, "curl_multi_setopt expects a 'char **' argument") -CURLWARNING(_curl_multi_setopt_err_pushcb, +CURLWARNING(Wcurl_multi_setopt_err_pushcb, "curl_multi_setopt expects a curl_push_callback argument") -CURLWARNING(_curl_multi_setopt_err_socketcb, +CURLWARNING(Wcurl_multi_setopt_err_notifycb, + "curl_multi_setopt expects a curl_notify_callback argument") +CURLWARNING(Wcurl_multi_setopt_err_socketcb, "curl_multi_setopt expects a curl_socket_callback argument") -CURLWARNING(_curl_multi_setopt_err_timercb, +CURLWARNING(Wcurl_multi_setopt_err_timercb, "curl_multi_setopt expects a curl_multi_timer_callback argument") -CURLWARNING(_curl_easy_setopt_err_long, +CURLWARNING(Wcurl_easy_setopt_err_long, "curl_easy_setopt expects a long argument") -CURLWARNING(_curl_easy_setopt_err_curl_off_t, +CURLWARNING(Wcurl_easy_setopt_err_curl_off_t, "curl_easy_setopt expects a curl_off_t argument") -CURLWARNING(_curl_easy_setopt_err_string, +CURLWARNING(Wcurl_easy_setopt_err_string, "curl_easy_setopt expects a " "string ('char *' or char[]) argument") -CURLWARNING(_curl_easy_setopt_err_write_callback, +CURLWARNING(Wcurl_easy_setopt_err_write_callback, "curl_easy_setopt expects a curl_write_callback argument") -CURLWARNING(_curl_easy_setopt_err_resolver_start_callback, +CURLWARNING(Wcurl_easy_setopt_err_resolver_start_callback, "curl_easy_setopt expects a " "curl_resolver_start_callback argument") -CURLWARNING(_curl_easy_setopt_err_read_cb, +CURLWARNING(Wcurl_easy_setopt_err_read_cb, "curl_easy_setopt expects a curl_read_callback argument") -CURLWARNING(_curl_easy_setopt_err_ioctl_cb, +CURLWARNING(Wcurl_easy_setopt_err_ioctl_cb, "curl_easy_setopt expects a curl_ioctl_callback argument") -CURLWARNING(_curl_easy_setopt_err_sockopt_cb, +CURLWARNING(Wcurl_easy_setopt_err_sockopt_cb, "curl_easy_setopt expects a curl_sockopt_callback argument") -CURLWARNING(_curl_easy_setopt_err_opensocket_cb, +CURLWARNING(Wcurl_easy_setopt_err_opensocket_cb, "curl_easy_setopt expects a " "curl_opensocket_callback argument") -CURLWARNING(_curl_easy_setopt_err_progress_cb, +CURLWARNING(Wcurl_easy_setopt_err_progress_cb, "curl_easy_setopt expects a curl_progress_callback argument") -CURLWARNING(_curl_easy_setopt_err_xferinfo_cb, +CURLWARNING(Wcurl_easy_setopt_err_xferinfo_cb, "curl_easy_setopt expects a curl_xferinfo_callback argument") -CURLWARNING(_curl_easy_setopt_err_debug_cb, +CURLWARNING(Wcurl_easy_setopt_err_debug_cb, "curl_easy_setopt expects a curl_debug_callback argument") -CURLWARNING(_curl_easy_setopt_err_ssl_ctx_cb, +CURLWARNING(Wcurl_easy_setopt_err_ssl_ctx_cb, "curl_easy_setopt expects a curl_ssl_ctx_callback argument") -CURLWARNING(_curl_easy_setopt_err_conv_cb, +CURLWARNING(Wcurl_easy_setopt_err_conv_cb, "curl_easy_setopt expects a curl_conv_callback argument") -CURLWARNING(_curl_easy_setopt_err_seek_cb, +CURLWARNING(Wcurl_easy_setopt_err_seek_cb, "curl_easy_setopt expects a curl_seek_callback argument") -CURLWARNING(_curl_easy_setopt_err_cb_data, +CURLWARNING(Wcurl_easy_setopt_err_cb_data, "curl_easy_setopt expects a " "private data pointer as argument") -CURLWARNING(_curl_easy_setopt_err_chunk_bgn_cb, +CURLWARNING(Wcurl_easy_setopt_err_chunk_bgn_cb, "curl_easy_setopt expects a curl_chunk_bgn_callback argument") -CURLWARNING(_curl_easy_setopt_err_chunk_end_cb, +CURLWARNING(Wcurl_easy_setopt_err_chunk_end_cb, "curl_easy_setopt expects a curl_chunk_end_callback argument") -CURLWARNING(_curl_easy_setopt_err_close_socket_cb, +CURLWARNING(Wcurl_easy_setopt_err_close_socket_cb, "curl_easy_setopt expects a curl_closesocket_callback argument") -CURLWARNING(_curl_easy_setopt_err_fnmatch_cb, +CURLWARNING(Wcurl_easy_setopt_err_fnmatch_cb, "curl_easy_setopt expects a curl_fnmatch_callback argument") -CURLWARNING(_curl_easy_setopt_err_hstsread_cb, +CURLWARNING(Wcurl_easy_setopt_err_hstsread_cb, "curl_easy_setopt expects a curl_hstsread_callback argument") -CURLWARNING(_curl_easy_setopt_err_hstswrite_cb, +CURLWARNING(Wcurl_easy_setopt_err_hstswrite_cb, "curl_easy_setopt expects a curl_hstswrite_callback argument") -CURLWARNING(_curl_easy_setopt_err_ssh_key_cb, +CURLWARNING(Wcurl_easy_setopt_err_ssh_key_cb, "curl_easy_setopt expects a curl_sshkeycallback argument") -CURLWARNING(_curl_easy_setopt_err_ssh_hostkey_cb, +CURLWARNING(Wcurl_easy_setopt_err_ssh_hostkey_cb, "curl_easy_setopt expects a curl_sshhostkeycallback argument") -CURLWARNING(_curl_easy_setopt_err_interleave_cb, +CURLWARNING(Wcurl_easy_setopt_err_interleave_cb, "curl_easy_setopt expects a curl_interleave_callback argument") -CURLWARNING(_curl_easy_setopt_err_prereq_cb, +CURLWARNING(Wcurl_easy_setopt_err_prereq_cb, "curl_easy_setopt expects a curl_prereq_callback argument") -CURLWARNING(_curl_easy_setopt_err_trailer_cb, +CURLWARNING(Wcurl_easy_setopt_err_trailer_cb, "curl_easy_setopt expects a curl_trailerfunc_ok argument") -CURLWARNING(_curl_easy_setopt_err_error_buffer, +CURLWARNING(Wcurl_easy_setopt_err_error_buffer, "curl_easy_setopt expects a " "char buffer of CURL_ERROR_SIZE as argument") -CURLWARNING(_curl_easy_setopt_err_curlu, +CURLWARNING(Wcurl_easy_setopt_err_curlu, "curl_easy_setopt expects a 'CURLU *' argument") -CURLWARNING(_curl_easy_setopt_err_curl, +CURLWARNING(Wcurl_easy_setopt_err_curl, "curl_easy_setopt expects a 'CURL *' argument") -CURLWARNING(_curl_easy_setopt_err_FILE, +CURLWARNING(Wcurl_easy_setopt_err_FILE, "curl_easy_setopt expects a 'FILE *' argument") -CURLWARNING(_curl_easy_setopt_err_postfields, +CURLWARNING(Wcurl_easy_setopt_err_postfields, "curl_easy_setopt expects a 'void *' or 'char *' argument") -CURLWARNING(_curl_easy_setopt_err_curl_httpost, +CURLWARNING(Wcurl_easy_setopt_err_curl_httpost, "curl_easy_setopt expects a 'struct curl_httppost *' " "argument") -CURLWARNING(_curl_easy_setopt_err_curl_mimepost, +CURLWARNING(Wcurl_easy_setopt_err_curl_mimepost, "curl_easy_setopt expects a 'curl_mime *' " "argument") -CURLWARNING(_curl_easy_setopt_err_curl_slist, +CURLWARNING(Wcurl_easy_setopt_err_curl_slist, "curl_easy_setopt expects a 'struct curl_slist *' argument") -CURLWARNING(_curl_easy_setopt_err_CURLSH, +CURLWARNING(Wcurl_easy_setopt_err_CURLSH, "curl_easy_setopt expects a CURLSH* argument") -CURLWARNING(_curl_easy_getinfo_err_string, +CURLWARNING(Wcurl_easy_getinfo_err_string, "curl_easy_getinfo expects a pointer to 'char *'") -CURLWARNING(_curl_easy_getinfo_err_long, +CURLWARNING(Wcurl_easy_getinfo_err_long, "curl_easy_getinfo expects a pointer to long") -CURLWARNING(_curl_easy_getinfo_err_double, +CURLWARNING(Wcurl_easy_getinfo_err_double, "curl_easy_getinfo expects a pointer to double") -CURLWARNING(_curl_easy_getinfo_err_curl_slist, +CURLWARNING(Wcurl_easy_getinfo_err_curl_slist, "curl_easy_getinfo expects a pointer to 'struct curl_slist *'") -CURLWARNING(_curl_easy_getinfo_err_curl_tlssessioninfo, +CURLWARNING(Wcurl_easy_getinfo_err_curl_tlssessioninfo, "curl_easy_getinfo expects a pointer to " "'struct curl_tlssessioninfo *'") -CURLWARNING(_curl_easy_getinfo_err_curl_certinfo, +CURLWARNING(Wcurl_easy_getinfo_err_curl_certinfo, "curl_easy_getinfo expects a pointer to " "'struct curl_certinfo *'") -CURLWARNING(_curl_easy_getinfo_err_curl_socket, +CURLWARNING(Wcurl_easy_getinfo_err_curl_socket, "curl_easy_getinfo expects a pointer to curl_socket_t") -CURLWARNING(_curl_easy_getinfo_err_curl_off_t, +CURLWARNING(Wcurl_easy_getinfo_err_curl_off_t, "curl_easy_getinfo expects a pointer to curl_off_t") /* groups of curl_easy_setops options that take the same type of argument */ /* evaluates to true if option takes a long argument */ -#define curlcheck_long_option(option) \ +#define curlcheck_long_option(option) \ (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT) #define curlcheck_off_t_option(option) \ (((option) > CURLOPTTYPE_OFF_T) && ((option) < CURLOPTTYPE_BLOB)) /* option takes a CURL * argument */ -#define curlcheck_curl_option(option) \ - ((option) == CURLOPT_STREAM_DEPENDS || \ - (option) == CURLOPT_STREAM_DEPENDS_E || \ +#define curlcheck_curl_option(option) \ + ((option) == CURLOPT_STREAM_DEPENDS || \ + (option) == CURLOPT_STREAM_DEPENDS_E || \ 0) /* evaluates to true if option takes a char* argument */ @@ -501,86 +512,89 @@ CURLWARNING(_curl_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)) - -/* typecheck helpers -- check whether given expression has requested type */ +/* + * typecheck helpers -- check whether given expression has requested type + */ /* For pointers, you can use the curlcheck_ptr/curlcheck_arr macros, * otherwise define a new macro. Search for __builtin_types_compatible_p @@ -592,7 +606,7 @@ CURLWARNING(_curl_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 */ @@ -630,7 +644,6 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, (curlcheck_NULL(expr) || \ __builtin_types_compatible_p(__typeof__(expr), CURL *)) - /* evaluates to true if expr is a long (no matter the signedness) * XXX: for now, int is also accepted (and therefore short and char, which * are promoted to int when passed to a variadic function) */ @@ -657,7 +670,7 @@ CURLWARNING(_curl_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 */ @@ -673,7 +686,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, (curlcheck_ptr((expr), void) || \ curlcheck_ptr((expr), FILE)) #else /* be less strict */ -#define curlcheck_cb_data(expr) \ +#define curlcheck_cb_data(expr) \ curlcheck_any_ptr(expr) #endif @@ -695,8 +708,8 @@ CURLWARNING(_curl_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" */ @@ -704,89 +717,89 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), __typeof__(fread) *) || \ curlcheck_cb_compatible((expr), curl_read_callback) || \ - curlcheck_cb_compatible((expr), _curl_read_callback1) || \ - curlcheck_cb_compatible((expr), _curl_read_callback2) || \ - curlcheck_cb_compatible((expr), _curl_read_callback3) || \ - curlcheck_cb_compatible((expr), _curl_read_callback4) || \ - curlcheck_cb_compatible((expr), _curl_read_callback5) || \ - curlcheck_cb_compatible((expr), _curl_read_callback6)) -typedef size_t (*_curl_read_callback1)(char *, size_t, size_t, void *); -typedef size_t (*_curl_read_callback2)(char *, size_t, size_t, const void *); -typedef size_t (*_curl_read_callback3)(char *, size_t, size_t, FILE *); -typedef size_t (*_curl_read_callback4)(void *, size_t, size_t, void *); -typedef size_t (*_curl_read_callback5)(void *, size_t, size_t, const void *); -typedef size_t (*_curl_read_callback6)(void *, size_t, size_t, FILE *); + curlcheck_cb_compatible((expr), Wcurl_read_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_read_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_read_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_read_callback4) || \ + curlcheck_cb_compatible((expr), Wcurl_read_callback5) || \ + curlcheck_cb_compatible((expr), Wcurl_read_callback6)) +typedef size_t (*Wcurl_read_callback1)(char *, size_t, size_t, void *); +typedef size_t (*Wcurl_read_callback2)(char *, size_t, size_t, const void *); +typedef size_t (*Wcurl_read_callback3)(char *, size_t, size_t, FILE *); +typedef size_t (*Wcurl_read_callback4)(void *, size_t, size_t, void *); +typedef size_t (*Wcurl_read_callback5)(void *, size_t, size_t, const void *); +typedef size_t (*Wcurl_read_callback6)(void *, size_t, size_t, FILE *); /* evaluates to true if expr is of type curl_write_callback or "similar" */ #define curlcheck_write_cb(expr) \ (curlcheck_read_cb(expr) || \ curlcheck_cb_compatible((expr), __typeof__(fwrite) *) || \ curlcheck_cb_compatible((expr), curl_write_callback) || \ - curlcheck_cb_compatible((expr), _curl_write_callback1) || \ - curlcheck_cb_compatible((expr), _curl_write_callback2) || \ - curlcheck_cb_compatible((expr), _curl_write_callback3) || \ - curlcheck_cb_compatible((expr), _curl_write_callback4) || \ - curlcheck_cb_compatible((expr), _curl_write_callback5) || \ - curlcheck_cb_compatible((expr), _curl_write_callback6)) -typedef size_t (*_curl_write_callback1)(const char *, size_t, size_t, void *); -typedef size_t (*_curl_write_callback2)(const char *, size_t, size_t, - const void *); -typedef size_t (*_curl_write_callback3)(const char *, size_t, size_t, FILE *); -typedef size_t (*_curl_write_callback4)(const void *, size_t, size_t, void *); -typedef size_t (*_curl_write_callback5)(const void *, size_t, size_t, - const void *); -typedef size_t (*_curl_write_callback6)(const void *, size_t, size_t, FILE *); + curlcheck_cb_compatible((expr), Wcurl_write_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_write_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_write_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_write_callback4) || \ + curlcheck_cb_compatible((expr), Wcurl_write_callback5) || \ + 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 *); +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 *); +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" */ #define curlcheck_ioctl_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_ioctl_callback) || \ - curlcheck_cb_compatible((expr), _curl_ioctl_callback1) || \ - curlcheck_cb_compatible((expr), _curl_ioctl_callback2) || \ - curlcheck_cb_compatible((expr), _curl_ioctl_callback3) || \ - curlcheck_cb_compatible((expr), _curl_ioctl_callback4)) -typedef curlioerr (*_curl_ioctl_callback1)(CURL *, int, void *); -typedef curlioerr (*_curl_ioctl_callback2)(CURL *, int, const void *); -typedef curlioerr (*_curl_ioctl_callback3)(CURL *, curliocmd, void *); -typedef curlioerr (*_curl_ioctl_callback4)(CURL *, curliocmd, const void *); + curlcheck_cb_compatible((expr), Wcurl_ioctl_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_ioctl_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_ioctl_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_ioctl_callback4)) +typedef curlioerr (*Wcurl_ioctl_callback1)(CURL *, int, void *); +typedef curlioerr (*Wcurl_ioctl_callback2)(CURL *, int, const void *); +typedef curlioerr (*Wcurl_ioctl_callback3)(CURL *, curliocmd, void *); +typedef curlioerr (*Wcurl_ioctl_callback4)(CURL *, curliocmd, const void *); /* evaluates to true if expr is of type curl_sockopt_callback or "similar" */ #define curlcheck_sockopt_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_sockopt_callback) || \ - curlcheck_cb_compatible((expr), _curl_sockopt_callback1) || \ - curlcheck_cb_compatible((expr), _curl_sockopt_callback2)) -typedef int (*_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); -typedef int (*_curl_sockopt_callback2)(const void *, curl_socket_t, - curlsocktype); + curlcheck_cb_compatible((expr), Wcurl_sockopt_callback1) || \ + 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); /* evaluates to true if expr is of type curl_opensocket_callback or "similar" */ #define curlcheck_opensocket_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_opensocket_callback) || \ - curlcheck_cb_compatible((expr), _curl_opensocket_callback1) || \ - curlcheck_cb_compatible((expr), _curl_opensocket_callback2) || \ - curlcheck_cb_compatible((expr), _curl_opensocket_callback3) || \ - curlcheck_cb_compatible((expr), _curl_opensocket_callback4)) -typedef curl_socket_t (*_curl_opensocket_callback1) + curlcheck_cb_compatible((expr), Wcurl_opensocket_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_opensocket_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_opensocket_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_opensocket_callback4)) +typedef curl_socket_t (*Wcurl_opensocket_callback1) (void *, curlsocktype, struct curl_sockaddr *); -typedef curl_socket_t (*_curl_opensocket_callback2) +typedef curl_socket_t (*Wcurl_opensocket_callback2) (void *, curlsocktype, const struct curl_sockaddr *); -typedef curl_socket_t (*_curl_opensocket_callback3) +typedef curl_socket_t (*Wcurl_opensocket_callback3) (const void *, curlsocktype, struct curl_sockaddr *); -typedef curl_socket_t (*_curl_opensocket_callback4) +typedef curl_socket_t (*Wcurl_opensocket_callback4) (const void *, curlsocktype, const struct curl_sockaddr *); /* evaluates to true if expr is of type curl_progress_callback or "similar" */ #define curlcheck_progress_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_progress_callback) || \ - curlcheck_cb_compatible((expr), _curl_progress_callback1) || \ - curlcheck_cb_compatible((expr), _curl_progress_callback2)) -typedef int (*_curl_progress_callback1)(void *, + curlcheck_cb_compatible((expr), Wcurl_progress_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_progress_callback2)) +typedef int (*Wcurl_progress_callback1)(void *, double, double, double, double); -typedef int (*_curl_progress_callback2)(const void *, +typedef int (*Wcurl_progress_callback2)(const void *, double, double, double, double); /* evaluates to true if expr is of type curl_xferinfo_callback */ @@ -798,29 +811,29 @@ typedef int (*_curl_progress_callback2)(const void *, #define curlcheck_debug_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_debug_callback) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback1) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback2) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback3) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback4) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback5) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback6) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback7) || \ - curlcheck_cb_compatible((expr), _curl_debug_callback8)) -typedef int (*_curl_debug_callback1) (CURL *, + curlcheck_cb_compatible((expr), Wcurl_debug_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_debug_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_debug_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_debug_callback4) || \ + curlcheck_cb_compatible((expr), Wcurl_debug_callback5) || \ + 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 *, curl_infotype, char *, size_t, void *); -typedef int (*_curl_debug_callback2) (CURL *, +typedef int (*Wcurl_debug_callback2)(CURL *, curl_infotype, char *, size_t, const void *); -typedef int (*_curl_debug_callback3) (CURL *, +typedef int (*Wcurl_debug_callback3)(CURL *, curl_infotype, const char *, size_t, void *); -typedef int (*_curl_debug_callback4) (CURL *, +typedef int (*Wcurl_debug_callback4)(CURL *, curl_infotype, const char *, size_t, const void *); -typedef int (*_curl_debug_callback5) (CURL *, +typedef int (*Wcurl_debug_callback5)(CURL *, curl_infotype, unsigned char *, size_t, void *); -typedef int (*_curl_debug_callback6) (CURL *, +typedef int (*Wcurl_debug_callback6)(CURL *, curl_infotype, unsigned char *, size_t, const void *); -typedef int (*_curl_debug_callback7) (CURL *, +typedef int (*Wcurl_debug_callback7)(CURL *, curl_infotype, const unsigned char *, size_t, void *); -typedef int (*_curl_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" */ @@ -828,66 +841,66 @@ typedef int (*_curl_debug_callback8) (CURL *, #define curlcheck_ssl_ctx_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_ssl_ctx_callback) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback1) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback2) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback3) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback4) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback5) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback6) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback7) || \ - curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback8)) -typedef CURLcode (*_curl_ssl_ctx_callback1)(CURL *, void *, void *); -typedef CURLcode (*_curl_ssl_ctx_callback2)(CURL *, void *, const void *); -typedef CURLcode (*_curl_ssl_ctx_callback3)(CURL *, const void *, void *); -typedef CURLcode (*_curl_ssl_ctx_callback4)(CURL *, const void *, + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback4) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback5) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback6) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback7) || \ + curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback8)) +typedef CURLcode (*Wcurl_ssl_ctx_callback1)(CURL *, void *, void *); +typedef CURLcode (*Wcurl_ssl_ctx_callback2)(CURL *, void *, const void *); +typedef CURLcode (*Wcurl_ssl_ctx_callback3)(CURL *, const void *, void *); +typedef CURLcode (*Wcurl_ssl_ctx_callback4)(CURL *, const void *, const void *); #ifdef HEADER_SSL_H /* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX - * this will of course break if we are included before OpenSSL headers... + * this of course breaks if we are included before OpenSSL headers... */ -typedef CURLcode (*_curl_ssl_ctx_callback5)(CURL *, SSL_CTX *, void *); -typedef CURLcode (*_curl_ssl_ctx_callback6)(CURL *, SSL_CTX *, const void *); -typedef CURLcode (*_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX *, void *); -typedef CURLcode (*_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX *, +typedef CURLcode (*Wcurl_ssl_ctx_callback5)(CURL *, SSL_CTX *, void *); +typedef CURLcode (*Wcurl_ssl_ctx_callback6)(CURL *, SSL_CTX *, const void *); +typedef CURLcode (*Wcurl_ssl_ctx_callback7)(CURL *, const SSL_CTX *, void *); +typedef CURLcode (*Wcurl_ssl_ctx_callback8)(CURL *, const SSL_CTX *, const void *); #else -typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5; -typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6; -typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback7; -typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8; +typedef Wcurl_ssl_ctx_callback1 Wcurl_ssl_ctx_callback5; +typedef Wcurl_ssl_ctx_callback1 Wcurl_ssl_ctx_callback6; +typedef Wcurl_ssl_ctx_callback1 Wcurl_ssl_ctx_callback7; +typedef Wcurl_ssl_ctx_callback1 Wcurl_ssl_ctx_callback8; #endif /* evaluates to true if expr is of type curl_conv_callback or "similar" */ #define curlcheck_conv_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_conv_callback) || \ - curlcheck_cb_compatible((expr), _curl_conv_callback1) || \ - curlcheck_cb_compatible((expr), _curl_conv_callback2) || \ - curlcheck_cb_compatible((expr), _curl_conv_callback3) || \ - curlcheck_cb_compatible((expr), _curl_conv_callback4)) -typedef CURLcode (*_curl_conv_callback1)(char *, size_t length); -typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length); -typedef CURLcode (*_curl_conv_callback3)(void *, size_t length); -typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length); + curlcheck_cb_compatible((expr), Wcurl_conv_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_conv_callback2) || \ + curlcheck_cb_compatible((expr), Wcurl_conv_callback3) || \ + curlcheck_cb_compatible((expr), Wcurl_conv_callback4)) +typedef CURLcode (*Wcurl_conv_callback1)(char *, size_t length); +typedef CURLcode (*Wcurl_conv_callback2)(const char *, size_t length); +typedef CURLcode (*Wcurl_conv_callback3)(void *, size_t length); +typedef CURLcode (*Wcurl_conv_callback4)(const void *, size_t length); /* evaluates to true if expr is of type curl_seek_callback or "similar" */ #define curlcheck_seek_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_seek_callback) || \ - curlcheck_cb_compatible((expr), _curl_seek_callback1) || \ - curlcheck_cb_compatible((expr), _curl_seek_callback2)) -typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int); -typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int); + curlcheck_cb_compatible((expr), Wcurl_seek_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_seek_callback2)) +typedef CURLcode (*Wcurl_seek_callback1)(void *, curl_off_t, int); +typedef CURLcode (*Wcurl_seek_callback2)(const void *, curl_off_t, int); /* evaluates to true if expr is of type curl_chunk_bgn_callback */ #define curlcheck_chunk_bgn_cb(expr) \ (curlcheck_NULL(expr) || \ curlcheck_cb_compatible((expr), curl_chunk_bgn_callback) || \ - curlcheck_cb_compatible((expr), _curl_chunk_bgn_callback1) || \ - curlcheck_cb_compatible((expr), _curl_chunk_bgn_callback2)) -typedef long (*_curl_chunk_bgn_callback1)(struct curl_fileinfo *, + curlcheck_cb_compatible((expr), Wcurl_chunk_bgn_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_chunk_bgn_callback2)) +typedef long (*Wcurl_chunk_bgn_callback1)(struct curl_fileinfo *, void *, int); -typedef long (*_curl_chunk_bgn_callback2)(void *, void *, int); +typedef long (*Wcurl_chunk_bgn_callback2)(void *, void *, int); /* evaluates to true if expr is of type curl_chunk_end_callback */ #define curlcheck_chunk_end_cb(expr) \ @@ -927,11 +940,11 @@ typedef long (*_curl_chunk_bgn_callback2)(void *, void *, int); /* evaluates to true if expr is of type curl_interleave_callback */ #define curlcheck_interleave_cb(expr) \ (curlcheck_NULL(expr) || \ - curlcheck_cb_compatible((expr), _curl_interleave_callback1) || \ - curlcheck_cb_compatible((expr), _curl_interleave_callback2)) -typedef size_t (*_curl_interleave_callback1)(void *p, size_t s, + curlcheck_cb_compatible((expr), Wcurl_interleave_callback1) || \ + curlcheck_cb_compatible((expr), Wcurl_interleave_callback2)) +typedef size_t (*Wcurl_interleave_callback1)(void *p, size_t s, size_t n, void *u); -typedef size_t (*_curl_interleave_callback2)(char *p, size_t s, +typedef size_t (*Wcurl_interleave_callback2)(char *p, size_t s, size_t n, void *u); /* evaluates to true if expr is of type curl_prereq_callback */ diff --git a/include/curl/urlapi.h b/include/curl/urlapi.h index 34c11a6bb7..b1f3a2316b 100644 --- a/include/curl/urlapi.h +++ b/include/curl/urlapi.h @@ -81,28 +81,28 @@ typedef enum { CURLUPART_ZONEID /* added in 7.65.0 */ } CURLUPart; -#define CURLU_DEFAULT_PORT (1<<0) /* return default port number */ -#define CURLU_NO_DEFAULT_PORT (1<<1) /* act as if no port number was set, - if the port number matches the - default for the scheme */ -#define CURLU_DEFAULT_SCHEME (1<<2) /* return default scheme if - missing */ -#define CURLU_NON_SUPPORT_SCHEME (1<<3) /* allow non-supported scheme */ -#define CURLU_PATH_AS_IS (1<<4) /* leave dot sequences */ -#define CURLU_DISALLOW_USER (1<<5) /* no user+password allowed */ -#define CURLU_URLDECODE (1<<6) /* URL decode on get */ -#define CURLU_URLENCODE (1<<7) /* URL encode on set */ -#define CURLU_APPENDQUERY (1<<8) /* append a form style part */ -#define CURLU_GUESS_SCHEME (1<<9) /* legacy curl-style guessing */ -#define CURLU_NO_AUTHORITY (1<<10) /* Allow empty authority when the - scheme is unknown. */ -#define CURLU_ALLOW_SPACE (1<<11) /* Allow spaces in the URL */ -#define CURLU_PUNYCODE (1<<12) /* get the hostname in punycode */ -#define CURLU_PUNY2IDN (1<<13) /* punycode => IDN conversion */ -#define CURLU_GET_EMPTY (1<<14) /* allow empty queries and fragments - when extracting the URL or the - components */ -#define CURLU_NO_GUESS_SCHEME (1<<15) /* for get, do not accept a guess */ +#define CURLU_DEFAULT_PORT (1 << 0) /* return default port number */ +#define CURLU_NO_DEFAULT_PORT (1 << 1) /* act as if no port number was set, + if the port number matches the + default for the scheme */ +#define CURLU_DEFAULT_SCHEME (1 << 2) /* return default scheme if + missing */ +#define CURLU_NON_SUPPORT_SCHEME (1 << 3) /* allow non-supported scheme */ +#define CURLU_PATH_AS_IS (1 << 4) /* leave dot sequences */ +#define CURLU_DISALLOW_USER (1 << 5) /* no user+password allowed */ +#define CURLU_URLDECODE (1 << 6) /* URL decode on get */ +#define CURLU_URLENCODE (1 << 7) /* URL encode on set */ +#define CURLU_APPENDQUERY (1 << 8) /* append a form style part */ +#define CURLU_GUESS_SCHEME (1 << 9) /* legacy curl-style guessing */ +#define CURLU_NO_AUTHORITY (1 << 10) /* Allow empty authority when the + scheme is unknown. */ +#define CURLU_ALLOW_SPACE (1 << 11) /* Allow spaces in the URL */ +#define CURLU_PUNYCODE (1 << 12) /* get the hostname in punycode */ +#define CURLU_PUNY2IDN (1 << 13) /* punycode => IDN conversion */ +#define CURLU_GET_EMPTY (1 << 14) /* allow empty queries and fragments + when extracting the URL or the + components */ +#define CURLU_NO_GUESS_SCHEME (1 << 15) /* for get, do not accept a guess */ typedef struct Curl_URL CURLU; @@ -114,10 +114,10 @@ CURL_EXTERN CURLU *curl_url(void); /* * curl_url_cleanup() frees the CURLU handle and related resources used for - * the URL parsing. It will not free strings previously returned with the URL + * the URL parsing. It does not free strings previously returned with the URL * API. */ -CURL_EXTERN void curl_url_cleanup(CURLU *handle); +CURL_EXTERN void curl_url_cleanup(CURLU *u); /* * curl_url_dup() duplicates a CURLU handle and returns a new copy. The new @@ -130,15 +130,15 @@ CURL_EXTERN CURLU *curl_url_dup(const CURLU *in); * handle. Returns error code. The returned pointer MUST be freed with * curl_free() afterwards. */ -CURL_EXTERN CURLUcode curl_url_get(const CURLU *handle, CURLUPart what, +CURL_EXTERN CURLUcode curl_url_get(const CURLU *u, CURLUPart what, char **part, unsigned int flags); /* * curl_url_set() sets a specific part of the URL in a CURLU handle. Returns - * error code. The passed in string will be copied. Passing a NULL instead of + * error code. The passed in string is copied. Passing a NULL instead of * a part string, clears that part. */ -CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what, +CURL_EXTERN CURLUcode curl_url_set(CURLU *u, CURLUPart what, const char *part, unsigned int flags); /* @@ -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/include/curl/websockets.h b/include/curl/websockets.h index df8590f399..bf93715abf 100644 --- a/include/curl/websockets.h +++ b/include/curl/websockets.h @@ -37,12 +37,12 @@ struct curl_ws_frame { }; /* flag bits */ -#define CURLWS_TEXT (1<<0) -#define CURLWS_BINARY (1<<1) -#define CURLWS_CONT (1<<2) -#define CURLWS_CLOSE (1<<3) -#define CURLWS_PING (1<<4) -#define CURLWS_OFFSET (1<<5) +#define CURLWS_TEXT (1 << 0) +#define CURLWS_BINARY (1 << 1) +#define CURLWS_CONT (1 << 2) +#define CURLWS_CLOSE (1 << 3) +#define CURLWS_PING (1 << 4) +#define CURLWS_OFFSET (1 << 5) /* * NAME curl_ws_recv() @@ -57,7 +57,7 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, const struct curl_ws_frame **metap); /* flags for curl_ws_send() */ -#define CURLWS_PONG (1<<6) +#define CURLWS_PONG (1 << 6) /* * NAME curl_ws_send() @@ -67,7 +67,7 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, * Sends data over the websocket connection. Use after successful * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. */ -CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, +CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer_arg, size_t buflen, size_t *sent, curl_off_t fragsize, unsigned int flags); @@ -86,8 +86,8 @@ CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl, curl_off_t frame_len); /* bits for the CURLOPT_WS_OPTIONS bitmask: */ -#define CURLWS_RAW_MODE (1L<<0) -#define CURLWS_NOAUTOPONG (1L<<1) +#define CURLWS_RAW_MODE (1L << 0) +#define CURLWS_NOAUTOPONG (1L << 1) CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *curl); diff --git a/lib/.checksrc b/lib/.checksrc deleted file mode 100644 index 22ca8e0b53..0000000000 --- a/lib/.checksrc +++ /dev/null @@ -1,5 +0,0 @@ -banfunc snprintf -banfunc sscanf -banfunc strerror -banfunc strtol -banfunc vsnprint diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 3476d55b09..aae466c677 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -26,7 +26,7 @@ set(LIBCURL_OUTPUT_NAME "${LIB_NAME}" CACHE STRING "Basename of the curl library set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_DEBUG_MACROS} "BUILDING_LIBCURL") -configure_file("curl_config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h") +configure_file("curl_config-cmake.h.in" "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h") # Get CSOURCES, HHEADERS, LIB_RCFILES variables curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") @@ -38,16 +38,17 @@ list(APPEND HHEADERS "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h") set_property(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES "${PROJECT_BINARY_DIR}/lib" # for "curl_config.h" + "${PROJECT_SOURCE_DIR}/lib" ) if(CURL_BUILD_TESTING) - # special libcurlu library just for unittests + # special libcurlu library for unittests add_library(curlu STATIC EXCLUDE_FROM_ALL ${HHEADERS} ${CSOURCES}) target_compile_definitions(curlu PUBLIC "CURL_STATICLIB" "UNITTESTS") - target_link_libraries(curlu PRIVATE ${CURL_LIBS}) + target_link_libraries(curlu PUBLIC ${CURL_LIBS}) # There is plenty of parallelism when building the testdeps target. # Override the curlu batch size with the maximum to optimize performance. - set_target_properties(curlu PROPERTIES UNITY_BUILD_BATCH_SIZE 0 C_CLANG_TIDY "") + set_target_properties(curlu PROPERTIES UNITY_BUILD ON UNITY_BUILD_BATCH_SIZE 0 C_CLANG_TIDY "") add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/unitprotos.h" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" @@ -98,9 +99,9 @@ if(NOT DEFINED SHARE_LIB_OBJECT) endif() endif() -if(SHARE_LIB_OBJECT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.12) +if(SHARE_LIB_OBJECT) set(LIB_OBJECT "libcurl_object") - add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES}) # Requires CMake 3.12 + add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES}) if(WIN32) # Define CURL_STATICLIB always, to disable __declspec(dllexport) for # exported libcurl symbols. We handle exports via libcurl.def instead. @@ -111,8 +112,9 @@ if(SHARE_LIB_OBJECT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.12) target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS}) set_target_properties(${LIB_OBJECT} PROPERTIES POSITION_INDEPENDENT_CODE ON) + set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAGS}") if(CURL_HIDES_PRIVATE_SYMBOLS) - set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}") + set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}") set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") endif() if(CURL_HAS_LTO) @@ -124,6 +126,17 @@ if(SHARE_LIB_OBJECT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.12) set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) endif() endif() + if(CURL_CLANG_TIDY) + set_target_properties(${LIB_OBJECT} PROPERTIES UNITY_BUILD OFF) + endif() + if(CURL_ANALYZER_CFLAGS) + set_target_properties(${LIB_OBJECT} PROPERTIES UNITY_BUILD OFF) + set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS}) + endif() + if(CURL_CODE_COVERAGE) + set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS}) + set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS}) + endif() target_include_directories(${LIB_OBJECT} INTERFACE "$" @@ -147,10 +160,10 @@ if(BUILD_STATIC_LIBS) set_target_properties(${LIB_STATIC} PROPERTIES PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}" SUFFIX "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}" - INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB" - INTERFACE_LINK_DIRECTORIES "${CURL_LIBDIRS}") + INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB") + set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAGS}") if(CURL_HIDES_PRIVATE_SYMBOLS) - set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}") + set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}") set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") endif() if(CURL_HAS_LTO) @@ -162,6 +175,21 @@ if(BUILD_STATIC_LIBS) set_target_properties(${LIB_STATIC} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) endif() endif() + if(CURL_CLANG_TIDY) + if(BUILD_SHARED_LIBS) # disable clang-tidy for static, and limit to the shared library, if both enabled + set_target_properties(${LIB_STATIC} PROPERTIES C_CLANG_TIDY "") + else() + set_target_properties(${LIB_STATIC} PROPERTIES UNITY_BUILD OFF) + endif() + endif() + if(CURL_ANALYZER_CFLAGS) + set_target_properties(${LIB_STATIC} PROPERTIES UNITY_BUILD OFF) + set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS}) + endif() + if(CURL_CODE_COVERAGE) + set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS}) + set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS}) + endif() target_include_directories(${LIB_STATIC} INTERFACE "$" @@ -185,8 +213,9 @@ if(BUILD_SHARED_LIBS) PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}" IMPORT_PREFIX "" IMPORT_SUFFIX "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}" POSITION_INDEPENDENT_CODE ON) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAGS}") if(CURL_HIDES_PRIVATE_SYMBOLS) - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}") + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}") set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS") endif() if(CURL_HAS_LTO) @@ -198,6 +227,18 @@ if(BUILD_SHARED_LIBS) set_target_properties(${LIB_SHARED} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) endif() endif() + if(CURL_CLANG_TIDY) + set_target_properties(${LIB_SHARED} PROPERTIES UNITY_BUILD OFF) + endif() + if(CURL_ANALYZER_CFLAGS) + set_target_properties(${LIB_SHARED} PROPERTIES UNITY_BUILD OFF) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS}) + endif() + if(CURL_CODE_COVERAGE) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS}) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS}) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS ${CURL_COVERAGE_LDFLAGS}) + endif() target_include_directories(${LIB_SHARED} INTERFACE "$" @@ -206,7 +247,7 @@ if(BUILD_SHARED_LIBS) if(CMAKE_DLL_NAME_WITH_SOVERSION OR CYGWIN OR APPLE OR - CMAKE_SYSTEM_NAME STREQUAL "AIX" OR + AIX OR CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR @@ -271,11 +312,7 @@ if(BUILD_SHARED_LIBS) set(CMAKE_REQUIRED_LINK_OPTIONS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers") check_c_source_compiles("int main(void) { return 0; }" HAVE_VERSIONED_SYMBOLS) if(HAVE_VERSIONED_SYMBOLS) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - set_target_properties(${LIB_SHARED} PROPERTIES LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}") - else() - set_target_properties(${LIB_SHARED} PROPERTIES LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}") - endif() + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}") else() message(WARNING "Versioned symbols requested, but not supported by the toolchain.") endif() @@ -287,7 +324,7 @@ add_library(${LIB_NAME} ALIAS ${LIB_SELECTED}) add_library(${PROJECT_NAME}::${LIB_NAME} ALIAS ${LIB_SELECTED}) if(CURL_ENABLE_EXPORT_TARGET) - if(BUILD_STATIC_LIBS) + if(NOT CURL_DISABLE_INSTALL AND BUILD_STATIC_LIBS) install(TARGETS ${LIB_STATIC} EXPORT ${TARGETS_EXPORT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -295,7 +332,7 @@ if(CURL_ENABLE_EXPORT_TARGET) RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) endif() - if(BUILD_SHARED_LIBS) + if(NOT CURL_DISABLE_INSTALL AND BUILD_SHARED_LIBS) install(TARGETS ${LIB_SHARED} EXPORT ${TARGETS_EXPORT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -309,3 +346,12 @@ if(CURL_ENABLE_EXPORT_TARGET) NAMESPACE ${PROJECT_NAME}:: ) endif() + +if(PERL_EXECUTABLE) + add_custom_target(curl-optiontable + COMMENT "Generating lib/easyoptions.c" VERBATIM USES_TERMINAL + COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/optiontable.pl" "${PROJECT_SOURCE_DIR}/include/curl/curl.h" + > "${PROJECT_SOURCE_DIR}/lib/easyoptions.c" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/optiontable.pl" "${PROJECT_SOURCE_DIR}/include/curl/curl.h" + ) +endif() diff --git a/lib/Makefile.am b/lib/Makefile.am index 973876f501..8a2bd4e68e 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -26,14 +26,11 @@ AUTOMAKE_OPTIONS = foreign nostdinc # Get CSOURCES, HHEADERS, LIB_RCFILES variables include Makefile.inc -CMAKE_DIST = CMakeLists.txt curl_config.h.cmake +CMAKE_DIST = CMakeLists.txt curl_config-cmake.h.in -CHECKSRC_DIST = .checksrc \ - curlx/.checksrc vauth/.checksrc vquic/.checksrc vssh/.checksrc vtls/.checksrc - -EXTRA_DIST = config-mac.h config-os400.h config-plan9.h config-riscos.h \ - config-win32.h curl_config.h.in $(LIB_RCFILES) libcurl.def \ - $(CMAKE_DIST) Makefile.soname optiontable.pl $(CHECKSRC_DIST) +EXTRA_DIST = config-mac.h config-os400.h config-riscos.h config-win32.h \ + curl_config.h.in $(LIB_RCFILES) libcurl.def $(CMAKE_DIST) Makefile.soname \ + optiontable.pl lib_LTLIBRARIES = libcurl.la @@ -61,19 +58,15 @@ CFLAGS += @CURL_CFLAG_EXTRAS@ AM_CPPFLAGS = -I$(top_srcdir)/include \ -I$(top_builddir)/lib \ + -I$(top_srcdir)/lib \ -I$(srcdir) # Prevent LIBS from being used for all link targets LIBS = $(BLANK_AT_MAKETIME) -AM_LDFLAGS = -AM_CFLAGS = if DEBUGBUILD AM_CPPFLAGS += -DDEBUGBUILD endif -if CURLDEBUG -AM_CPPFLAGS += -DCURLDEBUG -endif AM_CPPFLAGS += -DBUILDING_LIBCURL if DOING_NATIVE_WINDOWS @@ -139,12 +132,11 @@ libcurl_la_CFLAGS_EXTRA += $(CFLAG_CURL_SYMBOL_HIDING) endif libcurl_la_CPPFLAGS = $(AM_CPPFLAGS) $(libcurl_la_CPPFLAGS_EXTRA) -libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(CURL_LDFLAGS_LIB) $(LIBCURL_PC_LIBS_PRIVATE) -libcurl_la_CFLAGS = $(AM_CFLAGS) $(libcurl_la_CFLAGS_EXTRA) +libcurl_la_LDFLAGS = $(libcurl_la_LDFLAGS_EXTRA) $(CURL_LDFLAGS_LIB) $(LIBCURL_PC_LIBS_PRIVATE) +libcurl_la_CFLAGS = $(libcurl_la_CFLAGS_EXTRA) libcurlu_la_CPPFLAGS = $(AM_CPPFLAGS) -DCURL_STATICLIB -DUNITTESTS -libcurlu_la_LDFLAGS = $(AM_LDFLAGS) -static $(LIBCURL_PC_LIBS_PRIVATE) -libcurlu_la_CFLAGS = $(AM_CFLAGS) +libcurlu_la_LDFLAGS = -static $(LIBCURL_PC_LIBS_PRIVATE) CHECKSRC = $(CS_$(V)) CS_0 = @echo " RUN " $@; @@ -152,8 +144,7 @@ CS_1 = CS_ = $(CS_0) checksrc: - $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \ - $(CSOURCES) $(HHEADERS)) + $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) $(CSOURCES) $(HHEADERS)) if NOT_CURL_CI if DEBUGBUILD @@ -171,22 +162,24 @@ UNITV_ = $(UNITV_0) # UNITPROTOS depends on every C file in the lib/ dir $(UNITPROTOS): $(CSOURCES) - $(UNIT_V)(cd $(srcdir) && @PERL@ ../scripts/extract-unit-protos $(CSOURCES) > $(top_builddir)/lib/$(UNITPROTOS)) + $(UNIT_V)(cd $(srcdir) && @PERL@ ../scripts/extract-unit-protos $(CSOURCES)) > $(top_builddir)/lib/$(UNITPROTOS) -# disable the tests that are mostly causing false positives -TIDYFLAGS := -checks=-clang-analyzer-security.insecureAPI.bzero,-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-security.ArrayBound,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling -quiet +_tidy_cflags = +TIDYFLAGS = if CURL_WERROR -TIDYFLAGS += --warnings-as-errors=* +TIDYFLAGS += '--warnings-as-errors=*' +endif +if CLANG +_tidy_cflags += $(CFLAGS) endif - -TIDY := clang-tidy tidy: (_csources=`echo ' $(CSOURCES)' | sed -E -e 's/ +$$//' -e 's/ +/ /g' -e 's| | $(srcdir)/|g'`; \ - $(TIDY) $$_csources $(TIDYFLAGS) $(CURL_CLANG_TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H) + @CLANG_TIDY@ --config-file=$(top_srcdir)/.clang-tidy.yml $(TIDYFLAGS) $(CURL_CLANG_TIDYFLAGS) $$_csources \ + -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H $(_tidy_cflags)) optiontable: - @PERL@ $(srcdir)/optiontable.pl < $(top_srcdir)/include/curl/curl.h > $(srcdir)/easyoptions.c + @PERL@ $(srcdir)/optiontable.pl $(top_srcdir)/include/curl/curl.h > $(srcdir)/easyoptions.c if HAVE_WINDRES .rc.lo: diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 524fdcc53d..d762f72e42 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -23,36 +23,46 @@ ########################################################################### # Shared between CMakeLists.txt and Makefile.am -LIB_CURLX_CFILES = \ - curlx/base64.c \ - curlx/dynbuf.c \ - curlx/inet_ntop.c \ - curlx/inet_pton.c \ - curlx/multibyte.c \ - curlx/nonblock.c \ - curlx/strparse.c \ - curlx/timediff.c \ - curlx/timeval.c \ +LIB_CURLX_CFILES = \ + curlx/base64.c \ + curlx/basename.c \ + curlx/dynbuf.c \ + curlx/fopen.c \ + curlx/inet_ntop.c \ + curlx/inet_pton.c \ + curlx/multibyte.c \ + curlx/nonblock.c \ + curlx/snprintf.c \ + curlx/strcopy.c \ + curlx/strdup.c \ + curlx/strerr.c \ + curlx/strparse.c \ + curlx/timediff.c \ + curlx/timeval.c \ curlx/version_win32.c \ - curlx/wait.c \ - curlx/warnless.c \ + curlx/wait.c \ + curlx/warnless.c \ curlx/winapi.c -LIB_CURLX_HFILES = \ - curlx/binmode.h \ - curlx/base64.h \ - curlx/curlx.h \ - curlx/dynbuf.h \ - curlx/inet_ntop.h \ - curlx/inet_pton.h \ - curlx/multibyte.h \ - curlx/nonblock.h \ - curlx/strparse.h \ - curlx/timediff.h \ - curlx/timeval.h \ +LIB_CURLX_HFILES = \ + curlx/base64.h \ + curlx/basename.h \ + curlx/dynbuf.h \ + curlx/fopen.h \ + curlx/inet_ntop.h \ + curlx/inet_pton.h \ + curlx/multibyte.h \ + curlx/nonblock.h \ + curlx/snprintf.h \ + curlx/strcopy.h \ + curlx/strdup.h \ + curlx/strerr.h \ + curlx/strparse.h \ + curlx/timediff.h \ + curlx/timeval.h \ curlx/version_win32.h \ - curlx/wait.h \ - curlx/warnless.h \ + curlx/wait.h \ + curlx/warnless.h \ curlx/winapi.h LIB_VAUTH_CFILES = \ @@ -75,12 +85,12 @@ LIB_VAUTH_HFILES = \ vauth/vauth.h LIB_VTLS_CFILES = \ + vtls/apple.c \ vtls/cipher_suite.c \ vtls/gtls.c \ vtls/hostcheck.c \ vtls/keylog.c \ vtls/mbedtls.c \ - vtls/mbedtls_threadlock.c \ vtls/openssl.c \ vtls/rustls.c \ vtls/schannel.c \ @@ -92,12 +102,12 @@ LIB_VTLS_CFILES = \ vtls/x509asn1.c LIB_VTLS_HFILES = \ + vtls/apple.h \ vtls/cipher_suite.h \ vtls/gtls.h \ vtls/hostcheck.h \ vtls/keylog.h \ vtls/mbedtls.h \ - vtls/mbedtls_threadlock.h \ vtls/openssl.h \ vtls/rustls.h \ vtls/schannel.h \ @@ -109,29 +119,26 @@ LIB_VTLS_HFILES = \ vtls/wolfssl.h \ vtls/x509asn1.h -LIB_VQUIC_CFILES = \ +LIB_VQUIC_CFILES = \ vquic/curl_ngtcp2.c \ - vquic/curl_osslq.c \ vquic/curl_quiche.c \ - vquic/vquic.c \ + vquic/vquic.c \ vquic/vquic-tls.c -LIB_VQUIC_HFILES = \ +LIB_VQUIC_HFILES = \ vquic/curl_ngtcp2.h \ - vquic/curl_osslq.h \ vquic/curl_quiche.h \ - vquic/vquic.h \ - vquic/vquic_int.h \ + vquic/vquic.h \ + vquic/vquic_int.h \ vquic/vquic-tls.h -LIB_VSSH_CFILES = \ - vssh/libssh.c \ - vssh/libssh2.c \ - vssh/curl_path.c \ - vssh/wolfssh.c +LIB_VSSH_CFILES = \ + vssh/libssh.c \ + vssh/libssh2.c \ + vssh/vssh.c LIB_VSSH_HFILES = \ - vssh/curl_path.h \ + vssh/vssh.h \ vssh/ssh.h LIB_CFILES = \ @@ -142,6 +149,7 @@ LIB_CFILES = \ asyn-thrdd.c \ bufq.c \ bufref.c \ + cf-dns.c \ cf-h1-proxy.c \ cf-h2-proxy.c \ cf-haproxy.c \ @@ -155,24 +163,25 @@ LIB_CFILES = \ cookie.c \ cshutdn.c \ curl_addrinfo.c \ - curl_des.c \ curl_endian.c \ curl_fnmatch.c \ + curl_fopen.c \ curl_get_line.c \ curl_gethostname.c \ curl_gssapi.c \ curl_memrchr.c \ curl_ntlm_core.c \ curl_range.c \ - curl_rtmp.c \ curl_sasl.c \ curl_sha512_256.c \ + curl_share.c \ curl_sspi.c \ curl_threads.c \ curl_trc.c \ cw-out.c \ cw-pause.c \ dict.c \ + dnscache.c \ doh.c \ dynhds.c \ easy.c \ @@ -182,7 +191,6 @@ LIB_CFILES = \ fake_addrinfo.c \ file.c \ fileinfo.c \ - fopen.c \ formdata.c \ ftp.c \ ftplistparser.c \ @@ -209,7 +217,6 @@ LIB_CFILES = \ idn.c \ if2ip.c \ imap.c \ - krb5.c \ ldap.c \ llist.c \ macos.c \ @@ -221,6 +228,7 @@ LIB_CFILES = \ mqtt.c \ multi.c \ multi_ev.c \ + multi_ntfy.c \ netrc.c \ noproxy.c \ openldap.c \ @@ -228,16 +236,16 @@ LIB_CFILES = \ pingpong.c \ pop3.c \ progress.c \ + protocol.c \ psl.c \ rand.c \ - rename.c \ + ratelimit.c \ request.c \ rtsp.c \ select.c \ sendf.c \ setopt.c \ sha256.c \ - share.c \ slist.c \ smb.c \ smtp.c \ @@ -245,15 +253,15 @@ LIB_CFILES = \ socks.c \ socks_gssapi.c \ socks_sspi.c \ - speedcheck.c \ splay.c \ strcase.c \ - strdup.c \ strequal.c \ strerror.c \ system_win32.c \ telnet.c \ tftp.c \ + thrdpool.c \ + thrdqueue.c \ transfer.c \ uint-bset.c \ uint-hash.c \ @@ -271,6 +279,7 @@ LIB_HFILES = \ asyn.h \ bufq.h \ bufref.h \ + cf-dns.h \ cf-h1-proxy.h \ cf-h2-proxy.h \ cf-haproxy.h \ @@ -285,35 +294,32 @@ LIB_HFILES = \ cookie.h \ curl_addrinfo.h \ curl_ctype.h \ - curl_des.h \ curl_endian.h \ curl_fnmatch.h \ + curl_fopen.h \ curl_get_line.h \ curl_gethostname.h \ curl_gssapi.h \ curl_hmac.h \ - curl_krb5.h \ curl_ldap.h \ curl_md4.h \ curl_md5.h \ - curl_mem_undef.h \ - curl_memory.h \ curl_memrchr.h \ curl_ntlm_core.h \ curl_printf.h \ curl_range.h \ - curl_rtmp.h \ curl_sasl.h \ curl_setup.h \ - curl_setup_once.h \ curl_sha256.h \ curl_sha512_256.h \ + curl_share.h \ curl_sspi.h \ curl_threads.h \ curl_trc.h \ cw-out.h \ cw-pause.h \ dict.h \ + dnscache.h \ doh.h \ dynhds.h \ easy_lock.h \ @@ -323,9 +329,9 @@ LIB_HFILES = \ fake_addrinfo.h \ file.h \ fileinfo.h \ - fopen.h \ formdata.h \ ftp.h \ + ftp-int.h \ ftplistparser.h \ functypes.h \ getinfo.h \ @@ -349,11 +355,11 @@ LIB_HFILES = \ imap.h \ llist.h \ macos.h \ - memdebug.h \ mime.h \ mqtt.h \ multihandle.h \ multi_ev.h \ + multi_ntfy.h \ multiif.h \ netrc.h \ noproxy.h \ @@ -361,9 +367,10 @@ LIB_HFILES = \ pingpong.h \ pop3.h \ progress.h \ + protocol.h \ psl.h \ rand.h \ - rename.h \ + ratelimit.h \ request.h \ rtsp.h \ select.h \ @@ -372,7 +379,6 @@ LIB_HFILES = \ setup-os400.h \ setup-vms.h \ setup-win32.h \ - share.h \ sigpipe.h \ slist.h \ smb.h \ @@ -380,14 +386,14 @@ LIB_HFILES = \ sockaddr.h \ socketpair.h \ socks.h \ - speedcheck.h \ splay.h \ strcase.h \ - strdup.h \ strerror.h \ system_win32.h \ telnet.h \ tftp.h \ + thrdpool.h \ + thrdqueue.h \ transfer.h \ uint-bset.h \ uint-hash.h \ diff --git a/lib/Makefile.soname b/lib/Makefile.soname index 22c04e24c7..aadc7f413e 100644 --- a/lib/Makefile.soname +++ b/lib/Makefile.soname @@ -30,9 +30,8 @@ VERSIONDEL=8 # libtool version: VERSIONINFO=-version-info $(VERSIONCHANGE):$(VERSIONADD):$(VERSIONDEL) -# This flag accepts an argument of the form current[:revision[:age]]. So, -# passing -version-info 3:12:1 sets current to 3, revision to 12, and age to -# 1. +# This flag accepts an argument of the form current[:revision[:age]]. It means +# passing -version-info 3:12:1 sets current to 3, revision to 12, and age to 1. # # Here's the simplified rule guide on how to change -version-info: # (current version is C:R:A) diff --git a/lib/altsvc.c b/lib/altsvc.c index c7448692fb..e0834cb663 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -28,32 +28,41 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC) -#include #include "urldata.h" #include "altsvc.h" +#include "curl_fopen.h" #include "curl_get_line.h" #include "parsedate.h" -#include "sendf.h" -#include "curlx/warnless.h" -#include "fopen.h" -#include "rename.h" -#include "strdup.h" +#include "curl_trc.h" #include "curlx/inet_pton.h" #include "curlx/strparse.h" #include "connect.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define MAX_ALTSVC_LINE 4095 -#define MAX_ALTSVC_DATELEN 256 +#define MAX_ALTSVC_LINE 4095 +#define MAX_ALTSVC_DATELEN 17 #define MAX_ALTSVC_HOSTLEN 2048 #define MAX_ALTSVC_ALPNLEN 10 #define H3VERSION "h3" +#if defined(DEBUGBUILD) || defined(UNITTESTS) +/* to play well with debug builds, we can *set* a fixed time this will + return */ +static time_t altsvc_debugtime(void *unused) +{ + const char *timestr = getenv("CURL_TIME"); + (void)unused; + if(timestr) { + curl_off_t val; + curlx_str_number(×tr, &val, TIME_T_MAX); + return (time_t)val; + } + return time(NULL); +} +#undef time +#define time(x) altsvc_debugtime(x) +#endif + /* Given the ALPN ID, return the name */ const char *Curl_alpnid2str(enum alpnid id) { @@ -69,13 +78,7 @@ const char *Curl_alpnid2str(enum alpnid id) } } - -static void altsvc_free(struct altsvc *as) -{ - free(as->src.host); - free(as->dst.host); - free(as); -} +#define altsvc_free(x) curlx_free(x) static struct altsvc *altsvc_createid(const char *srchost, size_t hlen, @@ -86,38 +89,35 @@ static struct altsvc *altsvc_createid(const char *srchost, size_t srcport, size_t dstport) { - struct altsvc *as = calloc(1, sizeof(struct altsvc)); - if(!as) - return NULL; - DEBUGASSERT(hlen); - DEBUGASSERT(dlen); - if(!hlen || !dlen) - /* bad input */ - goto error; + struct altsvc *as; if((hlen > 2) && srchost[0] == '[') { /* IPv6 address, strip off brackets */ srchost++; hlen -= 2; } - else if(srchost[hlen - 1] == '.') { + else if(hlen && (srchost[hlen - 1] == '.')) { /* strip off trailing dot */ hlen--; - if(!hlen) - goto error; } if((dlen > 2) && dsthost[0] == '[') { /* IPv6 address, strip off brackets */ dsthost++; dlen -= 2; } + if(!hlen || !dlen) + /* bad input */ + return NULL; + /* struct size plus both strings */ + as = curlx_calloc(1, sizeof(struct altsvc) + (hlen + 1) + (dlen + 1)); + if(!as) + return NULL; + as->src.host = (char *)as + sizeof(struct altsvc); + memcpy(as->src.host, srchost, hlen); + /* the null terminator is already there */ - as->src.host = Curl_memdup0(srchost, hlen); - if(!as->src.host) - goto error; - - as->dst.host = Curl_memdup0(dsthost, dlen); - if(!as->dst.host) - goto error; + as->dst.host = (char *)as + sizeof(struct altsvc) + hlen + 1; + memcpy(as->dst.host, dsthost, dlen); + /* the null terminator is already there */ as->src.alpnid = srcalpnid; as->dst.alpnid = dstalpnid; @@ -125,9 +125,6 @@ static struct altsvc *altsvc_createid(const char *srchost, as->dst.port = (unsigned short)dstport; return as; -error: - altsvc_free(as); - return NULL; } static struct altsvc *altsvc_create(struct Curl_str *srchost, @@ -137,10 +134,8 @@ static struct altsvc *altsvc_create(struct Curl_str *srchost, size_t srcport, size_t dstport) { - enum alpnid dstalpnid = - Curl_alpn2alpnid(curlx_str(dstalpn), curlx_strlen(dstalpn)); - enum alpnid srcalpnid = - Curl_alpn2alpnid(curlx_str(srcalpn), curlx_strlen(srcalpn)); + enum alpnid dstalpnid = Curl_str2alpnid(dstalpn); + enum alpnid srcalpnid = Curl_str2alpnid(srcalpn); if(!srcalpnid || !dstalpnid) return NULL; return altsvc_createid(curlx_str(srchost), curlx_strlen(srchost), @@ -149,6 +144,20 @@ static struct altsvc *altsvc_create(struct Curl_str *srchost, srcport, dstport); } +/* append the new entry to the list after possibly removing an old entry + first */ +static void altsvc_append(struct altsvcinfo *asi, struct altsvc *as) +{ + while(Curl_llist_count(&asi->list) >= MAX_ALTSVC_ENTRIES) { + /* It's full. Remove the first entry in the list */ + struct Curl_llist_node *e = Curl_llist_head(&asi->list); + struct altsvc *oldas = Curl_node_elem(e); + Curl_node_remove(e); + altsvc_free(oldas); + } + Curl_llist_append(&asi->list, as, &as->node); +} + /* only returns SERIOUS errors */ static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line) { @@ -185,22 +194,26 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line) curlx_str_newline(&line)) ; else { - struct altsvc *as; char dbuf[MAX_ALTSVC_DATELEN + 1]; time_t expires = 0; + time_t now = time(NULL); /* The date parser works on a null-terminated string. The maximum length is upheld by curlx_str_quotedword(). */ memcpy(dbuf, curlx_str(&date), curlx_strlen(&date)); dbuf[curlx_strlen(&date)] = 0; Curl_getdate_capped(dbuf, &expires); - as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn, - (size_t)srcport, (size_t)dstport); - if(as) { - as->expires = expires; - as->prio = 0; /* not supported to just set zero */ - as->persist = persist ? 1 : 0; - Curl_llist_append(&asi->list, as, &as->node); + + if(now < expires) { + struct altsvc *as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn, + (size_t)srcport, (size_t)dstport); + if(as) { + as->expires = expires; + as->persist = persist ? 1 : 0; + altsvc_append(asi, as); + } + else + return CURLE_OUT_OF_MEMORY; } } @@ -222,23 +235,30 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) /* we need a private copy of the filename so that the altsvc cache file name survives an easy handle reset */ - free(asi->filename); - asi->filename = strdup(file); + curlx_free(asi->filename); + asi->filename = curlx_strdup(file); if(!asi->filename) return CURLE_OUT_OF_MEMORY; - fp = fopen(file, FOPEN_READTEXT); + fp = curlx_fopen(file, FOPEN_READTEXT); if(fp) { - struct dynbuf buf; - curlx_dyn_init(&buf, MAX_ALTSVC_LINE); - while(Curl_get_line(&buf, fp)) { - const char *lineptr = curlx_dyn_ptr(&buf); - curlx_str_passblanks(&lineptr); - if(curlx_str_single(&lineptr, '#')) - altsvc_add(asi, lineptr); + curlx_struct_stat stat; + if((curlx_fstat(fileno(fp), &stat) == -1) || !S_ISDIR(stat.st_mode)) { + bool eof = FALSE; + struct dynbuf buf; + curlx_dyn_init(&buf, MAX_ALTSVC_LINE); + do { + result = Curl_get_line(&buf, fp, &eof); + if(!result) { + const char *lineptr = curlx_dyn_ptr(&buf); + curlx_str_passblanks(&lineptr); + if(curlx_str_single(&lineptr, '#')) + altsvc_add(asi, lineptr); + } + } while(!result && !eof); + curlx_dyn_free(&buf); /* free the line buffer */ } - curlx_dyn_free(&buf); /* free the line buffer */ - fclose(fp); + curlx_fclose(fp); } return result; } @@ -246,7 +266,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; @@ -254,7 +273,7 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp) const char *dst6_post = ""; const char *src6_pre = ""; const char *src6_post = ""; - CURLcode result = Curl_gmtime(as->expires, &stamp); + CURLcode result = curlx_gmtime(as->expires, &stamp); if(result) return result; #ifdef USE_IPV6 @@ -270,23 +289,23 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp) } } #endif - fprintf(fp, - "%s %s%s%s %u " - "%s %s%s%s %u " - "\"%d%02d%02d " - "%02d:%02d:%02d\" " - "%u %u\n", - Curl_alpnid2str(as->src.alpnid), - src6_pre, as->src.host, src6_post, - as->src.port, + curl_mfprintf(fp, + "%s %s%s%s %u " + "%s %s%s%s %u " + "\"%d%02d%02d " + "%02d:%02d:%02d\" " + "%u 0\n", /* prio still always zero */ + Curl_alpnid2str(as->src.alpnid), + src6_pre, as->src.host, src6_post, + as->src.port, - Curl_alpnid2str(as->dst.alpnid), - dst6_pre, as->dst.host, dst6_post, - as->dst.port, + Curl_alpnid2str(as->dst.alpnid), + dst6_pre, as->dst.host, dst6_post, + as->dst.port, - stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, - stamp.tm_hour, stamp.tm_min, stamp.tm_sec, - as->persist, as->prio); + stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, + stamp.tm_hour, stamp.tm_min, stamp.tm_sec, + as->persist); return CURLE_OK; } @@ -298,7 +317,7 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp) */ struct altsvcinfo *Curl_altsvc_init(void) { - struct altsvcinfo *asi = calloc(1, sizeof(struct altsvcinfo)); + struct altsvcinfo *asi = curlx_calloc(1, sizeof(struct altsvcinfo)); if(!asi) return NULL; Curl_llist_init(&asi->list, NULL); @@ -327,10 +346,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; } @@ -338,20 +365,20 @@ 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); altsvc_free(as); } - free(altsvc->filename); - free(altsvc); - *altsvcp = NULL; /* clear the pointer */ + curlx_free(altsvc->filename); + curlx_free(altsvc); + *asi = NULL; /* clear the pointer */ } } @@ -359,21 +386,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; @@ -384,21 +411,21 @@ 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); if(result) break; } - fclose(out); - if(!result && tempstore && Curl_rename(tempstore, file)) + curlx_fclose(out); + if(!result && tempstore && curlx_rename(tempstore, file)) result = CURLE_WRITE_ERROR; if(result && tempstore) unlink(tempstore); } - free(tempstore); + curlx_free(tempstore); return result; } @@ -437,24 +464,6 @@ static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid, } } -#if defined(DEBUGBUILD) || defined(UNITTESTS) -/* to play well with debug builds, we can *set* a fixed time this will - return */ -static time_t altsvc_debugtime(void *unused) -{ - const char *timestr = getenv("CURL_TIME"); - (void)unused; - if(timestr) { - curl_off_t val; - curlx_str_number(×tr, &val, TIME_T_MAX); - return (time_t)val; - } - return time(NULL); -} -#undef time -#define time(x) altsvc_debugtime(x) -#endif - /* * Curl_altsvc_parse() takes an incoming alt-svc response header and stores * the data correctly in the cache. @@ -476,12 +485,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; - const char *sp; - time_t maxage = 24 * 3600; /* default is 24 hours */ - bool persist = FALSE; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif DEBUGASSERT(asi); @@ -503,47 +506,12 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, curlx_str_trimblanks(&alpn); - /* Handle the optional 'ma' and 'persist' flags once first, as they need to - be known for each alternative service. Unknown flags are skipped. */ - sp = strchr(p, ';'); - if(sp) { - sp++; /* pass the semicolon */ - for(;;) { - struct Curl_str name; - struct Curl_str val; - const char *vp; - curl_off_t num; - bool quoted; - /* allow some extra whitespaces around name and value */ - if(curlx_str_until(&sp, &name, 20, '=') || - curlx_str_single(&sp, '=') || - curlx_str_until(&sp, &val, 80, ';')) - break; - curlx_str_trimblanks(&name); - curlx_str_trimblanks(&val); - /* the value might be quoted */ - vp = curlx_str(&val); - quoted = (*vp == '\"'); - if(quoted) - vp++; - if(!curlx_str_number(&vp, &num, TIME_T_MAX)) { - if(curlx_str_casecompare(&name, "ma")) - maxage = (time_t)num; - else if(curlx_str_casecompare(&name, "persist") && (num == 1)) - persist = TRUE; - } - if(quoted && curlx_str_single(&sp, '\"')) - break; - if(curlx_str_single(&sp, ';')) - break; - } - } - do { if(!curlx_str_single(&p, '=')) { + time_t maxage = 24 * 3600; /* default is 24 hours */ + bool persist = FALSE; /* [protocol]="[host][:port], [protocol]="[host][:port]" */ - enum alpnid dstalpnid = - Curl_alpn2alpnid(curlx_str(&alpn), curlx_strlen(&alpn)); + enum alpnid dstalpnid = Curl_str2alpnid(&alpn); if(!curlx_str_single(&p, '\"')) { struct Curl_str dsthost; curl_off_t port = 0; @@ -556,7 +524,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, } } else { - /* IPv6 host name */ + /* IPv6 hostname */ if(curlx_str_until(&p, &dsthost, MAX_IPADR_LEN, ']') || curlx_str_single(&p, ']')) { infof(data, "Bad alt-svc IPv6 hostname, ignoring."); @@ -580,6 +548,45 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, if(curlx_str_single(&p, '\"')) break; + /* Handle the optional 'ma' and 'persist' flags. Unknown flags are + skipped. */ + curlx_str_passblanks(&p); + if(!curlx_str_single(&p, ';')) { + for(;;) { + struct Curl_str name; + struct Curl_str val; + const char *vp; + curl_off_t num; + bool quoted; + /* allow some extra whitespaces around name and value */ + if(curlx_str_until(&p, &name, 20, '=') || + curlx_str_single(&p, '=') || + curlx_str_cspn(&p, &val, ",;")) + break; + curlx_str_trimblanks(&name); + curlx_str_trimblanks(&val); + /* the value might be quoted */ + vp = curlx_str(&val); + quoted = (*vp == '\"'); + if(quoted) + vp++; + if(!curlx_str_number(&vp, &num, TIME_T_MAX)) { + if(curlx_str_casecompare(&name, "ma")) + maxage = (time_t)num; + else if(curlx_str_casecompare(&name, "persist") && (num == 1)) + persist = TRUE; + } + else + break; + p = vp; /* point to the byte ending the value */ + curlx_str_passblanks(&p); + if(quoted && curlx_str_single(&p, '\"')) + break; + curlx_str_passblanks(&p); + if(curlx_str_single(&p, ';')) + break; + } + } if(dstalpnid) { if(!entries++) /* Flush cached alternatives for this source origin, if any - when @@ -600,11 +607,13 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, else as->expires = maxage + secs; as->persist = persist; - Curl_llist_append(&asi->list, as, &as->node); + altsvc_append(asi, as); infof(data, "Added alt-svc: %.*s:%d over %s", (int)curlx_strlen(&dsthost), curlx_str(&dsthost), dstport, Curl_alpnid2str(dstalpnid)); } + else + return CURLE_OUT_OF_MEMORY; } } else @@ -634,7 +643,8 @@ bool Curl_altsvc_lookup(struct altsvcinfo *asi, enum alpnid srcalpnid, const char *srchost, int srcport, struct altsvc **dstentry, - const int versions) /* one or more bits */ + const int versions, /* one or more bits */ + bool *psame_destination) { struct Curl_llist_node *e; struct Curl_llist_node *n; @@ -643,6 +653,7 @@ bool Curl_altsvc_lookup(struct altsvcinfo *asi, DEBUGASSERT(srchost); DEBUGASSERT(dstentry); + *psame_destination = FALSE; for(e = Curl_llist_head(&asi->list); e; e = n) { struct altsvc *as = Curl_node_elem(e); n = Curl_node_next(e); @@ -658,6 +669,8 @@ bool Curl_altsvc_lookup(struct altsvcinfo *asi, (versions & (int)as->dst.alpnid)) { /* match */ *dstentry = as; + *psame_destination = (srcport == as->dst.port) && + hostcompare(srchost, as->dst.host); return TRUE; } } diff --git a/lib/altsvc.h b/lib/altsvc.h index 831cd09743..dc1740bce1 100644 --- a/lib/altsvc.h +++ b/lib/altsvc.h @@ -26,12 +26,14 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC) -#include #include "llist.h" +/* the maximum number of alt-svc entries kept in a single cache */ +#define MAX_ALTSVC_ENTRIES 5000 + struct althost { char *host; - unsigned short port; + uint16_t port; enum alpnid alpnid; }; @@ -40,7 +42,6 @@ struct altsvc { struct althost dst; time_t expires; struct Curl_llist_node node; - unsigned int prio; BIT(persist); }; @@ -55,20 +56,21 @@ 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, int srcport, struct altsvc **dstentry, - const int versions); /* CURLALTSVC_H* bits */ + const int versions, /* CURLALTSVC_H* bits */ + bool *psame_destination); #else /* disabled */ -#define Curl_altsvc_save(a,b,c) +#define Curl_altsvc_save(a, b, c) #define Curl_altsvc_cleanup(x) #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */ #endif /* HEADER_CURL_ALTSVC_H */ diff --git a/lib/amigaos.c b/lib/amigaos.c index ac6d6b4193..e4f3bfb77c 100644 --- a/lib/amigaos.c +++ b/lib/amigaos.c @@ -21,18 +21,16 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef __AMIGA__ -#include - #include "hostip.h" +#include "curl_addrinfo.h" #include "amigaos.h" #ifdef HAVE_PROTO_BSDSOCKET_H -# if defined(__amigaos4__) +# ifdef __amigaos4__ # include # elif !defined(USE_AMISSL) # include @@ -42,10 +40,6 @@ # endif #endif -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - #ifdef HAVE_PROTO_BSDSOCKET_H #ifdef __amigaos4__ @@ -120,12 +114,11 @@ void Curl_amiga_cleanup(void) * Because we need to handle the different cases in hostip4.c at runtime, * not at compile-time, based on what was detected in Curl_amiga_init(), * we replace it completely with our own as to not complicate the baseline - * code. Assumes malloc/calloc/free are thread safe because Curl_he2ai() + * code. Assumes malloc/calloc/free are thread-safe because Curl_he2ai() * allocates memory also. */ -struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, - int port) +struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, uint16_t port) { struct Curl_addrinfo *ai = NULL; struct hostent *h; @@ -135,7 +128,7 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, LONG h_errnop = 0; struct hostent *buf; - buf = calloc(1, CURL_HOSTENT_SIZE); + buf = curlx_calloc(1, CURL_HOSTENT_SIZE); if(buf) { h = gethostbyname_r((STRPTR)hostname, buf, (char *)buf + sizeof(struct hostent), @@ -144,12 +137,12 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, if(h) { ai = Curl_he2ai(h, port); } - free(buf); + curlx_free(buf); } } else { - #ifdef CURLRES_THREADED - /* gethostbyname() is not thread safe, so we need to reopen bsdsocket +#ifdef USE_RESOLV_THREADED + /* gethostbyname() is not thread-safe, so we need to reopen bsdsocket * on the thread's context */ struct Library *base = OpenLibrary("bsdsocket.library", 4); @@ -164,13 +157,13 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, } CloseLibrary(base); } - #else +#else /* not using threaded resolver - safe to use this as-is */ h = gethostbyname(hostname); if(h) { ai = Curl_he2ai(h, port); } - #endif +#endif } return ai; @@ -199,8 +192,9 @@ struct Library *SocketBase = NULL; #ifdef __libnix__ void __request(const char *msg); +#define CURL_AMIGA_REQUEST(msg) __request(msg) #else -# define __request(msg) Printf((const unsigned char *)(msg "\n\a"), 0) +#define CURL_AMIGA_REQUEST(msg) Printf((const unsigned char *)(msg "\n\a"), 0) #endif void Curl_amiga_cleanup(void) @@ -217,14 +211,13 @@ CURLcode Curl_amiga_init(void) SocketBase = OpenLibrary((const unsigned char *)"bsdsocket.library", 4); if(!SocketBase) { - __request("No TCP/IP Stack running!"); + CURL_AMIGA_REQUEST("No TCP/IP Stack running!"); return CURLE_FAILED_INIT; } - if(SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (ULONG) &errno, - SBTM_SETVAL(SBTC_LOGTAGPTR), (ULONG) "curl", - TAG_DONE)) { - __request("SocketBaseTags ERROR"); + if(SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (ULONG)&errno, + SBTM_SETVAL(SBTC_LOGTAGPTR), (ULONG)"curl", TAG_DONE)) { + CURL_AMIGA_REQUEST("SocketBaseTags ERROR"); return CURLE_FAILED_INIT; } diff --git a/lib/amigaos.h b/lib/amigaos.h index c99d963ece..58278f0935 100644 --- a/lib/amigaos.h +++ b/lib/amigaos.h @@ -33,7 +33,7 @@ void Curl_amiga_cleanup(void); #else -#define Curl_amiga_init() CURLE_OK +#define Curl_amiga_init() CURLE_OK #define Curl_amiga_cleanup() Curl_nop_stmt #endif diff --git a/lib/arpa_telnet.h b/lib/arpa_telnet.h index d641a01da8..b5faab419c 100644 --- a/lib/arpa_telnet.h +++ b/lib/arpa_telnet.h @@ -27,24 +27,22 @@ /* * 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 */ -static const char * const telnetoptions[]= -{ +static const char * const telnetoptions[] = { "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", "NAME", "STATUS", "TIMING MARK", "RCTE", "NAOL", "NAOP", "NAOCRD", "NAOHTS", @@ -56,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 @@ -79,15 +77,14 @@ static const char * const telnetoptions[]= #define CURL_WILL 251 /* Our side WILL use this option */ #define CURL_WONT 252 /* Our side will not use this option */ #define CURL_DO 253 /* DO use this option! */ -#define CURL_DONT 254 /* DON'T use this option! */ +#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: */ -static const char * const telnetcmds[]= -{ +static const char * const telnetcmds[] = { "EOF", "SUSP", "ABORT", "EOR", "SE", "NOP", "DMARK", "BRK", "IP", "AO", "AYT", "EC", "EL", "GA", "SB", @@ -103,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/lib/asyn-ares.c b/lib/asyn-ares.c index e955990878..2bf1d3c3b5 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -21,18 +21,16 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#ifdef CURLRES_ARES +#ifdef USE_RESOLV_ARES /*********************************************************************** - * Only for ares-enabled builds - * And only for functions that fulfill the asynch resolver backend API - * as defined in asyn.h, nothing else belongs in this file! + * Only for ares-enabled builds and only for functions that fulfill + * the asynch resolver backend API as defined in asyn.h, + * nothing else belongs in this file! **********************************************************************/ -#include #ifdef HAVE_NETINET_IN_H #include #endif @@ -49,10 +47,9 @@ #include "urldata.h" #include "cfilters.h" -#include "sendf.h" +#include "curl_addrinfo.h" +#include "curl_trc.h" #include "hostip.h" -#include "hash.h" -#include "share.h" #include "url.h" #include "multiif.h" #include "curlx/inet_pton.h" @@ -61,30 +58,10 @@ #include "progress.h" #include "curlx/timediff.h" #include "httpsrr.h" -#include "strdup.h" - #include -#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 -#define HAVE_CARES_SET_LOCAL 1 -#endif - -#if ARES_VERSION >= 0x010b00 -#define HAVE_CARES_PORTS_CSV 1 -#endif - -#if ARES_VERSION >= 0x011000 -/* 1.16.0 or later has ares_getaddrinfo */ -#define HAVE_CARES_GETADDRINFO 1 +#if ARES_VERSION < 0x011000 +#error "requires c-ares 1.16.0 or newer" #endif #ifdef USE_HTTPSRR @@ -94,28 +71,18 @@ #define HTTPSRR_WORKS #endif -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* How long we are willing to wait for additional parallel responses after - obtaining a "definitive" one. For old c-ares without getaddrinfo. - - This is intended to equal the c-ares default timeout. cURL always uses that - default value. Unfortunately, c-ares does not expose its default timeout in - its API, but it is officially documented as 5 seconds. - - See query_completed_cb() for an explanation of how this is used. - */ -#define HAPPY_EYEBALLS_DNS_TIMEOUT 5000 - #define CARES_TIMEOUT_PER_ATTEMPT 2000 static int ares_ver = 0; static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, - bool reset_on_null); + struct Curl_resolv_async *async); +static CURLcode async_ares_set_dns_interface(struct Curl_easy *data, + struct Curl_resolv_async *async); +static CURLcode async_ares_set_dns_local_ip4(struct Curl_easy *data, + struct Curl_resolv_async *async); +static CURLcode async_ares_set_dns_local_ip6(struct Curl_easy *data, + struct Curl_resolv_async *async); /* * Curl_async_global_init() - the generic low-level asynchronous name @@ -146,7 +113,6 @@ void Curl_async_global_cleanup(void) #endif } - static void sock_state_cb(void *data, ares_socket_t socket_fd, int readable, int writable) { @@ -157,14 +123,19 @@ static void sock_state_cb(void *data, ares_socket_t socket_fd, } } -static CURLcode async_ares_init(struct Curl_easy *data) +static CURLcode async_ares_init(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = &async->ares; int status; struct ares_options options; int optmask = ARES_OPT_SOCK_STATE_CB; CURLcode rc = CURLE_OK; + /* initial status - failed */ + ares->ares_status = ARES_ENOTFOUND; + async->queries_ongoing = 0; + options.sock_state_cb = sock_state_cb; options.sock_state_cb_data = data; @@ -187,24 +158,23 @@ static CURLcode async_ares_init(struct Curl_easy *data) status = ares_init_options(&ares->channel, &options, optmask); if(status != ARES_SUCCESS) { ares->channel = NULL; - rc = (status == ARES_ENOMEM) ? - CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT; + rc = (status == ARES_ENOMEM) ? CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT; goto out; } - rc = async_ares_set_dns_servers(data, FALSE); + rc = async_ares_set_dns_servers(data, async); if(rc && rc != CURLE_NOT_BUILT_IN) goto out; - rc = Curl_async_ares_set_dns_interface(data); + rc = async_ares_set_dns_interface(data, async); if(rc && rc != CURLE_NOT_BUILT_IN) goto out; - rc = Curl_async_ares_set_dns_local_ip4(data); + rc = async_ares_set_dns_local_ip4(data, async); if(rc && rc != CURLE_NOT_BUILT_IN) goto out; - rc = Curl_async_ares_set_dns_local_ip6(data); + rc = async_ares_set_dns_local_ip6(data, async); if(rc && rc != CURLE_NOT_BUILT_IN) goto out; @@ -218,390 +188,300 @@ out: return rc; } -static CURLcode async_ares_init_lazy(struct Curl_easy *data) -{ - struct async_ares_ctx *ares = &data->state.async.ares; - if(!ares->channel) - return async_ares_init(data); - return CURLE_OK; -} - -CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl) -{ - struct async_ares_ctx *ares = &data->state.async.ares; - CURLcode result = CURLE_OK; - if(!ares->channel) { - result = async_ares_init(data); - } - *impl = ares->channel; - return result; -} - /* * async_ares_cleanup() cleans up async resolver data. */ -static void async_ares_cleanup(struct Curl_easy *data) +static void async_ares_cleanup(struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; - if(ares->temp_ai) { - Curl_freeaddrinfo(ares->temp_ai); - ares->temp_ai = NULL; + struct async_ares_ctx *ares = &async->ares; + if(ares->res_A) { + Curl_freeaddrinfo(ares->res_A); + ares->res_A = NULL; + } + if(ares->res_AAAA) { + Curl_freeaddrinfo(ares->res_AAAA); + ares->res_AAAA = NULL; } #ifdef USE_HTTPSRR Curl_httpsrr_cleanup(&ares->hinfo); #endif } -void Curl_async_ares_shutdown(struct Curl_easy *data) +void Curl_async_ares_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async) { /* c-ares has a method to "cancel" operations on a channel, but * as reported in #18216, this does not totally reset the channel * and ares may get stuck. * We need to destroy the channel and on demand create a new * one to avoid that. */ - Curl_async_ares_destroy(data); + Curl_async_ares_destroy(data, async); } -void Curl_async_ares_destroy(struct Curl_easy *data) +void Curl_async_ares_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = &async->ares; + (void)data; if(ares->channel) { ares_destroy(ares->channel); ares->channel = NULL; } - async_ares_cleanup(data); + async_ares_cleanup(async); } -/* - * Curl_async_pollset() is called when someone from the outside world - * (using curl_multi_fdset()) wants to get our fd_set setup. - */ - -CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps) +CURLcode Curl_async_pollset(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct easy_pollset *ps) { - struct async_ares_ctx *ares = &data->state.async.ares; - if(ares->channel) - return Curl_ares_pollset(data, ares->channel, ps); - return CURLE_OK; + struct async_ares_ctx *ares = &async->ares; + CURLcode result = CURLE_OK; + + if(ares->channel) { + result = Curl_ares_pollset(data, ares->channel, ps); + if(!result) { + timediff_t ms = Curl_ares_timeout_ms(data, async, ares->channel); + Curl_expire(data, ms, EXPIRE_ASYNC_NAME); + } + } + return result; } /* - * Curl_async_is_resolved() is called repeatedly to check if a previous + * Curl_async_take_result() is called repeatedly to check if a previous * name resolve request has completed. It should also make sure to time-out if * the operation seems to take too long. * * Returns normal CURLcode errors. */ -CURLcode Curl_async_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns) +CURLcode Curl_async_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = &async->ares; CURLcode result = CURLE_OK; - DEBUGASSERT(dns); - *dns = NULL; + DEBUGASSERT(pdns); + *pdns = NULL; + if(!ares) + return CURLE_FAILED_INIT; - if(data->state.async.done) { - *dns = data->state.async.dns; - return CURLE_OK; + if(Curl_ares_perform(ares->channel, 0) < 0) { + result = CURLE_UNRECOVERABLE_POLL; + goto out; } - if(Curl_ares_perform(ares->channel, 0) < 0) - return CURLE_UNRECOVERABLE_POLL; - -#ifndef HAVE_CARES_GETADDRINFO - /* Now that we have checked for any last minute results above, see if there - are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer - expires. */ - if(ares->num_pending - /* This is only set to non-zero if the timer was started. */ - && (ares->happy_eyeballs_dns_time.tv_sec - || ares->happy_eyeballs_dns_time.tv_usec) - && (curlx_timediff(curlx_now(), ares->happy_eyeballs_dns_time) - >= HAPPY_EYEBALLS_DNS_TIMEOUT)) { - /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer - running. */ - memset(&ares->happy_eyeballs_dns_time, 0, - sizeof(ares->happy_eyeballs_dns_time)); - - /* Cancel the raw c-ares request, which will fire query_completed_cb() with - ARES_ECANCELLED synchronously for all pending responses. This will - leave us with res->num_pending == 0, which is perfect for the next - block. */ - ares_cancel(ares->channel); - DEBUGASSERT(ares->num_pending == 0); + if(async->queries_ongoing) { + result = CURLE_AGAIN; + goto out; } -#endif - if(!ares->num_pending) { - /* all c-ares operations done, what is the result to report? */ - Curl_resolv_unlink(data, &data->state.async.dns); - data->state.async.done = TRUE; - result = ares->result; - if(ares->ares_status == ARES_SUCCESS && !result) { - data->state.async.dns = - Curl_dnscache_mk_entry(data, ares->temp_ai, - data->state.async.hostname, 0, - data->state.async.port, FALSE); - ares->temp_ai = NULL; /* temp_ai now owned by entry */ + /* all c-ares operations done, what is the result to report? */ + result = ares->result; + if(ares->ares_status == ARES_SUCCESS && !result) { + struct Curl_dns_entry *dns = + Curl_dnscache_mk_entry2(data, async->dns_queries, + &ares->res_AAAA, &ares->res_A, + async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } #ifdef HTTPSRR_WORKS - if(data->state.async.dns) { + if(async->dns_queries & CURL_DNSQ_HTTPS) { + if(ares->hinfo.complete) { struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo); if(!lhrr) result = CURLE_OUT_OF_MEMORY; else - data->state.async.dns->hinfo = lhrr; + Curl_dns_entry_set_https_rr(dns, lhrr); } + else + Curl_dns_entry_set_https_rr(dns, NULL); + } #endif - if(!result && data->state.async.dns) - result = Curl_dnscache_add(data, data->state.async.dns); + if(!result) { + *pdns = dns; } - /* if we have not found anything, report the proper - * CURLE_COULDNT_RESOLVE_* code */ - if(!result && !data->state.async.dns) { - const char *msg = NULL; - if(ares->ares_status != ARES_SUCCESS) - msg = ares_strerror(ares->ares_status); - result = Curl_resolver_error(data, msg); - } - - if(result) - Curl_resolv_unlink(data, &data->state.async.dns); - *dns = data->state.async.dns; - CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound", - result, *dns ? "" : "not "); - async_ares_cleanup(data); } + /* if we have not found anything, report the proper + * CURLE_COULDNT_RESOLVE_* code */ + if(!result && !*pdns) { + const char *msg = NULL; + if(ares->ares_status != ARES_SUCCESS) + msg = ares_strerror(ares->ares_status); + result = Curl_resolver_error(data, msg); + } + + CURL_TRC_DNS(data, "ares: is_resolved() result=%d, dns=%sfound", + result, *pdns ? "" : "not "); + async_ares_cleanup(async); + +out: + if(result != CURLE_AGAIN) + ares->result = result; 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; +} + +static const struct Curl_addrinfo *async_ares_get_ai( + const struct Curl_addrinfo *ai, + int ai_family, + unsigned int index) +{ + unsigned int i = 0; + for(i = 0; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + if(i == index) + return ai; + ++i; + } + } + return NULL; +} + +const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data, + struct Curl_resolv_async *async, + int ai_family, + unsigned int index) +{ + struct async_ares_ctx *ares = &async->ares; + + (void)data; + switch(ai_family) { + case AF_INET: + if(ares->res_A) + return async_ares_get_ai(ares->res_A, ai_family, index); + break; + case AF_INET6: + if(ares->res_AAAA) + return async_ares_get_ai(ares->res_AAAA, ai_family, index); + break; + default: + break; + } + return NULL; +} + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_async_get_https( + struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + if(Curl_async_knows_https(data, async)) + return &async->ares.hinfo; + return NULL; +} + +bool Curl_async_knows_https(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + (void)data; + if(async->dns_queries & CURL_DNSQ_HTTPS) + return ((async->dns_responses & CURL_DNSQ_HTTPS) || + !async->queries_ongoing); + return TRUE; /* we know it will never come */ +} + +#endif /* USE_HTTPSRR */ + /* * Curl_async_await() * * Waits for a resolve to finish. This function should be avoided since using * this risk getting the multi interface to "hang". * - * 'entry' MUST be non-NULL. + * 'pdns' MUST be non-NULL. * * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. */ -CURLcode Curl_async_await(struct Curl_easy *data, - struct Curl_dns_entry **entry) +CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + struct async_ares_ctx *ares = async ? &async->ares : NULL; + struct curltime start = *Curl_pgrs_now(data); CURLcode result = CURLE_OK; - timediff_t timeout; - struct curltime now = curlx_now(); - DEBUGASSERT(entry); - *entry = NULL; /* clear on entry */ + DEBUGASSERT(pdns); + *pdns = NULL; /* clear on entry */ - timeout = Curl_timeleft(data, &now, TRUE); - if(timeout < 0) { - /* already expired! */ - connclose(data->conn, "Timed out before name resolve started"); - return CURLE_OPERATION_TIMEDOUT; - } - if(!timeout) - timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */ + if(!ares) + return CURLE_FAILED_INIT; - /* Wait for the name resolve query to complete. */ + /* Wait for the name resolve query to complete or time out. */ while(!result) { - struct timeval *tvp, tv, store; - int itimeout; timediff_t timeout_ms; -#if TIMEDIFF_T_MAX > INT_MAX - itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout; -#else - itimeout = (int)timeout; -#endif + 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_MS) + timeout_ms = CURL_TIMEOUT_RESOLVE_MS - elapsed_ms; + else + timeout_ms = -1; + } - store.tv_sec = itimeout/1000; - store.tv_usec = (itimeout%1000)*1000; + if(timeout_ms < 0) { + result = CURLE_OPERATION_TIMEDOUT; + break; + } - tvp = ares_timeout(ares->channel, &store, &tv); + if(Curl_ares_perform(ares->channel, + async_ares_poll_timeout(ares, timeout_ms)) < 0) { + result = CURLE_UNRECOVERABLE_POLL; + break; + } - /* 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(!tvp->tv_sec) - timeout_ms = (timediff_t)(tvp->tv_usec/1000); - else - timeout_ms = 1000; - - if(Curl_ares_perform(ares->channel, timeout_ms) < 0) - return CURLE_UNRECOVERABLE_POLL; - - result = Curl_async_is_resolved(data, entry); - if(result || data->state.async.done) + result = Curl_async_take_result(data, async, pdns); + if(result == CURLE_AGAIN) + result = CURLE_OK; + else if(result || *pdns) break; - if(Curl_pgrsUpdate(data)) + if(Curl_pgrsUpdate(data)) { result = CURLE_ABORTED_BY_CALLBACK; - else { - struct curltime now2 = curlx_now(); - timediff_t timediff = curlx_timediff(now2, now); /* spent time */ - if(timediff <= 0) - timeout -= 1; /* always deduct at least 1 */ - else if(timediff > timeout) - timeout = -1; - else - timeout -= timediff; - now = now2; /* for next loop */ + break; } - if(timeout < 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; - if(result) ares_cancel(ares->channel); return result; } -#ifndef HAVE_CARES_GETADDRINFO - -/* Connects results to the list */ -static void async_addr_concat(struct Curl_addrinfo **pbase, - struct Curl_addrinfo *ai) -{ - if(!ai) - return; - - /* When adding `ai` to an existing address list, we prefer ipv6 - * to be in front. */ -#ifdef USE_IPV6 /* CURLRES_IPV6 */ - if(*pbase && (*pbase)->ai_family == PF_INET6) { - /* ipv6 already in front, append `ai` */ - struct Curl_addrinfo *tail = *pbase; - while(tail->ai_next) - tail = tail->ai_next; - tail->ai_next = ai; - } - else -#endif /* CURLRES_IPV6 */ - { - /* prepend to the (possibly) existing list. */ - struct Curl_addrinfo *tail = ai; - while(tail->ai_next) - tail = tail->ai_next; - tail->ai_next = *pbase; - *pbase = ai; - } -} - -/* - * ares_query_completed_cb() is the callback that ares will call when - * the host query initiated by ares_gethostbyname() from - * Curl_async_getaddrinfo(), when using ares, is completed either - * successfully or with failure. - */ -static void async_ares_hostbyname_cb(void *user_data, - int status, - int timeouts, - struct hostent *hostent) -{ - struct Curl_easy *data = (struct Curl_easy *)user_data; - struct async_ares_ctx *ares = &data->state.async.ares; - - (void)timeouts; /* ignored */ - - if(ARES_EDESTRUCTION == status) - /* when this ares handle is getting destroyed, the 'arg' pointer may not - be valid so only defer it when we know the 'status' says its fine! */ - return; - - if(ARES_SUCCESS == status) { - ares->ares_status = status; /* one success overrules any error */ - async_addr_concat(&ares->temp_ai, - Curl_he2ai(hostent, data->state.async.port)); - } - else if(ares->ares_status != ARES_SUCCESS) { - /* no success so far, remember last error */ - ares->ares_status = status; - } - - ares->num_pending--; - - CURL_TRC_DNS(data, "ares: hostbyname done, status=%d, pending=%d, " - "addr=%sfound", - status, ares->num_pending, ares->temp_ai ? "" : "not "); - /* If there are responses still pending, we presume they must be the - complementary IPv4 or IPv6 lookups that we started in parallel in - Curl_async_getaddrinfo() (for Happy Eyeballs). If we have got a - "definitive" response from one of a set of parallel queries, we need to - think about how long we are willing to wait for more responses. */ - if(ares->num_pending - /* Only these c-ares status values count as "definitive" for these - purposes. For example, ARES_ENODATA is what we expect when there is - no IPv6 entry for a domain name, and that is not a reason to get more - aggressive in our timeouts for the other response. Other errors are - either a result of bad input (which should affect all parallel - requests), local or network conditions, non-definitive server - responses, or us cancelling the request. */ - && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) { - /* Right now, there can only be up to two parallel queries, so do not - bother handling any other cases. */ - DEBUGASSERT(ares->num_pending == 1); - - /* it is possible that one of these parallel queries could succeed - quickly, but the other could always fail or timeout (when we are - 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). - - 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 - pending responses. Even though it is typical that this resolved - request came back quickly, that needn't 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 - 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 - 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. - */ - ares->happy_eyeballs_dns_time = curlx_now(); - Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT, - EXPIRE_HAPPY_EYEBALLS_DNS); - } -} - -#else -/* c-ares 1.16.0 or later */ - /* * async_ares_node2addr() converts an address list provided by c-ares * to an internal libcurl compatible list. */ -static struct Curl_addrinfo * -async_ares_node2addr(struct ares_addrinfo_node *node) +static struct Curl_addrinfo *async_ares_node2addr( + struct ares_addrinfo_node *node) { /* traverse the ares_addrinfo_node list */ struct ares_addrinfo_node *ai; @@ -612,8 +492,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 @@ -631,14 +511,14 @@ async_ares_node2addr(struct ares_addrinfo_node *node) if((size_t)ai->ai_addrlen < ss_size) continue; - ca = malloc(sizeof(struct Curl_addrinfo) + ss_size); + ca = curlx_malloc(sizeof(struct Curl_addrinfo) + ss_size); if(!ca) { error = EAI_MEMORY; 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; @@ -671,156 +551,161 @@ async_ares_node2addr(struct ares_addrinfo_node *node) return cafirst; } -static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts, - struct ares_addrinfo *ares_ai) +static void async_ares_A_cb(void *user_data, int status, int timeouts, + struct ares_addrinfo *ares_ai) { - struct Curl_easy *data = (struct Curl_easy *)user_data; - struct async_ares_ctx *ares = &data->state.async.ares; + struct Curl_resolv_async *async = user_data; + struct async_ares_ctx *ares = async ? &async->ares : NULL; + (void)timeouts; - if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */ - ares->ares_status = status; + if(!async) + return; + + async->dns_responses |= CURL_DNSQ_A; + async->queries_ongoing--; if(status == ARES_SUCCESS) { - ares->temp_ai = async_ares_node2addr(ares_ai->nodes); + ares->ares_status = ARES_SUCCESS; + ares->res_A = async_ares_node2addr(ares_ai->nodes); ares_freeaddrinfo(ares_ai); } - ares->num_pending--; - CURL_TRC_DNS(data, "ares: addrinfo done, query status=%d, " - "overall status=%d, pending=%d, addr=%sfound", - status, ares->ares_status, ares->num_pending, - ares->temp_ai ? "" : "not "); + else if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */ + ares->ares_status = status; } -#endif +#ifdef CURLRES_IPV6 +static void async_ares_AAAA_cb(void *user_data, int status, int timeouts, + struct ares_addrinfo *ares_ai) +{ + struct Curl_resolv_async *async = user_data; + struct async_ares_ctx *ares = async ? &async->ares : NULL; + + (void)timeouts; + if(!async) + return; + + async->dns_responses |= CURL_DNSQ_AAAA; + async->queries_ongoing--; + if(status == ARES_SUCCESS) { + ares->ares_status = ARES_SUCCESS; + ares->res_AAAA = async_ares_node2addr(ares_ai->nodes); + ares_freeaddrinfo(ares_ai); + } + else if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */ + ares->ares_status = status; +} +#endif /* CURLRES_IPV6 */ #ifdef USE_HTTPSRR static void async_ares_rr_done(void *user_data, ares_status_t status, size_t timeouts, const ares_dns_record_t *dnsrec) { - struct Curl_easy *data = user_data; - struct async_ares_ctx *ares = &data->state.async.ares; + struct Curl_resolv_async *async = user_data; + struct async_ares_ctx *ares = async ? &async->ares : NULL; + + if(!async) + return; (void)timeouts; - --ares->num_pending; - CURL_TRC_DNS(data, "ares: httpsrr done, status=%d, pending=%d, " - "dnsres=%sfound", - status, ares->num_pending, - (dnsrec && - ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER)) ? - "" : "not "); + async->dns_responses |= CURL_DNSQ_HTTPS; + async->queries_ongoing--; if((ARES_SUCCESS != status) || !dnsrec) return; - ares->result = Curl_httpsrr_from_ares(data, dnsrec, &ares->hinfo); + ares->result = Curl_httpsrr_from_ares(dnsrec, &ares->hinfo); } #endif /* USE_HTTPSRR */ /* * Curl_async_getaddrinfo() - when using ares * - * Returns name information about the given hostname and port number. If - * successful, the 'hostent' is returned and the fourth argument will point to - * memory we need to free after use. That memory *MUST* be freed with - * Curl_freeaddrinfo(), nothing else. + * Starts a name resolve for the given hostname and port number. */ -struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - int *waitp) +CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; - *waitp = 0; /* default to synchronous response */ + struct async_ares_ctx *ares = &async->ares; + char service[12]; + int socktype; + CURLcode result = CURLE_OK; - if(async_ares_init_lazy(data)) - return NULL; + if(ares->channel) { + DEBUGASSERT(0); + result = CURLE_FAILED_INIT; + goto out; + } - data->state.async.done = FALSE; /* not done */ - data->state.async.dns = NULL; /* clear */ - data->state.async.port = port; - data->state.async.ip_version = ip_version; - data->state.async.hostname = strdup(hostname); - if(!data->state.async.hostname) - return NULL; + result = async_ares_init(data, async); + if(result) + goto out; - /* initial status - failed */ - ares->ares_status = ARES_ENOTFOUND; - ares->result = CURLE_OK; + result = Curl_resolv_announce_start(data, ares->channel); + if(result) + goto out; -#if ARES_VERSION >= 0x011800 /* >= v1.24.0 */ - CURL_TRC_DNS(data, "asyn-ares: servers=%s", - ares_get_servers_csv(ares->channel)); +#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, "ares: servers=%s", csv); + ares_free_string(csv); + } #endif -#ifdef HAVE_CARES_GETADDRINFO - { - struct ares_addrinfo_hints hints; - char service[12]; - int pf = PF_INET; - memset(&hints, 0, sizeof(hints)); + curl_msnprintf(service, sizeof(service), "%d", async->port); + socktype = + (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? + SOCK_STREAM : SOCK_DGRAM; + #ifdef CURLRES_IPV6 - 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; - else - pf = PF_UNSPEC; - } -#endif /* CURLRES_IPV6 */ - CURL_TRC_DNS(data, "asyn-ares: fire off getaddrinfo for %s", - (pf == PF_UNSPEC) ? "A+AAAA" : - ((pf == PF_INET) ? "A" : "AAAA")); - hints.ai_family = pf; - hints.ai_socktype = - (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? - SOCK_STREAM : SOCK_DGRAM; - /* Since the service is a numerical one, set the hint flags - * accordingly to save a call to getservbyname in inside C-Ares - */ + if(async->dns_queries & CURL_DNSQ_AAAA) { + struct ares_addrinfo_hints hints; + + memset(&hints, 0, sizeof(hints)); + CURL_TRC_DNS(data, "ares: query AAAA records for %s", async->hostname); + hints.ai_family = PF_INET6; + hints.ai_socktype = socktype; hints.ai_flags = ARES_AI_NUMERICSERV; - msnprintf(service, sizeof(service), "%d", port); - ares->num_pending = 1; - ares_getaddrinfo(ares->channel, data->state.async.hostname, - service, &hints, async_ares_addrinfo_cb, data); + ares_getaddrinfo(ares->channel, async->hostname, + service, &hints, async_ares_AAAA_cb, async); + async->queries_ongoing++; } -#else +#endif /* CURLRES_IPV6 */ -#ifdef HAVE_CARES_IPV6 - 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 */ - CURL_TRC_DNS(data, "asyn-ares: fire off query for A"); - ares_gethostbyname(ares->channel, hostname, PF_INET, - async_ares_hostbyname_cb, data); - CURL_TRC_DNS(data, "asyn-ares: fire off query for AAAA"); - ares->num_pending = 2; - ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET6, - async_ares_hostbyname_cb, data); + if(async->dns_queries & CURL_DNSQ_A) { + struct ares_addrinfo_hints hints; + + memset(&hints, 0, sizeof(hints)); + CURL_TRC_DNS(data, "ares: query A records for %s", async->hostname); + hints.ai_family = PF_INET; + hints.ai_socktype = socktype; + hints.ai_flags = ARES_AI_NUMERICSERV; + ares_getaddrinfo(ares->channel, async->hostname, + service, &hints, async_ares_A_cb, async); + async->queries_ongoing++; } - else -#endif - { - /* areschannel is already setup in the Curl_open() function */ - CURL_TRC_DNS(data, "asyn-ares: fire off query for A"); - ares->num_pending = 1; - ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET, - async_ares_hostbyname_cb, data); - } -#endif + #ifdef USE_HTTPSRR - { - CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR"); - memset(&ares->hinfo, 0, sizeof(ares->hinfo)); - ares->hinfo.port = -1; - ares->num_pending++; /* one more */ - ares_query_dnsrec(ares->channel, data->state.async.hostname, + memset(&ares->hinfo, 0, sizeof(ares->hinfo)); + if(async->dns_queries & CURL_DNSQ_HTTPS) { + char *rrname = NULL; + if(async->port != 443) { + rrname = curl_maprintf("_%d._https.%s", async->port, async->hostname); + if(!rrname) + return CURLE_OUT_OF_MEMORY; + } + CURL_TRC_DNS(data, "ares: query HTTPS records for %s", + rrname ? rrname : async->hostname); + ares->hinfo.rrname = rrname; + async->queries_ongoing++; + ares_query_dnsrec(ares->channel, + rrname ? rrname : async->hostname, ARES_CLASS_IN, ARES_REC_TYPE_HTTPS, - async_ares_rr_done, data, NULL); + async_ares_rr_done, async, NULL); } -#endif - *waitp = 1; /* expect asynchronous response */ +#endif /* USE_HTTPSRR */ - return NULL; /* no struct yet */ +out: + ares->result = result; + return result ? result : (async->queries_ongoing ? CURLE_AGAIN : CURLE_OK); } /* Set what DNS server are is to use. This is called in 2 situations: @@ -830,33 +715,24 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data, * 2. When we lazy init the ares channel and NULL means that there * are no preferences and we do not reset any existing channel. */ static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, - bool reset_on_null) + struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = async ? &async->ares : NULL; CURLcode result = CURLE_NOT_BUILT_IN; const char *servers = data->set.str[STRING_DNS_SERVERS]; int ares_result = ARES_SUCCESS; -#if defined(CURLDEBUG) && defined(HAVE_CARES_SERVERS_CSV) +#ifdef DEBUGBUILD if(getenv("CURL_DNS_SERVER")) servers = getenv("CURL_DNS_SERVER"); #endif - if(!servers) { - if(reset_on_null) { - Curl_async_destroy(data); - } + if(!servers) return CURLE_OK; - } -#ifdef HAVE_CARES_SERVERS_CSV - /* if channel is not there, this is just a parameter check */ - if(ares->channel) -#ifdef HAVE_CARES_PORTS_CSV + /* if channel is not there, this is a parameter check */ + if(ares && ares->channel) ares_result = ares_set_servers_ports_csv(ares->channel, servers); -#else - ares_result = ares_set_servers_csv(ares->channel, servers); -#endif switch(ares_result) { case ARES_SUCCESS: result = CURLE_OK; @@ -872,47 +748,33 @@ static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, result = CURLE_BAD_FUNCTION_ARGUMENT; break; } -#else /* too old c-ares version! */ - (void)data; - (void)(ares_result); -#endif return result; } -CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data) +static CURLcode async_ares_set_dns_interface(struct Curl_easy *data, + struct Curl_resolv_async *async) { - return async_ares_set_dns_servers(data, TRUE); -} - -CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data) -{ -#ifdef HAVE_CARES_LOCAL_DEV - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = async ? &async->ares : NULL; const char *interf = data->set.str[STRING_DNS_INTERFACE]; if(!interf) interf = ""; - /* if channel is not there, this is just a parameter check */ - if(ares->channel) + /* if channel is not there, this is a parameter check */ + if(ares && ares->channel) ares_set_local_dev(ares->channel, interf); return CURLE_OK; -#else /* c-ares version too old! */ - (void)data; - (void)interf; - return CURLE_NOT_BUILT_IN; -#endif } -CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data) +static CURLcode async_ares_set_dns_local_ip4(struct Curl_easy *data, + struct Curl_resolv_async *async) { -#ifdef HAVE_CARES_SET_LOCAL - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = async ? &async->ares : NULL; 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 { @@ -922,26 +784,22 @@ 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(ares->channel) + /* if channel is not there yet, this is a parameter check */ + if(ares && ares->channel) ares_set_local_ip4(ares->channel, ntohl(a4.s_addr)); return CURLE_OK; -#else /* c-ares version too old! */ - (void)data; - (void)local_ip4; - return CURLE_NOT_BUILT_IN; -#endif } -CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data) +static CURLcode async_ares_set_dns_local_ip6(struct Curl_easy *data, + struct Curl_resolv_async *async) { -#if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6) - struct async_ares_ctx *ares = &data->state.async.ares; +#ifdef USE_IPV6 + struct async_ares_ctx *ares = async ? &async->ares : NULL; 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)); } @@ -952,15 +810,16 @@ CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data) } } - /* if channel is not there, this is just a parameter check */ - if(ares->channel) + /* if channel is not there, this is a parameter check */ + if(ares && ares->channel) ares_set_local_ip6(ares->channel, a6); return CURLE_OK; -#else /* c-ares version too old! */ +#else /* no IPv6 support */ (void)data; + (void)async; return CURLE_NOT_BUILT_IN; #endif } -#endif /* CURLRES_ARES */ +#endif /* USE_RESOLV_ARES */ diff --git a/lib/asyn-base.c b/lib/asyn-base.c index eb2240b816..9e400a914b 100644 --- a/lib/asyn-base.c +++ b/lib/asyn-base.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_NETINET_IN_H @@ -40,33 +39,37 @@ #ifdef USE_ARES #include -#include /* really old c-ares did not include this by - itself */ #endif #include "urldata.h" -#include "asyn.h" -#include "sendf.h" +#include "connect.h" +#include "curl_trc.h" #include "hostip.h" -#include "hash.h" #include "multiif.h" +#include "progress.h" #include "select.h" -#include "share.h" #include "url.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" /*********************************************************************** * Only for builds using asynchronous name resolves **********************************************************************/ #ifdef CURLRES_ASYNCH +timediff_t Curl_async_timeleft_ms(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + if(async->timeout_ms) { + timediff_t elapsed_ms = + curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); + return async->timeout_ms - elapsed_ms; + } + return Curl_timeleft_ms(data); +} #ifdef USE_ARES -#if ARES_VERSION < 0x010600 -#error "requires c-ares 1.6.0 or newer" +#if ARES_VERSION < 0x011000 +#error "requires c-ares 1.16.0 or newer" #endif /* @@ -77,18 +80,12 @@ * * Returns: sockets-in-use-bitmap */ - - CURLcode Curl_ares_pollset(struct Curl_easy *data, ares_channel channel, struct easy_pollset *ps) { - struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 }; - struct timeval timebuf; curl_socket_t sockets[16]; /* ARES documented limit */ unsigned int bitmap, i; - struct timeval *timeout; - timediff_t milli; CURLcode result = CURLE_OK; DEBUGASSERT(channel); @@ -109,15 +106,33 @@ CURLcode Curl_ares_pollset(struct Curl_easy *data, if(result) return result; } - - timeout = ares_timeout(channel, &maxtime, &timebuf); - if(!timeout) - timeout = &maxtime; - milli = curlx_tvtoms(timeout); - Curl_expire(data, milli, EXPIRE_ASYNC_NAME); return result; } +timediff_t Curl_ares_timeout_ms(struct Curl_easy *data, + struct Curl_resolv_async *async, + ares_channel channel) +{ + timediff_t async_timeout_ms; + + DEBUGASSERT(channel); + if(!channel) + return -1; + + async_timeout_ms = Curl_async_timeleft_ms(data, async); + if((async_timeout_ms > 0) && (async_timeout_ms < INT_MAX)) { + struct timeval timebuf; + struct timeval *timeout; + struct timeval end = { (int)async_timeout_ms / 1000, + ((int)async_timeout_ms % 1000) * 1000 }; + + timeout = ares_timeout(channel, &end, &timebuf); + if(timeout) + return curlx_tvtoms(timeout); + } + return async_timeout_ms; +} + /* * Curl_ares_perform() * @@ -127,8 +142,7 @@ CURLcode Curl_ares_pollset(struct Curl_easy *data, * * return number of sockets it worked on, or -1 on error */ -int Curl_ares_perform(ares_channel channel, - timediff_t timeout_ms) +int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms) { int nfds; int bitmask; @@ -147,11 +161,11 @@ int Curl_ares_perform(ares_channel channel, pfd[i].revents = 0; if(ARES_GETSOCK_READABLE(bitmask, i)) { pfd[i].fd = socks[i]; - pfd[i].events |= POLLRDNORM|POLLIN; + pfd[i].events |= POLLRDNORM | POLLIN; } if(ARES_GETSOCK_WRITABLE(bitmask, i)) { pfd[i].fd = socks[i]; - pfd[i].events |= POLLWRNORM|POLLOUT; + pfd[i].events |= POLLWRNORM | POLLOUT; } if(pfd[i].events) num++; @@ -168,22 +182,22 @@ int Curl_ares_perform(ares_channel channel, 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 { /* move through the descriptors and ask for processing on them */ for(i = 0; i < num; i++) ares_process_fd(channel, - (pfd[i].revents & (POLLRDNORM|POLLIN)) ? + (pfd[i].revents & (POLLRDNORM | POLLIN)) ? pfd[i].fd : ARES_SOCKET_BAD, - (pfd[i].revents & (POLLWRNORM|POLLOUT)) ? + (pfd[i].revents & (POLLWRNORM | POLLOUT)) ? pfd[i].fd : ARES_SOCKET_BAD); } return nfds; } -#endif +#endif /* USE_ARES */ #endif /* CURLRES_ASYNCH */ @@ -191,32 +205,41 @@ int Curl_ares_perform(ares_channel channel, #include "doh.h" -void Curl_async_shutdown(struct Curl_easy *data) +void Curl_async_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async) { -#ifdef CURLRES_ARES - Curl_async_ares_shutdown(data); + if(async) { + CURL_TRC_DNS(data, "[%u] shutdown async", async->id); + async->shutdown = TRUE; +#ifdef USE_RESOLV_ARES + Curl_async_ares_shutdown(data, async); #endif -#ifdef CURLRES_THREADED - Curl_async_thrdd_shutdown(data); +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_shutdown(data, async); #endif #ifndef CURL_DISABLE_DOH - Curl_doh_cleanup(data); + Curl_doh_cleanup(data, async); #endif - Curl_safefree(data->state.async.hostname); + } } -void Curl_async_destroy(struct Curl_easy *data) +void Curl_async_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async) { -#ifdef CURLRES_ARES - Curl_async_ares_destroy(data); + if(async) { + CURL_TRC_DNS(data, "[%u] destroy async", async->id); + async->shutdown = TRUE; +#ifdef USE_RESOLV_ARES + Curl_async_ares_destroy(data, async); #endif -#ifdef CURLRES_THREADED - Curl_async_thrdd_destroy(data); +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_destroy(data, async); #endif #ifndef CURL_DISABLE_DOH - Curl_doh_cleanup(data); + Curl_doh_cleanup(data, async); #endif - Curl_safefree(data->state.async.hostname); + curlx_safefree(async); + } } #endif /* USE_CURL_ASYNC */ diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index ca6830a0be..79e1fd2449 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -21,14 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include "socketpair.h" /*********************************************************************** * Only for threaded name resolves builds **********************************************************************/ -#ifdef CURLRES_THREADED +#ifdef USE_RESOLV_THREADED #ifdef HAVE_NETINET_IN_H #include @@ -44,27 +42,27 @@ #include #endif -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) -# include -#endif - #ifdef HAVE_GETADDRINFO -# define RESOLVER_ENOMEM EAI_MEMORY /* = WSA_NOT_ENOUGH_MEMORY on Windows */ +#define RESOLVER_ENOMEM EAI_MEMORY /* = WSA_NOT_ENOUGH_MEMORY on Windows */ #else -# define RESOLVER_ENOMEM SOCKENOMEM +#define RESOLVER_ENOMEM SOCKENOMEM #endif #include "urldata.h" #include "cfilters.h" -#include "sendf.h" +#include "curl_addrinfo.h" +#include "curl_trc.h" #include "hostip.h" -#include "hash.h" -#include "share.h" +#include "httpsrr.h" #include "url.h" #include "multiif.h" #include "curl_threads.h" +#include "progress.h" +#include "rand.h" #include "select.h" -#include "strdup.h" +#include "thrdqueue.h" +#include "curlx/strparse.h" +#include "curlx/wait.h" #ifdef USE_ARES #include @@ -73,11 +71,6 @@ #endif #endif -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - /* * Curl_async_global_init() @@ -106,288 +99,93 @@ void Curl_async_global_cleanup(void) #endif } -static void async_thrdd_destroy(struct Curl_easy *); -static void async_thrdd_shutdown(struct Curl_easy *); +#ifdef CURLVERBOSE +#define CURL_ASYN_ITEM_DESC_LEN 64 +#define async_item_description(x) (x)->description +#else +#define async_item_description(x) NULL +#endif -CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl) -{ - (void)data; - *impl = NULL; - return CURLE_OK; -} +struct async_thrdd_item { + struct Curl_addrinfo *res; +#ifdef CURLVERBOSE + char description[CURL_ASYN_ITEM_DESC_LEN]; +#endif + int sock_error; + uint32_t mid; + uint32_t resolv_id; + uint16_t port; + uint8_t transport; + uint8_t dns_queries; +#ifdef DEBUGBUILD + uint32_t delay_ms; + uint32_t delay_fail_ms; +#endif + char hostname[1]; +}; /* Give up reference to add_ctx */ -static void addr_ctx_unlink(struct async_thrdd_addr_ctx **paddr_ctx, - struct Curl_easy *data) +static void async_thrdd_item_destroy(struct async_thrdd_item *item) { - struct async_thrdd_addr_ctx *addr_ctx = *paddr_ctx; - bool destroy; - - (void)data; - if(!addr_ctx) - return; - - Curl_mutex_acquire(&addr_ctx->mutx); - if(!data) /* called by resolving thread */ - addr_ctx->thrd_done = TRUE; - - DEBUGASSERT(addr_ctx->ref_count); - --addr_ctx->ref_count; - destroy = !addr_ctx->ref_count; - Curl_mutex_release(&addr_ctx->mutx); - - if(destroy) { - Curl_mutex_destroy(&addr_ctx->mutx); - 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 - free(addr_ctx); + if(item) { + if(item->res) + Curl_freeaddrinfo(item->res); + curlx_free(item); } - *paddr_ctx = NULL; } /* Initialize context for threaded resolver */ -static struct async_thrdd_addr_ctx * -addr_ctx_create(struct Curl_easy *data, - const char *hostname, int port, - const struct addrinfo *hints) +static struct async_thrdd_item *async_thrdd_item_create( + struct Curl_easy *data, + uint32_t resolv_id, uint8_t dns_queries, + const char *hostname, uint16_t port, + uint8_t transport) { - struct async_thrdd_addr_ctx *addr_ctx = calloc(1, sizeof(*addr_ctx)); - if(!addr_ctx) + size_t hostlen = strlen(hostname); + struct async_thrdd_item *item; + + item = curlx_calloc(1, sizeof(*item) + hostlen); + if(!item) return NULL; - addr_ctx->thread_hnd = curl_thread_t_null; - addr_ctx->port = port; - addr_ctx->ref_count = 1; + if(hostlen) /* NUL byte of name already in struct size */ + memcpy(item->hostname, hostname, hostlen); + item->mid = data->mid; + item->resolv_id = resolv_id; + item->dns_queries = dns_queries; + item->port = port; + item->transport = transport; -#ifdef HAVE_GETADDRINFO - DEBUGASSERT(hints); - addr_ctx->hints = *hints; -#else - (void)hints; +#ifdef CURLVERBOSE + curl_msnprintf(item->description, sizeof(item->description), + "[%" FMT_OFF_T "/%u] %s %s:%u", + data->id, item->resolv_id, + Curl_resolv_query_str(dns_queries), + item->hostname, item->port); #endif - Curl_mutex_init(&addr_ctx->mutx); - -#ifndef CURL_DISABLE_SOCKETPAIR - /* create socket pair or pipe */ - if(wakeup_create(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; - } -#endif - addr_ctx->sock_error = 0; - - /* Copying hostname string because original can be destroyed by parent - * thread during gethostbyname execution. - */ - addr_ctx->hostname = strdup(hostname); - if(!addr_ctx->hostname) - goto err_exit; - - return addr_ctx; - -err_exit: - addr_ctx_unlink(&addr_ctx, data); - return NULL; -} - -static void async_thrd_cleanup(void *arg) -{ - struct async_thrdd_addr_ctx *addr_ctx = arg; - - Curl_thread_disable_cancel(); - addr_ctx_unlink(&addr_ctx, NULL); -} - -#ifdef HAVE_GETADDRINFO - -/* - * getaddrinfo_thread() resolves a name and then exits. - * - * For builds without ARES, but with USE_IPV6, create a resolver thread - * and wait on it. - */ -static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg) -{ - struct async_thrdd_addr_ctx *addr_ctx = arg; - bool do_abort; - -/* clang complains about empty statements and the pthread_cleanup* macros - * are pretty ill defined. */ -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wextra-semi-stmt" -#endif - - Curl_thread_push_cleanup(async_thrd_cleanup, addr_ctx); - - Curl_mutex_acquire(&addr_ctx->mutx); - do_abort = addr_ctx->do_abort; - Curl_mutex_release(&addr_ctx->mutx); - - if(!do_abort) { - char service[12]; - int rc; - #ifdef DEBUGBUILD - Curl_resolve_test_delay(); -#endif - msnprintf(service, sizeof(service), "%d", addr_ctx->port); - - rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service, - &addr_ctx->hints, &addr_ctx->res); - - if(rc) { - addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc; - if(addr_ctx->sock_error == 0) - addr_ctx->sock_error = RESOLVER_ENOMEM; - } - else { - Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port); - } - - Curl_mutex_acquire(&addr_ctx->mutx); - do_abort = addr_ctx->do_abort; - 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) { - /* update sock_error to errno */ - addr_ctx->sock_error = SOCKERRNO; + { + const char *p = getenv("CURL_DBG_RESOLV_DELAY"); + if(p) { + curl_off_t l; + if(!curlx_str_number(&p, &l, UINT32_MAX)) { + item->delay_ms = (uint32_t)l; } } -#endif - - } - - Curl_thread_pop_cleanup(); -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - - addr_ctx_unlink(&addr_ctx, NULL); - return 0; -} - -#else /* HAVE_GETADDRINFO */ - -/* - * gethostbyname_thread() resolves a name and then exits. - */ -static CURL_THREAD_RETURN_T CURL_STDCALL gethostbyname_thread(void *arg) -{ - struct async_thrdd_addr_ctx *addr_ctx = arg; - bool do_abort; - -/* clang complains about empty statements and the pthread_cleanup* macros - * are pretty ill defined. */ -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wextra-semi-stmt" -#endif - - Curl_thread_push_cleanup(async_thrd_cleanup, addr_ctx); - - Curl_mutex_acquire(&addr_ctx->mutx); - do_abort = addr_ctx->do_abort; - Curl_mutex_release(&addr_ctx->mutx); - - if(!do_abort) { -#ifdef DEBUGBUILD - Curl_resolve_test_delay(); -#endif - - addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port); - if(!addr_ctx->res) { - addr_ctx->sock_error = SOCKERRNO; - if(addr_ctx->sock_error == 0) - addr_ctx->sock_error = RESOLVER_ENOMEM; - } - - Curl_mutex_acquire(&addr_ctx->mutx); - do_abort = addr_ctx->do_abort; - 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) { - /* update sock_error to errno */ - addr_ctx->sock_error = SOCKERRNO; + p = getenv("CURL_DBG_RESOLV_FAIL_DELAY"); + if(p) { + curl_off_t l; + if(!curlx_str_number(&p, &l, UINT32_MAX)) { + unsigned char c = 0; + Curl_rand_bytes(data, FALSE, &c, 1); + item->delay_fail_ms = (uint32_t)l + c; } } -#endif } - - Curl_thread_pop_cleanup(); -#if defined(__clang__) -#pragma clang diagnostic pop #endif - async_thrd_cleanup(addr_ctx); - return 0; -} - -#endif /* HAVE_GETADDRINFO */ - -/* - * async_thrdd_destroy() cleans up async resolver data and thread handle. - */ -static void async_thrdd_destroy(struct Curl_easy *data) -{ - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - struct async_thrdd_addr_ctx *addr = thrdd->addr; - -#ifdef USE_HTTPSRR_ARES - if(thrdd->rr.channel) { - ares_destroy(thrdd->rr.channel); - thrdd->rr.channel = NULL; - } - Curl_httpsrr_cleanup(&thrdd->rr.hinfo); -#endif - - if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null)) { - bool done; - - Curl_mutex_acquire(&addr->mutx); -#ifndef CURL_DISABLE_SOCKETPAIR - if(!addr->do_abort) - Curl_multi_will_close(data, addr->sock_pair[0]); -#endif - addr->do_abort = TRUE; - done = addr->thrd_done; - Curl_mutex_release(&addr->mutx); - - if(done) { - Curl_thread_join(&addr->thread_hnd); - CURL_TRC_DNS(data, "async_thrdd_destroy, thread joined"); - } - else { - /* thread is still running. Detach the thread while mutexed, it will - * trigger the cleanup when it releases its reference. */ - Curl_thread_destroy(&addr->thread_hnd); - CURL_TRC_DNS(data, "async_thrdd_destroy, thread detached"); - } - } - addr_ctx_unlink(&thrdd->addr, data); + return item; } #ifdef USE_HTTPSRR_ARES @@ -396,239 +194,507 @@ static void async_thrdd_rr_done(void *user_data, ares_status_t status, size_t timeouts, const ares_dns_record_t *dnsrec) { - struct Curl_easy *data = user_data; - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; + struct Curl_resolv_async *async = user_data; + struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL; (void)timeouts; - thrdd->rr.done = TRUE; - if((ARES_SUCCESS != status) || !dnsrec) + if(!thrdd) return; - thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo); + + async->dns_responses |= CURL_DNSQ_HTTPS; + async->queries_ongoing--; + async->done = !async->queries_ongoing; + if((ARES_SUCCESS == status) && dnsrec) + async->result = Curl_httpsrr_from_ares(dnsrec, &thrdd->rr.hinfo); } -static CURLcode async_rr_start(struct Curl_easy *data) +static CURLcode async_rr_start(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; + struct async_thrdd_ctx *thrdd = &async->thrdd; int status; + char *rrname = NULL; DEBUGASSERT(!thrdd->rr.channel); + if(async->port != 443) { + rrname = curl_maprintf("_%d_.https.%s", async->port, async->hostname); + if(!rrname) + return CURLE_OUT_OF_MEMORY; + } status = ares_init_options(&thrdd->rr.channel, NULL, 0); if(status != ARES_SUCCESS) { thrdd->rr.channel = NULL; + 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); - if(status) + if(status) { + curlx_free(rrname); return CURLE_FAILED_INIT; + } } #endif memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo)); - thrdd->rr.hinfo.port = -1; + thrdd->rr.hinfo.rrname = rrname; ares_query_dnsrec(thrdd->rr.channel, - data->conn->host.name, ARES_CLASS_IN, + rrname ? rrname : async->hostname, ARES_CLASS_IN, ARES_REC_TYPE_HTTPS, - async_thrdd_rr_done, data, NULL); - CURL_TRC_DNS(data, "Issued HTTPS-RR request for %s", data->conn->host.name); + async_thrdd_rr_done, async, NULL); + async->queries_ongoing++; + CURL_TRC_DNS(data, "[HTTPS-RR] initiated request for %s", + rrname ? rrname : async->hostname); return CURLE_OK; } #endif -/* - * async_thrdd_init() starts a new thread that performs the actual - * resolve. This function returns before the resolve is done. - * - * Returns FALSE in case of failure, otherwise TRUE. - */ -static bool async_thrdd_init(struct Curl_easy *data, - const char *hostname, int port, int ip_version, - const struct addrinfo *hints) +void Curl_async_thrdd_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - struct async_thrdd_addr_ctx *addr_ctx; + Curl_async_thrdd_destroy(data, async); +} - /* !checksrc! disable ERRNOVAR 1 */ - int err = ENOMEM; +struct async_thrdd_match_ctx { + uint32_t mid; + uint32_t resolv_id; +}; - if(thrdd->addr -#ifdef USE_HTTPSRR_ARES - || thrdd->rr.channel -#endif - ) { - CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up"); - async_thrdd_destroy(data); - DEBUGASSERT(!thrdd->addr); -#ifdef USE_HTTPSRR_ARES - DEBUGASSERT(!thrdd->rr.channel); -#endif +static bool async_thrdd_match_item(void *qitem, void *match_data) +{ + const struct async_thrdd_match_ctx *ctx = match_data; + struct async_thrdd_item *item = qitem; + return (item->mid == ctx->mid) && (item->resolv_id == ctx->resolv_id); +} + +void Curl_async_thrdd_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + (void)data; + if(async->queries_ongoing && !async->done && + data->multi && data->multi->resolv_thrdq) { + /* Remove any resolve items still queued */ + struct async_thrdd_match_ctx mctx; + mctx.mid = data->mid; + mctx.resolv_id = async->id; + Curl_thrdq_clear(data->multi->resolv_thrdq, + async_thrdd_match_item, &mctx); } +#ifdef USE_HTTPSRR_ARES + if(async->thrdd.rr.channel) { + ares_destroy(async->thrdd.rr.channel); + async->thrdd.rr.channel = NULL; + } + Curl_httpsrr_cleanup(&async->thrdd.rr.hinfo); +#endif + async_thrdd_item_destroy(async->thrdd.res_A); + async->thrdd.res_A = NULL; + async_thrdd_item_destroy(async->thrdd.res_AAAA); + async->thrdd.res_AAAA = NULL; +} - data->state.async.dns = NULL; - data->state.async.done = FALSE; - data->state.async.port = port; - data->state.async.ip_version = ip_version; - free(data->state.async.hostname); - data->state.async.hostname = strdup(hostname); - if(!data->state.async.hostname) - goto err_exit; +/* + * Waits for a resolve to finish. This function should be avoided since using + * this risk getting the multi interface to "hang". + */ +CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL; + timediff_t milli, ms; - addr_ctx = addr_ctx_create(data, hostname, port, hints); - if(!addr_ctx) - goto err_exit; - thrdd->addr = addr_ctx; + if(!thrdd) + return CURLE_FAILED_INIT; - /* passing addr_ctx to the thread adds a reference */ - addr_ctx->ref_count = 2; - addr_ctx->start = curlx_now(); + while(async->queries_ongoing && !async->done) { + Curl_async_thrdd_multi_process(data->multi); + if(async->done) + break; + + ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); + if(ms < 3) + milli = 0; + else if(ms <= 50) + milli = ms / 3; + else if(ms <= 250) + milli = 50; + else + milli = 200; + CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli); + curlx_wait_ms(milli); + } + return Curl_async_take_result(data, async, pdns); +} #ifdef HAVE_GETADDRINFO - addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx); + +/* Process the item, using Curl_getaddrinfo_ex() */ +static void async_thrdd_item_process(void *arg) +{ + struct async_thrdd_item *item = arg; + struct addrinfo hints; + char service[12]; + int pf = PF_INET; + int rc; + +#ifdef DEBUGBUILD + if(item->delay_ms) { + curlx_wait_ms(item->delay_ms); + } + if(item->delay_fail_ms) { + curlx_wait_ms(item->delay_fail_ms); + return; + } +#endif + + memset(&hints, 0, sizeof(hints)); +#ifdef CURLRES_IPV6 + if(item->dns_queries & CURL_DNSQ_AAAA) { + pf = (item->dns_queries & CURL_DNSQ_A) ? PF_UNSPEC : PF_INET6; + } +#endif + hints.ai_family = pf; + hints.ai_socktype = Curl_socktype_for_transport(item->transport); + hints.ai_protocol = Curl_protocol_for_transport(item->transport); +#ifdef __APPLE__ + /* If we leave `ai_flags == 0` then macOS is looking for IPV4MAPPED + * when doing AAAA queries. We do not want this "help". */ + hints.ai_flags = AI_ADDRCONFIG; +#endif + + curl_msnprintf(service, sizeof(service), "%u", item->port); +#ifdef AI_NUMERICSERV + hints.ai_flags |= AI_NUMERICSERV; +#endif + + rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res); + if(rc) { + item->sock_error = SOCKERRNO ? SOCKERRNO : rc; + if(item->sock_error == 0) + item->sock_error = RESOLVER_ENOMEM; + } + else { + Curl_addrinfo_set_port(item->res, item->port); + } +} + +#else /* HAVE_GETADDRINFO */ + +/* Process the item, using Curl_ipv4_resolve_r() */ +static void async_thrdd_item_process(void *item) +{ + struct async_thrdd_item *item = arg; + +#ifdef DEBUGBUILD + if(item->delay_ms) { + curlx_wait_ms(item->delay_ms); + } + if(item->delay_fail_ms) { + curlx_wait_ms(item->delay_fail_ms); + return; + } +#endif + item->res = Curl_ipv4_resolve_r(item->hostname, item->port); + if(!item->res) { + item->sock_error = SOCKERRNO; + if(item->sock_error == 0) + item->sock_error = RESOLVER_ENOMEM; + } +} + +#endif /* HAVE_GETADDRINFO */ + +#ifdef ENABLE_WAKEUP +static void async_thrdd_event(const struct curl_thrdq *tqueue, + Curl_thrdq_event ev, + void *user_data) +{ + struct Curl_multi *multi = user_data; + (void)tqueue; + switch(ev) { + case CURL_THRDQ_EV_ITEM_DONE: + (void)curl_multi_wakeup(multi); + break; + default: + break; + } +} #else - addr_ctx->thread_hnd = Curl_thread_create(gethostbyname_thread, addr_ctx); +#define async_thrdd_event NULL #endif - if(addr_ctx->thread_hnd == curl_thread_t_null) { - /* The thread never started */ - addr_ctx->ref_count = 1; - addr_ctx->thrd_done = TRUE; - err = errno; - goto err_exit; - } - -#ifdef USE_HTTPSRR_ARES - if(async_rr_start(data)) - infof(data, "Failed HTTPS RR operation"); -#endif - CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port); - return TRUE; - -err_exit: - CURL_TRC_DNS(data, "resolve thread failed init: %d", err); - async_thrdd_destroy(data); - CURL_SETERRNO(err); - return FALSE; +static void async_thrdd_item_free(void *item) +{ + async_thrdd_item_destroy(item); } -static void async_thrdd_shutdown(struct Curl_easy *data) +/* Create a thread queue for processing resolv items */ +CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms) { - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - struct async_thrdd_addr_ctx *addr_ctx = thrdd->addr; - bool done; - - if(!addr_ctx) - return; - if(addr_ctx->thread_hnd == curl_thread_t_null) - return; - - Curl_mutex_acquire(&addr_ctx->mutx); -#ifndef CURL_DISABLE_SOCKETPAIR - if(!addr_ctx->do_abort) - Curl_multi_will_close(data, addr_ctx->sock_pair[0]); -#endif - addr_ctx->do_abort = TRUE; - done = addr_ctx->thrd_done; - Curl_mutex_release(&addr_ctx->mutx); - - DEBUGASSERT(addr_ctx->thread_hnd != curl_thread_t_null); - if(!done && (addr_ctx->thread_hnd != curl_thread_t_null)) { - CURL_TRC_DNS(data, "cancelling resolve thread"); - (void)Curl_thread_cancel(&addr_ctx->thread_hnd); - } -} - -/* - * 'entry' may be NULL and then no data is returned - */ -static CURLcode asyn_thrdd_await(struct Curl_easy *data, - struct async_thrdd_addr_ctx *addr_ctx, - struct Curl_dns_entry **entry) -{ - CURLcode result = CURLE_OK; - - if(addr_ctx->thread_hnd != curl_thread_t_null) { - /* not interested in result? cancel, if still running... */ - if(!entry) - async_thrdd_shutdown(data); - - CURL_TRC_DNS(data, "resolve, wait for thread to finish"); - if(!Curl_thread_join(&addr_ctx->thread_hnd)) { - DEBUGASSERT(0); + CURLcode result; + DEBUGASSERT(!multi->resolv_thrdq); + result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS", 0, + min_threads, max_threads, idle_time_ms, + async_thrdd_item_free, + async_thrdd_item_process, + async_thrdd_event, + multi); +#ifdef DEBUGBUILD + if(!result) { + const char *p = getenv("CURL_DBG_RESOLV_MAX_THREADS"); + if(p) { + curl_off_t l; + if(!curlx_str_number(&p, &l, UINT32_MAX)) { + result = Curl_async_thrdd_multi_set_props( + multi, min_threads, (uint32_t)l, idle_time_ms); + } } - - if(entry) - result = Curl_async_is_resolved(data, entry); } - - data->state.async.done = TRUE; - if(entry) - *entry = data->state.async.dns; - +#endif return result; } -/* - * Until we gain a way to signal the resolver threads to stop early, we must - * simply wait for them and ignore their results. - */ -void Curl_async_thrdd_shutdown(struct Curl_easy *data) +/* Tear down the thread queue, joining active threads or detaching them */ +void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join) { - async_thrdd_shutdown(data); -} - -void Curl_async_thrdd_destroy(struct Curl_easy *data) -{ - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - - if(thrdd->addr && !data->set.quick_exit) { - (void)asyn_thrdd_await(data, thrdd->addr, NULL); + if(multi->resolv_thrdq) { +#ifdef CURLVERBOSE + CURL_TRC_DNS(multi->admin, "destroy thread queue+pool, join=%d", join); + Curl_thrdq_trace(multi->resolv_thrdq, multi->admin); +#endif + Curl_thrdq_destroy(multi->resolv_thrdq, join); + multi->resolv_thrdq = NULL; } - async_thrdd_destroy(data); } -/* - * Curl_async_await() - * - * Waits for a resolve to finish. This function should be avoided since using - * this risk getting the multi interface to "hang". - * - * If 'entry' is non-NULL, make it point to the resolved dns entry - * - * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, - * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. - * - * This is the version for resolves-in-a-thread. - */ -CURLcode Curl_async_await(struct Curl_easy *data, - struct Curl_dns_entry **entry) +#ifdef CURLVERBOSE +static void async_thrdd_report_item(struct Curl_easy *data, + struct async_thrdd_item *item) { - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - if(thrdd->addr) - return asyn_thrdd_await(data, thrdd->addr, entry); - return CURLE_FAILED_INIT; + char buf[MAX_IPADR_LEN]; + struct dynbuf tmp; + const char *sep = ""; + const struct Curl_addrinfo *ai = item->res; + int ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET; + CURLcode result; + + if(!Curl_trc_is_verbose(data)) + return; + + curlx_dyn_init(&tmp, 1024); + for(; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + Curl_printable_address(ai, buf, sizeof(buf)); + result = curlx_dyn_addf(&tmp, "%s%s", sep, buf); + if(result) { + CURL_TRC_DNS(data, "too many IP, cannot show"); + goto out; + } + sep = ", "; + } + } + + infof(data, "Host %s:%u resolved IPv%c: %s", item->hostname, item->port, + (item->dns_queries & CURL_DNSQ_AAAA) ? '6' : '4', + (curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)")); +out: + curlx_dyn_free(&tmp); +} +#endif /* CURLVERBOSE */ + +/* Process the receiving end of the thread queue, dispatching + * processed items to their transfer when it can still be found + * and has an `async` state present. Otherwise, destroy the item. */ +void Curl_async_thrdd_multi_process(struct Curl_multi *multi) +{ + struct Curl_easy *data; + void *qitem; + + while(!Curl_thrdq_recv(multi->resolv_thrdq, &qitem)) { + /* dispatch resolve result */ + struct async_thrdd_item *item = qitem; + struct Curl_resolv_async *async = NULL; + + data = Curl_multi_get_easy(multi, item->mid); + if(data) + async = Curl_async_get(data, item->resolv_id); + if(async) { + struct async_thrdd_item **pdest = &async->thrdd.res_A; + + async->dns_responses |= item->dns_queries; + --async->queries_ongoing; + async->done = !async->queries_ongoing; + +#ifdef CURLRES_IPV6 + if(item->dns_queries & CURL_DNSQ_AAAA) + pdest = &async->thrdd.res_AAAA; +#endif + if(!*pdest) { + VERBOSE(async_thrdd_report_item(data, item)); + *pdest = item; + item = NULL; + } + else + DEBUGASSERT(0); /* should not receive duplicates here */ + Curl_multi_mark_dirty(data); + } + async_thrdd_item_free(item); + } +#ifdef CURLVERBOSE + Curl_thrdq_trace(multi->resolv_thrdq, multi->admin); +#endif +} + +CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms) +{ + return Curl_thrdq_set_props(multi->resolv_thrdq, 0, + min_threads, max_threads, idle_time_ms); +} + +static CURLcode async_thrdd_query(struct Curl_easy *data, + struct Curl_resolv_async *async, + uint8_t dns_queries) +{ + struct async_thrdd_item *item; + CURLcode result; + + item = async_thrdd_item_create(data, async->id, dns_queries, + async->hostname, async->port, + async->transport); + if(!item) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + CURL_TRC_DNS(data, "queueing query %s", item->description); + result = Curl_thrdq_send(data->multi->resolv_thrdq, item, + async_item_description(item), async->timeout_ms); + if(result) + goto out; + item = NULL; + async->queries_ongoing++; + +out: + if(item) + async_thrdd_item_free(item); + return result; +} + +CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + CURLcode result = CURLE_FAILED_INIT; + void *resolver = NULL; + + if(async->queries_ongoing || async->done) + return CURLE_FAILED_INIT; + +#ifdef USE_HTTPSRR_ARES + DEBUGASSERT(!async->thrdd.rr.channel); + if((async->dns_queries & CURL_DNSQ_HTTPS) && !async->is_ipaddr) { + result = async_rr_start(data, async); + if(result) + goto out; + resolver = async->thrdd.rr.channel; + } +#endif + + result = Curl_resolv_announce_start(data, resolver); + if(result) + return result; + +#ifdef CURL_IPRESOLVE_V6 + /* Do not start an AAAA query for an ipv4 address when + * we will start an A query for it. */ + if((async->dns_queries & CURL_DNSQ_AAAA) && + !(async->is_ipv4addr && (async->dns_queries & CURL_DNSQ_A))) { + result = async_thrdd_query(data, async, CURL_DNSQ_AAAA); + if(result) + goto out; + } +#endif + if(async->dns_queries & CURL_DNSQ_A) { + result = async_thrdd_query(data, async, CURL_DNSQ_A); + if(result) + goto out; + } + if(result) + goto out; + +#ifdef CURLVERBOSE + Curl_thrdq_trace(data->multi->resolv_thrdq, data); +#endif + +out: + if(result) + CURL_TRC_DNS(data, "error queueing query %s:%d -> %d", + async->hostname, async->port, result); + return result; +} + +CURLcode Curl_async_pollset(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct easy_pollset *ps) +{ + timediff_t timeout_ms; + + timeout_ms = Curl_async_timeleft_ms(data, async); +#ifdef USE_HTTPSRR_ARES + if(async->thrdd.rr.channel) { + CURLcode result = Curl_ares_pollset(data, async->thrdd.rr.channel, ps); + if(result) + return result; + timeout_ms = Curl_ares_timeout_ms(data, async, async->thrdd.rr.channel); + } +#else + (void)ps; +#endif + + if(!async->done) { +#ifndef ENABLE_WAKEUP + timediff_t stutter_ms, elapsed_ms; + elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); + if(elapsed_ms < 3) + stutter_ms = 1; + else if(elapsed_ms <= 50) + stutter_ms = elapsed_ms / 3; + else if(elapsed_ms <= 250) + stutter_ms = 50; + else + stutter_ms = 200; + timeout_ms = CURLMIN(stutter_ms, timeout_ms); +#endif + Curl_expire(data, timeout_ms, EXPIRE_ASYNC_NAME); + } + return CURLE_OK; } /* - * Curl_async_is_resolved() is called repeatedly to check if a previous + * Curl_async_take_result() is called repeatedly to check if a previous * name resolve request has completed. It should also make sure to time-out if * the operation seems to take too long. */ -CURLcode Curl_async_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns) +CURLcode Curl_async_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns) { - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - bool done = FALSE; + struct async_thrdd_ctx *thrdd = &async->thrdd; + struct Curl_dns_entry *dns = NULL; + CURLcode result = CURLE_OK; - DEBUGASSERT(dns); - *dns = NULL; - - if(data->state.async.done) { - *dns = data->state.async.dns; - CURL_TRC_DNS(data, "threaded: is_resolved(), already done, dns=%sfound", - *dns ? "" : "not "); - return CURLE_OK; + DEBUGASSERT(pdns); + *pdns = NULL; + if(!async->queries_ongoing && !async->done) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; } #ifdef USE_HTTPSRR_ARES @@ -637,194 +703,126 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data, (void)Curl_ares_perform(thrdd->rr.channel, 0); #endif - DEBUGASSERT(thrdd->addr); - if(!thrdd->addr) - return CURLE_FAILED_INIT; + if(!async->done) + return CURLE_AGAIN; - Curl_mutex_acquire(&thrdd->addr->mutx); - done = thrdd->addr->thrd_done; - Curl_mutex_release(&thrdd->addr->mutx); + Curl_expire_done(data, EXPIRE_ASYNC_NAME); + if(async->result) + goto out; - if(done) { - CURLcode result = CURLE_OK; - - data->state.async.done = TRUE; - Curl_resolv_unlink(data, &data->state.async.dns); - - if(thrdd->addr->res) { - data->state.async.dns = - 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; - -#ifdef USE_HTTPSRR_ARES - if(thrdd->rr.channel) { - result = thrdd->rr.result; - if(!result) { - struct Curl_https_rrinfo *lhrr; - lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo); - if(!lhrr) - result = CURLE_OUT_OF_MEMORY; - else - data->state.async.dns->hinfo = lhrr; - } - } -#endif - if(!result && data->state.async.dns) - result = Curl_dnscache_add(data, data->state.async.dns); + if((thrdd->res_A && thrdd->res_A->res) || + (thrdd->res_AAAA && thrdd->res_AAAA->res)) { + dns = Curl_dnscache_mk_entry2( + data, async->dns_queries, + thrdd->res_A ? &thrdd->res_A->res : NULL, + thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL, + async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; } - if(!result && !data->state.async.dns) - result = Curl_resolver_error(data, NULL); - if(result) - Curl_resolv_unlink(data, &data->state.async.dns); - *dns = data->state.async.dns; - CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound", - result, *dns ? "" : "not "); - async_thrdd_shutdown(data); - return result; - } - else { - /* poll for name lookup done with exponential backoff up to 250ms */ - /* should be fine even if this converts to 32-bit */ - timediff_t elapsed = curlx_timediff(curlx_now(), - data->progress.t_startsingle); - if(elapsed < 0) - elapsed = 0; - - if(thrdd->addr->poll_interval == 0) - /* Start at 1ms poll interval */ - thrdd->addr->poll_interval = 1; - else if(elapsed >= thrdd->addr->interval_end) - /* Back-off exponentially if last interval expired */ - thrdd->addr->poll_interval *= 2; - - if(thrdd->addr->poll_interval > 250) - thrdd->addr->poll_interval = 250; - - thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval; - Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME); - return CURLE_OK; - } -} - -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; - -#if !defined(USE_HTTPSRR_ARES) && defined(CURL_DISABLE_SOCKETPAIR) - (void)ps; -#endif - #ifdef USE_HTTPSRR_ARES - if(thrdd->rr.channel) { - result = Curl_ares_pollset(data, thrdd->rr.channel, ps); - if(result) - return result; + if(thrdd->rr.channel) { + struct Curl_https_rrinfo *lhrr = NULL; + if(thrdd->rr.hinfo.complete) { + lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo); + if(!lhrr) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + Curl_httpsrr_trace(data, lhrr); + Curl_dns_entry_set_https_rr(dns, lhrr); + } +#endif } -#endif - if(!thrdd->addr) - return result; - Curl_mutex_acquire(&thrdd->addr->mutx); - thrd_done = thrdd->addr->thrd_done; - Curl_mutex_release(&thrdd->addr->mutx); - - if(!thrd_done) { -#ifndef CURL_DISABLE_SOCKETPAIR - /* return read fd to client for polling the DNS resolution status */ - result = Curl_pollset_add_in(data, ps, thrdd->addr->sock_pair[0]); -#else - timediff_t milli; - timediff_t ms = curlx_timediff(curlx_now(), thrdd->addr->start); - if(ms < 3) - milli = 0; - else if(ms <= 50) - milli = ms/3; - else if(ms <= 250) - milli = 50; - else - milli = 200; - Curl_expire(data, milli, EXPIRE_ASYNC_NAME); + if(dns) { + CURL_TRC_DNS(data, "resolving complete"); + *pdns = dns; + dns = NULL; + } +#ifdef CURLVERBOSE + Curl_thrdq_trace(data->multi->resolv_thrdq, data); #endif + +out: + Curl_dns_entry_unlink(data, &dns); + Curl_async_thrdd_shutdown(data, async); + if(!result && !*pdns) + result = Curl_resolver_error(data, NULL); + if(result && + (result != CURLE_COULDNT_RESOLVE_HOST) && + (result != CURLE_COULDNT_RESOLVE_PROXY)) { + CURL_TRC_DNS(data, "Error %d resolving %s:%d", + result, async->hostname, async->port); } return result; } -#ifndef HAVE_GETADDRINFO -/* - * Curl_async_getaddrinfo() - for platforms without getaddrinfo - */ -struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - int *waitp) +static const struct Curl_addrinfo *async_thrdd_get_ai( + const struct Curl_addrinfo *ai, + int ai_family, unsigned int index) { - (void)ip_version; - *waitp = 0; /* default to synchronous response */ - - /* fire up a new resolver thread! */ - if(async_thrdd_init(data, hostname, port, ip_version, NULL)) { - *waitp = 1; /* expect asynchronous response */ - return NULL; + unsigned int i = 0; + for(i = 0; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + if(i == index) + return ai; + ++i; + } } - - failf(data, "getaddrinfo() thread failed"); - return NULL; } -#else /* !HAVE_GETADDRINFO */ - -/* - * Curl_async_getaddrinfo() - for getaddrinfo - */ -struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - int *waitp) +const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data, + struct Curl_resolv_async *async, + int ai_family, + unsigned int index) { - struct addrinfo hints; - int pf = PF_INET; - *waitp = 0; /* default to synchronous response */ + struct async_thrdd_ctx *thrdd = &async->thrdd; - CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port); -#ifdef CURLRES_IPV6 - 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; - else - pf = PF_UNSPEC; + (void)data; + switch(ai_family) { + case AF_INET: + if(thrdd->res_A) + return async_thrdd_get_ai(thrdd->res_A->res, ai_family, index); + break; + case AF_INET6: + if(thrdd->res_AAAA) + return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index); + break; + default: + break; } + return NULL; +} + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_async_get_https( + struct Curl_easy *data, + struct Curl_resolv_async *async) +{ +#ifdef USE_HTTPSRR_ARES + if(Curl_async_knows_https(data, async)) + return &async->thrdd.rr.hinfo; #else - (void)ip_version; -#endif /* CURLRES_IPV6 */ - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = pf; - hints.ai_socktype = - (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? - SOCK_STREAM : SOCK_DGRAM; - - /* fire up a new resolver thread! */ - if(async_thrdd_init(data, hostname, port, ip_version, &hints)) { - *waitp = 1; /* expect asynchronous response */ - return NULL; - } - - failf(data, "getaddrinfo() thread failed to start"); + (void)data; + (void)async; +#endif return NULL; - } -#endif /* !HAVE_GETADDRINFO */ +bool Curl_async_knows_https(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + (void)data; + if(async->dns_queries & CURL_DNSQ_HTTPS) + return ((async->dns_responses & CURL_DNSQ_HTTPS) || async->done); + return TRUE; /* we know it will never come */ +} -#endif /* CURLRES_THREADED */ +#endif /* USE_HTTPSRR */ + +#endif /* USE_RESOLV_THREADED */ diff --git a/lib/asyn.h b/lib/asyn.h index 7863042bbe..06387a85ae 100644 --- a/lib/asyn.h +++ b/lib/asyn.h @@ -23,24 +23,28 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" +#if defined(USE_HTTPSRR) && defined(USE_ARES) +#include "httpsrr.h" +#endif + struct Curl_easy; struct Curl_dns_entry; +struct Curl_resolv_async; +struct Curl_multi; +struct easy_pollset; #ifdef CURLRES_ASYNCH #include "curl_addrinfo.h" -#include "httpsrr.h" -struct addrinfo; struct hostent; struct connectdata; struct easy_pollset; -#if defined(CURLRES_ARES) && defined(CURLRES_THREADED) -#error cannot have both CURLRES_ARES and CURLRES_THREADED defined +#if defined(USE_RESOLV_ARES) && defined(USE_RESOLV_THREADED) +#error cannot have both USE_RESOLV_ARES and USE_RESOLV_THREADED defined #endif /* @@ -64,49 +68,6 @@ int Curl_async_global_init(void); */ void Curl_async_global_cleanup(void); -/* - * Curl_async_get_impl() - * 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); - -/* Curl_async_pollset() - * - * This function is called from the Curl_multi_pollset() function. 'sock' is a - * pointer to an array to hold the file descriptors, with 'numsock' being the - * size of that array (in number of entries). This function is supposed to - * return bitmask indicating what file descriptors (referring to array indexes - * in the 'sock' array) to wait for, read/write. - */ -CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps); - -/* - * Curl_async_is_resolved() - * - * Called repeatedly to check if a previous name resolve request has - * completed. It should also make sure to time-out if the operation seems to - * take too long. - * - * Returns normal CURLcode errors. - */ -CURLcode Curl_async_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns); - -/* - * Curl_async_await() - * - * Waits for a resolve to finish. This function should be avoided since using - * this risk getting the multi interface to "hang". - * - * On return 'entry' is assigned the resolved dns (CURLE_OK or NULL otherwise. - * - * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, - * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. - */ -CURLcode Curl_async_await(struct Curl_easy *data, - struct Curl_dns_entry **dnsentry); - /* * Curl_async_getaddrinfo() - when using this resolver * @@ -118,11 +79,21 @@ CURLcode Curl_async_await(struct Curl_easy *data, * Each resolver backend must of course make sure to return data in the * correct format to comply with this. */ -struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - int *waitp); +CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, + struct Curl_resolv_async *async); + +const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data, + struct Curl_resolv_async *async, + int ai_family, + unsigned int index); + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_async_get_https( + struct Curl_easy *data, + struct Curl_resolv_async *async); +bool Curl_async_knows_https(struct Curl_easy *data, + struct Curl_resolv_async *async); +#endif /* USE_HTTPSRR */ #ifdef USE_ARES /* common functions for c-ares and threaded resolver with HTTPSRR */ @@ -132,106 +103,117 @@ CURLcode Curl_ares_pollset(struct Curl_easy *data, ares_channel channel, struct easy_pollset *ps); -int Curl_ares_perform(ares_channel channel, - timediff_t timeout_ms); +timediff_t Curl_ares_timeout_ms(struct Curl_easy *data, + struct Curl_resolv_async *async, + ares_channel channel); + +int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms); #endif -#ifdef CURLRES_ARES +#ifdef USE_RESOLV_ARES /* async resolving implementation using c-ares alone */ struct async_ares_ctx { ares_channel channel; - int num_pending; /* number of outstanding c-ares requests */ - struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares - parts */ - int ares_status; /* ARES_SUCCESS, ARES_ENOTFOUND, etc. */ - CURLcode result; /* CURLE_OK or error handling response */ -#ifndef HAVE_CARES_GETADDRINFO + struct Curl_addrinfo *res_A; + struct Curl_addrinfo *res_AAAA; + int ares_status; /* ARES_SUCCESS, ARES_ENOTFOUND, etc. */ + CURLcode result; /* CURLE_OK or error handling response */ struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ -#endif #ifdef USE_HTTPSRR struct Curl_https_rrinfo hinfo; #endif }; -void Curl_async_ares_shutdown(struct Curl_easy *data); -void Curl_async_ares_destroy(struct Curl_easy *data); +void Curl_async_ares_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async); +void Curl_async_ares_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async); -/* Set the DNS server to use by ares, from `data` settings. */ -CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data); +#endif /* USE_RESOLV_ARES */ -/* Set the DNS interfacer to use by ares, from `data` settings. */ -CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data); +#ifdef USE_RESOLV_THREADED -/* Set the local ipv4 address to use by ares, from `data` settings. */ -CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data); - -/* Set the local ipv6 address to use by ares, from `data` settings. */ -CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data); - -#endif /* CURLRES_ARES */ - -#ifdef CURLRES_THREADED -/* async resolving implementation using POSIX threads */ -#include "curl_threads.h" - -/* Context for threaded address resolver */ -struct async_thrdd_addr_ctx { - curl_thread_t thread_hnd; - char *hostname; /* hostname to resolve, Curl_async.hostname - duplicate */ - curl_mutex_t mutx; -#ifndef CURL_DISABLE_SOCKETPAIR - curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */ -#endif - struct Curl_addrinfo *res; -#ifdef HAVE_GETADDRINFO - struct addrinfo hints; -#endif - struct curltime start; - timediff_t interval_end; - unsigned int poll_interval; - int port; - int sock_error; - int ref_count; - BIT(thrd_done); - BIT(do_abort); -}; +struct async_thrdd_item; /* Context for threaded resolver */ struct async_thrdd_ctx { - /* `addr` is a pointer since this memory is shared with a started - * thread. Since threads cannot be killed, we use reference counting - * so that we can "release" our pointer to this memory while the - * thread is still running. */ - struct async_thrdd_addr_ctx *addr; + struct async_thrdd_item *res_A; /* ipv4 result */ + struct async_thrdd_item *res_AAAA; /* ipv6 result */ #if defined(USE_HTTPSRR) && defined(USE_ARES) struct { ares_channel channel; struct Curl_https_rrinfo hinfo; - CURLcode result; - BIT(done); } rr; #endif }; -void Curl_async_thrdd_shutdown(struct Curl_easy *data); -void Curl_async_thrdd_destroy(struct Curl_easy *data); +void Curl_async_thrdd_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async); +void Curl_async_thrdd_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async); -#endif /* CURLRES_THREADED */ +CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms); +void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join); +void Curl_async_thrdd_multi_process(struct Curl_multi *multi); + +CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms); + +#endif /* USE_RESOLV_THREADED */ #ifndef CURL_DISABLE_DOH struct doh_probes; #endif +/* + * Curl_async_await() + * + * Waits for a resolve to finish. This function should be avoided since using + * this risk getting the multi interface to "hang". + * + * On return 'dns' is assigned the resolved dns (CURLE_OK or NULL otherwise. + * + * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, + * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. + */ +CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns); + +/* + * Take the result of an async resolve operation. + * Returns CURLE_OK with `*pdns` != NULL, CURLE_AGAIN while still + * ongoing or an error code for a failed resolve. + */ +CURLcode Curl_async_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns); + +/* Curl_async_pollset() + * + * This function is called from the Curl_multi_pollset() function. 'sock' is a + * pointer to an array to hold the file descriptors, with 'numsock' being the + * size of that array (in number of entries). This function is supposed to + * return bitmask indicating what file descriptors (referring to array indexes + * in the 'sock' array) to wait for, read/write. + */ +CURLcode Curl_async_pollset(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct easy_pollset *ps); + #else /* CURLRES_ASYNCH */ /* convert these functions if an asynch resolver is not used */ -#define Curl_async_get_impl(x,y) (*(y) = NULL, CURLE_OK) -#define Curl_async_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST -#define Curl_async_await(x,y) CURLE_COULDNT_RESOLVE_HOST -#define Curl_async_global_init() CURLE_OK -#define Curl_async_global_cleanup() Curl_nop_stmt - +#define Curl_async_global_init() CURLE_OK +#define Curl_async_global_cleanup() Curl_nop_stmt +#define Curl_async_get_ai(a, b, c, d) NULL +#define Curl_async_await(a, b, c) CURLE_COULDNT_RESOLVE_HOST +#define Curl_async_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST +#define Curl_async_pollset(x, y, z) CURLE_OK #endif /* !CURLRES_ASYNCH */ #if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH) @@ -239,40 +221,50 @@ struct doh_probes; #endif #ifdef USE_CURL_ASYNC -struct Curl_async { -#ifdef CURLRES_ARES + +struct Curl_resolv_async { + struct Curl_resolv_async *next; +#ifdef USE_RESOLV_ARES struct async_ares_ctx ares; -#elif defined(CURLRES_THREADED) +#elif defined(USE_RESOLV_THREADED) struct async_thrdd_ctx thrdd; #endif #ifndef CURL_DISABLE_DOH struct doh_probes *doh; /* DoH specific data for this request */ #endif - struct Curl_dns_entry *dns; /* result of resolving on success */ - char *hostname; /* copy of the params resolv started with */ - int port; - int ip_version; + struct curltime start; + timediff_t interval_end; + timediff_t timeout_ms; + CURLcode result; + uint32_t poll_interval; + uint32_t id; /* unique id per easy handle of the resolve operation */ + /* what is being resolved */ + uint16_t port; + uint8_t dns_queries; /* what queries are being performed */ + uint8_t dns_responses; /* what queries had responses so far. */ + uint8_t transport; + uint8_t queries_ongoing; + BIT(is_ipaddr); + BIT(is_ipv4addr); BIT(done); + BIT(shutdown); + char hostname[1]; }; -/* - * Curl_async_shutdown(). - * - * This shuts down all ongoing operations. - */ -void Curl_async_shutdown(struct Curl_easy *data); +timediff_t Curl_async_timeleft_ms(struct Curl_easy *data, + struct Curl_resolv_async *async); + +/* Shut down the given async resolve. */ +void Curl_async_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async); + +/* Frees the resources of the given async resolve and the struct itself. */ +void Curl_async_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async); -/* - * Curl_async_destroy(). - * - * This frees the resources of any async resolve. - */ -void Curl_async_destroy(struct Curl_easy *data); #else /* !USE_CURL_ASYNC */ -#define Curl_async_shutdown(x) Curl_nop_stmt -#define Curl_async_destroy(x) Curl_nop_stmt +#define Curl_async_shutdown(x, y) Curl_nop_stmt #endif /* USE_CURL_ASYNC */ - /********** end of generic resolver interface functions *****************/ #endif /* HEADER_CURL_ASYN_H */ diff --git a/lib/bufq.c b/lib/bufq.c index 5cfd7520f2..750d86888f 100644 --- a/lib/bufq.c +++ b/lib/bufq.c @@ -21,14 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include "bufq.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "bufq.h" static bool chunk_is_empty(const struct buf_chunk *chunk) { @@ -52,9 +47,9 @@ static void chunk_reset(struct buf_chunk *chunk) } static size_t chunk_append(struct buf_chunk *chunk, - const unsigned char *buf, size_t len) + const uint8_t *buf, size_t len) { - unsigned char *p = &chunk->x.data[chunk->w_offset]; + uint8_t *p = &chunk->x.data[chunk->w_offset]; size_t n = chunk->dlen - chunk->w_offset; DEBUGASSERT(chunk->dlen >= chunk->w_offset); if(n) { @@ -66,9 +61,9 @@ static size_t chunk_append(struct buf_chunk *chunk, } static size_t chunk_read(struct buf_chunk *chunk, - unsigned char *buf, size_t len) + uint8_t *buf, size_t len) { - unsigned char *p = &chunk->x.data[chunk->r_offset]; + uint8_t *p = &chunk->x.data[chunk->r_offset]; size_t n = chunk->w_offset - chunk->r_offset; DEBUGASSERT(chunk->w_offset >= chunk->r_offset); if(!n) { @@ -90,7 +85,7 @@ static CURLcode chunk_slurpn(struct buf_chunk *chunk, size_t max_len, Curl_bufq_reader *reader, void *reader_ctx, size_t *pnread) { - unsigned char *p = &chunk->x.data[chunk->w_offset]; + uint8_t *p = &chunk->x.data[chunk->w_offset]; size_t n = chunk->dlen - chunk->w_offset; /* free amount */ CURLcode result; @@ -109,7 +104,7 @@ static CURLcode chunk_slurpn(struct buf_chunk *chunk, size_t max_len, } static void chunk_peek(const struct buf_chunk *chunk, - const unsigned char **pbuf, size_t *plen) + const uint8_t **pbuf, size_t *plen) { DEBUGASSERT(chunk->w_offset >= chunk->r_offset); *pbuf = &chunk->x.data[chunk->r_offset]; @@ -117,7 +112,7 @@ static void chunk_peek(const struct buf_chunk *chunk, } static void chunk_peek_at(const struct buf_chunk *chunk, size_t offset, - const unsigned char **pbuf, size_t *plen) + const uint8_t **pbuf, size_t *plen) { offset += chunk->r_offset; DEBUGASSERT(chunk->w_offset >= offset); @@ -144,11 +139,10 @@ static void chunk_list_free(struct buf_chunk **anchor) while(*anchor) { chunk = *anchor; *anchor = chunk->next; - free(chunk); + curlx_free(chunk); } } - void Curl_bufcp_init(struct bufc_pool *pool, size_t chunk_size, size_t spare_max) { @@ -179,7 +173,7 @@ static CURLcode bufcp_take(struct bufc_pool *pool, return CURLE_OUT_OF_MEMORY; } - chunk = calloc(1, sizeof(*chunk) + pool->chunk_size); + chunk = curlx_calloc(1, sizeof(*chunk) + pool->chunk_size); if(!chunk) { *pchunk = NULL; return CURLE_OUT_OF_MEMORY; @@ -193,7 +187,7 @@ static void bufcp_put(struct bufc_pool *pool, struct buf_chunk *chunk) { if(pool->spare_count >= pool->spare_max) { - free(chunk); + curlx_free(chunk); } else { chunk_reset(chunk); @@ -312,7 +306,7 @@ static struct buf_chunk *get_spare(struct bufq *q) return NULL; } - chunk = calloc(1, sizeof(*chunk) + q->chunk_size); + chunk = curlx_calloc(1, sizeof(*chunk) + q->chunk_size); if(!chunk) return NULL; chunk->dlen = q->chunk_size; @@ -335,11 +329,11 @@ static void prune_head(struct bufq *q) --q->chunk_count; } else if((q->chunk_count > q->max_chunks) || - (q->opts & BUFQ_OPT_NO_SPARES)) { + (q->opts & BUFQ_OPT_NO_SPARES)) { /* SOFT_LIMIT allowed us more than max. free spares until * we are at max again. Or free them if we are configured * to not use spares. */ - free(chunk); + curlx_free(chunk); --q->chunk_count; } else { @@ -371,7 +365,7 @@ static struct buf_chunk *get_non_full_tail(struct bufq *q) } CURLcode Curl_bufq_write(struct bufq *q, - const unsigned char *buf, size_t len, + const uint8_t *buf, size_t len, size_t *pnwritten) { struct buf_chunk *tail; @@ -401,10 +395,10 @@ CURLcode Curl_bufq_cwrite(struct bufq *q, const char *buf, size_t len, size_t *pnwritten) { - return Curl_bufq_write(q, (const unsigned char *)buf, len, pnwritten); + return Curl_bufq_write(q, (const uint8_t *)buf, len, pnwritten); } -CURLcode Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, +CURLcode Curl_bufq_read(struct bufq *q, uint8_t *buf, size_t len, size_t *pnread) { *pnread = 0; @@ -423,11 +417,11 @@ CURLcode Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len, size_t *pnread) { - return Curl_bufq_read(q, (unsigned char *)buf, len, pnread); + return Curl_bufq_read(q, (uint8_t *)buf, len, pnread); } bool Curl_bufq_peek(struct bufq *q, - const unsigned char **pbuf, size_t *plen) + const uint8_t **pbuf, size_t *plen) { if(q->head && chunk_is_empty(q->head)) { prune_head(q); @@ -442,7 +436,7 @@ bool Curl_bufq_peek(struct bufq *q, } bool Curl_bufq_peek_at(struct bufq *q, size_t offset, - const unsigned char **pbuf, size_t *plen) + const uint8_t **pbuf, size_t *plen) { struct buf_chunk *c = q->head; size_t clen; @@ -478,7 +472,7 @@ void Curl_bufq_skip(struct bufq *q, size_t amount) CURLcode Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, void *writer_ctx, size_t *pwritten) { - const unsigned char *buf; + const uint8_t *buf; size_t blen; CURLcode result = CURLE_OK; @@ -508,7 +502,7 @@ CURLcode Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, } CURLcode Curl_bufq_write_pass(struct bufq *q, - const unsigned char *buf, size_t len, + const uint8_t *buf, size_t len, Curl_bufq_writer *writer, void *writer_ctx, size_t *pwritten) { diff --git a/lib/bufq.h b/lib/bufq.h index 7cd1826a95..da411b586d 100644 --- a/lib/bufq.h +++ b/lib/bufq.h @@ -25,8 +25,6 @@ ***************************************************************************/ #include "curl_setup.h" -#include - /** * A chunk of bytes for reading and writing. * The size is fixed a creation with read and write offset @@ -38,7 +36,7 @@ struct buf_chunk { size_t r_offset; /* first unread bytes */ size_t w_offset; /* one after last written byte */ union { - unsigned char data[1]; /* the buffer for `dlen` bytes */ + uint8_t data[1]; /* the buffer for `dlen` bytes */ void *dummy; /* alignment */ } x; }; @@ -46,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 */ @@ -79,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 @@ -106,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. @@ -135,8 +132,8 @@ void Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool, size_t max_chunks, int opts); /** - * Reset the buffer queue to be empty. Will keep any allocated buffer - * chunks around. + * Reset the buffer queue to be empty. Keep any allocated buffer chunks + * around. */ void Curl_bufq_reset(struct bufq *q); @@ -166,7 +163,7 @@ bool Curl_bufq_is_full(const struct bufq *q); * CURLE_AGAIN is returned if the buffer queue is full. */ CURLcode Curl_bufq_write(struct bufq *q, - const unsigned char *buf, size_t len, + const uint8_t *buf, size_t len, size_t *pnwritten); CURLcode Curl_bufq_cwrite(struct bufq *q, @@ -177,7 +174,7 @@ CURLcode Curl_bufq_cwrite(struct bufq *q, * Read buf from the start of the buffer queue. The buf is copied * and the amount of copied bytes is returned. */ -CURLcode Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, +CURLcode Curl_bufq_read(struct bufq *q, uint8_t *buf, size_t len, size_t *pnread); CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len, @@ -193,20 +190,19 @@ CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len, * is modified, see `Curl_bufq_skip()`` */ bool Curl_bufq_peek(struct bufq *q, - const unsigned char **pbuf, size_t *plen); + const uint8_t **pbuf, size_t *plen); bool Curl_bufq_peek_at(struct bufq *q, size_t offset, - const unsigned char **pbuf, size_t *plen); + 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); typedef CURLcode Curl_bufq_writer(void *writer_ctx, - const unsigned char *buf, size_t len, + const uint8_t *buf, size_t len, size_t *pwritten); /** * Passes the chunks in the buffer queue to the writer and returns @@ -221,11 +217,11 @@ CURLcode Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, void *writer_ctx, size_t *pwritten); typedef CURLcode Curl_bufq_reader(void *reader_ctx, - unsigned char *buf, size_t len, + uint8_t *buf, size_t len, size_t *pnread); /** - * Read date and append it to the end of the buffer queue until the + * Read bytes and append them to the end of the buffer queue until the * reader returns blocking or the queue is full. A reader returns * CURLE_AGAIN to indicate blocking. * Returns the total amount of buf read (may be 0) in `pnread` on success. @@ -247,13 +243,13 @@ CURLcode Curl_bufq_sipn(struct bufq *q, size_t max_len, /** * Write buf to the end of the buffer queue. - * Will write bufq content or passed `buf` directly using the `writer` + * Write bufq content or passed `buf` directly using the `writer` * callback when it sees fit. 'buf' might get passed directly * on or is placed into the buffer, depending on `len` and current * amount buffered, chunk size, etc. */ CURLcode Curl_bufq_write_pass(struct bufq *q, - const unsigned char *buf, size_t len, + const uint8_t *buf, size_t len, Curl_bufq_writer *writer, void *writer_ctx, size_t *pwritten); diff --git a/lib/bufref.c b/lib/bufref.c index ac0612071d..50fafdac03 100644 --- a/lib/bufref.c +++ b/lib/bufref.c @@ -21,14 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #include "urldata.h" #include "bufref.h" -#include "strdup.h" - -#include "curl_memory.h" -#include "memdebug.h" +#include "curlx/strdup.h" #ifdef DEBUGBUILD #define SIGNATURE 0x5c48e9b2 /* Random pattern. */ @@ -79,7 +76,7 @@ void Curl_bufref_set(struct bufref *br, const void *ptr, size_t len, DEBUGASSERT(len <= CURL_MAX_INPUT_LENGTH); Curl_bufref_free(br); - br->ptr = (const unsigned char *) ptr; + br->ptr = (const unsigned char *)ptr; br->len = len; br->dtor = dtor; } @@ -87,7 +84,7 @@ void Curl_bufref_set(struct bufref *br, const void *ptr, size_t len, /* * Get a pointer to the referenced buffer. */ -const unsigned char *Curl_bufref_ptr(const struct bufref *br) +const unsigned char *Curl_bufref_uptr(const struct bufref *br) { DEBUGASSERT(br); DEBUGASSERT(br->signature == SIGNATURE); @@ -96,6 +93,18 @@ const unsigned char *Curl_bufref_ptr(const struct bufref *br) return br->ptr; } +/* + * Get a pointer to the referenced string. + */ +const char *Curl_bufref_ptr(const struct bufref *br) +{ + DEBUGASSERT(br); + DEBUGASSERT(br->signature == SIGNATURE); + DEBUGASSERT(br->ptr || !br->len); + + return (const char *)br->ptr; +} + /* * Get the length of the referenced buffer data. */ @@ -108,7 +117,7 @@ size_t Curl_bufref_len(const struct bufref *br) return br->len; } -CURLcode Curl_bufref_memdup(struct bufref *br, const void *ptr, size_t len) +CURLcode Curl_bufref_memdup0(struct bufref *br, const void *ptr, size_t len) { unsigned char *cpy = NULL; @@ -119,7 +128,7 @@ CURLcode Curl_bufref_memdup(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/lib/bufref.h b/lib/bufref.h index dd424f18f5..5d331adbdf 100644 --- a/lib/bufref.h +++ b/lib/bufref.h @@ -36,13 +36,16 @@ struct bufref { #endif }; - void Curl_bufref_init(struct bufref *br); void Curl_bufref_set(struct bufref *br, const void *ptr, size_t len, void (*dtor)(void *)); -const unsigned char *Curl_bufref_ptr(const struct bufref *br); +const char *Curl_bufref_ptr(const struct bufref *br); +const unsigned char *Curl_bufref_uptr(const struct bufref *br); size_t Curl_bufref_len(const struct bufref *br); -CURLcode Curl_bufref_memdup(struct bufref *br, const void *ptr, size_t len); +CURLcode Curl_bufref_memdup0(struct bufref *br, const void *ptr, size_t len); void Curl_bufref_free(struct bufref *br); +/* return a strdup() version of the buffer */ +#define Curl_bufref_dup(x) curlx_strdup(Curl_bufref_ptr(x)) + #endif diff --git a/lib/cf-dns.c b/lib/cf-dns.c new file mode 100644 index 0000000000..21a52a030b --- /dev/null +++ b/lib/cf-dns.c @@ -0,0 +1,635 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "urldata.h" +#include "curl_addrinfo.h" +#include "cfilters.h" +#include "connect.h" +#include "dnscache.h" +#include "curl_trc.h" +#include "progress.h" +#include "url.h" +#include "cf-dns.h" + + +struct cf_dns_ctx { + struct Curl_dns_entry *dns; + CURLcode resolv_result; + uint32_t resolv_id; + uint16_t port; + uint8_t dns_queries; + uint8_t transport; + BIT(started); + BIT(announced); + BIT(abstract_unix_socket); + BIT(complete_resolve); + char hostname[1]; +}; + +static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, uint8_t transport, + bool abstract_unix_socket, + bool complete_resolve, + struct Curl_dns_entry *dns) +{ + struct cf_dns_ctx *ctx; + size_t hlen = strlen(hostname); + + ctx = curlx_calloc(1, sizeof(*ctx) + hlen); + if(!ctx) + return NULL; + + ctx->port = port; + ctx->dns_queries = dns_queries; + ctx->transport = transport; + ctx->abstract_unix_socket = abstract_unix_socket; + ctx->complete_resolve = complete_resolve; + ctx->dns = Curl_dns_entry_link(data, dns); + ctx->started = !!ctx->dns; + if(hlen) + memcpy(ctx->hostname, hostname, hlen); + + CURL_TRC_DNS(data, "created DNS filter for %s:%u, transport=%x, queries=%x", + ctx->hostname, ctx->port, ctx->transport, ctx->dns_queries); + return ctx; +} + +static void cf_dns_ctx_destroy(struct Curl_easy *data, + struct cf_dns_ctx *ctx) +{ + if(ctx) { + Curl_dns_entry_unlink(data, &ctx->dns); + curlx_free(ctx); + } +} + +#ifdef CURLVERBOSE +static void cf_dns_report_addr(struct Curl_easy *data, + struct dynbuf *tmp, + const char *label, + int ai_family, + const struct Curl_addrinfo *ai) +{ + char buf[MAX_IPADR_LEN]; + const char *sep = ""; + CURLcode result; + + curlx_dyn_reset(tmp); + for(; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + Curl_printable_address(ai, buf, sizeof(buf)); + result = curlx_dyn_addf(tmp, "%s%s", sep, buf); + if(result) { + infof(data, "too many IP, cannot show"); + return; + } + sep = ", "; + } + } + + infof(data, "%s%s", label, + (curlx_dyn_len(tmp) ? curlx_dyn_ptr(tmp) : "(none)")); +} + +static void cf_dns_report(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_dns_entry *dns) +{ + struct cf_dns_ctx *ctx = cf->ctx; + struct dynbuf tmp; + + if(!Curl_trc_is_verbose(data) || + /* ignore no name or numerical IP addresses */ + !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) + return; + + switch(ctx->transport) { + case TRNSPRT_UNIX: +#ifdef USE_UNIX_SOCKETS + CURL_TRC_CF(data, cf, "resolved unix domain %s", + Curl_conn_get_unix_path(data->conn)); +#else + DEBUGASSERT(0); +#endif + break; + default: + curlx_dyn_init(&tmp, 1024); + infof(data, "Host %s:%d was resolved.", dns->hostname, dns->port); +#ifdef CURLRES_IPV6 + cf_dns_report_addr(data, &tmp, "IPv6: ", AF_INET6, dns->addr); +#endif + cf_dns_report_addr(data, &tmp, "IPv4: ", AF_INET, dns->addr); + curlx_dyn_free(&tmp); + break; + } +} +#else +#define cf_dns_report(x, y, z) Curl_nop_stmt +#endif + +/************************************************************* + * Resolve the address of the server or proxy + *************************************************************/ +static CURLcode cf_dns_start(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_dns_entry **pdns) +{ + struct cf_dns_ctx *ctx = cf->ctx; + timediff_t timeout_ms = Curl_timeleft_ms(data); + CURLcode result; + + *pdns = NULL; + +#ifdef USE_UNIX_SOCKETS + if(ctx->transport == TRNSPRT_UNIX) { + CURL_TRC_CF(data, cf, "resolve unix socket %s", ctx->hostname); + return Curl_resolv_unix(data, ctx->hostname, + (bool)cf->conn->bits.abstract_unix_socket, pdns); + } +#endif + + /* Resolve target host right on */ + CURL_TRC_CF(data, cf, "cf_dns_start host %s:%u", ctx->hostname, ctx->port); + if(Curl_is_ipv4addr(ctx->hostname)) + ctx->dns_queries |= CURL_DNSQ_A; +#ifdef USE_IPV6 + else if(Curl_is_ipaddr(ctx->hostname)) /* not ipv4, must be ipv6 then */ + ctx->dns_queries |= CURL_DNSQ_AAAA; +#endif + result = Curl_resolv(data, ctx->dns_queries, + ctx->hostname, ctx->port, ctx->transport, + timeout_ms, &ctx->resolv_id, pdns); + DEBUGASSERT(!result || !*pdns); + if(!result) { /* resolved right away, either sync or from dnscache */ + DEBUGASSERT(*pdns); + return CURLE_OK; + } + else if(result == CURLE_AGAIN) { /* async resolv in progress */ + return CURLE_OK; + } + else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */ + failf(data, "Failed to resolve '%s' with timeout after %" + FMT_TIMEDIFF_T " ms", ctx->hostname, + curlx_ptimediff_ms(Curl_pgrs_now(data), + &data->progress.t_startsingle)); + return CURLE_OPERATION_TIMEDOUT; + } + else { + DEBUGASSERT(result); + failf(data, "Could not resolve: %s", ctx->hostname); + return result; + } +} + +static CURLcode cf_dns_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + struct cf_dns_ctx *ctx = cf->ctx; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + *done = FALSE; + if(!ctx->started) { + ctx->started = TRUE; + ctx->resolv_result = cf_dns_start(cf, data, &ctx->dns); + } + + if(!ctx->dns && !ctx->resolv_result) { + ctx->resolv_result = + Curl_resolv_take_result(data, ctx->resolv_id, &ctx->dns); + if(!ctx->dns && !ctx->resolv_result) + CURL_TRC_CF(data, cf, "DNS resolution ongoing for %s:%u", + ctx->hostname, ctx->port); + } + + if(ctx->resolv_result) { + CURL_TRC_CF(data, cf, "error resolving: %d", ctx->resolv_result); + return ctx->resolv_result; + } + + if(ctx->dns && !ctx->announced) { + ctx->announced = TRUE; + if(cf->sockindex == FIRSTSOCKET) { + cf->conn->bits.dns_resolved = TRUE; + Curl_pgrsTime(data, TIMER_NAMELOOKUP); + } + cf_dns_report(cf, data, ctx->dns); + } + + if(cf->next && !cf->next->connected) { + bool sub_done; + CURLcode result = Curl_conn_cf_connect(cf->next, data, &sub_done); + CURL_TRC_CF(data, cf, "connect subfilters -> %d, done=%d", + result, sub_done); + if(result || !sub_done) + return result; + DEBUGASSERT(sub_done); + } + + /* sub filter chain is connected */ + if(ctx->complete_resolve && !ctx->dns && !ctx->resolv_result) { + /* This filter only connects when it has resolved everything. */ + return CURLE_OK; + } + *done = TRUE; + cf->connected = TRUE; + Curl_resolv_destroy(data, ctx->resolv_id); + return CURLE_OK; +} + +static void cf_dns_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_dns_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "destroy"); + cf_dns_ctx_destroy(data, ctx); +} + +static void cf_dns_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + cf->connected = FALSE; + if(cf->next) + cf->next->cft->do_close(cf->next, data); +} + +static CURLcode cf_dns_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ +#ifdef USE_CURL_ASYNC + if(!cf->connected) + return Curl_resolv_pollset(data, ps); +#else + (void)cf; + (void)data; + (void)ps; +#endif + return CURLE_OK; +} + +static CURLcode cf_dns_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_dns_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_DATA_DONE: + if(ctx->dns) { + /* Should only come here when the connect attempt failed and + * `data` is giving up on it. On a successful connect, we already + * unlinked the DNS entry. */ + Curl_dns_entry_unlink(data, &ctx->dns); + } + break; + default: + break; + } + return result; +} + +struct Curl_cftype Curl_cft_dns = { + "DNS", + CF_TYPE_SETUP, + CURL_LOG_LVL_NONE, + cf_dns_destroy, + cf_dns_connect, + cf_dns_close, + Curl_cf_def_shutdown, + cf_dns_adjust_pollset, + Curl_cf_def_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + cf_dns_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +static CURLcode cf_dns_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool abstract_unix_socket, + bool complete_resolve, + struct Curl_dns_entry *dns) +{ + struct Curl_cfilter *cf = NULL; + struct cf_dns_ctx *ctx; + CURLcode result = CURLE_OK; + + (void)data; + ctx = cf_dns_ctx_create(data, dns_queries, hostname, port, transport, + abstract_unix_socket, complete_resolve, dns); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + result = Curl_cf_create(&cf, &Curl_cft_dns, ctx); + +out: + *pcf = result ? NULL : cf; + if(result) + cf_dns_ctx_destroy(data, ctx); + return result; +} + +/* Create a "resolv" filter for the transfer's connection. Figures + * out the hostname/path and port where to connect to. */ +static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + uint8_t dns_queries, + uint8_t transport, + bool complete_resolve, + struct Curl_dns_entry *dns) +{ + struct connectdata *conn = data->conn; + const char *hostname = NULL; + uint16_t port = 0; + bool abstract_unix_socket = FALSE; + +#ifdef USE_UNIX_SOCKETS + { + const char *unix_path = Curl_conn_get_unix_path(conn); + if(unix_path) { + DEBUGASSERT(transport == TRNSPRT_UNIX); + hostname = unix_path; + abstract_unix_socket = (bool)conn->bits.abstract_unix_socket; + } + } +#endif + +#ifndef CURL_DISABLE_PROXY + if(!hostname && CONN_IS_PROXIED(conn)) { + struct hostname *ehost; + ehost = conn->bits.socksproxy ? &conn->socks_proxy.host : + &conn->http_proxy.host; + hostname = ehost->name; + port = conn->bits.socksproxy ? conn->socks_proxy.port : + conn->http_proxy.port; + } +#endif + if(!hostname) { + struct hostname *ehost; + ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host; + /* If not connecting via a proxy, extract the port from the URL, if it is + * there, thus overriding any defaults that might have been set above. */ + hostname = ehost->name; + port = conn->bits.conn_to_port ? + conn->conn_to_port : (uint16_t)conn->remote_port; + } + + if(!hostname) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + return cf_dns_create(pcf, data, dns_queries, + hostname, port, transport, + abstract_unix_socket, complete_resolve, dns); +} + +/* Adds a "resolv" filter at the top of the connection's filter chain. + * For FIRSTSOCKET, the `dns` parameter may be NULL. The filter will + * figure out hostname and port to connect to and start the DNS resolve + * on the first connect attempt. + * For SECONDARYSOCKET, the `dns` parameter must be given. + */ +CURLcode Curl_cf_dns_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + uint8_t dns_queries, + uint8_t transport, + struct Curl_dns_entry *dns) +{ + struct Curl_cfilter *cf = NULL; + CURLcode result; + + DEBUGASSERT(data); + if(sockindex == FIRSTSOCKET) + result = cf_dns_conn_create(&cf, data, dns_queries, transport, FALSE, dns); + else if(dns) { + result = cf_dns_create(&cf, data, dns_queries, + dns->hostname, dns->port, transport, + FALSE, FALSE, dns); + } + else { + DEBUGASSERT(0); + result = CURLE_FAILED_INIT; + } + if(result) + goto out; + Curl_conn_cf_add(data, conn, sockindex, cf); +out: + return result; +} + +/* Insert a new "resolv" filter directly after `cf`. It will + * start a DNS resolve for the given hostnmae and port on the + * first connect attempt. + * See socks.c on how this is used to make a non-blocking DNS + * resolve during connect. + */ +CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool complete_resolve) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = cf_dns_create(&cf, data, dns_queries, + hostname, port, transport, + FALSE, complete_resolve, NULL); + if(result) + return result; + + Curl_conn_cf_insert_after(cf_at, cf); + return CURLE_OK; +} + +/* Return the resolv result from the first "resolv" filter, starting + * the given filter `cf` downwards. + */ +CURLcode Curl_cf_dns_result(struct Curl_cfilter *cf) +{ + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->dns || ctx->resolv_result) + return ctx->resolv_result; + return CURLE_AGAIN; + } + } + return CURLE_FAILED_INIT; +} + +/* Return the result of the DNS resolution. Searches for a "resolv" + * filter from the top of the filter chain down. Returns + * - CURLE_AGAIN when not done yet + * - CURLE_OK when DNS was successfully resolved + * - CURLR_FAILED_INIT when no resolv filter was found + * - error returned by the DNS resolv + */ +CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex) +{ + return Curl_cf_dns_result(conn->cfilter[sockindex]); +} + +static const struct Curl_addrinfo *cf_dns_get_nth_ai( + struct Curl_cfilter *cf, + const struct Curl_addrinfo *ai, + int ai_family, unsigned int index) +{ + struct cf_dns_ctx *ctx = cf->ctx; + unsigned int i = 0; + + if((ai_family == AF_INET) && !(ctx->dns_queries & CURL_DNSQ_A)) + return NULL; +#ifdef USE_IPV6 + if((ai_family == AF_INET6) && !(ctx->dns_queries & CURL_DNSQ_AAAA)) + return NULL; +#endif + for(i = 0; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + if(i == index) + return ai; + ++i; + } + } + return NULL; +} + +bool Curl_conn_dns_has_any_ai(struct Curl_easy *data, int sockindex) +{ + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->resolv_result) + return FALSE; + else if(ctx->dns) + return !!ctx->dns->addr; + else +#ifdef USE_IPV6 + return Curl_resolv_get_ai(data, ctx->resolv_id, AF_INET, 0) || + Curl_resolv_get_ai(data, ctx->resolv_id, AF_INET6, 0); +#else + return !!Curl_resolv_get_ai(data, ctx->resolv_id, AF_INET, 0); +#endif + } + } + return FALSE; +} + +/* Return the addrinfo at `index` for the given `family` from the + * first "resolve" filter underneath `cf`. If the DNS resolving is + * not done yet or if no address for the family exists, returns NULL. + */ +const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, + struct Curl_easy *data, + int ai_family, + unsigned int index) +{ + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->resolv_result) + return NULL; + else if(ctx->dns) + return cf_dns_get_nth_ai(cf, ctx->dns->addr, ai_family, index); + else + return Curl_resolv_get_ai(data, ctx->resolv_id, ai_family, index); + } + } + return NULL; +} + +/* Return the addrinfo at `index` for the given `family` from the + * first "resolve" filter at the connection. If the DNS resolving is + * not done yet or if no address for the family exists, returns NULL. + */ +const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, + int sockindex, int ai_family, + unsigned int index) +{ + struct connectdata *conn = data->conn; + return Curl_cf_dns_get_ai(conn->cfilter[sockindex], data, ai_family, index); +} + +#ifdef USE_HTTPSRR +/* Return the HTTPS-RR info from the first "resolve" filter at the + * connection. If the DNS resolving is not done yet or if there + * is no HTTPS-RR info, returns NULL. + */ +const struct Curl_https_rrinfo *Curl_conn_dns_get_https(struct Curl_easy *data, + int sockindex) +{ + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->dns) + return ctx->dns->hinfo; + else + return Curl_resolv_get_https(data, ctx->resolv_id); + } + } + return NULL; +} + +bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex) +{ + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->dns) + return TRUE; + else + return Curl_resolv_knows_https(data, ctx->resolv_id); + } + } + return FALSE; +} + +#endif /* USE_HTTPSRR */ diff --git a/lib/cf-dns.h b/lib/cf-dns.h new file mode 100644 index 0000000000..fc97601dc3 --- /dev/null +++ b/lib/cf-dns.h @@ -0,0 +1,76 @@ +#ifndef HEADER_CURL_CF_DNS_H +#define HEADER_CURL_CF_DNS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +struct Curl_easy; +struct connectdata; +struct Curl_dns_entry; +struct Curl_addrinfo; + +CURLcode Curl_cf_dns_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + uint8_t dns_queries, + uint8_t transport, + struct Curl_dns_entry *dns); + +CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool complete_resolve); + +CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex); +CURLcode Curl_cf_dns_result(struct Curl_cfilter *cf); + +/* Returns TRUE if any addressinfo is available via + * `Curl_conn_dns_get_ai()`. */ +bool Curl_conn_dns_has_any_ai(struct Curl_easy *data, int sockindex); + +const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, + int sockindex, + int ai_family, + unsigned int index); + +const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, + struct Curl_easy *data, + int ai_family, + unsigned int index); + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_conn_dns_get_https(struct Curl_easy *data, + int sockindex); +bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex); +#else +#define Curl_conn_dns_get_https(a, b) NULL +#define Curl_conn_dns_resolved_https(a, b) TRUE +#endif + +extern struct Curl_cftype Curl_cft_dns; + +#endif /* HEADER_CURL_CF_DNS_H */ diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index 798d6e9f66..3c2c8374d1 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -21,19 +21,16 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) -#include #include "urldata.h" #include "curlx/dynbuf.h" #include "sendf.h" #include "http.h" #include "http1.h" #include "http_proxy.h" -#include "url.h" #include "select.h" #include "progress.h" #include "cfilters.h" @@ -41,24 +38,16 @@ #include "connect.h" #include "curl_trc.h" #include "strcase.h" -#include "vtls/vtls.h" -#include "transfer.h" -#include "multiif.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - typedef enum { - H1_TUNNEL_INIT, /* init/default/no tunnel state */ - H1_TUNNEL_CONNECT, /* CONNECT request is being send */ - H1_TUNNEL_RECEIVE, /* CONNECT answer is being received */ - H1_TUNNEL_RESPONSE, /* CONNECT response received completely */ - H1_TUNNEL_ESTABLISHED, - H1_TUNNEL_FAILED + H1_TUNNEL_INIT, /* init/default/no tunnel state */ + H1_TUNNEL_CONNECT, /* CONNECT request is being send */ + H1_TUNNEL_RECEIVE, /* CONNECT answer is being received */ + H1_TUNNEL_RESPONSE, /* CONNECT response received completely */ + H1_TUNNEL_ESTABLISHED, + H1_TUNNEL_FAILED } h1_tunnel_state; /* struct for HTTP CONNECT tunneling */ @@ -77,9 +66,10 @@ struct h1_tunnel_state { h1_tunnel_state tunnel_state; BIT(chunked_encoding); BIT(close_connection); + BIT(maybe_folded); + BIT(leading_unfold); }; - static bool tunnel_is_established(struct h1_tunnel_state *ts) { return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED); @@ -103,6 +93,8 @@ static CURLcode tunnel_reinit(struct Curl_cfilter *cf, ts->keepon = KEEPON_CONNECT; ts->cl = 0; ts->close_connection = FALSE; + ts->maybe_folded = FALSE; + ts->leading_unfold = FALSE; return CURLE_OK; } @@ -112,12 +104,12 @@ 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; } - ts = calloc(1, sizeof(*ts)); + ts = curlx_calloc(1, sizeof(*ts)); if(!ts) return CURLE_OUT_OF_MEMORY; @@ -127,8 +119,7 @@ static CURLcode tunnel_init(struct Curl_cfilter *cf, curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST); Curl_httpchunk_init(data, &ts->ch, TRUE); - *pts = ts; - connkeep(cf->conn, "HTTP proxy CONNECT"); + *pts = ts; return tunnel_reinit(cf, data, ts); } @@ -176,12 +167,12 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, curlx_dyn_reset(&ts->rcvbuf); curlx_dyn_reset(&ts->request_data); /* restore the protocol pointer */ - data->info.httpcode = 0; /* clear it as it might've been used for the + data->info.httpcode = 0; /* clear it as it might have been used for the proxy */ /* If a proxy-authorization header was used for the proxy, then we should make sure that it is not accidentally used for the document request - after we have connected. So let's free and clear it here. */ - Curl_safefree(data->state.aptr.proxyuserpwd); + after we have connected. Let's thus free and clear it here. */ + curlx_safefree(data->req.proxyuserpwd); break; } } @@ -196,7 +187,7 @@ static void tunnel_free(struct Curl_cfilter *cf, curlx_dyn_free(&ts->rcvbuf); curlx_dyn_free(&ts->request_data); Curl_httpchunk_free(data, &ts->ch); - free(ts); + curlx_free(ts); cf->ctx = NULL; } } @@ -215,10 +206,9 @@ 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. */ - Curl_safefree(data->req.newurl); + /* 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. */ + curlx_safefree(data->req.newurl); result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1); if(result) @@ -248,7 +238,7 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf, struct h1_tunnel_state *ts, bool *done) { - char *buf = curlx_dyn_ptr(&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; @@ -269,7 +259,7 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf, DEBUGASSERT(blen >= nwritten); ts->nsent += nwritten; - Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten); + Curl_debug(data, CURLINFO_HEADER_OUT, (const char *)buf, nwritten); out: if(result) @@ -287,10 +277,8 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf, struct SingleRequest *k = &data->req; (void)cf; - if((checkprefix("WWW-Authenticate:", header) && - (401 == k->httpcode)) || - (checkprefix("Proxy-authenticate:", header) && - (407 == k->httpcode))) { + if((checkprefix("WWW-Authenticate:", header) && (401 == k->httpcode)) || + (checkprefix("Proxy-authenticate:", header) && (407 == k->httpcode))) { bool proxy = (k->httpcode == 407); char *auth = Curl_copy_header_value(header); @@ -300,13 +288,13 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header); result = Curl_http_input_auth(data, proxy, auth); - free(auth); + curlx_free(auth); if(result) return result; } else if(checkprefix("Content-Length:", header)) { - if(k->httpcode/100 == 2) { + if(k->httpcode / 100 == 2) { /* A client MUST ignore any Content-Length or Transfer-Encoding header fields received in a successful response to CONNECT. "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ @@ -325,7 +313,7 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf, STRCONST("Connection:"), STRCONST("close"))) ts->close_connection = TRUE; else if(checkprefix("Transfer-Encoding:", header)) { - if(k->httpcode/100 == 2) { + if(k->httpcode / 100 == 2) { /* A client MUST ignore any Content-Length or Transfer-Encoding header fields received in a successful response to CONNECT. "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ @@ -351,22 +339,88 @@ 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; } +static CURLcode single_header(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts) +{ + CURLcode result = CURLE_OK; + const char *linep = curlx_dyn_ptr(&ts->rcvbuf); + size_t line_len = curlx_dyn_len(&ts->rcvbuf); /* bytes in this line */ + const struct SingleRequest *k = &data->req; + int writetype; + ts->headerlines++; + + /* output debug if that is requested */ + Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len); + + /* send the header to the callback */ + writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | + (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); + result = Curl_client_write(data, writetype, linep, line_len); + if(result) + return result; + + result = Curl_bump_headersize(data, line_len, TRUE); + if(result) + return result; + + /* Newlines are CRLF, so the CR is ignored as the line is not + really terminated until the LF comes. Treat a following CR + as end-of-headers as well.*/ + + if(ISNEWLINE(linep[0])) { + /* end of response-headers from the proxy */ + + if((407 == k->httpcode) && !data->state.authproblem) { + /* If we get a 407 response code with content length + when we have no auth problem, we must ignore the + whole response-body */ + ts->keepon = KEEPON_IGNORE; + + if(ts->cl) { + infof(data, "Ignore %" FMT_OFF_T " bytes of response-body", ts->cl); + } + else if(ts->chunked_encoding) { + infof(data, "Ignore chunked response-body"); + } + else { + /* without content-length or chunked encoding, we + cannot keep the connection alive since the close is + the end signal so we bail out at once instead */ + CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked"); + ts->keepon = KEEPON_DONE; + } + } + else { + ts->keepon = KEEPON_DONE; + } + + DEBUGASSERT(ts->keepon == KEEPON_IGNORE || + ts->keepon == KEEPON_DONE); + return result; + } + + result = on_resp_header(cf, data, ts, linep); + if(result) + return result; + + curlx_dyn_reset(&ts->rcvbuf); + return result; +} + static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts, bool *done) { CURLcode result = CURLE_OK; - struct SingleRequest *k = &data->req; - char *linep; - size_t line_len; - int error, writetype; + int error; #define SELECT_OK 0 #define SELECT_ERROR 1 @@ -385,8 +439,8 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, /* socket buffer drained, return */ return CURLE_OK; - if(Curl_pgrsUpdate(data)) - return CURLE_ABORTED_BY_CALLBACK; + if(!result) + result = Curl_pgrsUpdate(data); if(result) { ts->keepon = KEEPON_DONE; @@ -395,7 +449,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, if(!nread) { if(data->set.proxyauth && data->state.authproxy.avail && - data->state.aptr.proxyuserpwd) { + data->req.proxyuserpwd) { /* proxy auth was requested and there was proxy auth available, then deem this as "mere" proxy disconnect */ ts->close_connection = TRUE; @@ -411,17 +465,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, if(ts->keepon == KEEPON_IGNORE) { /* This means we are currently ignoring a response-body */ - - if(ts->cl) { - /* A Content-Length based body: simply count down the counter - and make sure to break out of the loop when we are done! */ - ts->cl--; - if(ts->cl <= 0) { - ts->keepon = KEEPON_DONE; - break; - } - } - else if(ts->chunked_encoding) { + if(ts->chunked_encoding) { /* chunked-encoded body, so we need to do the chunked dance properly to know when the end of the body is reached */ size_t consumed = 0; @@ -437,9 +481,43 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, ts->keepon = KEEPON_DONE; } } + else if(ts->cl) { + /* 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) { + ts->keepon = KEEPON_DONE; + break; + } + } continue; } + if(ts->maybe_folded) { + if(ISBLANK(byte)) { + Curl_http_to_fold(&ts->rcvbuf); + ts->leading_unfold = TRUE; + } + else { + result = single_header(cf, data, ts); + if(result) + return result; + /* now handle the new byte */ + } + ts->maybe_folded = FALSE; + } + + if(ts->leading_unfold) { + if(ISBLANK(byte)) + /* skip a bit brother */ + continue; + /* non-blank, insert a space then continue the unfolding */ + if(curlx_dyn_addn(&ts->rcvbuf, " ", 1)) { + failf(data, "CONNECT response too large"); + return CURLE_RECV_ERROR; + } + ts->leading_unfold = FALSE; + } if(curlx_dyn_addn(&ts->rcvbuf, &byte, 1)) { failf(data, "CONNECT response too large"); return CURLE_RECV_ERROR; @@ -448,73 +526,25 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, /* if this is not the end of a header line then continue */ if(byte != 0x0a) continue; - - ts->headerlines++; - linep = curlx_dyn_ptr(&ts->rcvbuf); - line_len = curlx_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */ - - /* output debug if that is requested */ - Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len); - - /* send the header to the callback */ - writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | - (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); - result = Curl_client_write(data, writetype, linep, line_len); - if(result) - return result; - - result = Curl_bump_headersize(data, line_len, TRUE); - if(result) - return result; - - /* Newlines are CRLF, so the CR is ignored as the line is not - really terminated until the LF comes. Treat a following CR - as end-of-headers as well.*/ - - if(('\r' == linep[0]) || - ('\n' == linep[0])) { - /* end of response-headers from the proxy */ - - if((407 == k->httpcode) && !data->state.authproblem) { - /* If we get a 407 response code with content length - when we have no auth problem, we must ignore the - whole response-body */ - ts->keepon = KEEPON_IGNORE; - - if(ts->cl) { - infof(data, "Ignore %" FMT_OFF_T " bytes of response-body", ts->cl); - } - else if(ts->chunked_encoding) { - infof(data, "Ignore chunked response-body"); - } - else { - /* without content-length or chunked encoding, we - cannot keep the connection alive since the close is - the end signal so we bail out at once instead */ - CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked"); - ts->keepon = KEEPON_DONE; - } + else { + const char *linep = curlx_dyn_ptr(&ts->rcvbuf); + size_t hlen = curlx_dyn_len(&ts->rcvbuf); + if(hlen && ISNEWLINE(linep[0])) { + /* end of headers */ + result = single_header(cf, data, ts); + if(result) + return result; } - else { - ts->keepon = KEEPON_DONE; - } - - DEBUGASSERT(ts->keepon == KEEPON_IGNORE - || ts->keepon == KEEPON_DONE); - continue; + else + ts->maybe_folded = TRUE; } - result = on_resp_header(cf, data, ts, linep); - if(result) - return result; - - curlx_dyn_reset(&ts->rcvbuf); } /* while there is buffer left and loop is requested */ if(error) result = CURLE_RECV_ERROR; *done = (ts->keepon == KEEPON_DONE); - if(!result && *done && data->info.httpproxycode/100 != 2) { + if(!result && *done && data->info.httpproxycode / 100 != 2) { /* Deal with the possibly already received authenticate headers. 'newurl' is set to a new URL if we must loop. */ result = Curl_http_auth_act(data); @@ -536,10 +566,8 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */ do { - timediff_t check; - check = Curl_timeleft(data, NULL, TRUE); - if(check <= 0) { + if(Curl_timeleft_ms(data) < 0) { failf(data, "Proxy CONNECT aborted due to timeout"); result = CURLE_OPERATION_TIMEDOUT; goto out; @@ -568,10 +596,10 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, /* read what is there */ CURL_TRC_CF(data, cf, "CONNECT receive"); result = recv_CONNECT_resp(cf, data, ts, &done); - if(Curl_pgrsUpdate(data)) { - result = CURLE_ABORTED_BY_CALLBACK; - goto out; - } + if(result) + CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d", result); + if(!result) + result = Curl_pgrsUpdate(data); /* error or not complete yet. return for more multi-multi */ if(result || !done) goto out; @@ -596,7 +624,6 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "CONNECT need to close+open"); infof(data, "Connect me again please"); Curl_conn_cf_close(cf, data); - connkeep(conn, "HTTP proxy CONNECT"); result = Curl_conn_cf_connect(cf->next, data, &done); goto out; } @@ -614,14 +641,12 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, } while(data->req.newurl); DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE); - if(data->info.httpproxycode/100 != 2) { + if(data->info.httpproxycode / 100 != 2) { /* a non-2xx response and we have no next URL to try. */ - Curl_safefree(data->req.newurl); - /* failure, close this connection to avoid reuse */ - streamclose(conn, "proxy CONNECT failure"); + curlx_safefree(data->req.newurl); h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); - return CURLE_RECV_ERROR; + return CURLE_COULDNT_CONNECT; } /* 2xx response, SUCCESS! */ h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); @@ -665,7 +690,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, result = H1_CONNECT(cf, data, ts); if(result) goto out; - Curl_safefree(data->state.aptr.proxyuserpwd); + curlx_safefree(data->req.proxyuserpwd); out: *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx); @@ -674,8 +699,7 @@ out: /* The real request will follow the CONNECT, reset request partially */ Curl_req_soft_reset(&data->req, data); Curl_client_reset(data); - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); + Curl_pgrsReset(data); tunnel_free(cf, data); } @@ -730,10 +754,9 @@ static void cf_h1_proxy_close(struct Curl_cfilter *cf, } } - struct Curl_cftype Curl_cft_h1_proxy = { "H1-PROXY", - CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, + CF_TYPE_IP_CONNECT | CF_TYPE_PROXY, 0, cf_h1_proxy_destroy, cf_h1_proxy_connect, @@ -762,4 +785,4 @@ CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, return result; } -#endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */ +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ diff --git a/lib/cf-h1-proxy.h b/lib/cf-h1-proxy.h index ac5bed0b2b..6544ec58d0 100644 --- a/lib/cf-h1-proxy.h +++ b/lib/cf-h1-proxy.h @@ -23,17 +23,15 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #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; - #endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ #endif /* HEADER_CURL_H1_PROXY_H */ diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index d67bbd55ad..4dd4f4ab35 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -21,13 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) && \ defined(USE_NGHTTP2) #include + #include "urldata.h" #include "url.h" #include "cfilters.h" @@ -36,7 +36,6 @@ #include "bufq.h" #include "curlx/dynbuf.h" #include "dynhds.h" -#include "http1.h" #include "http2.h" #include "http_proxy.h" #include "multiif.h" @@ -44,17 +43,12 @@ #include "select.h" #include "cf-h2-proxy.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define PROXY_H2_CHUNK_SIZE (16*1024) +#define PROXY_H2_CHUNK_SIZE (16 * 1024) #define PROXY_HTTP2_HUGE_WINDOW_SIZE (100 * 1024 * 1024) #define H2_TUNNEL_WINDOW_SIZE (10 * 1024 * 1024) -#define PROXY_H2_NW_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE) +#define PROXY_H2_NW_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE) #define PROXY_H2_NW_SEND_CHUNKS 1 #define H2_TUNNEL_RECV_CHUNKS (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE) @@ -62,11 +56,11 @@ typedef enum { - H2_TUNNEL_INIT, /* init/default/no tunnel state */ - H2_TUNNEL_CONNECT, /* CONNECT request is being send */ - H2_TUNNEL_RESPONSE, /* CONNECT response received completely */ - H2_TUNNEL_ESTABLISHED, - H2_TUNNEL_FAILED + H2_TUNNEL_INIT, /* init/default/no tunnel state */ + H2_TUNNEL_CONNECT, /* CONNECT request is being send */ + H2_TUNNEL_RESPONSE, /* CONNECT response received completely */ + H2_TUNNEL_ESTABLISHED, + H2_TUNNEL_FAILED } h2_tunnel_state; struct tunnel_stream { @@ -86,9 +80,8 @@ static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, struct tunnel_stream *ts) { const char *hostname; - int port; + uint16_t port; bool ipv6_ip; - CURLcode result; ts->state = H2_TUNNEL_INIT; ts->stream_id = -1; @@ -96,13 +89,11 @@ static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, BUFQ_OPT_SOFT_LIMIT); Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); - result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); - if(result) - return result; + Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); - ts->authority = /* host:port with IPv6 support */ - aprintf("%s%s%s:%d", ipv6_ip ? "[":"", hostname, - ipv6_ip ? "]" : "", port); + /* host:port with IPv6 support */ + ts->authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, + ipv6_ip ? "]" : "", port); if(!ts->authority) return CURLE_OUT_OF_MEMORY; @@ -114,7 +105,7 @@ static void tunnel_stream_clear(struct tunnel_stream *ts) Curl_http_resp_free(ts->resp); Curl_bufq_free(&ts->recvbuf); Curl_bufq_free(&ts->sendbuf); - Curl_safefree(ts->authority); + curlx_safefree(ts->authority); memset(ts, 0, sizeof(*ts)); ts->state = H2_TUNNEL_INIT; } @@ -166,8 +157,8 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf, ts->state = new_state; /* If a proxy-authorization header was used for the proxy, then we should make sure that it is not accidentally used for the document request - after we have connected. So let's free and clear it here. */ - Curl_safefree(data->state.aptr.proxyuserpwd); + after we have connected. Let's thus free and clear it here. */ + curlx_safefree(data->req.proxyuserpwd); break; } } @@ -191,8 +182,7 @@ struct cf_h2_proxy_ctx { /* How to access `call_data` from a cf_h2 filter */ #undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) \ - ((struct cf_h2_proxy_ctx *)(cf)->ctx)->call_data +#define CF_CTX_CALL_DATA(cf) ((struct cf_h2_proxy_ctx *)(cf)->ctx)->call_data static void cf_h2_proxy_ctx_clear(struct cf_h2_proxy_ctx *ctx) { @@ -212,7 +202,7 @@ static void cf_h2_proxy_ctx_free(struct cf_h2_proxy_ctx *ctx) { if(ctx) { cf_h2_proxy_ctx_clear(ctx); - free(ctx); + curlx_free(ctx); } } @@ -228,7 +218,7 @@ static void drain_tunnel(struct Curl_cfilter *cf, } static CURLcode proxy_h2_nw_out_writer(void *writer_ctx, - const unsigned char *buf, size_t buflen, + const uint8_t *buf, size_t buflen, size_t *pnwritten) { struct Curl_cfilter *cf = writer_ctx; @@ -236,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, (const char *)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; @@ -250,8 +239,8 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf, { struct cf_h2_proxy_ctx *ctx = cf->ctx; nghttp2_option *o; - nghttp2_mem mem = {NULL, Curl_nghttp2_malloc, Curl_nghttp2_free, - Curl_nghttp2_calloc, Curl_nghttp2_realloc}; + nghttp2_mem mem = { NULL, Curl_nghttp2_malloc, Curl_nghttp2_free, + Curl_nghttp2_calloc, Curl_nghttp2_realloc }; int rc = nghttp2_option_new(&o); if(rc) @@ -269,114 +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, "Couldn't 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, "Couldn't 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) && @@ -390,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; @@ -413,27 +293,30 @@ static CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf, * This function returns 0 if it succeeds, or -1 and error code will * be assigned to *err. */ -static int proxy_h2_process_pending_input(struct Curl_cfilter *cf, - struct Curl_easy *data, - CURLcode *err) +static CURLcode proxy_h2_process_pending_input(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_h2_proxy_ctx *ctx = cf->ctx; const unsigned char *buf; - size_t blen; + size_t blen, nread; ssize_t rv; while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) { rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen); CURL_TRC_CF(data, cf, "[0] %zu bytes to nghttp2 -> %zd", blen, rv); - if(rv < 0) { + if(!curlx_sztouz(rv, &nread)) { failf(data, "process_pending_input: nghttp2_session_mem_recv() returned " "%zd:%s", rv, nghttp2_strerror((int)rv)); - *err = CURLE_RECV_ERROR; - return -1; + return CURLE_RECV_ERROR; } - Curl_bufq_skip(&ctx->inbufq, (size_t)rv); + else if(!nread) { + /* nghttp2 does not want to process more, but has no error. This + * probably cannot happen, but be safe. */ + break; + } + Curl_bufq_skip(&ctx->inbufq, nread); if(Curl_bufq_is_empty(&ctx->inbufq)) { CURL_TRC_CF(data, cf, "[0] all data in connection buffer processed"); break; @@ -443,8 +326,7 @@ static int proxy_h2_process_pending_input(struct Curl_cfilter *cf, "in connection buffer", Curl_bufq_len(&ctx->inbufq)); } } - - return 0; + return CURLE_OK; } static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, @@ -454,18 +336,19 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; size_t nread; - /* Process network input buffer fist */ + /* Process network input buffer first */ if(!Curl_bufq_is_empty(&ctx->inbufq)) { CURL_TRC_CF(data, cf, "[0] process %zu bytes in connection buffer", Curl_bufq_len(&ctx->inbufq)); - if(proxy_h2_process_pending_input(cf, data, &result) < 0) + result = proxy_h2_process_pending_input(cf, data); + if(result) return result; } /* Receive data from the "lower" filters, e.g. network until * it is time to stop or we have enough data for this stream */ - while(!ctx->conn_closed && /* not closed the connection */ - !ctx->tunnel.closed && /* nor the tunnel */ + while(!ctx->conn_closed && /* not closed the connection */ + !ctx->tunnel.closed && /* nor the tunnel */ Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */ !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) { @@ -484,14 +367,11 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, break; } - if(proxy_h2_process_pending_input(cf, data, &result)) + result = proxy_h2_process_pending_input(cf, data); + if(result) return result; } - if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { - connclose(cf->conn, "GOAWAY received"); - } - return CURLE_OK; } @@ -541,81 +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 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 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 msnprintf(buffer, blen, - "FRAME[PRIORITY, len=%d, flags=%d]", - (int)frame->hd.length, frame->hd.flags); - } - case NGHTTP2_RST_STREAM: { - return 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 msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]"); - } - return msnprintf(buffer, blen, - "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); - } - case NGHTTP2_PUSH_PROMISE: - return 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 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 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 msnprintf(buffer, blen, - "FRAME[WINDOW_UPDATE, incr=%d]", - frame->window_update.window_size_increment); - } - default: - return 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) @@ -625,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, @@ -647,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); + 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 */ @@ -667,7 +477,7 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session, * window and *assume* that we treat this like a WINDOW_UPDATE. Some * servers send an explicit WINDOW_UPDATE, but not all seem to do that. * To be safe, we UNHOLD a stream in order not to stall. */ - if(CURL_WANT_SEND(data)) { + if(CURL_REQ_WANT_SEND(data)) { drain_tunnel(cf, data, &ctx->tunnel); } break; @@ -702,10 +512,14 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session, } break; case NGHTTP2_WINDOW_UPDATE: - if(CURL_WANT_SEND(data)) { + if(CURL_REQ_WANT_SEND(data)) { drain_tunnel(cf, data, &ctx->tunnel); } break; + case NGHTTP2_RST_STREAM: + if(frame->rst_stream.error_code) + ctx->tunnel.reset = TRUE; + break; default: break; } @@ -726,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) { @@ -795,7 +608,6 @@ static ssize_t tunnel_send_callback(nghttp2_session *session, size_t nread; (void)source; - (void)data; (void)ctx; if(!stream_id) @@ -846,7 +658,7 @@ static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, #endif } /* tunnel.recbuf has soft limit, any success MUST add all data */ - DEBUGASSERT((size_t)nwritten == len); + DEBUGASSERT(nwritten == len); return 0; } @@ -859,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; @@ -868,19 +679,22 @@ 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; } -static CURLcode proxy_h2_submit(int32_t *pstream_id, - struct Curl_cfilter *cf, - struct Curl_easy *data, - nghttp2_session *h2, - struct httpreq *req, - const nghttp2_priority_spec *pri_spec, - void *stream_user_data, - nghttp2_data_source_read_callback read_callback, - void *read_ctx) +static CURLcode proxy_h2_submit( + int32_t *pstream_id, + struct Curl_cfilter *cf, + struct Curl_easy *data, + nghttp2_session *h2, + struct httpreq *req, + const nghttp2_priority_spec *pri_spec, + void *stream_user_data, + nghttp2_data_source_read_callback read_callback, + void *read_ctx) { struct dynhds h2_headers; nghttp2_nv *nva = NULL; @@ -922,7 +736,7 @@ static CURLcode proxy_h2_submit(int32_t *pstream_id, result = CURLE_OK; out: - free(nva); + curlx_free(nva); Curl_dynhds_free(&h2_headers); *pstream_id = stream_id; return result; @@ -969,7 +783,7 @@ static CURLcode inspect_response(struct Curl_cfilter *cf, (void)cf; DEBUGASSERT(ts->resp); - if(ts->resp->status/100 == 2) { + if(ts->resp->status / 100 == 2) { infof(data, "CONNECT tunnel established, response %d", ts->resp->status); h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data); return CURLE_OK; @@ -991,14 +805,14 @@ static CURLcode inspect_response(struct Curl_cfilter *cf, return result; if(data->req.newurl) { /* Indicator that we should try again */ - Curl_safefree(data->req.newurl); + curlx_safefree(data->req.newurl); h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data); return CURLE_OK; } } /* Seems to have failed */ - return CURLE_RECV_ERROR; + return CURLE_COULDNT_CONNECT; } static CURLcode H2_CONNECT(struct Curl_cfilter *cf, @@ -1065,6 +879,89 @@ out: 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) @@ -1072,7 +969,6 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, struct cf_h2_proxy_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; struct cf_call_data save; - timediff_t check; struct tunnel_stream *ts = &ctx->tunnel; if(cf->connected) { @@ -1097,8 +993,7 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, } DEBUGASSERT(ts->authority); - check = Curl_timeleft(data, NULL, TRUE); - if(check <= 0) { + if(Curl_timeleft_ms(data) < 0) { failf(data, "Proxy CONNECT aborted due to timeout"); result = CURLE_OPERATION_TIMEDOUT; goto out; @@ -1229,7 +1124,7 @@ static CURLcode cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, c_exhaust = !nghttp2_session_get_remote_window_size(ctx->h2); s_exhaust = ctx->tunnel.stream_id >= 0 && !nghttp2_session_get_stream_remote_window_size( - ctx->h2, ctx->tunnel.stream_id); + ctx->h2, ctx->tunnel.stream_id); want_recv = (want_recv || c_exhaust || s_exhaust); want_send = (!s_exhaust && want_send) || (!c_exhaust && nghttp2_session_want_write(ctx->h2)) || @@ -1263,20 +1158,10 @@ 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); - connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ - 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 %u reset by %s (error 0x%x %s)", + ctx->tunnel.stream_id, ctx->tunnel.reset ? "server" : "curl", + ctx->tunnel.error, nghttp2_http2_strerror(ctx->tunnel.error)); return CURLE_RECV_ERROR; } @@ -1361,7 +1246,7 @@ out: static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) { struct cf_h2_proxy_ctx *ctx = cf->ctx; @@ -1423,7 +1308,7 @@ out: "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", ctx->tunnel.stream_id, len, result, *pnwritten, nghttp2_session_get_stream_remote_window_size( - ctx->h2, ctx->tunnel.stream_id), + ctx->h2, ctx->tunnel.stream_id), nghttp2_session_get_remote_window_size(ctx->h2), Curl_bufq_len(&ctx->tunnel.sendbuf), Curl_bufq_len(&ctx->outbufq)); @@ -1484,7 +1369,7 @@ static bool proxy_h2_connisalive(struct Curl_cfilter *cf, *input_pending = FALSE; result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); if(!result) { - if(proxy_h2_process_pending_input(cf, data, &result) < 0) + if(proxy_h2_process_pending_input(cf, data)) /* immediate error, considered dead */ alive = FALSE; else { @@ -1575,7 +1460,7 @@ static CURLcode cf_h2_proxy_cntrl(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_h2_proxy = { "H2-PROXY", - CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, + CF_TYPE_IP_CONNECT | CF_TYPE_PROXY, CURL_LOG_LVL_NONE, cf_h2_proxy_destroy, cf_h2_proxy_connect, @@ -1599,7 +1484,7 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, CURLcode result = CURLE_OUT_OF_MEMORY; (void)data; - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) goto out; diff --git a/lib/cf-h2-proxy.h b/lib/cf-h2-proxy.h index 1b3f85aaaf..318ce1973f 100644 --- a/lib/cf-h2-proxy.h +++ b/lib/cf-h2-proxy.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) diff --git a/lib/cf-haproxy.c b/lib/cf-haproxy.c index 2d66efea90..46d0541055 100644 --- a/lib/cf-haproxy.c +++ b/lib/cf-haproxy.c @@ -21,29 +21,21 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifndef CURL_DISABLE_PROXY -#include #include "urldata.h" #include "cfilters.h" #include "cf-haproxy.h" #include "curl_trc.h" -#include "multiif.h" #include "select.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - typedef enum { - HAPROXY_INIT, /* init/default/no tunnel state */ - HAPROXY_SEND, /* data_out being sent */ - HAPROXY_DONE /* all work done */ + HAPROXY_INIT, /* init/default/no tunnel state */ + HAPROXY_SEND, /* data_out being sent */ + HAPROXY_DONE /* all work done */ } haproxy_state; struct cf_haproxy_ctx { @@ -62,11 +54,11 @@ static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx) { if(ctx) { curlx_dyn_free(&ctx->data_out); - free(ctx); + curlx_free(ctx); } } -static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf, +static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_haproxy_ctx *ctx = cf->ctx; @@ -134,7 +126,7 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf, if(len > 0) { size_t nwritten; result = Curl_conn_cf_send(cf->next, data, - curlx_dyn_ptr(&ctx->data_out), len, FALSE, + curlx_dyn_uptr(&ctx->data_out), len, FALSE, &nwritten); if(result) { if(result != CURLE_AGAIN) @@ -164,7 +156,6 @@ out: 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); } @@ -194,7 +185,7 @@ static CURLcode cf_haproxy_adjust_pollset(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_haproxy = { "HAPROXY", - CF_TYPE_PROXY, + CF_TYPE_PROXY | CF_TYPE_SETUP, 0, cf_haproxy_destroy, cf_haproxy_connect, @@ -218,7 +209,7 @@ static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf, CURLcode result; (void)data; - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; diff --git a/lib/cf-haproxy.h b/lib/cf-haproxy.h index 9190dd5b57..6d67597cd7 100644 --- a/lib/cf-haproxy.h +++ b/lib/cf-haproxy.h @@ -23,8 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #include "urldata.h" #ifndef CURL_DISABLE_PROXY diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index b4a4605295..5434cd7ed6 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -21,29 +21,26 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifndef CURL_DISABLE_HTTP #include "urldata.h" -#include #include "curl_trc.h" #include "cfilters.h" +#include "cf-dns.h" #include "connect.h" #include "hostip.h" +#include "httpsrr.h" #include "multiif.h" #include "cf-https-connect.h" #include "http2.h" +#include "progress.h" #include "select.h" #include "vquic/vquic.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - typedef enum { + CF_HC_RESOLV, CF_HC_INIT, CF_HC_CONNECT, CF_HC_SUCCESS, @@ -56,24 +53,22 @@ struct cf_hc_baller { CURLcode result; struct curltime started; int reply_ms; - unsigned char transport; + uint8_t transport; enum alpnid alpn_id; BIT(shutdown); }; -static void cf_hc_baller_reset(struct cf_hc_baller *b, - struct Curl_easy *data) +static void cf_hc_baller_discard(struct cf_hc_baller *b, + struct Curl_easy *data) { if(b->cf) { Curl_conn_cf_close(b->cf, data); Curl_conn_cf_discard_chain(&b->cf, data); b->cf = NULL; } - b->result = CURLE_OK; - b->reply_ms = -1; } -static bool cf_hc_baller_is_active(struct cf_hc_baller *b) +static bool cf_hc_baller_is_connecting(struct cf_hc_baller *b) { return b->cf && !b->result; } @@ -117,18 +112,46 @@ struct cf_hc_ctx { cf_hc_state state; struct curltime started; /* when connect started */ CURLcode result; /* overall result */ + CURLcode check_h3_result; struct cf_hc_baller ballers[2]; size_t baller_count; timediff_t soft_eyeballs_timeout_ms; timediff_t hard_eyeballs_timeout_ms; + uint8_t def_transport; + BIT(httpsrr_resolved); + BIT(checked_h3); + BIT(ballers_complete); }; +static void cf_hc_ctx_close(struct Curl_easy *data, + struct cf_hc_ctx *ctx) +{ + if(ctx) { + size_t i; + for(i = 0; i < ctx->baller_count; ++i) + cf_hc_baller_discard(&ctx->ballers[i], data); + } +} + +static void cf_hc_ctx_destroy(struct Curl_easy *data, + struct cf_hc_ctx *ctx) +{ + if(ctx) { + cf_hc_ctx_close(data, ctx); + curlx_free(ctx); + } +} + static void cf_hc_baller_assign(struct cf_hc_baller *b, enum alpnid alpn_id, - unsigned char def_transport) + uint8_t def_transport) { b->alpn_id = alpn_id; b->transport = def_transport; + b->cf = NULL; + b->result = CURLE_OK; + b->reply_ms = -1; + b->shutdown = FALSE; switch(b->alpn_id) { case ALPN_h3: b->name = "h3"; @@ -140,6 +163,9 @@ static void cf_hc_baller_assign(struct cf_hc_baller *b, case ALPN_h1: b->name = "h1"; break; + case ALPN_none: + b->name = "no-alpn"; + break; default: b->result = CURLE_FAILED_INIT; break; @@ -148,24 +174,14 @@ static void cf_hc_baller_assign(struct cf_hc_baller *b, static void cf_hc_baller_init(struct cf_hc_baller *b, struct Curl_cfilter *cf, - struct Curl_easy *data, - int transport) + struct Curl_easy *data) { struct Curl_cfilter *save = cf->next; cf->next = NULL; - b->started = curlx_now(); - switch(b->alpn_id) { - case ALPN_h3: - transport = TRNSPRT_QUIC; - break; - default: - break; - } - - if(!b->result) - b->result = Curl_cf_setup_insert_after(cf, data, transport, - CURL_CF_SSL_ENABLE); + b->started = *Curl_pgrs_now(data); + b->result = Curl_cf_setup_insert_after(cf, data, b->transport, + CURL_CF_SSL_ENABLE); b->cf = cf->next; cf->next = save; } @@ -184,56 +200,39 @@ static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b, return b->result; } -static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_hc_ctx *ctx = cf->ctx; - size_t i; - - if(ctx) { - for(i = 0; i < ctx->baller_count; ++i) - cf_hc_baller_reset(&ctx->ballers[i], data); - ctx->state = CF_HC_INIT; - ctx->result = CURLE_OK; - ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout; - ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 4; - } -} - static CURLcode baller_connected(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_hc_baller *winner) { struct cf_hc_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - int reply_ms; - size_t i; - DEBUGASSERT(winner->cf); - for(i = 0; i < ctx->baller_count; ++i) - if(winner != &ctx->ballers[i]) - cf_hc_baller_reset(&ctx->ballers[i], data); + /* Make the winner's connection filter out own sub-filter, check, move, + * close all remaining. */ + if(cf->next) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + if(!winner->cf) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } - reply_ms = cf_hc_baller_reply_ms(winner, data); - if(reply_ms >= 0) - CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", - winner->name, (int)curlx_timediff(curlx_now(), - winner->started), reply_ms); - else - CURL_TRC_CF(data, cf, "deferred handshake %s: %dms", - winner->name, (int)curlx_timediff(curlx_now(), - winner->started)); - - /* install the winning filter below this one. */ cf->next = winner->cf; winner->cf = NULL; + ctx->state = CF_HC_SUCCESS; + cf->connected = TRUE; + + cf_hc_ctx_close(data, ctx); + /* ballers may have failf()'d, the winner resets it, so our + * errorbuf is clean again. */ + Curl_reset_fail(data); #ifdef USE_NGHTTP2 { - /* Using nghttp2, we add the filter "below" us, so when the conn - * closes, we tear it down for a fresh reconnect */ + /* For a negotiated HTTP/2 connection insert the h2 filter. */ const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data); if(alpn && !strcmp("h2", alpn)) { - result = Curl_http2_switch_at(cf, data); + CURLcode result = Curl_http2_switch_at(cf, data); if(result) { ctx->state = CF_HC_FAILURE; ctx->result = result; @@ -242,65 +241,243 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, } } #endif - - ctx->state = CF_HC_SUCCESS; - cf->connected = TRUE; - return result; + return CURLE_OK; } - -static bool time_to_start_next(struct Curl_cfilter *cf, - struct Curl_easy *data, - size_t idx, struct curltime now) +static bool time_to_start_baller2(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_hc_ctx *ctx = cf->ctx; timediff_t elapsed_ms; - size_t i; - if(idx >= ctx->baller_count) + if(ctx->baller_count < 2) return FALSE; - if(cf_hc_baller_has_started(&ctx->ballers[idx])) + else if(cf_hc_baller_has_started(&ctx->ballers[1])) return FALSE; - for(i = 0; i < idx; i++) { - if(!ctx->ballers[i].result) - break; - } - if(i == idx) { - CURL_TRC_CF(data, cf, "all previous attempts failed, starting %s", - ctx->ballers[idx].name); + else if(ctx->ballers[0].result) { + CURL_TRC_CF(data, cf, "%s baller failed, starting %s", + ctx->ballers[0].name, ctx->ballers[1].name); return TRUE; } - elapsed_ms = curlx_timediff(now, ctx->started); + + elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->started); if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) { - CURL_TRC_CF(data, cf, "hard timeout of %" FMT_TIMEDIFF_T "ms reached, " - "starting %s", - ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name); + CURL_TRC_CF(data, cf, "%s inconclusive after %" FMT_TIMEDIFF_T ", " + "starting %s", ctx->ballers[0].name, + ctx->hard_eyeballs_timeout_ms, ctx->ballers[1].name); return TRUE; } - - if((idx > 0) && (elapsed_ms >= ctx->soft_eyeballs_timeout_ms)) { - if(cf_hc_baller_reply_ms(&ctx->ballers[idx - 1], data) < 0) { - CURL_TRC_CF(data, cf, "soft timeout of %" FMT_TIMEDIFF_T "ms reached, " - "%s has not seen any data, starting %s", - ctx->soft_eyeballs_timeout_ms, - ctx->ballers[idx - 1].name, ctx->ballers[idx].name); + else if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) { + if(cf_hc_baller_reply_ms(&ctx->ballers[0], data) < 0) { + CURL_TRC_CF(data, cf, "%s has not seen any data after %" + FMT_TIMEDIFF_T "ms, starting %s", + ctx->ballers[0].name, ctx->soft_eyeballs_timeout_ms, + ctx->ballers[1].name); return TRUE; } - /* set the effective hard timeout again */ - Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms, - EXPIRE_ALPN_EYEBALLS); } return FALSE; } +static bool cf_hc_may_h3(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + if(!ctx->checked_h3) { + ctx->check_h3_result = + Curl_conn_may_http3(data, cf->conn, ctx->def_transport); + ctx->checked_h3 = TRUE; + } + return !ctx->check_h3_result; +} + +static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf, + struct Curl_easy *data, + enum alpnid not_this_one) +{ +#ifdef USE_HTTPSRR + /* Is there an HTTPSRR use its ALPNs here. + * We are here after having selected a connection to a host+port and + * can no longer change that. Any HTTPSRR advice for other hosts and ports + * we need to ignore. */ + const struct Curl_https_rrinfo *rr; + size_t i; + + /* Do we have HTTPS-RR information? */ + rr = Curl_conn_dns_get_https(data, cf->sockindex); + + if(rr && !rr->no_def_alpn && /* ALPNs are defaults */ + (!rr->target || /* for same host */ + !rr->target[0] || + (rr->target[0] == '.' && + !rr->target[1])) && + (!rr->port_set || /* for same port */ + rr->port == cf->conn->remote_port)) { + for(i = 0; i < CURL_ARRAYSIZE(rr->alpns); ++i) { + enum alpnid alpn_rr = (enum alpnid)rr->alpns[i]; + if(alpn_rr == not_this_one) /* don't want this one */ + continue; + switch(alpn_rr) { + case ALPN_h3: + if((data->state.http_neg.allowed & CURL_HTTP_V3x) && + cf_hc_may_h3(cf, data)) { + return alpn_rr; + } + break; + case ALPN_h2: + if(data->state.http_neg.allowed & CURL_HTTP_V2x) { + return alpn_rr; + } + break; + case ALPN_h1: + if(data->state.http_neg.allowed & CURL_HTTP_V1x) { + return alpn_rr; + } + break; + default: /* ignore */ + break; + } + } + } +#else + (void)cf; + (void)data; + (void)not_this_one; +#endif + return ALPN_none; +} + +static enum alpnid cf_hc_get_pref_alpn(struct Curl_cfilter *cf, + struct Curl_easy *data, + enum alpnid not_this_one) +{ + if((data->state.http_neg.preferred & data->state.http_neg.allowed)) { + switch(data->state.http_neg.preferred) { + case CURL_HTTP_V3x: + if(cf_hc_may_h3(cf, data) && (ALPN_h3 != not_this_one)) + return ALPN_h3; + break; + case CURL_HTTP_V2x: + if(ALPN_h2 != not_this_one) + return ALPN_h2; + break; + case CURL_HTTP_V1x: + /* If we are trying h2 already, h1 is already used as fallback */ + if((ALPN_h1 != not_this_one) && (ALPN_h2 != not_this_one)) + return ALPN_h1; + break; + default: + break; + } + } + return ALPN_none; +} + +static enum alpnid cf_hc_get_first_alpn(struct Curl_cfilter *cf, + struct Curl_easy *data, + http_majors choices, + enum alpnid not_this_one) +{ + if((ALPN_h3 != not_this_one) && (choices & CURL_HTTP_V3x) && + cf_hc_may_h3(cf, data)) { + return ALPN_h3; + } + if((ALPN_h2 != not_this_one) && (choices & CURL_HTTP_V2x)) { + return ALPN_h2; + } + /* If we are trying h2 already, h1 is already used as fallback */ + if((ALPN_h1 != not_this_one) && (ALPN_h2 != not_this_one) && + (choices & CURL_HTTP_V1x)) { + return ALPN_h1; + } + return ALPN_none; +} + +static CURLcode cf_hc_set_baller1(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + enum alpnid alpn1 = ALPN_none; + VERBOSE(const char *source = "HTTPS-RR"); + + DEBUGASSERT(cf->conn->bits.tls_enable_alpn); + + alpn1 = cf_hc_get_httpsrr_alpn(cf, data, ALPN_none); + if(alpn1 == ALPN_none) { + /* preference is configured and allowed, can we use it? */ + VERBOSE(source = "preferred version"); + alpn1 = cf_hc_get_pref_alpn(cf, data, ALPN_none); + } + if(alpn1 == ALPN_none) { + VERBOSE(source = "wanted versions"); + alpn1 = cf_hc_get_first_alpn(cf, data, + data->state.http_neg.wanted, + ALPN_none); + } + if(alpn1 == ALPN_none) { + VERBOSE(source = "allowed versions"); + alpn1 = cf_hc_get_first_alpn(cf, data, + data->state.http_neg.allowed, + ALPN_none); + } + + if(alpn1 == ALPN_none) { + /* None of the wanted/allowed HTTP versions could be chosen */ + if(ctx->check_h3_result) { + CURL_TRC_CF(data, cf, "unable to use HTTP/3"); + return ctx->check_h3_result; + } + CURL_TRC_CF(data, cf, "unable to select HTTP version"); + return CURLE_FAILED_INIT; + } + + cf_hc_baller_assign(&ctx->ballers[0], alpn1, ctx->def_transport); + ctx->baller_count = 1; + CURL_TRC_CF(data, cf, "1st attempt uses %s from %s", + ctx->ballers[0].name, source); + return CURLE_OK; +} + +static void cf_hc_set_baller2(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + enum alpnid alpn2 = ALPN_none, alpn1 = ctx->ballers[0].alpn_id; + VERBOSE(const char *source = "HTTPS-RR"); + + if(ctx->ballers_complete) + return; /* already done */ + if(!ctx->httpsrr_resolved) + return; /* HTTPS-RR pending */ + + alpn2 = cf_hc_get_httpsrr_alpn(cf, data, alpn1); + if(alpn2 == ALPN_none) { + /* preference is configured and allowed, can we use it? */ + VERBOSE(source = "preferred version"); + alpn2 = cf_hc_get_pref_alpn(cf, data, alpn1); + } + if(alpn2 == ALPN_none) { + VERBOSE(source = "wanted versions"); + alpn2 = cf_hc_get_first_alpn(cf, data, + data->state.http_neg.wanted, + alpn1); + } + + if(alpn2 != ALPN_none) { + cf_hc_baller_assign(&ctx->ballers[1], alpn2, ctx->def_transport); + ctx->baller_count = 2; + CURL_TRC_CF(data, cf, "2nd attempt uses %s from %s", + ctx->ballers[1].name, source); + } + ctx->ballers_complete = TRUE; +} + static CURLcode cf_hc_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { struct cf_hc_ctx *ctx = cf->ctx; - struct curltime now; CURLcode result = CURLE_OK; - size_t i, failed_ballers; if(cf->connected) { *done = TRUE; @@ -308,25 +485,41 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } *done = FALSE; - now = curlx_now(); + + if(!ctx->httpsrr_resolved) + ctx->httpsrr_resolved = Curl_conn_dns_resolved_https(data, cf->sockindex); + switch(ctx->state) { + case CF_HC_RESOLV: + /* Without any addressinfo, delay the start of balling. */ + if(!Curl_conn_dns_has_any_ai(data, cf->sockindex)) + return CURLE_OK; + ctx->state = CF_HC_INIT; + FALLTHROUGH(); + case CF_HC_INIT: DEBUGASSERT(!cf->next); - for(i = 0; i < ctx->baller_count; i++) - DEBUGASSERT(!ctx->ballers[i].cf); CURL_TRC_CF(data, cf, "connect, init"); - ctx->started = now; - cf_hc_baller_init(&ctx->ballers[0], cf, data, ctx->ballers[0].transport); - if(ctx->baller_count > 1) { + result = cf_hc_set_baller1(cf, data); + if(result) { + ctx->result = result; + ctx->state = CF_HC_FAILURE; + goto out; + } + cf_hc_set_baller2(cf, data); + ctx->started = *Curl_pgrs_now(data); + cf_hc_baller_init(&ctx->ballers[0], cf, data); + if((ctx->baller_count > 1) || !ctx->ballers_complete) { Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); - CURL_TRC_CF(data, cf, "set next attempt to start in %" FMT_TIMEDIFF_T - "ms", ctx->soft_eyeballs_timeout_ms); } ctx->state = CF_HC_CONNECT; FALLTHROUGH(); case CF_HC_CONNECT: - if(cf_hc_baller_is_active(&ctx->ballers[0])) { + if(!ctx->ballers_complete) + cf_hc_set_baller2(cf, data); + + if(cf_hc_baller_is_connecting(&ctx->ballers[0])) { result = cf_hc_baller_connect(&ctx->ballers[0], cf, data, done); if(!result && *done) { result = baller_connected(cf, data, &ctx->ballers[0]); @@ -334,12 +527,11 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } } - if(time_to_start_next(cf, data, 1, now)) { - cf_hc_baller_init(&ctx->ballers[1], cf, data, ctx->ballers[1].transport); + if(time_to_start_baller2(cf, data)) { + cf_hc_baller_init(&ctx->ballers[1], cf, data); } - if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) { - CURL_TRC_CF(data, cf, "connect, check %s", ctx->ballers[1].name); + if(cf_hc_baller_is_connecting(&ctx->ballers[1])) { result = cf_hc_baller_connect(&ctx->ballers[1], cf, data, done); if(!result && *done) { result = baller_connected(cf, data, &ctx->ballers[1]); @@ -347,21 +539,12 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } } - failed_ballers = 0; - for(i = 0; i < ctx->baller_count; i++) { - if(ctx->ballers[i].result) - ++failed_ballers; - } - - if(failed_ballers == ctx->baller_count) { + if(ctx->ballers[0].result && + (ctx->ballers[1].result || + (ctx->ballers_complete && (ctx->baller_count < 2)))) { /* all have failed. we give up */ CURL_TRC_CF(data, cf, "connect, all attempts failed"); - for(i = 0; i < ctx->baller_count; i++) { - if(ctx->ballers[i].result) { - result = ctx->ballers[i].result; - break; - } - } + ctx->result = result = ctx->ballers[0].result; ctx->state = CF_HC_FAILURE; goto out; } @@ -405,7 +588,7 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf, for(i = 0; i < ctx->baller_count; i++) { struct cf_hc_baller *b = &ctx->ballers[i]; bool bdone = FALSE; - if(!cf_hc_baller_is_active(b) || b->shutdown) + if(!cf_hc_baller_is_connecting(b) || b->shutdown) continue; b->result = b->cf->cft->do_shutdown(b->cf, data, &bdone); if(b->result || bdone) @@ -438,7 +621,7 @@ static CURLcode cf_hc_adjust_pollset(struct Curl_cfilter *cf, for(i = 0; (i < ctx->baller_count) && !result; i++) { struct cf_hc_baller *b = &ctx->ballers[i]; - if(!cf_hc_baller_is_active(b)) + if(!cf_hc_baller_is_connecting(b)) continue; result = Curl_conn_cf_adjust_pollset(b->cf, data, ps); } @@ -475,7 +658,7 @@ static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf, struct Curl_cfilter *cfb = ctx->ballers[i].cf; memset(&t, 0, sizeof(t)); if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) { - if((t.tv_sec || t.tv_usec) && curlx_timediff_us(t, tmax) > 0) + if((t.tv_sec || t.tv_usec) && curlx_ptimediff_us(&t, &tmax) > 0) tmax = t; } } @@ -541,7 +724,7 @@ out: static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) { CURL_TRC_CF(data, cf, "close"); - cf_hc_reset(cf, data); + cf_hc_ctx_close(data, cf->ctx); cf->connected = FALSE; if(cf->next) { @@ -554,15 +737,13 @@ 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); + cf_hc_ctx_destroy(data, ctx); } struct Curl_cftype Curl_cft_http_connect = { "HTTPS-CONNECT", - 0, + CF_TYPE_SETUP, CURL_LOG_LVL_NONE, cf_hc_destroy, cf_hc_connect, @@ -580,57 +761,40 @@ struct Curl_cftype Curl_cft_http_connect = { static CURLcode cf_hc_create(struct Curl_cfilter **pcf, struct Curl_easy *data, - enum alpnid *alpnids, size_t alpn_count, - unsigned char def_transport) + uint8_t def_transport) { struct Curl_cfilter *cf = NULL; struct cf_hc_ctx *ctx; CURLcode result = CURLE_OK; - size_t i; - DEBUGASSERT(alpnids); - DEBUGASSERT(alpn_count); - DEBUGASSERT(alpn_count <= CURL_ARRAYSIZE(ctx->ballers)); - if(!alpn_count || (alpn_count > CURL_ARRAYSIZE(ctx->ballers))) { - failf(data, "https-connect filter create with unsupported %zu ALPN ids", - alpn_count); - return CURLE_FAILED_INIT; - } - - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - for(i = 0; i < alpn_count; ++i) - cf_hc_baller_assign(&ctx->ballers[i], alpnids[i], def_transport); - for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i) - ctx->ballers[i].alpn_id = ALPN_none; - ctx->baller_count = alpn_count; + ctx->def_transport = def_transport; result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx); if(result) goto out; ctx = NULL; - cf_hc_reset(cf, data); out: *pcf = result ? NULL : cf; - free(ctx); + cf_hc_ctx_destroy(data, ctx); return result; } -static CURLcode cf_http_connect_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - enum alpnid *alpn_ids, size_t alpn_count, - unsigned char def_transport) +static CURLcode cf_hc_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + uint8_t def_transport) { struct Curl_cfilter *cf; CURLcode result = CURLE_OK; DEBUGASSERT(data); - result = cf_hc_create(&cf, data, alpn_ids, alpn_count, def_transport); + result = cf_hc_create(&cf, data, def_transport); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); @@ -638,113 +802,19 @@ out: return result; } -static bool cf_https_alpns_contain(enum alpnid id, - enum alpnid *list, size_t len) -{ - size_t i; - for(i = 0; i < len; ++i) { - if(id == list[i]) - return TRUE; - } - return FALSE; -} - CURLcode Curl_cf_https_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex) { - enum alpnid alpn_ids[2]; - size_t alpn_count = 0; CURLcode result = CURLE_OK; - struct Curl_cfilter cf_fake, *cf = NULL; - (void)sockindex; - /* we want to log for the filter before we create it, fake it. */ - memset(&cf_fake, 0, sizeof(cf_fake)); - cf_fake.cft = &Curl_cft_http_connect; - cf = &cf_fake; + DEBUGASSERT(conn->scheme->protocol == CURLPROTO_HTTPS); - if(conn->bits.tls_enable_alpn) { -#ifdef USE_HTTPSRR - /* Is there an HTTPSRR use its ALPNs here. - * We are here after having selected a connection to a host+port and - * can no longer change that. Any HTTPSRR advice for other hosts and ports - * we need to ignore. */ - struct Curl_dns_entry *dns = data->state.dns[sockindex]; - struct Curl_https_rrinfo *rr = dns ? dns->hinfo : NULL; - if(rr && !rr->no_def_alpn && /* ALPNs are defaults */ - (!rr->target || /* for same host */ - !rr->target[0] || - (rr->target[0] == '.' && - !rr->target[1])) && - (rr->port < 0 || /* for same port */ - rr->port == conn->remote_port)) { - size_t i; - for(i = 0; i < CURL_ARRAYSIZE(rr->alpns) && - alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) { - enum alpnid alpn = rr->alpns[i]; - if(cf_https_alpns_contain(alpn, alpn_ids, alpn_count)) - continue; - switch(alpn) { - case ALPN_h3: - if(Curl_conn_may_http3(data, conn, conn->transport_wanted)) - break; /* not possible */ - if(data->state.http_neg.allowed & CURL_HTTP_V3x) { - CURL_TRC_CF(data, cf, "adding h3 via HTTPS-RR"); - alpn_ids[alpn_count++] = alpn; - } - break; - case ALPN_h2: - if(data->state.http_neg.allowed & CURL_HTTP_V2x) { - CURL_TRC_CF(data, cf, "adding h2 via HTTPS-RR"); - alpn_ids[alpn_count++] = alpn; - } - break; - case ALPN_h1: - if(data->state.http_neg.allowed & CURL_HTTP_V1x) { - CURL_TRC_CF(data, cf, "adding h1 via HTTPS-RR"); - alpn_ids[alpn_count++] = alpn; - } - break; - default: /* ignore */ - break; - } - } - } -#endif + if((conn->scheme->protocol != CURLPROTO_HTTPS) || + !conn->bits.tls_enable_alpn) + goto out; - if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.wanted & CURL_HTTP_V3x) && - !cf_https_alpns_contain(ALPN_h3, alpn_ids, alpn_count)) { - result = Curl_conn_may_http3(data, conn, conn->transport_wanted); - if(!result) { - CURL_TRC_CF(data, cf, "adding wanted h3"); - alpn_ids[alpn_count++] = ALPN_h3; - } - else if(data->state.http_neg.wanted == CURL_HTTP_V3x) - goto out; /* only h3 allowed, not possible, error out */ - } - if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.wanted & CURL_HTTP_V2x) && - !cf_https_alpns_contain(ALPN_h2, alpn_ids, alpn_count)) { - CURL_TRC_CF(data, cf, "adding wanted h2"); - alpn_ids[alpn_count++] = ALPN_h2; - } - else if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.wanted & CURL_HTTP_V1x) && - !cf_https_alpns_contain(ALPN_h1, alpn_ids, alpn_count)) { - CURL_TRC_CF(data, cf, "adding wanted h1"); - alpn_ids[alpn_count++] = ALPN_h1; - } - } - - /* If we identified ALPNs to use, install our filter. Otherwise, - * install nothing, so our call will use a default connect setup. */ - if(alpn_count) { - result = cf_http_connect_add(data, conn, sockindex, - alpn_ids, alpn_count, - conn->transport_wanted); - } + result = cf_hc_add(data, conn, sockindex, conn->transport_wanted); out: return result; diff --git a/lib/cf-https-connect.h b/lib/cf-https-connect.h index df51b62479..3160c0382a 100644 --- a/lib/cf-https-connect.h +++ b/lib/cf-https-connect.h @@ -31,19 +31,12 @@ struct Curl_cfilter; struct Curl_easy; struct connectdata; struct Curl_cftype; -struct Curl_dns_entry; extern struct Curl_cftype Curl_cft_http_connect; -CURLcode Curl_cf_http_connect_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool try_h3, bool try_h21); - CURLcode Curl_cf_https_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex); - #endif /* !CURL_DISABLE_HTTP */ #endif /* HEADER_CURL_CF_HTTP_H */ diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index 5e4c20444e..072f9b2c44 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -21,15 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_NETINET_IN_H #include /* may need it */ #endif -#ifdef HAVE_SYS_UN_H -#include /* for sockaddr_un */ -#endif #ifdef HAVE_LINUX_TCP_H #include #elif defined(HAVE_NETINET_TCP_H) @@ -41,9 +37,6 @@ #ifdef HAVE_NETDB_H #include #endif -#ifdef HAVE_FCNTL_H -#include -#endif #ifdef HAVE_ARPA_INET_H #include #endif @@ -56,21 +49,18 @@ #include "urldata.h" #include "connect.h" #include "cfilters.h" +#include "cf-dns.h" #include "cf-ip-happy.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "multiif.h" #include "progress.h" #include "select.h" #include "vquic/vquic.h" /* for quic cfilters */ -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - struct transport_provider { - int transport; + uint8_t transport; cf_ip_connect_create *cf_create; }; @@ -91,7 +81,7 @@ struct transport_provider transport_providers[] = { #endif }; -static cf_ip_connect_create *get_cf_create(int transport) +static cf_ip_connect_create *get_cf_create(uint8_t transport) { size_t i; for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) { @@ -103,8 +93,10 @@ static cf_ip_connect_create *get_cf_create(int transport) #ifdef UNITTESTS /* used by unit2600.c */ -void Curl_debug_set_transport_provider(int transport, - cf_ip_connect_create *cf_create) +UNITTEST void Curl_debug_set_transport_provider( + uint8_t transport, cf_ip_connect_create *cf_create); +UNITTEST void Curl_debug_set_transport_provider( + uint8_t transport, cf_ip_connect_create *cf_create) { size_t i; for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) { @@ -116,62 +108,53 @@ void Curl_debug_set_transport_provider(int transport, } #endif /* UNITTESTS */ - struct cf_ai_iter { - const struct Curl_addrinfo *head; - const struct Curl_addrinfo *last; + struct Curl_cfilter *cf; int ai_family; - int n; + unsigned int n; }; static void cf_ai_iter_init(struct cf_ai_iter *iter, - const struct Curl_addrinfo *list, + struct Curl_cfilter *cf, int ai_family) { - iter->head = list; + iter->cf = cf; iter->ai_family = ai_family; - iter->last = NULL; - iter->n = -1; + iter->n = 0; } -static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter) +static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter, + struct Curl_easy *data) { const struct Curl_addrinfo *addr; - if(iter->n < 0) { + + if(!iter->cf) + return NULL; + + addr = Curl_conn_dns_get_ai(data, iter->cf->sockindex, + iter->ai_family, iter->n); + if(addr) iter->n++; - for(addr = iter->head; addr; addr = addr->ai_next) { - if(addr->ai_family == iter->ai_family) - break; - } - iter->last = addr; - } - else if(iter->last) { - iter->n++; - for(addr = iter->last->ai_next; addr; addr = addr->ai_next) { - if(addr->ai_family == iter->ai_family) - break; - } - iter->last = addr; - } - return iter->last; + return addr; } -#ifdef USE_IPV6 -static bool cf_ai_iter_done(struct cf_ai_iter *iter) +static bool cf_ai_iter_has_more(struct cf_ai_iter *iter, + struct Curl_easy *data) { - return (iter->n >= 0) && !iter->last; + return (iter->cf && + !!Curl_conn_dns_get_ai(data, iter->cf->sockindex, + iter->ai_family, iter->n)); } -#endif struct cf_ip_attempt { struct cf_ip_attempt *next; - const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */ + struct Curl_sockaddr_ex addr; struct Curl_cfilter *cf; /* current sub-cfilter connecting */ cf_ip_connect_create *cf_create; struct curltime started; /* start of current attempt */ CURLcode result; int ai_family; - int transport; + uint8_t transport; int error; BIT(connected); /* cf has connected */ BIT(shutdown); /* cf has shutdown */ @@ -185,16 +168,16 @@ static void cf_ip_attempt_free(struct cf_ip_attempt *a, if(a) { if(a->cf) Curl_conn_cf_discard_chain(&a->cf, data); - free(a); + curlx_free(a); } } static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa, struct Curl_cfilter *cf, struct Curl_easy *data, - const struct Curl_addrinfo *addr, + struct Curl_sockaddr_ex *addr, int ai_family, - int transport, + uint8_t transport, cf_ip_connect_create *cf_create) { struct Curl_cfilter *wcf; @@ -202,18 +185,18 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa, CURLcode result = CURLE_OK; *pa = NULL; - a = calloc(1, sizeof(*a)); + a = curlx_calloc(1, sizeof(*a)); if(!a) return CURLE_OUT_OF_MEMORY; - a->addr = addr; + a->addr = *addr; a->ai_family = ai_family; a->transport = transport; a->result = CURLE_OK; a->cf_create = cf_create; *pa = a; - result = a->cf_create(&a->cf, data, cf->conn, a->addr, transport); + result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport); if(result) goto out; @@ -235,8 +218,8 @@ static CURLcode cf_ip_attempt_connect(struct cf_ip_attempt *a, struct Curl_easy *data, bool *connected) { - *connected = a->connected; - if(!a->result && !*connected) { + *connected = (bool)a->connected; + if(!a->result && !*connected) { /* evaluate again */ a->result = Curl_conn_cf_connect(a->cf, data, connected); @@ -245,8 +228,12 @@ static CURLcode cf_ip_attempt_connect(struct cf_ip_attempt *a, a->connected = TRUE; } } - else if(a->result == CURLE_WEIRD_SERVER_REPLY) - a->inconclusive = TRUE; + else { + if(a->result == CURLE_WEIRD_SERVER_REPLY) + a->inconclusive = TRUE; + if(a->cf) + Curl_conn_cf_discard_chain(&a->cf, data); + } } return a->result; } @@ -263,26 +250,26 @@ struct cf_ip_ballers { struct curltime last_attempt_started; timediff_t attempt_delay_ms; int last_attempt_ai_family; - int transport; + uint32_t max_concurrent; + uint8_t transport; }; static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a, struct Curl_cfilter *cf, struct Curl_easy *data) { - struct Curl_cfilter *cf_prev = a->cf; struct Curl_cfilter *wcf; CURLcode result; - /* When restarting, we tear down and existing filter *after* we - * started up the new one. This gives us a new socket number and - * probably a new local port. Which may prevent confusion. */ + if(a->cf) + Curl_conn_cf_discard_chain(&a->cf, data); + a->result = CURLE_OK; a->connected = FALSE; a->inconclusive = FALSE; a->cf = NULL; - result = a->cf_create(&a->cf, data, cf->conn, a->addr, a->transport); + result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport); if(!result) { bool dummy; /* the new filter might have sub-filters */ @@ -292,8 +279,6 @@ static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a, } a->result = cf_ip_attempt_connect(a, data, &dummy); } - if(cf_prev) - Curl_conn_cf_discard_chain(&cf_prev, data); return result; } @@ -311,68 +296,88 @@ static void cf_ip_ballers_clear(struct Curl_cfilter *cf, bs->winner = NULL; } -static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, int ip_version, - const struct Curl_addrinfo *addr_list, +static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, + struct Curl_cfilter *cf, cf_ip_connect_create *cf_create, - int transport, - timediff_t attempt_delay_ms) + uint8_t transport, + timediff_t attempt_delay_ms, + uint32_t max_concurrent) { memset(bs, 0, sizeof(*bs)); bs->cf_create = cf_create; bs->transport = transport; bs->attempt_delay_ms = attempt_delay_ms; + bs->max_concurrent = max_concurrent; bs->last_attempt_ai_family = AF_INET; /* so AF_INET6 is next */ if(transport == TRNSPRT_UNIX) { #ifdef USE_UNIX_SOCKETS - cf_ai_iter_init(&bs->addr_iter, addr_list, AF_UNIX); + cf_ai_iter_init(&bs->addr_iter, cf, AF_UNIX); #else return CURLE_UNSUPPORTED_PROTOCOL; #endif } else { /* TCP/UDP/QUIC */ #ifdef USE_IPV6 - if(ip_version == CURL_IPRESOLVE_V6) - cf_ai_iter_init(&bs->addr_iter, NULL, AF_INET); - else - cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET); - - if(ip_version == CURL_IPRESOLVE_V4) - cf_ai_iter_init(&bs->ipv6_iter, NULL, AF_INET6); - else - cf_ai_iter_init(&bs->ipv6_iter, addr_list, AF_INET6); -#else - (void)ip_version; - cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET); + cf_ai_iter_init(&bs->ipv6_iter, cf, AF_INET6); #endif + cf_ai_iter_init(&bs->addr_iter, cf, AF_INET); } return CURLE_OK; } +static void cf_ip_ballers_prune(struct cf_ip_ballers *bs, + struct Curl_cfilter *cf, + struct Curl_easy *data, + uint32_t max_concurrent) +{ + struct cf_ip_attempt *a = NULL, **panchor; + uint32_t ongoing = 0; + + for(a = bs->running; a; a = a->next) { + if(!a->result && !a->connected) + ++ongoing; + } + + panchor = &bs->running; + while(*panchor && (ongoing > max_concurrent)) { + a = *panchor; + if(!a->result && !a->connected) { + *panchor = a->next; + a->next = NULL; + cf_ip_attempt_free(a, data); + --ongoing; + CURL_TRC_CF(data, cf, "discarding oldest attempt to keep limit"); + } + else { + panchor = &a->next; + } + } +} + static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, struct Curl_cfilter *cf, struct Curl_easy *data, + bool dns_resolved, bool *connected) { CURLcode result = CURLE_OK; struct cf_ip_attempt *a = NULL, **panchor; - bool do_more, more_possible; - struct curltime now; + bool do_more; timediff_t next_expire_ms; - int i, inconclusive, ongoing; + uint32_t inconclusive, ongoing; + VERBOSE(int i); if(bs->winner) return CURLE_OK; evaluate: - now = curlx_now(); ongoing = inconclusive = 0; - more_possible = TRUE; /* 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) { @@ -403,12 +408,18 @@ evaluate: /* no attempt connected yet, start another one? */ if(!ongoing) { if(!bs->started.tv_sec && !bs->started.tv_usec) - bs->started = now; + bs->started = *Curl_pgrs_now(data); do_more = TRUE; } else { - do_more = (curlx_timediff(now, bs->last_attempt_started) >= - bs->attempt_delay_ms); + bool more_possible = cf_ai_iter_has_more(&bs->addr_iter, data); +#ifdef USE_IPV6 + if(!more_possible) + more_possible = cf_ai_iter_has_more(&bs->ipv6_iter, data); +#endif + do_more = more_possible && + (curlx_ptimediff_ms(Curl_pgrs_now(data), &bs->last_attempt_started) >= + bs->attempt_delay_ms); if(do_more) CURL_TRC_CF(data, cf, "happy eyeballs timeout expired, " "start next attempt"); @@ -417,23 +428,42 @@ evaluate: if(do_more) { /* start the next attempt if there is another ip address to try. * Alternate between address families when possible. */ - const struct Curl_addrinfo *addr = NULL; + const struct Curl_addrinfo *ai = NULL; int ai_family = 0; + CURL_TRC_CF(data, cf, "want to do more"); #ifdef USE_IPV6 if((bs->last_attempt_ai_family == AF_INET) || - cf_ai_iter_done(&bs->addr_iter)) { - addr = cf_ai_iter_next(&bs->ipv6_iter); - ai_family = bs->ipv6_iter.ai_family; + !cf_ai_iter_has_more(&bs->addr_iter, data)) { + ai = cf_ai_iter_next(&bs->ipv6_iter, data); + ai_family = bs->ipv6_iter.ai_family; + CURL_TRC_CF(data, cf, "check for next AAAA address: %s", + ai ? "found" : "none"); } #endif - if(!addr) { - addr = cf_ai_iter_next(&bs->addr_iter); + if(!ai) { + ai = cf_ai_iter_next(&bs->addr_iter, data); ai_family = bs->addr_iter.ai_family; + CURL_TRC_CF(data, cf, "check for next A address: %s", + ai ? "found" : "none"); } + /* 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, - bs->transport, bs->cf_create); + if(ai) { /* try another address */ + struct Curl_sockaddr_ex addr; + + /* Discard oldest to make room for new attempt */ + if(bs->max_concurrent) + cf_ip_ballers_prune(bs, cf, data, bs->max_concurrent - 1); + + result = Curl_socket_addr_from_ai(&addr, ai, bs->transport); + if(result) + goto out; + + result = cf_ip_attempt_new(&a, cf, data, &addr, ai_family, + bs->transport, bs->cf_create); CURL_TRC_CF(data, cf, "starting %s attempt for ipv%s -> %d", bs->running ? "next" : "first", (ai_family == AF_INET) ? "4" : "6", result); @@ -446,7 +476,7 @@ evaluate: while(*panchor) panchor = &((*panchor)->next); *panchor = a; - bs->last_attempt_started = now; + bs->last_attempt_started = *Curl_pgrs_now(data); bs->last_attempt_ai_family = ai_family; /* and run everything again */ goto evaluate; @@ -454,35 +484,39 @@ evaluate: else if(inconclusive) { /* tried all addresses, no success but some where inconclusive. * Let's restart the inconclusive ones. */ - if(curlx_timediff(now, bs->last_attempt_started) >= - bs->attempt_delay_ms) { - CURL_TRC_CF(data, cf, "tried all addresses with inconclusive results" - ", restarting one"); - i = -1; + timediff_t since_ms = + curlx_ptimediff_ms(Curl_pgrs_now(data), &bs->last_attempt_started); + timediff_t delay_ms = bs->attempt_delay_ms - since_ms; + if(delay_ms <= 0) { + CURL_TRC_CF(data, cf, "all attempts inconclusive, restarting one"); + 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); CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, result); if(result) /* serious failure */ goto out; - bs->last_attempt_started = now; + bs->last_attempt_started = *Curl_pgrs_now(data); goto evaluate; } DEBUGASSERT(0); /* should not come here */ } + else { + /* let's wait some more before restarting */ + infof(data, "connect attempts inconclusive, retrying " + "in %" FMT_TIMEDIFF_T "ms", delay_ms); + Curl_expire(data, delay_ms, EXPIRE_HAPPY_EYEBALLS); + } /* attempt timeout for restart has not expired yet */ goto out; } - else if(ongoing) { + else if(!ongoing && dns_resolved) { /* no more addresses, no inconclusive attempts */ - more_possible = FALSE; - } - else { 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) @@ -493,21 +527,36 @@ evaluate: out: if(!result) { - /* when do we need to be called again? */ - next_expire_ms = Curl_timeleft(data, &now, TRUE); - if(more_possible) { - timediff_t expire_ms, elapsed_ms; - elapsed_ms = curlx_timediff(now, bs->last_attempt_started); - expire_ms = CURLMAX(bs->attempt_delay_ms - elapsed_ms, 0); - next_expire_ms = CURLMIN(next_expire_ms, expire_ms); - } + bool more_possible; - if(next_expire_ms <= 0) { + /* when do we need to be called again? */ + next_expire_ms = Curl_timeleft_ms(data); + if(next_expire_ms < 0) { failf(data, "Connection timeout after %" FMT_OFF_T " ms", - curlx_timediff(now, data->progress.t_startsingle)); + curlx_ptimediff_ms(Curl_pgrs_now(data), + &data->progress.t_startsingle)); return CURLE_OPERATION_TIMEDOUT; } - Curl_expire(data, next_expire_ms, EXPIRE_HAPPY_EYEBALLS); + + more_possible = cf_ai_iter_has_more(&bs->addr_iter, data); +#ifdef USE_IPV6 + if(!more_possible) + more_possible = cf_ai_iter_has_more(&bs->ipv6_iter, data); +#endif + if(more_possible) { + timediff_t expire_ms, elapsed_ms; + elapsed_ms = + curlx_ptimediff_ms(Curl_pgrs_now(data), &bs->last_attempt_started); + expire_ms = CURLMAX(bs->attempt_delay_ms - elapsed_ms, 0); + next_expire_ms = CURLMIN(next_expire_ms, expire_ms); + if(next_expire_ms <= 0) { + CURL_TRC_CF(data, cf, "HAPPY_EYEBALLS timeout due, re-evaluate"); + goto evaluate; + } + CURL_TRC_CF(data, cf, "next HAPPY_EYEBALLS timeout in %" FMT_TIMEDIFF_T + "ms", next_expire_ms); + Curl_expire(data, next_expire_ms, EXPIRE_HAPPY_EYEBALLS); + } } return result; } @@ -523,7 +572,7 @@ static CURLcode cf_ip_ballers_shutdown(struct cf_ip_ballers *bs, *done = TRUE; for(a = bs->running; a; a = a->next) { bool bdone = FALSE; - if(a->shutdown) + if(a->shutdown || !a->cf) continue; a->result = a->cf->cft->do_shutdown(a->cf, data, &bdone); if(a->result || bdone) @@ -556,7 +605,7 @@ static bool cf_ip_ballers_pending(struct cf_ip_ballers *bs, for(a = bs->running; a; a = a->next) { if(a->result) continue; - if(a->cf->cft->has_data_pending(a->cf, data)) + if(a->cf && a->cf->cft->has_data_pending(a->cf, data)) return TRUE; } return FALSE; @@ -572,8 +621,8 @@ static struct curltime cf_ip_ballers_max_time(struct cf_ip_ballers *bs, memset(&tmax, 0, sizeof(tmax)); for(a = bs->running; a; a = a->next) { memset(&t, 0, sizeof(t)); - if(!a->cf->cft->query(a->cf, data, query, NULL, &t)) { - if((t.tv_sec || t.tv_usec) && curlx_timediff_us(t, tmax) > 0) + if(a->cf && !a->cf->cft->query(a->cf, data, query, NULL, &t)) { + if((t.tv_sec || t.tv_usec) && curlx_ptimediff_us(&t, &tmax) > 0) tmax = t; } } @@ -587,8 +636,8 @@ static int cf_ip_ballers_min_reply_ms(struct cf_ip_ballers *bs, struct cf_ip_attempt *a; for(a = bs->running; a; a = a->next) { - if(!a->cf->cft->query(a->cf, data, CF_QUERY_CONNECT_REPLY_MS, - &breply_ms, NULL)) { + if(a->cf && !a->cf->cft->query(a->cf, data, CF_QUERY_CONNECT_REPLY_MS, + &breply_ms, NULL)) { if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms)) reply_ms = breply_ms; } @@ -596,7 +645,6 @@ static int cf_ip_ballers_min_reply_ms(struct cf_ip_ballers *bs, return reply_ms; } - typedef enum { SCFST_INIT, SCFST_WAITING, @@ -604,14 +652,14 @@ typedef enum { } cf_connect_state; struct cf_ip_happy_ctx { - int transport; + uint8_t transport; cf_ip_connect_create *cf_create; cf_connect_state state; struct cf_ip_ballers ballers; struct curltime started; + BIT(dns_resolved); }; - static CURLcode is_connected(struct Curl_cfilter *cf, struct Curl_easy *data, bool *connected) @@ -620,37 +668,49 @@ static CURLcode is_connected(struct Curl_cfilter *cf, struct connectdata *conn = cf->conn; CURLcode result; - result = cf_ip_ballers_run(&ctx->ballers, cf, data, connected); + result = cf_ip_ballers_run(&ctx->ballers, cf, data, + (bool)ctx->dns_resolved, connected); if(!result) return CURLE_OK; { const char *hostname, *proxy_name = NULL; - int port; + char viamsg[160]; #ifndef CURL_DISABLE_PROXY if(conn->bits.socksproxy) proxy_name = conn->socks_proxy.host.name; else if(conn->bits.httpproxy) proxy_name = conn->http_proxy.host.name; #endif - hostname = conn->bits.conn_to_host ? - conn->conn_to_host.name : conn->host.name; + hostname = conn->bits.conn_to_host ? conn->conn_to_host.name : + conn->host.name; - if(cf->sockindex == SECONDARYSOCKET) - port = conn->secondary_port; - else if(cf->conn->bits.conn_to_port) - port = conn->conn_to_port; +#ifdef USE_UNIX_SOCKETS + if(conn->unix_domain_socket) + curl_msnprintf(viamsg, sizeof(viamsg), "over %s", + conn->unix_domain_socket); else - port = conn->remote_port; +#endif + { + uint16_t port; + if(cf->sockindex == SECONDARYSOCKET) + port = conn->secondary_port; + else if(cf->conn->bits.conn_to_port) + port = conn->conn_to_port; + else + port = conn->remote_port; + curl_msnprintf(viamsg, sizeof(viamsg), "port %u", port); + } - failf(data, "Failed to connect to %s port %u %s%s%safter " + failf(data, "Failed to connect to %s %s %s%s%safter " "%" FMT_TIMEDIFF_T " ms: %s", - hostname, port, + hostname, viamsg, proxy_name ? "via " : "", proxy_name ? proxy_name : "", proxy_name ? " " : "", - curlx_timediff(curlx_now(), data->progress.t_startsingle), + curlx_ptimediff_ms(Curl_pgrs_now(data), + &data->progress.t_startsingle), curl_easy_strerror(result)); } @@ -662,30 +722,25 @@ static CURLcode is_connected(struct Curl_cfilter *cf, return result; } -/* - * Connect to the given host with timeout, proxy or remote does not matter. - * There might be more than one IP address to try out. - */ -static CURLcode start_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) +#define IP_HE_MAX_CONCURRENT_ATTEMPTS 6 + +static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_ip_happy_ctx *ctx = cf->ctx; - struct Curl_dns_entry *dns = data->state.dns[cf->sockindex]; - if(!dns) - return CURLE_FAILED_INIT; - - if(Curl_timeleft(data, NULL, 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; } - CURL_TRC_CF(data, cf, "init ip ballers for transport %d", ctx->transport); - ctx->started = curlx_now(); - return cf_ip_ballers_init(&ctx->ballers, cf->conn->ip_version, - dns->addr, ctx->cf_create, ctx->transport, - data->set.happy_eyeballs_timeout); + CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->transport); + ctx->started = *Curl_pgrs_now(data); + return cf_ip_ballers_init(&ctx->ballers, cf, + ctx->cf_create, ctx->transport, + data->set.happy_eyeballs_timeout, + IP_HE_MAX_CONCURRENT_ATTEMPTS); } static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf, @@ -698,6 +753,12 @@ static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf, cf_ip_ballers_clear(cf, data, &ctx->ballers); } +static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx) +{ + if(ctx) + curlx_free(ctx); +} + static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -737,6 +798,10 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, struct cf_ip_happy_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; + /* -Werror=null-dereference finds false positives suddenly. */ + if(!data) + return CURLE_FAILED_INIT; + if(cf->connected) { *done = TRUE; return CURLE_OK; @@ -745,52 +810,64 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, DEBUGASSERT(ctx); *done = FALSE; - switch(ctx->state) { - case SCFST_INIT: - DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data)); - DEBUGASSERT(!cf->connected); - result = start_connect(cf, data); - if(result) - return result; - ctx->state = SCFST_WAITING; - FALLTHROUGH(); - case SCFST_WAITING: - result = is_connected(cf, data, done); - if(!result && *done) { - DEBUGASSERT(ctx->ballers.winner); - DEBUGASSERT(ctx->ballers.winner->cf); - DEBUGASSERT(ctx->ballers.winner->cf->connected); - /* we have a winner. Install and activate it. - * close/free all others. */ - ctx->state = SCFST_DONE; - cf->connected = TRUE; - cf->next = ctx->ballers.winner->cf; - ctx->ballers.winner->cf = NULL; - cf_ip_happy_ctx_clear(cf, data); - Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS); - - if(cf->conn->handler->protocol & PROTO_FAMILY_SSH) - Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(Curl_trc_cf_is_verbose(cf, data)) { - struct ip_quadruple ipquad; - bool is_ipv6; - if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) { - const char *host; - int port; - Curl_conn_get_current_host(data, cf->sockindex, &host, &port); - CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u", - host, ipquad.remote_ip, ipquad.remote_port); - } - } -#endif - data->info.numconnects++; /* to track the # of connections made */ - } - break; - case SCFST_DONE: - *done = TRUE; - break; + if(!ctx->dns_resolved) { + result = Curl_conn_dns_result(cf->conn, cf->sockindex); + if(!result) + ctx->dns_resolved = TRUE; + else if(result == CURLE_AGAIN) /* not complete yet */ + result = CURLE_OK; + else /* real error */ + goto out; } + + switch(ctx->state) { + case SCFST_INIT: + DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data)); + DEBUGASSERT(!cf->connected); + result = cf_ip_happy_init(cf, data); + if(result) + goto out; + ctx->state = SCFST_WAITING; + FALLTHROUGH(); + case SCFST_WAITING: + result = is_connected(cf, data, done); + if(!result && *done) { + DEBUGASSERT(ctx->ballers.winner); + DEBUGASSERT(ctx->ballers.winner->cf); + DEBUGASSERT(ctx->ballers.winner->cf->connected); + /* we have a winner. Install and activate it. + * close/free all others. */ + ctx->state = SCFST_DONE; + cf->connected = TRUE; + cf->next = ctx->ballers.winner->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->scheme->protocol & PROTO_FAMILY_SSH) + Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */ +#ifdef CURLVERBOSE + if(Curl_trc_cf_is_verbose(cf, data)) { + struct ip_quadruple ipquad; + bool is_ipv6; + if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) { + const char *host; + Curl_conn_get_current_host(data, cf->sockindex, &host, NULL); + CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u", + host, ipquad.remote_ip, ipquad.remote_port); + } + } +#endif + data->info.numconnects++; /* to track the # of connections made */ + } + break; + case SCFST_DONE: + *done = TRUE; + break; + } +out: return result; } @@ -864,14 +941,13 @@ static void cf_ip_happy_destroy(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "destroy"); if(ctx) { cf_ip_happy_ctx_clear(cf, data); + cf_ip_happy_ctx_destroy(ctx); } - /* release any resources held in state */ - Curl_safefree(ctx); } struct Curl_cftype Curl_cft_ip_happy = { "HAPPY-EYEBALLS", - 0, + CF_TYPE_SETUP, CURL_LOG_LVL_NONE, cf_ip_happy_destroy, cf_ip_happy_connect, @@ -901,7 +977,7 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, cf_ip_connect_create *cf_create, - int transport) + uint8_t transport) { struct cf_ip_happy_ctx *ctx = NULL; CURLcode result; @@ -909,7 +985,7 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, (void)data; (void)conn; *pcf = NULL; - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -921,15 +997,15 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, out: if(result) { - Curl_safefree(*pcf); - free(ctx); + curlx_safefree(*pcf); + cf_ip_happy_ctx_destroy(ctx); } return result; } CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - int transport) + uint8_t transport) { cf_ip_connect_create *cf_create; struct Curl_cfilter *cf; @@ -939,7 +1015,7 @@ CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, DEBUGASSERT(cf_at); cf_create = get_cf_create(transport); if(!cf_create) { - CURL_TRC_CF(data, cf_at, "unsupported transport type %d", transport); + CURL_TRC_CF(data, cf_at, "unsupported transport type %u", transport); return CURLE_UNSUPPORTED_PROTOCOL; } result = cf_ip_happy_create(&cf, data, cf_at->conn, cf_create, transport); diff --git a/lib/cf-ip-happy.h b/lib/cf-ip-happy.h index 96e619ae43..547ee4b4ac 100644 --- a/lib/cf-ip-happy.h +++ b/lib/cf-ip-happy.h @@ -25,8 +25,11 @@ ***************************************************************************/ #include "curl_setup.h" -#include "curlx/nonblock.h" /* for curlx_nonblock() */ -#include "sockaddr.h" +struct connectdata; +struct Curl_addrinfo; +struct Curl_cfilter; +struct Curl_easy; +struct Curl_sockaddr_ex; /** * Create a cfilter for making an "ip" connection to the @@ -42,18 +45,13 @@ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport); + struct Curl_sockaddr_ex *addr, + uint8_t transport); CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - int transport); + uint8_t transport); extern struct Curl_cftype Curl_cft_ip_happy; -#ifdef UNITTESTS -void Curl_debug_set_transport_provider(int transport, - cf_ip_connect_create *cf_create); -#endif - #endif /* HEADER_CURL_IP_HAPPY_H */ diff --git a/lib/cf-socket.c b/lib/cf-socket.c index f449ca36ca..796c39075e 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -21,15 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_NETINET_IN_H #include /* may need it */ #endif -#ifdef HAVE_SYS_UN_H -#include /* for sockaddr_un */ -#endif #ifdef HAVE_LINUX_TCP_H #include #elif defined(HAVE_NETINET_TCP_H) @@ -44,9 +40,6 @@ #ifdef HAVE_NETDB_H #include #endif -#ifdef HAVE_FCNTL_H -#include -#endif #ifdef HAVE_ARPA_INET_H #include #endif @@ -62,184 +55,147 @@ #endif #include "urldata.h" -#include "bufq.h" -#include "sendf.h" +#include "curl_trc.h" #include "if2ip.h" -#include "strerror.h" #include "cfilters.h" #include "cf-socket.h" #include "connect.h" +#include "curl_addrinfo.h" #include "select.h" -#include "url.h" /* for Curl_safefree() */ #include "multiif.h" -#include "sockaddr.h" /* required for Curl_sockaddr_storage */ #include "curlx/inet_pton.h" #include "progress.h" -#include "curlx/warnless.h" #include "conncache.h" #include "multihandle.h" #include "rand.h" -#include "share.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "system_win32.h" +#include "curlx/nonblock.h" #include "curlx/version_win32.h" +#include "curlx/strerr.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if defined(USE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32) -/* It makes support for IPv4-mapped IPv6 addresses. - * Linux kernel, NetBSD, FreeBSD and Darwin: default is off; - * Windows Vista and later: default is on; - * DragonFly BSD: acts like off, and dummy setting; - * OpenBSD and earlier Windows: unsupported. - * Linux: controlled by /proc/sys/net/ipv6/bindv6only. - */ -static void set_ipv6_v6only(curl_socket_t sockfd, int on) -{ - (void)setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on)); -} -#else -#define set_ipv6_v6only(x,y) -#endif - -static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd) +static void tcpnodelay(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t sockfd) { #if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED) - curl_socklen_t onoff = (curl_socklen_t) 1; + 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) - infof(data, "Could not set TCP_NODELAY: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + CURL_TRC_CF(data, cf, "Could not set TCP_NODELAY: %s", + curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); #else + (void)cf; (void)data; (void)sockfd; #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_easy *data, - curl_socket_t sockfd) -{ - int onoff = 1; - (void)data; - if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, - (void *)&onoff, sizeof(onoff)) < 0) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char buffer[STRERROR_LEN]; - infof(data, "Could not set SO_NOSIGPIPE: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); -#endif - } -} -#else -#define nosigpipe(x,y) Curl_nop_stmt -#endif - -#if defined(USE_WINSOCK) && \ - defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT) -/* Win 10, v 1709 (10.0.16299) and later can use SetSockOpt TCP_KEEP____ - * so should use seconds */ -#define CURL_WINSOCK_KEEP_SSO -#define KEEPALIVE_FACTOR(x) -#elif defined(USE_WINSOCK) || \ - (defined(__sun) && !defined(TCP_KEEPIDLE)) || \ - (defined(__DragonFly__) && __DragonFly_version < 500702) || \ - (defined(_WIN32) && !defined(TCP_KEEPIDLE)) +#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 - -/* Offered by mingw-w64 and MS SDK. Latter only when targeting Win7+. */ -#if defined(USE_WINSOCK) && !defined(SIO_KEEPALIVE_VALS) -#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) - -struct tcp_keepalive { - u_long onoff; - u_long keepalivetime; - u_long keepaliveinterval; -}; #endif -static void -tcpkeepalive(struct Curl_easy *data, - curl_socket_t sockfd) +static void tcpkeepalive(struct Curl_cfilter *cf, + struct Curl_easy *data, + curl_socket_t sockfd) { int optval = data->set.tcp_keepalive ? 1 : 0; /* only set IDLE and INTVL if setting KEEPALIVE is successful */ if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set SO_KEEPALIVE on fd " - "%" FMT_SOCKET_T ": errno %d", - sockfd, SOCKERRNO); + CURL_TRC_CF(data, cf, "Failed to set SO_KEEPALIVE on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); } else { -#ifdef SIO_KEEPALIVE_VALS /* Windows */ -/* Windows 10, version 1709 (10.0.16299) and later versions */ -#ifdef CURL_WINSOCK_KEEP_SSO - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, - (const char *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPIDLE on fd " - "%" FMT_SOCKET_T ": errno %d", - sockfd, SOCKERRNO); - } - optval = curlx_sltosi(data->set.tcp_keepintvl); - KEEPALIVE_FACTOR(optval); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, - (const char *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPINTVL on fd " - "%" FMT_SOCKET_T ": errno %d", - sockfd, SOCKERRNO); - } - optval = curlx_sltosi(data->set.tcp_keepcnt); - if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, - (const char *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPCNT on fd " - "%" FMT_SOCKET_T ": errno %d", - sockfd, SOCKERRNO); - } -#else /* Windows < 10.0.16299 */ - struct tcp_keepalive vals; - DWORD dummy; - vals.onoff = 1; - optval = curlx_sltosi(data->set.tcp_keepidle); - KEEPALIVE_FACTOR(optval); - vals.keepalivetime = (u_long)optval; - optval = curlx_sltosi(data->set.tcp_keepintvl); - KEEPALIVE_FACTOR(optval); - vals.keepaliveinterval = (u_long)optval; - if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals), - NULL, 0, &dummy, NULL, NULL) != 0) { - infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd " - "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); - } +#ifdef USE_WINSOCK + /* Windows 10, version 1709 (10.0.16299) and later versions can use + setsockopt() TCP_KEEP*. Older versions return with failure. */ + if(curlx_verify_windows_version(10, 0, 16299, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL)) { + CURL_TRC_CF(data, cf, "Set TCP_KEEP* on fd=%" FMT_SOCKET_T, sockfd); + optval = curlx_sltosi(data->set.tcp_keepidle); +/* Offered by mingw-w64 v12+. MS SDK 6.0A+. */ +#ifndef TCP_KEEPALIVE +#define TCP_KEEPALIVE 3 #endif -#else /* !Windows */ +/* Offered by mingw-w64 v12+. MS SDK ~10+/~VS2017+. */ +#ifndef TCP_KEEPCNT +#define TCP_KEEPCNT 16 +#endif +#ifndef TCP_KEEPIDLE +#define TCP_KEEPIDLE TCP_KEEPALIVE +#endif +#ifndef TCP_KEEPINTVL +#define TCP_KEEPINTVL 17 +#endif + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, + (const char *)&optval, sizeof(optval)) < 0) { + CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPIDLE on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); + } + optval = curlx_sltosi(data->set.tcp_keepintvl); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, + (const char *)&optval, sizeof(optval)) < 0) { + CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPINTVL on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); + } + optval = curlx_sltosi(data->set.tcp_keepcnt); + if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, + (const char *)&optval, sizeof(optval)) < 0) { + CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPCNT on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); + } + } + else { +/* Offered by mingw-w64 and MS SDK. Latter only when targeting Win7+. */ +#ifndef SIO_KEEPALIVE_VALS +#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR, 4) + struct tcp_keepalive { + u_long onoff; + u_long keepalivetime; + u_long keepaliveinterval; + }; +#endif + struct tcp_keepalive vals; + DWORD dummy; + vals.onoff = 1; + optval = curlx_sltosi(data->set.tcp_keepidle); + KEEPALIVE_FACTOR(optval); + vals.keepalivetime = (u_long)optval; + optval = curlx_sltosi(data->set.tcp_keepintvl); + KEEPALIVE_FACTOR(optval); + vals.keepaliveinterval = (u_long)optval; + if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID)&vals, sizeof(vals), + NULL, 0, &dummy, NULL, NULL) != 0) { + CURL_TRC_CF(data, cf, "Failed to set SIO_KEEPALIVE_VALS on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); + } + } +#else /* !USE_WINSOCK */ #ifdef TCP_KEEPIDLE optval = curlx_sltosi(data->set.tcp_keepidle); KEEPALIVE_FACTOR(optval); if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPIDLE on fd " - "%" FMT_SOCKET_T ": errno %d", - sockfd, SOCKERRNO); + CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPIDLE on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); } #elif defined(TCP_KEEPALIVE) /* macOS style */ @@ -247,9 +203,8 @@ tcpkeepalive(struct Curl_easy *data, KEEPALIVE_FACTOR(optval); if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPALIVE on fd " - "%" FMT_SOCKET_T ": errno %d", - sockfd, SOCKERRNO); + CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPALIVE on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); } #elif defined(TCP_KEEPALIVE_THRESHOLD) /* Solaris <11.4 style */ @@ -257,9 +212,8 @@ tcpkeepalive(struct Curl_easy *data, KEEPALIVE_FACTOR(optval); if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD, (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPALIVE_THRESHOLD on fd " - "%" FMT_SOCKET_T ": errno %d", - sockfd, SOCKERRNO); + CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPALIVE_THRESHOLD on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); } #endif #ifdef TCP_KEEPINTVL @@ -267,9 +221,8 @@ tcpkeepalive(struct Curl_easy *data, KEEPALIVE_FACTOR(optval); if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPINTVL on fd " - "%" FMT_SOCKET_T ": errno %d", - sockfd, SOCKERRNO); + CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPINTVL on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); } #elif defined(TCP_KEEPALIVE_ABORT_THRESHOLD) /* Solaris <11.4 style */ @@ -283,84 +236,93 @@ tcpkeepalive(struct Curl_easy *data, * Note that the consequent probes will not be sent * at equal intervals on Solaris, but will be sent * using the exponential backoff algorithm. */ - optval = curlx_sltosi(data->set.tcp_keepcnt) * - curlx_sltosi(data->set.tcp_keepintvl); + { + int keepcnt = curlx_sltosi(data->set.tcp_keepcnt); + int keepintvl = curlx_sltosi(data->set.tcp_keepintvl); + + if(keepcnt > 0 && keepintvl > (INT_MAX / keepcnt)) + optval = INT_MAX; + else + optval = keepcnt * keepintvl; + } KEEPALIVE_FACTOR(optval); if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPALIVE_ABORT_THRESHOLD on fd " - "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); + CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPALIVE_ABORT_THRESHOLD" + " on fd %" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); } #endif #ifdef TCP_KEEPCNT optval = curlx_sltosi(data->set.tcp_keepcnt); if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, (void *)&optval, sizeof(optval)) < 0) { - infof(data, "Failed to set TCP_KEEPCNT on fd " - "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); + CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPCNT on fd " + "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO); } #endif -#endif +#endif /* USE_WINSOCK */ } } /** - * Assign the address `ai` to the Curl_sockaddr_ex `dest` and - * set the transport used. + * Assign the addrinfo `ai` to the Curl_sockaddr_ex `addr` with + * transport determining socktype and protocol. */ -static CURLcode sock_assign_addr(struct Curl_sockaddr_ex *dest, - const struct Curl_addrinfo *ai, - int transport) +CURLcode Curl_socket_addr_from_ai(struct Curl_sockaddr_ex *addr, + const struct Curl_addrinfo *ai, + uint8_t transport) { /* - * The Curl_sockaddr_ex structure is basically libcurl's external API + * 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) { - case TRNSPRT_TCP: - dest->socktype = SOCK_STREAM; - dest->protocol = IPPROTO_TCP; - break; - case TRNSPRT_UNIX: - dest->socktype = SOCK_STREAM; - dest->protocol = IPPROTO_IP; - break; - default: /* UDP and QUIC */ - dest->socktype = SOCK_DGRAM; - dest->protocol = IPPROTO_UDP; - break; - } - dest->addrlen = (unsigned int)ai->ai_addrlen; + addr->family = ai->ai_family; + addr->socktype = Curl_socktype_for_transport(transport); + addr->protocol = Curl_protocol_for_transport(transport); + addr->addrlen = (unsigned int)ai->ai_addrlen; - if(dest->addrlen > sizeof(struct Curl_sockaddr_storage)) { - DEBUGASSERT(0); + DEBUGASSERT(addr->addrlen <= sizeof(addr->curl_sa_addrbuf)); + if(addr->addrlen > sizeof(addr->curl_sa_addrbuf)) return CURLE_TOO_LARGE; - } - memcpy(&dest->curl_sa_addr, ai->ai_addr, dest->addrlen); + memcpy(&addr->curl_sa_addrbuf, ai->ai_addr, addr->addrlen); 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) { - /* - * If the opensocket callback is set, all the destination address - * information is passed to the callback. Depending on this information the - * callback may opt to abort the connection, this is indicated returning - * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When - * the callback returns a valid socket the destination address information - * might have been changed and this 'new' address will actually be used - * here to connect. - */ + /* + * If the opensocket callback is set, all the destination address + * information is passed to the callback. Depending on this information the + * callback may opt to abort the connection, this is indicated returning + * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When + * the callback returns a valid socket the destination address information + * might have been changed and this 'new' address will actually be used + * here to connect. + */ Curl_set_in_callback(data, TRUE); *sockfd = data->set.fopensocket(data->set.opensocket_client, CURLSOCKTYPE_IPCXN, @@ -368,13 +330,44 @@ 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 */ - *sockfd = socket(addr->family, addr->socktype, addr->protocol); + /* 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; } - if(*sockfd == CURL_SOCKET_BAD) + if(*sockfd == CURL_SOCKET_BAD) { /* no socket, no connection */ + failf(data, "failed to open socket: %s", + curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf))); return CURLE_COULDNT_CONNECT; + } + +#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))); + sclose(*sockfd); + *sockfd = CURL_SOCKET_BAD; + return CURLE_COULDNT_CONNECT; + } +#endif #if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) if(data->conn->scope_id && (addr->family == AF_INET6)) { @@ -397,7 +390,7 @@ static CURLcode socket_open(struct Curl_easy *data, CURLcode Curl_socket_open(struct Curl_easy *data, const struct Curl_addrinfo *ai, struct Curl_sockaddr_ex *addr, - int transport, + uint8_t transport, curl_socket_t *sockfd) { struct Curl_sockaddr_ex dummy; @@ -407,7 +400,7 @@ CURLcode Curl_socket_open(struct Curl_easy *data, /* if the caller does not want info back, use a local temp copy */ addr = &dummy; - result = sock_assign_addr(addr, ai, transport); + result = Curl_socket_addr_from_ai(addr, ai, transport); if(result) return result; @@ -417,7 +410,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) { @@ -449,37 +442,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://support.microsoft.com/kb/823764 - - 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() * @@ -525,14 +487,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))) { @@ -542,13 +504,13 @@ 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) { - free(*iface); + curlx_free(*iface); *iface = NULL; return CURLE_OUT_OF_MEMORY; } @@ -557,13 +519,14 @@ 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; } #ifndef CURL_DISABLE_BINDLOCAL static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sockfd, int af, unsigned int scope) + curl_socket_t sockfd, int af, unsigned int scope, + uint8_t transport) { struct Curl_sockaddr_storage sa; struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */ @@ -597,7 +560,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, if(!iface && !host && !port) /* no local kind of binding was requested */ return CURLE_OK; - else if(iface && (strlen(iface) >= 255) ) + else if(iface && (strlen(iface) >= 255)) return CURLE_BAD_FUNCTION_ARGUMENT; memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); @@ -613,10 +576,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, @@ -637,33 +600,33 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, /* Discover IP from input device, then bind to it */ if2ip_result = Curl_if2ip(af, #ifdef USE_IPV6 - scope, conn->scope_id, + scope, conn->scope_id, #endif - iface, myhost, sizeof(myhost)); + iface, myhost, sizeof(myhost)); } switch(if2ip_result) { - case IF2IP_NOT_FOUND: - if(iface_input && !host_input) { - /* Do not fall back to treating it as a hostname */ - char buffer[STRERROR_LEN]; - data->state.os_errno = error = SOCKERRNO; - failf(data, "Couldn't bind to interface '%s' with errno %d: %s", - iface, error, Curl_strerror(error, buffer, sizeof(buffer))); - return CURLE_INTERFACE_FAILED; - } - break; - case IF2IP_AF_NOT_SUPPORTED: - /* Signal the caller to try another address family if available */ - return CURLE_UNSUPPORTED_PROTOCOL; - case IF2IP_FOUND: - /* - * We now have the numerical IP address in the 'myhost' buffer - */ - host = myhost; - infof(data, "Local Interface %s is ip %s using address family %i", - iface, host, af); - done = 1; - break; + case IF2IP_NOT_FOUND: + if(iface_input && !host_input) { + /* Do not fall back to treating it as a hostname */ + char buffer[STRERROR_LEN]; + data->state.os_errno = error = SOCKERRNO; + failf(data, "Could not bind to interface '%s' with errno %d: %s", + iface, error, curlx_strerror(error, buffer, sizeof(buffer))); + return CURLE_INTERFACE_FAILED; + } + break; + case IF2IP_AF_NOT_SUPPORTED: + /* Signal the caller to try another address family if available */ + return CURLE_UNSUPPORTED_PROTOCOL; + case IF2IP_FOUND: + /* + * We now have the numerical IP address in the 'myhost' buffer + */ + host = myhost; + infof(data, "Local Interface %s is ip %s using address family %i", + iface, host, af); + done = 1; + break; } if(!iface_input || host_input) { /* @@ -674,21 +637,21 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, * of the connection. The resolve functions should really be changed * to take a type parameter instead. */ - int ip_version = (af == AF_INET) ? - CURL_IPRESOLVE_V4 : CURL_IPRESOLVE_WHATEVER; + uint8_t dns_queries = (af == AF_INET) ? + CURL_DNSQ_A : (CURL_DNSQ_A | CURL_DNSQ_AAAA); #ifdef USE_IPV6 if(af == AF_INET6) - ip_version = CURL_IPRESOLVE_V6; + dns_queries = CURL_DNSQ_AAAA; #endif - (void)Curl_resolv_blocking(data, host, 80, ip_version, &h); + (void)Curl_resolv_blocking(data, dns_queries, host, 80, transport, &h); if(h) { int h_af = h->addr->ai_family; /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ Curl_printable_address(h->addr, myhost, sizeof(myhost)); infof(data, "Name '%s' family %i resolved to '%s' family %i", host, af, myhost, h_af); - Curl_resolv_unlink(data, &h); /* this will NULL, potential free h */ + Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */ if(af != h_af) { /* bad IP version combo, signal the caller to try another address family if available */ @@ -721,11 +684,11 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, if(scope_ptr) { /* The "myhost" string either comes from Curl_if2ip or from Curl_printable_address. The latter returns only numeric scope - IDs and the former returns none at all. So the scope ID, if - present, is known to be numeric */ + IDs and the former returns none at all. Making the scope ID, + if present, known to be numeric */ curl_off_t scope_id; if(curlx_str_number((const char **)CURL_UNCONST(&scope_ptr), - &scope_id, UINT_MAX)) + &scope_id, UINT_MAX)) return CURLE_UNSUPPORTED_PROTOCOL; si6->sin6_scope_id = (unsigned int)scope_id; } @@ -751,8 +714,8 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, char buffer[STRERROR_LEN]; data->state.errorbuf = FALSE; data->state.os_errno = error = SOCKERRNO; - failf(data, "Couldn't bind to '%s' with errno %d: %s", - host, error, Curl_strerror(error, buffer, sizeof(buffer))); + failf(data, "Could not bind to '%s' with errno %d: %s", host, + error, curlx_strerror(error, buffer, sizeof(buffer))); return CURLE_INTERFACE_FAILED; } } @@ -790,10 +753,10 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, infof(data, "Bind to local port %d failed, trying next", port - 1); /* We reuse/clobber the port variable here below */ if(sock->sa_family == AF_INET) - si4->sin_port = ntohs(port); + si4->sin_port = htons(port); #ifdef USE_IPV6 else - si6->sin6_port = ntohs(port); + si6->sin6_port = htons(port); #endif } else @@ -803,7 +766,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, char buffer[STRERROR_LEN]; data->state.os_errno = error = SOCKERRNO; failf(data, "bind failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); + error, curlx_strerror(error, buffer, sizeof(buffer))); } return CURLE_INTERFACE_FAILED; @@ -825,35 +788,22 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) * In October 2003 we effectively nullified this function on Windows due to * problems with it using all CPU in multi-threaded cases. * - * In May 2004, we bring it back to offer more info back on connect failures. - * Gisle Vanem could reproduce the former problems with this function, but + * In May 2004, we brought it back to offer more info back on connect + * failures. We could reproduce the former problems with this function, but * could avoid them by adding this SleepEx() call below: * * "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." */ - -#ifdef UNDER_CE - Sleep(0); -#else SleepEx(0, FALSE); -#endif - #endif if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize)) err = SOCKERRNO; -#ifdef UNDER_CE - /* Old Windows CE versions do not support SO_ERROR */ - if(WSAENOPROTOOPT == err) { - SET_SOCKERRNO(0); - err = 0; - } -#endif #if defined(EBADIOCTL) && defined(__minix) /* Minix 3.1.x does not support getsockopt on UDP sockets */ if(EBADIOCTL == err) { @@ -899,15 +849,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]; - infof(data, "Immediate connect fail for %s: %s", - ipaddress, Curl_strerror(error, buffer, sizeof(buffer))); + 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; @@ -915,7 +862,7 @@ static CURLcode socket_connect_result(struct Curl_easy *data, } struct cf_socket_ctx { - int transport; + uint8_t transport; struct Curl_sockaddr_ex addr; /* address to connect to */ curl_socket_t sock; /* current attempt socket */ struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */ @@ -931,7 +878,7 @@ struct cf_socket_ctx { int wblock_percent; /* percent of writes doing EAGAIN */ int wpartial_percent; /* percent of bytes written in send */ int rblock_percent; /* percent of reads doing EAGAIN */ - size_t recv_max; /* max enforced read size */ + size_t recv_max; /* max enforced read size */ #endif BIT(got_first_byte); /* if first byte was received */ BIT(listening); /* socket is listening */ @@ -941,18 +888,13 @@ struct cf_socket_ctx { }; static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx, - const struct Curl_addrinfo *ai, - int transport) + struct Curl_sockaddr_ex *addr, + uint8_t transport) { - CURLcode result; - memset(ctx, 0, sizeof(*ctx)); ctx->sock = CURL_SOCKET_BAD; ctx->transport = transport; - - result = sock_assign_addr(&ctx->addr, ai, transport); - if(result) - return result; + ctx->addr = *addr; #ifdef DEBUGBUILD { @@ -983,14 +925,14 @@ static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx, } #endif - return result; + return CURLE_OK; } 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; @@ -1032,44 +974,40 @@ static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) cf_socket_close(cf, data); CURL_TRC_CF(data, cf, "destroy"); - free(ctx); + curlx_free(ctx); cf->ctx = NULL; } -static CURLcode set_local_ip(struct Curl_cfilter *cf, - struct Curl_easy *data) +static void set_local_ip(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; + ctx->ip.local_ip[0] = 0; + ctx->ip.local_port = 0; #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; - failf(data, "getsockname() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return CURLE_FAILED_INIT; + if(getsockname(ctx->sock, (struct sockaddr *)&ssloc, &slen)) { + VERBOSE(int error = SOCKERRNO); + infof(data, "getsockname() failed with errno %d: %s", + error, curlx_strerror(error, buffer, sizeof(buffer))); } - if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, - ctx->ip.local_ip, &ctx->ip.local_port)) { - failf(data, "ssloc inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return CURLE_FAILED_INIT; + else if(!Curl_addr2string((struct sockaddr *)&ssloc, slen, + ctx->ip.local_ip, &ctx->ip.local_port)) { + infof(data, "ssloc inet_ntop() failed with errno %d: %s", + errno, curlx_strerror(errno, buffer, sizeof(buffer))); } } #else (void)data; - ctx->ip.local_ip[0] = 0; - ctx->ip.local_port = -1; #endif - return CURLE_OK; } static CURLcode set_remote_ip(struct Curl_cfilter *cf, @@ -1078,6 +1016,7 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf, struct cf_socket_ctx *ctx = cf->ctx; /* store remote address and port used in this connection attempt */ + ctx->ip.transport = ctx->transport; if(!Curl_addr2string(&ctx->addr.curl_sa_addr, (curl_socklen_t)ctx->addr.addrlen, ctx->ip.remote_ip, &ctx->ip.remote_port)) { @@ -1086,12 +1025,25 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf, ctx->error = errno; /* malformed address or bug in inet_ntop, try next address */ failf(data, "curl_sa_addr inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); + errno, curlx_strerror(errno, buffer, sizeof(buffer))); return CURLE_FAILED_INIT; } 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) { @@ -1101,9 +1053,8 @@ 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 = curlx_now(); + ctx->started_at = *Curl_pgrs_now(data); #ifdef SOCK_NONBLOCK /* Do not tuck SOCK_NONBLOCK into socktype when opensocket callback is set * because we would not know how socketype is about to be used in the @@ -1127,7 +1078,18 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, #ifdef USE_IPV6 if(ctx->addr.family == AF_INET6) { - set_ipv6_v6only(ctx->sock, 0); +#ifdef USE_WINSOCK + /* Turn on support for IPv4-mapped IPv6 addresses. + * Linux kernel, NetBSD, FreeBSD, Darwin, lwIP: default is off; + * Windows Vista and later: default is on; + * DragonFly BSD: acts like off, and dummy setting; + * OpenBSD and earlier Windows: unsupported. + * Linux: controlled by /proc/sys/net/ipv6/bindv6only. + */ + int on = 0; + (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_V6ONLY, + (void *)&on, sizeof(on)); +#endif infof(data, " Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port); } else @@ -1135,22 +1097,18 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, infof(data, " Trying %s:%d...", ctx->ip.remote_ip, ctx->ip.remote_port); #ifdef USE_IPV6 - is_tcp = (ctx->addr.family == AF_INET - || ctx->addr.family == AF_INET6) && - ctx->addr.socktype == SOCK_STREAM; + is_tcp = (ctx->addr.family == AF_INET || + ctx->addr.family == AF_INET6) && + 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(data, ctx->sock); - - nosigpipe(data, ctx->sock); - - Curl_sndbuf_init(ctx->sock); + tcpnodelay(cf, data, ctx->sock); if(is_tcp && data->set.tcp_keepalive) - tcpkeepalive(data, ctx->sock); + tcpkeepalive(cf, data, ctx->sock); if(data->set.fsockopt) { /* activate callback for setting socket options */ @@ -1176,7 +1134,8 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, #endif ) { result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family, - Curl_ipv6_scope(&ctx->addr.curl_sa_addr)); + Curl_ipv6_scope(&ctx->addr.curl_sa_addr), + ctx->transport); if(result) { if(result == CURLE_UNSUPPORTED_PROTOCOL) { /* The address family is not supported on this interface. @@ -1209,7 +1168,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) { @@ -1219,7 +1178,7 @@ out: } else if(isconnected) { set_local_ip(cf, data); - ctx->connected_at = curlx_now(); + ctx->connected_at = *Curl_pgrs_now(data); cf->connected = TRUE; } CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T, @@ -1264,8 +1223,8 @@ static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data, #elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */ if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, (void *)&optval, sizeof(optval)) < 0) - infof(data, "Failed to enable TCP Fast Open on fd %" FMT_SOCKET_T, - ctx->sock); + CURL_TRC_CF(data, cf, "Failed to enable TCP Fast Open on fd %" + FMT_SOCKET_T, ctx->sock); rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, ctx->addr.addrlen); #elif defined(MSG_FASTOPEN) /* old Linux */ @@ -1290,7 +1249,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; @@ -1310,12 +1268,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; } @@ -1332,17 +1290,17 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, rc = SOCKET_WRITABLE(ctx->sock, 0); if(rc == 0) { /* no connection yet */ - CURL_TRC_CF(data, cf, "not connected yet"); + CURL_TRC_CF(data, cf, "not connected yet on fd=%" FMT_SOCKET_T, ctx->sock); return CURLE_OK; } else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) { if(verifyconnect(ctx->sock, &ctx->error)) { /* we are connected with TCP, awesome! */ - ctx->connected_at = curlx_now(); + ctx->connected_at = *Curl_pgrs_now(data); set_local_ip(cf, data); *done = TRUE; cf->connected = TRUE; - CURL_TRC_CF(data, cf, "connected"); + CURL_TRC_CF(data, cf, "connected on fd=%" FMT_SOCKET_T, ctx->sock); return CURLE_OK; } } @@ -1354,18 +1312,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, - Curl_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); @@ -1414,35 +1368,36 @@ static CURLcode cf_socket_adjust_pollset(struct Curl_cfilter *cf, #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B #endif -static void win_update_sndbuf_size(struct cf_socket_ctx *ctx) +static void win_update_sndbuf_size(struct Curl_easy *data, + struct cf_socket_ctx *ctx) { ULONG ideal; DWORD ideallen; - struct curltime n = curlx_now(); - if(curlx_timediff(n, ctx->last_sndbuf_query_at) > 1000) { + if(curlx_ptimediff_ms(Curl_pgrs_now(data), + &ctx->last_sndbuf_query_at) > 1000) { if(!WSAIoctl(ctx->sock, SIO_IDEAL_SEND_BACKLOG_QUERY, 0, 0, - &ideal, sizeof(ideal), &ideallen, 0, 0) && + &ideal, sizeof(ideal), &ideallen, 0, 0) && ideal != ctx->sndbuf_size && !setsockopt(ctx->sock, SOL_SOCKET, SO_SNDBUF, (const char *)&ideal, sizeof(ideal))) { ctx->sndbuf_size = ideal; } - ctx->last_sndbuf_query_at = n; + ctx->last_sndbuf_query_at = *Curl_pgrs_now(data); } } #endif /* USE_WINSOCK */ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) { struct cf_socket_ctx *ctx = cf->ctx; curl_socket_t fdsave; - ssize_t nwritten; - size_t orig_len = len; + ssize_t rv; CURLcode result = CURLE_OK; + VERBOSE(size_t orig_len = len); (void)eos; *pnwritten = 0; @@ -1454,7 +1409,7 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(ctx->wblock_percent > 0) { unsigned char c = 0; Curl_rand_bytes(data, FALSE, &c, 1); - if(c >= ((100-ctx->wblock_percent)*256/100)) { + if(c >= ((100 - ctx->wblock_percent) * 256 / 100)) { CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len); cf->conn->sock[cf->sockindex] = fdsave; return CURLE_AGAIN; @@ -1471,15 +1426,15 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, #if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ if(cf->conn->bits.tcp_fastopen) { - nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN, - &ctx->addr.curl_sa_addr, ctx->addr.addrlen); + rv = sendto(ctx->sock, buf, len, MSG_FASTOPEN, + &ctx->addr.curl_sa_addr, ctx->addr.addrlen); cf->conn->bits.tcp_fastopen = FALSE; } else #endif - nwritten = swrite(ctx->sock, buf, len); + rv = swrite(ctx->sock, buf, len); - if(nwritten < 0) { + if(!curlx_sztouz(rv, pnwritten)) { int sockerr = SOCKERRNO; if( @@ -1495,23 +1450,21 @@ 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 { char buffer[STRERROR_LEN]; failf(data, "Send failure: %s", - Curl_strerror(sockerr, buffer, sizeof(buffer))); + curlx_strerror(sockerr, buffer, sizeof(buffer))); data->state.os_errno = sockerr; result = CURLE_SEND_ERROR; } } - else - *pnwritten = (size_t)nwritten; #ifdef USE_WINSOCK if(!result) - win_update_sndbuf_size(ctx); + win_update_sndbuf_size(data, ctx); #endif CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, %zu", @@ -1525,7 +1478,7 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, { struct cf_socket_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - ssize_t nread; + ssize_t rv; *pnread = 0; #ifdef DEBUGBUILD @@ -1533,22 +1486,21 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) { unsigned char c = 0; Curl_rand(data, &c, 1); - if(c >= ((100-ctx->rblock_percent)*256/100)) { + if(c >= ((100 - ctx->rblock_percent) * 256 / 100)) { CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len); return CURLE_AGAIN; } } 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 - nread = sread(ctx->sock, buf, len); + rv = sread(ctx->sock, buf, len); - if(nread < 0) { + if(!curlx_sztouz(rv, pnread)) { int sockerr = SOCKERRNO; if( @@ -1563,23 +1515,21 @@ 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 { char buffer[STRERROR_LEN]; failf(data, "Recv failure: %s", - Curl_strerror(sockerr, buffer, sizeof(buffer))); + curlx_strerror(sockerr, buffer, sizeof(buffer))); data->state.os_errno = sockerr; result = CURLE_RECV_ERROR; } } - else - *pnread = (size_t)nread; CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, result, *pnread); if(!result && !ctx->got_first_byte) { - ctx->first_byte_at = curlx_now(); + ctx->first_byte_at = *Curl_pgrs_now(data); ctx->got_first_byte = TRUE; } return result; @@ -1643,13 +1593,13 @@ 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; /* Check with 0 timeout if there are any events pending on the socket */ pfd[0].fd = ctx->sock; - pfd[0].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI; + pfd[0].events = POLLRDNORM | POLLIN | POLLRDBAND | POLLPRI; pfd[0].revents = 0; r = Curl_poll(pfd, 1, 0); @@ -1661,7 +1611,7 @@ static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive"); return TRUE; } - else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) { + else if(pfd[0].revents & (POLLERR | POLLHUP | POLLPRI | POLLNVAL)) { CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead"); return FALSE; } @@ -1693,7 +1643,8 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf, return CURLE_OK; case CF_QUERY_CONNECT_REPLY_MS: if(ctx->got_first_byte) { - timediff_t ms = curlx_timediff(ctx->first_byte_at, ctx->started_at); + timediff_t ms = curlx_ptimediff_ms(&ctx->first_byte_at, + &ctx->started_at); *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX; } else @@ -1754,8 +1705,8 @@ struct Curl_cftype Curl_cft_tcp = { CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport) + struct Curl_sockaddr_ex *addr, + uint8_t transport) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; @@ -1764,13 +1715,18 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, (void)data; (void)conn; DEBUGASSERT(transport == TRNSPRT_TCP); - ctx = calloc(1, sizeof(*ctx)); + if(!addr) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = cf_socket_ctx_init(ctx, ai, transport); + result = cf_socket_ctx_init(ctx, addr, transport); if(result) goto out; @@ -1779,21 +1735,56 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, out: *pcf = (!result) ? cf : NULL; if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); + curlx_safefree(cf); + curlx_safefree(ctx); } return result; } +#ifdef __linux__ +static void linux_quic_mtu(struct cf_socket_ctx *ctx) +{ + int val; + switch(ctx->addr.family) { +#ifdef IP_MTU_DISCOVER + case AF_INET: + val = IP_PMTUDISC_DO; + (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, + sizeof(val)); + break; +#endif +#ifdef IPV6_MTU_DISCOVER + case AF_INET6: + val = IPV6_PMTUDISC_DO; + (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, + sizeof(val)); + break; +#endif + } +} +#else +#define linux_quic_mtu(x) +#endif + +#if defined(UDP_GRO) && \ + (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) && \ + ((defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE)) +static void linux_quic_gro(struct cf_socket_ctx *ctx) +{ + int one = 1; + (void)setsockopt(ctx->sock, IPPROTO_UDP, UDP_GRO, &one, + (socklen_t)sizeof(one)); +} +#else +#define linux_quic_gro(x) +#endif + static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; int rc; - int one = 1; - - (void)one; /* QUIC needs a connected socket, nonblocking */ DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD); @@ -1802,7 +1793,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; @@ -1818,33 +1809,8 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, * non-blocking socket created by cf_socket_open() to it. Thus, we * do not need to call curlx_nonblock() in cf_udp_setup_quic() anymore. */ -#ifdef __linux__ - switch(ctx->addr.family) { -#ifdef IP_MTU_DISCOVER - case AF_INET: { - int val = IP_PMTUDISC_DO; - (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, - sizeof(val)); - break; - } -#endif -#ifdef IPV6_MTU_DISCOVER - case AF_INET6: { - int val = IPV6_PMTUDISC_DO; - (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, - sizeof(val)); - break; - } -#endif - } - -#if defined(UDP_GRO) && \ - (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) && \ - ((defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE)) - (void)setsockopt(ctx->sock, IPPROTO_UDP, UDP_GRO, &one, - (socklen_t)sizeof(one)); -#endif -#endif + linux_quic_mtu(ctx); + linux_quic_gro(ctx); return CURLE_OK; } @@ -1860,6 +1826,7 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf, *done = TRUE; return CURLE_OK; } + *done = FALSE; if(ctx->sock == CURL_SOCKET_BAD) { result = cf_socket_open(cf, data); @@ -1876,10 +1843,6 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf, FMT_SOCKET_T " (%s:%d)", ctx->sock, ctx->ip.local_ip, ctx->ip.local_port); } - else { - CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%" - FMT_SOCKET_T " (unconnected)", ctx->sock); - } *done = TRUE; cf->connected = TRUE; } @@ -1908,8 +1871,8 @@ struct Curl_cftype Curl_cft_udp = { CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport) + struct Curl_sockaddr_ex *addr, + uint8_t transport) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; @@ -1918,13 +1881,13 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, (void)data; (void)conn; DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC); - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = cf_socket_ctx_init(ctx, ai, transport); + result = cf_socket_ctx_init(ctx, addr, transport); if(result) goto out; @@ -1933,8 +1896,8 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, out: *pcf = (!result) ? cf : NULL; if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); + curlx_safefree(cf); + curlx_safefree(ctx); } return result; @@ -1962,8 +1925,8 @@ struct Curl_cftype Curl_cft_unix = { CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport) + struct Curl_sockaddr_ex *addr, + uint8_t transport) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; @@ -1972,13 +1935,13 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, (void)data; (void)conn; DEBUGASSERT(transport == TRNSPRT_UNIX); - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = cf_socket_ctx_init(ctx, ai, transport); + result = cf_socket_ctx_init(ctx, addr, transport); if(result) goto out; @@ -1987,8 +1950,8 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, out: *pcf = (!result) ? cf : NULL; if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); + curlx_safefree(cf); + curlx_safefree(ctx); } return result; @@ -1999,24 +1962,22 @@ static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf, { struct cf_socket_ctx *ctx = cf->ctx; timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT; - timediff_t other; - struct curltime now; + timediff_t other_ms; #ifndef CURL_DISABLE_FTP if(data->set.accepttimeout > 0) timeout_ms = data->set.accepttimeout; #endif - now = curlx_now(); /* check if the generic timeout possibly is set shorter */ - other = Curl_timeleft(data, &now, FALSE); - if(other && (other < timeout_ms)) - /* note that this also works fine for when other happens to be negative + 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 */ - timeout_ms = other; + timeout_ms = other_ms; else { /* subtract elapsed time */ - timeout_ms -= curlx_timediff(now, ctx->started_at); + timeout_ms -= curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->started_at); if(!timeout_ms) /* avoid returning 0 as that means no timeout! */ timeout_ms = -1; @@ -2037,16 +1998,16 @@ static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf, ctx->ip.remote_port = 0; plen = sizeof(ssrem); memset(&ssrem, 0, plen); - if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) { + if(getpeername(ctx->sock, (struct sockaddr *)&ssrem, &plen)) { int error = SOCKERRNO; failf(data, "getpeername() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); + error, curlx_strerror(error, buffer, sizeof(buffer))); return; } - if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, + if(!Curl_addr2string((struct sockaddr *)&ssrem, plen, ctx->ip.remote_ip, &ctx->ip.remote_port)) { failf(data, "ssrem inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); + errno, curlx_strerror(errno, buffer, sizeof(buffer))); return; } #else @@ -2061,12 +2022,13 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, bool *done) { struct cf_socket_ctx *ctx = cf->ctx; + char errbuf[STRERROR_LEN]; #ifdef USE_IPV6 struct Curl_sockaddr_storage add; #else struct sockaddr_in add; #endif - curl_socklen_t size = (curl_socklen_t) sizeof(add); + curl_socklen_t size = (curl_socklen_t)sizeof(add); curl_socket_t s_accepted = CURL_SOCKET_BAD; timediff_t timeout_ms; int socketstate = 0; @@ -2079,6 +2041,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, return CURLE_OK; } + *done = FALSE; timeout_ms = cf_tcp_accept_timeleft(cf, data); if(timeout_ms < 0) { /* if a timeout was already reached, bail out */ @@ -2088,8 +2051,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "Checking for incoming on fd=%" FMT_SOCKET_T " ip=%s:%d", ctx->sock, ctx->ip.local_ip, ctx->ip.local_port); - socketstate = Curl_socket_check(ctx->sock, CURL_SOCKET_BAD, - CURL_SOCKET_BAD, 0); + socketstate = SOCKET_READABLE(ctx->sock, 0); CURL_TRC_CF(data, cf, "socket_check -> %x", socketstate); switch(socketstate) { case -1: /* error */ @@ -2106,29 +2068,40 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, if(!incoming) { CURL_TRC_CF(data, cf, "nothing heard from the server yet"); - *done = FALSE; return CURLE_OK; } - if(!getsockname(ctx->sock, (struct sockaddr *) &add, &size)) { - size = sizeof(add); + size = sizeof(add); #ifdef HAVE_ACCEPT4 - s_accepted = accept4(ctx->sock, (struct sockaddr *) &add, &size, - SOCK_NONBLOCK | SOCK_CLOEXEC); + s_accepted = CURL_ACCEPT4(ctx->sock, (struct sockaddr *)&add, &size, + SOCK_NONBLOCK | SOCK_CLOEXEC); #else - s_accepted = CURL_ACCEPT(ctx->sock, (struct sockaddr *) &add, &size); + s_accepted = CURL_ACCEPT(ctx->sock, (struct sockaddr *)&add, &size); #endif - } - if(CURL_SOCKET_BAD == s_accepted) { - failf(data, "Error accept()ing server connect"); - return CURLE_FTP_PORT_FAILED; + 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; } - - infof(data, "Connection accepted from server"); #ifndef HAVE_ACCEPT4 - (void)curlx_nonblock(s_accepted, TRUE); /* enable non-blocking */ -#endif +#ifdef HAVE_FCNTL + if(fcntl(s_accepted, F_SETFD, FD_CLOEXEC) < 0) { + failf(data, "fcntl set CLOEXEC: %s", + curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf))); + Curl_socket_close(data, cf->conn, s_accepted); + return CURLE_FTP_ACCEPT_FAILED; + } +#endif /* HAVE_FCNTL */ + if(curlx_nonblock(s_accepted, TRUE) < 0) { + failf(data, "set socket NONBLOCK: %s", + curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf))); + Curl_socket_close(data, cf->conn, s_accepted); + return CURLE_FTP_ACCEPT_FAILED; + } +#endif /* !HAVE_ACCEPT4 */ + infof(data, "Connection accepted from server"); + /* Replace any filter on SECONDARY with one listening on this socket */ ctx->listening = FALSE; ctx->accepted = TRUE; @@ -2139,7 +2112,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, cf_tcp_set_accepted_remote_ip(cf, data); set_local_ip(cf, data); ctx->active = TRUE; - ctx->connected_at = curlx_now(); + ctx->connected_at = *Curl_pgrs_now(data); cf->connected = TRUE; CURL_TRC_CF(data, cf, "accepted_set(sock=%" FMT_SOCKET_T ", remote=%s port=%d)", @@ -2191,7 +2164,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, Curl_conn_cf_discard_all(data, conn, sockindex); DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD); - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -2205,7 +2178,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, goto out; Curl_conn_cf_add(data, conn, sockindex, cf); - ctx->started_at = curlx_now(); + ctx->started_at = *Curl_pgrs_now(data); conn->sock[sockindex] = ctx->sock; set_local_ip(cf, data); CURL_TRC_CF(data, cf, "set filter for listen socket fd=%" FMT_SOCKET_T @@ -2214,8 +2187,8 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, out: if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); + curlx_safefree(cf); + curlx_safefree(ctx); } return result; } diff --git a/lib/cf-socket.h b/lib/cf-socket.h index 88c08fe7c0..40c001cc14 100644 --- a/lib/cf-socket.h +++ b/lib/cf-socket.h @@ -25,8 +25,7 @@ ***************************************************************************/ #include "curl_setup.h" -#include "curlx/nonblock.h" /* for curlx_nonblock() */ -#include "sockaddr.h" +#include "sockaddr.h" /* required for Curl_sockaddr_storage */ struct Curl_addrinfo; struct Curl_cfilter; @@ -36,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. @@ -48,18 +47,23 @@ struct Curl_sockaddr_ex { int protocol; unsigned int addrlen; union { - struct sockaddr addr; - struct Curl_sockaddr_storage buff; - } _sa_ex_u; + struct sockaddr sa; + struct Curl_sockaddr_storage buf; + } addr; }; -#define curl_sa_addr _sa_ex_u.addr +#define curl_sa_addr addr.sa +#define curl_sa_addrbuf addr.buf /* * Parse interface option, and return the interface name and the host part. -*/ + */ CURLcode Curl_parse_interface(const char *input, char **dev, char **iface, char **host); +CURLcode Curl_socket_addr_from_ai(struct Curl_sockaddr_ex *addr, + const struct Curl_addrinfo *ai, + uint8_t transport); + /* * Create a socket based on info from 'conn' and 'ai'. * @@ -70,27 +74,17 @@ CURLcode Curl_parse_interface(const char *input, CURLcode Curl_socket_open(struct Curl_easy *data, const struct Curl_addrinfo *ai, struct Curl_sockaddr_ex *addr, - int transport, + 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://support.microsoft.com/kb/823764 - - 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. @@ -101,8 +95,8 @@ void Curl_sndbuf_init(curl_socket_t sockfd); CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport); + struct Curl_sockaddr_ex *addr, + uint8_t transport); /** * Creates a cfilter that opens a UDP socket to the given address @@ -114,8 +108,8 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport); + struct Curl_sockaddr_ex *addr, + uint8_t transport); /** * Creates a cfilter that opens a UNIX socket to the given address @@ -127,8 +121,8 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport); + struct Curl_sockaddr_ex *addr, + uint8_t transport); /** * Creates a cfilter that keeps a listening socket. @@ -157,7 +151,7 @@ CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t *psock, const struct Curl_sockaddr_ex **paddr, - struct ip_quadruple *pip); + struct ip_quadruple *pip) WARN_UNUSED_RESULT; extern struct Curl_cftype Curl_cft_tcp; extern struct Curl_cftype Curl_cft_udp; diff --git a/lib/cfilters.c b/lib/cfilters.c index efd2ac6f63..c3e79bb78c 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #include "urldata.h" @@ -29,25 +28,17 @@ #include "cfilters.h" #include "connect.h" #include "url.h" -#include "sendf.h" -#include "sockaddr.h" /* required for Curl_sockaddr_storage */ -#include "multiif.h" +#include "curl_trc.h" #include "progress.h" #include "select.h" -#include "curlx/warnless.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.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) +UNITTEST void Curl_cf_def_close(struct Curl_cfilter *cf, + struct Curl_easy *data); +UNITTEST void Curl_cf_def_close(struct Curl_cfilter *cf, + struct Curl_easy *data) { cf->connected = FALSE; if(cf->next) @@ -64,9 +55,6 @@ CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, return CURLE_OK; } -static void conn_report_connect_stats(struct Curl_easy *data, - struct connectdata *conn); - CURLcode Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) @@ -86,7 +74,7 @@ bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, } CURLcode Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) { if(cf->next) @@ -144,29 +132,29 @@ void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, */ cf->next = NULL; cf->cft->destroy(cf, data); - free(cf); + curlx_free(cf); cf = cfn; } } } 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) @@ -174,7 +162,6 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) struct Curl_cfilter *cf; CURLcode result = CURLE_OK; timediff_t timeout_ms; - struct curltime now; DEBUGASSERT(data->conn); @@ -192,14 +179,11 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) } *done = FALSE; - now = curlx_now(); if(!Curl_shutdown_started(data, sockindex)) { - CURL_TRC_M(data, "shutdown start on%s connection", - sockindex ? " secondary" : ""); - Curl_shutdown_start(data, sockindex, 0, &now); + Curl_shutdown_start(data, sockindex, 0); } else { - timeout_ms = Curl_shutdown_timeleft(data->conn, sockindex, &now); + timeout_ms = Curl_shutdown_timeleft(data, data->conn, sockindex); if(timeout_ms < 0) { /* info message, since this might be regarded as acceptable */ infof(data, "shutdown timeout"); @@ -228,14 +212,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) @@ -246,19 +230,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 void *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); @@ -297,12 +281,11 @@ CURLcode Curl_cf_recv_bufq(struct Curl_cfilter *cf, } static CURLcode cf_bufq_writer(void *writer_ctx, - const unsigned char *buf, size_t buflen, + const uint8_t *buf, size_t buflen, size_t *pnwritten) { struct cf_io_ctx *io = writer_ctx; - return Curl_conn_cf_send(io->cf, io->data, (const char *)buf, - buflen, FALSE, pnwritten); + return Curl_conn_cf_send(io->cf, io->data, buf, buflen, FALSE, pnwritten); } CURLcode Curl_cf_send_bufq(struct Curl_cfilter *cf, @@ -334,7 +317,7 @@ CURLcode Curl_cf_create(struct Curl_cfilter **pcf, CURLcode result = CURLE_OUT_OF_MEMORY; DEBUGASSERT(cft); - cf = calloc(1, sizeof(*cf)); + cf = curlx_calloc(1, sizeof(*cf)); if(!cf) goto out; @@ -348,18 +331,17 @@ out: 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"); } @@ -383,29 +365,26 @@ void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at, *pnext = tail; } -bool Curl_conn_cf_discard_sub(struct Curl_cfilter *cf, - struct Curl_cfilter *discard, - struct Curl_easy *data, - bool destroy_always) +bool Curl_conn_cf_discard(struct Curl_cfilter **pcf, + struct Curl_easy *data) { - struct Curl_cfilter **pprev = &cf->next; + struct Curl_cfilter *cf = pcf ? *pcf : NULL; bool found = FALSE; - - /* remove from sub-chain and destroy */ - DEBUGASSERT(cf); - while(*pprev) { - if(*pprev == cf) { - *pprev = discard->next; - discard->next = NULL; - found = TRUE; - break; + if(cf) { + if(cf->conn) { + /* unlink if present in connection filter chain */ + struct Curl_cfilter **pprev = &cf->conn->cfilter[cf->sockindex]; + while(*pprev) { + if(*pprev == *pcf) { + *pprev = (*pcf)->next; + cf->next = NULL; + found = TRUE; + break; + } + pprev = &((*pprev)->next); + } } - pprev = &((*pprev)->next); - } - if(found || destroy_always) { - discard->next = NULL; - discard->cft->destroy(discard, data); - free(discard); + Curl_conn_cf_discard_chain(pcf, data); } return found; } @@ -426,7 +405,7 @@ void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) } CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) { if(cf) @@ -444,7 +423,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) { @@ -467,6 +446,69 @@ 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); + } +} + +static void conn_remove_setup_filters(struct Curl_easy *data, + int sockindex) +{ + struct Curl_cfilter **anchor = &data->conn->cfilter[sockindex]; + while(*anchor) { + struct Curl_cfilter *cf = *anchor; + if(cf->connected && (cf->cft->flags & CF_TYPE_SETUP)) { + *anchor = cf->next; + cf->next = NULL; + CURL_TRC_CF(data, cf, "removing connected setup filter"); + cf->cft->destroy(cf, data); + curlx_free(cf); + } + else + anchor = &cf->next; + } +} + CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex, bool blocking, @@ -484,13 +526,18 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, if(!CONN_SOCK_IDX_VALID(sockindex)) return CURLE_BAD_FUNCTION_ARGUMENT; + if(data->conn->scheme->flags & PROTOPT_NONETWORK) { + *done = TRUE; + return CURLE_OK; + } + cf = data->conn->cfilter[sockindex]; if(!cf) { *done = FALSE; return CURLE_FAILED_INIT; } - *done = cf->connected; + *done = (bool)cf->connected; if(*done) return CURLE_OK; @@ -512,17 +559,15 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, * persist information at the connection. E.g. cf-socket sets the * socket and ip related information. */ cf_cntrl_update_info(data, data->conn); - conn_report_connect_stats(data, data->conn); - data->conn->keepalive = curlx_now(); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - result = cf_verboseconnect(data, cf); -#endif + conn_report_connect_stats(cf, data); + data->conn->keepalive = *Curl_pgrs_now(data); + VERBOSE(result = cf_verboseconnect(data, cf)); + conn_remove_setup_filters(data, sockindex); goto out; } else if(result) { - CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", - result); - conn_report_connect_stats(data, data->conn); + CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", result); + conn_report_connect_stats(cf, data); goto out; } @@ -530,7 +575,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, goto out; else { /* check allowed time left */ - const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + const timediff_t timeout_ms = Curl_timeleft_ms(data); curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); int rc; @@ -546,8 +591,9 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, Curl_pollfds_reset(&cpfds); /* In general, we want to send after connect, wait on that. */ if(sockfd != CURL_SOCKET_BAD) - Curl_pollset_set_out_only(data, &ps, sockfd); - result = Curl_conn_adjust_pollset(data, data->conn, &ps); + result = Curl_pollset_set_out_only(data, &ps, sockfd); + if(!result) + result = Curl_conn_adjust_pollset(data, data->conn, &ps); if(result) goto out; result = Curl_pollfds_add_ps(&cpfds, &ps); @@ -586,7 +632,11 @@ bool Curl_conn_is_connected(struct connectdata *conn, int sockindex) if(!CONN_SOCK_IDX_VALID(sockindex)) return FALSE; cf = conn->cfilter[sockindex]; - return cf && cf->connected; + if(cf) + return (bool)cf->connected; + else if(conn->scheme->flags & PROTOPT_NONETWORK) + return TRUE; + return FALSE; } bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex) @@ -626,14 +676,16 @@ bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex) bool Curl_conn_get_ssl_info(struct Curl_easy *data, struct connectdata *conn, int sockindex, + int query, struct curl_tlssessioninfo *info) { if(!CONN_SOCK_IDX_VALID(sockindex)) return FALSE; if(Curl_conn_is_ssl(conn, sockindex)) { struct Curl_cfilter *cf = conn->cfilter[sockindex]; - CURLcode result = cf ? cf->cft->query(cf, data, CF_QUERY_SSL_INFO, - NULL, (void *)info) : CURLE_UNKNOWN_OPTION; + CURLcode result = cf ? + cf->cft->query(cf, data, query, NULL, (void *)info) : + CURLE_UNKNOWN_OPTION; return !result; } return FALSE; @@ -661,7 +713,7 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex) for(; cf; cf = cf->next) { if(cf->cft->flags & CF_TYPE_MULTIPLEX) return TRUE; - if(cf->cft->flags & (CF_TYPE_IP_CONNECT|CF_TYPE_SSL)) + if(cf->cft->flags & (CF_TYPE_IP_CONNECT | CF_TYPE_SSL)) return FALSE; } return FALSE; @@ -674,6 +726,30 @@ unsigned char Curl_conn_get_transport(struct Curl_easy *data, return Curl_conn_cf_get_transport(cf, data); } +int Curl_socktype_for_transport(uint8_t transport) +{ + switch(transport) { + case TRNSPRT_TCP: + return SOCK_STREAM; + case TRNSPRT_UNIX: + return SOCK_STREAM; + default: /* UDP and QUIC */ + return SOCK_DGRAM; + } +} + +int Curl_protocol_for_transport(uint8_t transport) +{ + switch(transport) { + case TRNSPRT_TCP: + return IPPROTO_TCP; + case TRNSPRT_UNIX: + return IPPROTO_IP; + default: /* UDP and QUIC */ + return IPPROTO_UDP; + } +} + const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data, struct connectdata *conn) { @@ -699,7 +775,7 @@ unsigned char Curl_conn_http_version(struct Curl_easy *data, v = (unsigned char)value; break; } - if(cf->cft->flags & (CF_TYPE_IP_CONNECT|CF_TYPE_SSL)) + if(cf->cft->flags & (CF_TYPE_IP_CONNECT | CF_TYPE_SSL)) break; } return (unsigned char)(result ? 0 : v); @@ -777,12 +853,19 @@ CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data, return result; } +/* + * Return values: + * -1 = error + * 0 = timeout + * N = number of structures with non zero revent fields + */ int Curl_conn_cf_poll(struct Curl_cfilter *cf, struct Curl_easy *data, timediff_t timeout_ms) { struct easy_pollset ps; - int result; + int rc; + CURLcode result; DEBUGASSERT(cf); DEBUGASSERT(data); @@ -791,41 +874,47 @@ int Curl_conn_cf_poll(struct Curl_cfilter *cf, result = Curl_conn_cf_adjust_pollset(cf, data, &ps); if(!result) - result = Curl_pollset_poll(data, &ps, timeout_ms); + rc = Curl_pollset_poll(data, &ps, timeout_ms); + else + rc = -1; Curl_pollset_cleanup(&ps); - return result; + return rc; } void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex, const char **phost, int *pport) { struct Curl_cfilter *cf, *cf_proxy = NULL; + int portarg = -1; if(!data->conn) { DEBUGASSERT(0); *phost = ""; - *pport = -1; + if(pport) + *pport = -1; return; } cf = CONN_SOCK_IDX_VALID(sockindex) ? data->conn->cfilter[sockindex] : NULL; /* Find the "lowest" tunneling proxy filter that has not connected yet. */ while(cf && !cf->connected) { - if((cf->cft->flags & (CF_TYPE_IP_CONNECT|CF_TYPE_PROXY)) == - (CF_TYPE_IP_CONNECT|CF_TYPE_PROXY)) - cf_proxy = cf; + if((cf->cft->flags & (CF_TYPE_IP_CONNECT | CF_TYPE_PROXY)) == + (CF_TYPE_IP_CONNECT | CF_TYPE_PROXY)) + cf_proxy = cf; cf = cf->next; } /* cf_proxy (!= NULL) is not connected yet. It is talking * to an interim host and any authentication or other things apply * to this interim host and port. */ if(!cf_proxy || cf_proxy->cft->query(cf_proxy, data, CF_QUERY_HOST_PORT, - pport, CURL_UNCONST(phost))) { + &portarg, CURL_UNCONST(phost))) { /* Everything connected or query unsuccessful, the overall * connection's destination is the answer */ *phost = data->conn->host.name; - *pport = data->conn->remote_port; + portarg = data->conn->remote_port; } + if(pport) + *pport = portarg; } CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf, @@ -848,7 +937,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) @@ -934,48 +1023,11 @@ Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex) return cf ? cf_get_remote_addr(cf, data) : NULL; } -void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex) -{ - if(data->conn && CONN_SOCK_IDX_VALID(sockindex)) { - struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; - if(cf) - (void)Curl_conn_cf_cntrl(cf, data, TRUE, - CF_CTRL_FORGET_SOCKET, 0, NULL); - fake_sclose(data->conn->sock[sockindex]); - data->conn->sock[sockindex] = CURL_SOCKET_BAD; - } -} - -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); + return cf_cntrl_all(data->conn, data, FALSE, CF_CTRL_DATA_SETUP, 0, NULL); } -CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data) -{ - return cf_cntrl_all(data->conn, data, FALSE, - CF_CTRL_DATA_IDLE, 0, NULL); -} - - CURLcode Curl_conn_flush(struct Curl_easy *data, int sockindex) { if(!CONN_SOCK_IDX_VALID(sockindex)) @@ -1008,35 +1060,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_easy *data, - struct connectdata *conn) -{ - struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET]; - if(cf) { - struct curltime connected; - struct curltime appconnected; - - memset(&connected, 0, sizeof(connected)); - cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected); - if(connected.tv_sec || connected.tv_usec) - Curl_pgrsTimeWas(data, TIMER_CONNECT, connected); - - memset(&appconnected, 0, sizeof(appconnected)); - cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected); - if(appconnected.tv_sec || appconnected.tv_usec) - Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected); - } -} - bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn, bool *input_pending) { @@ -1072,7 +1095,7 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, result = cf ? cf->cft->query(cf, data, CF_QUERY_MAX_CONCURRENT, &n, NULL) : CURLE_UNKNOWN_OPTION; /* If no filter answered the query, the default is a non-multiplexed - * connection with limit 1. Otherwise, the the query may return 0 + * connection with limit 1. Otherwise, the query may return 0 * for connections that are in shutdown, e.g. server HTTP/2 GOAWAY. */ return (result || n < 0) ? 1 : (size_t)n; } @@ -1103,23 +1126,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); @@ -1128,18 +1151,18 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, return CURLE_BAD_FUNCTION_ARGUMENT; #ifdef DEBUGBUILD if(write_len) { - /* Allow debug builds to override this logic to force short sends - */ + /* Allow debug builds to override this logic to force short sends */ 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/lib/cfilters.h b/lib/cfilters.h index 815b72a6e8..fc77e61dfe 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curlx/timediff.h" struct bufq; @@ -89,7 +88,7 @@ typedef bool Curl_cft_data_pending(struct Curl_cfilter *cf, typedef CURLcode Curl_cft_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* transfer */ - const void *buf, /* data to write */ + const uint8_t *buf, /* data to write */ size_t len, /* amount to write */ bool eos, /* last chunk */ size_t *pnwritten); /* how much sent */ @@ -116,16 +115,16 @@ typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf, * "ignored" meaning return values are ignored and the event is distributed * to all filters in the chain. Overall result is always CURLE_OK. */ -/* data event arg1 arg2 return */ -#define CF_CTRL_DATA_SETUP 4 /* 0 NULL first fail */ -#define CF_CTRL_DATA_IDLE 5 /* 0 NULL first fail */ -#define CF_CTRL_DATA_PAUSE 6 /* on/off NULL first fail */ -#define CF_CTRL_DATA_DONE 7 /* premature NULL ignored */ -#define CF_CTRL_DATA_DONE_SEND 8 /* 0 NULL ignored */ +/* data event arg1 arg2 return */ +#define CF_CTRL_DATA_SETUP 4 /* 0 NULL first fail */ +/* 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 */ /* update conn info at connection and data */ -#define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0 NULL ignored */ -#define CF_CTRL_FORGET_SOCKET (256+1) /* 0 NULL ignored */ -#define CF_CTRL_FLUSH (256+2) /* 0 NULL first fail */ +#define CF_CTRL_CONN_INFO_UPDATE (256 + 0) /* 0 NULL ignored */ +#define CF_CTRL_FORGET_SOCKET (256 + 1) /* 0 NULL ignored */ +#define CF_CTRL_FLUSH (256 + 2) /* 0 NULL first fail */ /** * Handle event/control for the filter. @@ -135,7 +134,6 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, struct Curl_easy *data, int event, int arg1, void *arg2); - /** * Queries to ask via a `Curl_cft_query *query` method on a cfilter chain. * - MAX_CONCURRENT: the maximum number of parallel transfers the filter @@ -172,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 *` */ @@ -201,30 +199,33 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf, * CF_TYPE_MULTIPLEX: provides multiplexing of easy handles * CF_TYPE_PROXY provides proxying * CF_TYPE_HTTP implement a version of the HTTP protocol + * CF_TYPE_SETUP filter is only needed for connection setup and + * can be removed once connected */ #define CF_TYPE_IP_CONNECT (1 << 0) #define CF_TYPE_SSL (1 << 1) #define CF_TYPE_MULTIPLEX (1 << 2) #define CF_TYPE_PROXY (1 << 3) #define CF_TYPE_HTTP (1 << 4) +#define CF_TYPE_SETUP (1 << 5) /* A connection filter type, e.g. specific implementation. */ struct Curl_cftype { - const char *name; /* name of the filter type */ - int flags; /* flags of filter type */ - int log_level; /* log level for such filters */ - Curl_cft_destroy_this *destroy; /* destroy resources of this cf */ - Curl_cft_connect *do_connect; /* establish connection */ - Curl_cft_close *do_close; /* close conn */ - Curl_cft_shutdown *do_shutdown; /* shutdown conn */ + const char *name; /* name of the filter type */ + int flags; /* flags of filter type */ + int log_level; /* log level for such filters */ + Curl_cft_destroy_this *destroy; /* destroy resources of this cf */ + Curl_cft_connect *do_connect; /* establish connection */ + Curl_cft_close *do_close; /* close conn */ + Curl_cft_shutdown *do_shutdown; /* shutdown conn */ Curl_cft_adjust_pollset *adjust_pollset; /* adjust transfer poll set */ - Curl_cft_data_pending *has_data_pending;/* conn has data pending */ - Curl_cft_send *do_send; /* send data */ - Curl_cft_recv *do_recv; /* receive data */ - Curl_cft_cntrl *cntrl; /* events/control */ - Curl_cft_conn_is_alive *is_alive; /* FALSE if conn is dead, Jim! */ - Curl_cft_conn_keep_alive *keep_alive; /* try to keep it alive */ - Curl_cft_query *query; /* query filter chain */ + Curl_cft_data_pending *has_data_pending; /* conn has data pending */ + Curl_cft_send *do_send; /* send data */ + Curl_cft_recv *do_recv; /* receive data */ + Curl_cft_cntrl *cntrl; /* events/control */ + Curl_cft_conn_is_alive *is_alive; /* FALSE if conn is dead, Jim! */ + Curl_cft_conn_keep_alive *keep_alive; /* try to keep it alive */ + Curl_cft_query *query; /* query filter chain */ }; /* A connection filter instance, e.g. registered at a connection */ @@ -250,7 +251,7 @@ CURLcode Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data); CURLcode Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten); CURLcode Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, size_t *pnread); @@ -297,16 +298,12 @@ void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at, struct Curl_cfilter *cf_new); /** - * Discard, e.g. remove and destroy `discard` iff - * it still is in the filter chain below `cf`. If `discard` - * is no longer found beneath `cf` return FALSE. - * if `destroy_always` is TRUE, will call `discard`s destroy - * function and free it even if not found in the subchain. + * Extract filter `*pcf` from its connection filter chain. + * Destroy `*pcf`, even if it was not part of the chain and NULL it. + * Returns TRUE of cf has been part of chain. */ -bool Curl_conn_cf_discard_sub(struct Curl_cfilter *cf, - struct Curl_cfilter *discard, - struct Curl_easy *data, - bool destroy_always); +bool Curl_conn_cf_discard(struct Curl_cfilter **pcf, + struct Curl_easy *data); /** * Discard all cfilters starting with `*pcf` and clearing it afterwards. @@ -318,16 +315,14 @@ 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, bool *done); void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data); CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten); CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, size_t *pnread); @@ -353,10 +348,13 @@ bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf, unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf, struct Curl_easy *data); +int Curl_socktype_for_transport(uint8_t transport); +int Curl_protocol_for_transport(uint8_t transport); + 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 @@ -396,12 +394,13 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex); bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex); /* - * Fill `info` with information about the TLS instance securing - * the connection when available, otherwise e.g. when - * Curl_conn_is_ssl() is FALSE, return FALSE. + * Fill `info` with information about the TLS instance securing the connection + * when available, otherwise e.g. when Curl_conn_is_ssl() is FALSE, return + * FALSE. 'query' should be CF_QUERY_SSL_INFO or CF_QUERY_SSL_CTX_INFO. */ bool Curl_conn_get_ssl_info(struct Curl_easy *data, struct connectdata *conn, int sockindex, + int query, struct curl_tlssessioninfo *info); CURLcode Curl_conn_get_ip_info(struct Curl_easy *data, @@ -430,14 +429,14 @@ const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data, /** * Close the filter chain at `sockindex` for connection `data->conn`. - * Filters remain in place and may be connected again afterwards. + * Filters remain in place and may be connected again afterwards. */ void Curl_conn_close(struct Curl_easy *data, int sockindex); /** * Shutdown the connection at `sockindex` non-blocking, using timeout * from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS. - * Will return CURLE_OK and *done == FALSE if not finished. + * Return CURLE_OK and *done == FALSE if not finished. */ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done); @@ -512,12 +511,12 @@ CURLcode Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf, * in `*pnwritten` or on error. */ CURLcode Curl_cf_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t len, bool eos, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten); /** * Receive bytes from connection filter `cf` into `bufq`. - * Convenience wrappter around `Curl_bufq_sipn()`, + * Convenience wrapper around `Curl_bufq_sipn()`, * so users do not have to implement a callback. */ CURLcode Curl_cf_recv_bufq(struct Curl_cfilter *cf, @@ -543,12 +542,6 @@ CURLcode Curl_cf_send_bufq(struct Curl_cfilter *cf, */ CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data); -/** - * Notify connection filters that now would be a good time to - * perform any idle, e.g. time related, actions. - */ -CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data); - /** * Notify connection filters that the transfer represented by `data` * is done with sending data (e.g. has uploaded everything). @@ -579,10 +572,6 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data, struct connectdata *conn, int sockindex); -#ifdef UNITTESTS -void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data); -#endif - /** * Get the remote hostname and port that the connection is currently * talking to (or will talk to). @@ -618,21 +607,19 @@ int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd); /* * Receive data on the connection, using FIRSTSOCKET/SECONDARYSOCKET. - * Will return CURLE_AGAIN iff blocked on receiving. + * 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. + * 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); - /** * Types and macros used to keep the current easy handle in filter calls, * allowing for nested invocations. See #10336. @@ -664,7 +651,7 @@ struct cf_call_data { * a member in the cfilter's `ctx`. * * #define CF_CTX_CALL_DATA(cf) -> struct cf_call_data instance -*/ + */ #ifdef DEBUGBUILD @@ -698,7 +685,6 @@ struct cf_call_data { #endif /* !DEBUGBUILD */ -#define CF_DATA_CURRENT(cf) \ - ((cf)? (CF_CTX_CALL_DATA(cf).data) : NULL) +#define CF_DATA_CURRENT(cf) ((cf) ? (CF_CTX_CALL_DATA(cf).data) : NULL) #endif /* HEADER_CURL_CFILTERS_H */ diff --git a/lib/config-mac.h b/lib/config-mac.h index 6da544fe8f..fd9d3e7b60 100644 --- a/lib/config-mac.h +++ b/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 @@ -54,7 +51,6 @@ #define HAVE_SYS_UTIME_H 1 #define HAVE_SYS_IOCTL_H 1 #define HAVE_ALARM 1 -#define HAVE_FTRUNCATE 1 #define HAVE_UTIME 1 #define HAVE_SELECT 1 #define HAVE_SOCKET 1 @@ -62,18 +58,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 +76,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/lib/config-os400.h b/lib/config-os400.h index bccdb4a897..3d8d59c97e 100644 --- a/lib/config-os400.h +++ b/lib/config-os400.h @@ -30,260 +30,263 @@ #pragma enum(int) -/* Define cpu-machine-OS */ -#ifndef CURL_OS -#define CURL_OS "OS/400" -#endif +/* ---------------------------------------------------------------- */ +/* Global configuration parameters: normally generated by autoconf. */ +/* ---------------------------------------------------------------- */ -/* 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 - * a local wrapper in setup-os400.h) in a threadsafe way: we can then - * configure getaddrinfo() as such and get rid of gethostbyname_r() without - * loss of threadsafeness. */ -#undef HAVE_GETHOSTBYNAME_R -#undef HAVE_GETHOSTBYNAME_R_3 -#undef HAVE_GETHOSTBYNAME_R_5 -#undef HAVE_GETHOSTBYNAME_R_6 -#define HAVE_GETADDRINFO -#define HAVE_GETADDRINFO_THREADSAFE +/* Location of default ca bundle */ +/* Use the system keyring as the default CA bundle. */ +#define CURL_CA_BUNDLE "/QIBM/UserData/ICSS/Cert/Server/DEFAULT.KDB" -/* Define if you need the _REENTRANT define for some functions */ -#undef NEED_REENTRANT - -/* Define if you want to enable IPv6 support */ -#define 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 to 1 if you have the alarm function. */ -#define HAVE_ALARM 1 - -/* 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 - -/* Define if you have the `geteuid' function. */ -#define HAVE_GETEUID - -/* Define if you have the `gethostname' function. */ -#define HAVE_GETHOSTNAME - -/* Define if you have the `getpass_r' function. */ -#undef HAVE_GETPASS_R - -/* Define to 1 if you have the getpeername function. */ -#define HAVE_GETPEERNAME 1 - -/* Define if you have the `getpwuid' function. */ -#define HAVE_GETPWUID - -/* Define to 1 if you have the getsockname function. */ -#define HAVE_GETSOCKNAME 1 - -/* 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 GSS API. */ -#define HAVE_GSSAPI - -/* Define if you have the GNU gssapi libraries */ -#undef HAVE_GSSGNU - -/* Define if you have the header file. */ -#define HAVE_NETDB_H - -/* Define if you have the header file. */ -#define HAVE_NETINET_IN_H - -/* Define if you have the header file. */ -#define HAVE_NET_IF_H - -/* Define if you have the header file. */ -#define HAVE_PWD_H - -/* Define if you have the `select' function. */ -#define HAVE_SELECT - -/* Define if you have the `sigaction' function. */ -#define HAVE_SIGACTION - -/* Define if you have the `signal' function. */ -#undef HAVE_SIGNAL - -/* Define if you have the `socket' function. */ -#define HAVE_SOCKET - - -/* The following define is needed on OS400 to enable strcmpi(), stricmp() and - strdup(). */ -#define __cplusplus__strings__ - -/* Define if you have the `strcasecmp' function. */ -#undef HAVE_STRCASECMP - -/* Define if you have the `strcmpi' function. */ -#define HAVE_STRCMPI - -/* 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 - -/* Define if you have the header file. */ -#undef HAVE_STROPTS_H - -/* Define if you have the header file. */ -#define 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_SYS_UN_H - -/* Define if you have the header file. */ -#define HAVE_SYS_IOCTL_H - -/* Define if you have the header file. */ -#undef 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 - -/* 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 - -/* The size of `size_t', as computed by sizeof. */ -#define SIZEOF_SIZE_T 4 - -/* The size of `curl_off_t', as computed by sizeof. */ -#define SIZEOF_CURL_OFF_T 8 - -/* Define this if you have struct sockaddr_storage */ -#define HAVE_STRUCT_SOCKADDR_STORAGE - -/* Define if you have the ANSI C header files. */ -#define 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 for large files, on AIX-style hosts. */ -#define _LARGE_FILES - -/* Define to empty if `const' does not conform to ANSI C. */ -#undef const - -/* Define to `unsigned' if does not define. */ -#undef size_t - -/* Define if you have a working ioctl FIONBIO function. */ -#define HAVE_IOCTL_FIONBIO - -/* Define if you have a working ioctl SIOCGIFADDR function. */ -#define HAVE_IOCTL_SIOCGIFADDR - -/* To disable LDAP */ -#undef CURL_DISABLE_LDAP /* Definition to make a library symbol externally visible. */ #define CURL_EXTERN_SYMBOL -/* Define if you have the ldap_url_parse procedure. */ -/* #define HAVE_LDAP_URL_PARSE */ /* Disabled because of an IBM bug. */ +/* cpu-machine-OS */ +#ifndef CURL_OS +#define CURL_OS "OS/400" +#endif -/* Define if you have the recv function. */ -#define HAVE_RECV +/* Define to 1 if you have the alarm function. */ +#define HAVE_ALARM 1 -/* Define to the type of arg 1 for recv. */ -#define RECV_TYPE_ARG1 int +/* Define if you have the header file. */ +#define HAVE_ARPA_INET_H 1 -/* Define to the type of arg 2 for recv. */ -#define RECV_TYPE_ARG2 char * +/* if you have */ +#define HAVE_DIRENT_H -/* Define to the type of arg 3 for recv. */ -#define RECV_TYPE_ARG3 int +/* Define to 1 if you have the fcntl function. */ +#define HAVE_FCNTL 1 -/* Define to the type of arg 4 for recv. */ -#define RECV_TYPE_ARG4 int +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 -/* Define to the function return type for recv. */ -#define RECV_TYPE_RETV int +/* Define to 1 if you have a working fcntl O_NONBLOCK function. */ +#define HAVE_FCNTL_O_NONBLOCK 1 -/* Define if you have the send function. */ -#define HAVE_SEND +/* Define to 1 if you have the freeaddrinfo function. */ +#define HAVE_FREEADDRINFO 1 -/* Define to the type of arg 1 for send. */ -#define SEND_TYPE_ARG1 int +/* Define to 1 if you have a working getaddrinfo function. */ +#define HAVE_GETADDRINFO 1 -/* Define to the type qualifier of arg 2 for send. */ -#define SEND_QUAL_ARG2 +/* Define to 1 if the getaddrinfo function is thread-safe. */ +#define HAVE_GETADDRINFO_THREADSAFE 1 -/* Define to the type of arg 2 for send. */ -#define SEND_TYPE_ARG2 char * +/* Define to 1 if you have the 'geteuid' function. */ +#define HAVE_GETEUID 1 -/* Define to the type of arg 3 for send. */ -#define SEND_TYPE_ARG3 int +/* Define to 1 if you have the gethostname function. */ +#define HAVE_GETHOSTNAME 1 -/* Define to the type of arg 4 for send. */ -#define SEND_TYPE_ARG4 int +/* Define to 1 if you have the getpeername function. */ +#define HAVE_GETPEERNAME 1 -/* Define to the function return type for send. */ -#define SEND_TYPE_RETV int +/* Define to 1 if you have the 'getppid' function. */ +#define HAVE_GETPPID 1 + +/* Define to 1 if you have the 'getpwuid' function. */ +#define HAVE_GETPWUID 1 + +/* Define to 1 if you have the getsockname function. */ +#define HAVE_GETSOCKNAME 1 + +/* Define to 1 if you have the 'gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* if you have GSS-API libraries */ +#define HAVE_GSSAPI + +/* Define to 1 if you have a working ioctl FIONBIO function. */ +#define HAVE_IOCTL_FIONBIO 1 + +/* Define to 1 if you have a working ioctl SIOCGIFADDR function. */ +#define HAVE_IOCTL_SIOCGIFADDR 1 + +/* Define to 1 if you have the lber.h header file. */ +#define HAVE_LBER_H 1 + +/* Define to 1 if you have the ldap.h header file. */ +#define HAVE_LDAP_H 1 + +/* Define to 1 if you have a working localtime_r function. */ +#define HAVE_LOCALTIME_R 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_TCP_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_UDP_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NET_IF_H 1 + +/* if you have opendir */ +#define HAVE_OPENDIR + +/* Define to 1 if you have the 'pipe' function. */ +#define HAVE_PIPE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_PWD_H 1 + +/* Define to 1 if you have the recv function. */ +#define HAVE_RECV 1 + +/* Define to 1 if symbol `sa_family_t' exists */ +#define HAVE_SA_FAMILY_T 1 + +/* Define to 1 if you have the select function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the send function. */ +#define HAVE_SEND 1 + +/* Define to 1 if you have the 'sendmsg' function. */ +#define HAVE_SENDMSG 1 + +/* Define to 1 if you have the sigaction function. */ +#define HAVE_SIGACTION 1 + +/* Define to 1 if struct sockaddr_in6 has the sin6_scope_id member */ +#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 + +/* Define to 1 if you have the socketpair function. */ +#define HAVE_SOCKET 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the stricmp function. */ +#define HAVE_STRICMP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* if struct sockaddr_storage is defined */ +#define HAVE_STRUCT_SOCKADDR_STORAGE + +/* Define to 1 if you have the timeval struct. */ +#define HAVE_STRUCT_TIMEVAL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNICODE_UIDNA_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the 'utime' function. */ +#define HAVE_UTIME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UTIME_H 1 + +/* Size of curl_off_t in number of bytes */ +#define SIZEOF_CURL_OFF_T 8 + +/* Size of curl_socket_t in number of bytes */ +#define SIZEOF_CURL_SOCKET_T 4 + +/* Size of int in number of bytes */ +#define SIZEOF_INT 4 + +/* Size of long in number of bytes */ +#define SIZEOF_LONG 4 + +/* Size of off_t in number of bytes */ +#define SIZEOF_OFF_T 8 /* _LARGE_FILES (*IFS64IO) version. */ + +/* Size of size_t in number of bytes */ +#define SIZEOF_SIZE_T 4 + +/* Size of time_t in number of bytes */ +#define SIZEOF_TIME_T 4 + +/* Define to 1 if all of the C89 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#define STDC_HEADERS 1 + +/* Define if you want to enable IPv6 support */ +#define USE_IPV6 + +/* Use Unix domain sockets */ +#define USE_UNIX_SOCKETS + +/* Define to 1 if OS is AIX. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif + +/* ---------------------------------------------------------------- */ +/* recv/send parameter types (see lib/functypes.h) */ +/* ---------------------------------------------------------------- */ + +/* int recv(int, char *, int, int); */ +#define RECV_TYPE_ARG1 int +#define RECV_TYPE_ARG2 char * +#define RECV_TYPE_ARG3 int +#define RECV_TYPE_ARG4 int +#define RECV_TYPE_RETV int + +/* int send(int, char *, int, int); */ +#define SEND_TYPE_ARG1 int +#define SEND_NONCONST_ARG2 +#define SEND_TYPE_ARG2 char * +#define SEND_TYPE_ARG3 int +#define SEND_TYPE_ARG4 int +#define SEND_TYPE_RETV int + +/* ---------------------------------------------------------------- */ +/* Additional definitions specific to OS/400 */ +/* ---------------------------------------------------------------- */ + +/* The header file is in the main system include directory. */ +#define HAVE_GSSAPI_H /* Define to use the OS/400 crypto library. */ #define USE_OS400CRYPTO -/* Define to use Unix sockets. */ -#define USE_UNIX_SOCKETS - -/* Use the system keyring as the default CA bundle. */ -#define CURL_CA_BUNDLE "/QIBM/UserData/ICSS/Cert/Server/DEFAULT.KDB" - -/* ---------------------------------------------------------------- */ -/* ADDITIONAL DEFINITIONS */ -/* ---------------------------------------------------------------- */ +/* The following definition is required on OS/400 to enable strcmpi(), + stricmp() and strdup(). */ +#define __cplusplus__strings__ /* The following must be defined BEFORE system header files inclusion. */ diff --git a/lib/config-plan9.h b/lib/config-plan9.h deleted file mode 100644 index 68d0cf7f56..0000000000 --- a/lib/config-plan9.h +++ /dev/null @@ -1,133 +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_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/lib/config-riscos.h b/lib/config-riscos.h index 3158c984ee..afff218a76 100644 --- a/lib/config-riscos.h +++ b/lib/config-riscos.h @@ -36,69 +36,30 @@ /* 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 -/* Define if you have the `ftruncate' function. */ -#define HAVE_FTRUNCATE - /* 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 +69,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 +126,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/lib/config-win32.h b/lib/config-win32.h index b7f83927a4..b1fc102209 100644 --- a/lib/config-win32.h +++ b/lib/config-win32.h @@ -28,94 +28,22 @@ /* Hand crafted config file for Windows */ /* ================================================================ */ -#ifndef UNDER_CE - -/* 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 - /* Default target settings and minimum build target check for - VS2008 and VS2010 */ -# else -# define VS2008_MIN_TARGET 0x0501 /* XP */ - /* VS2008 default build target is Windows Vista (0x0600). - We override default target to be Windows XP. */ -# define VS2008_DEF_TARGET 0x0501 /* XP */ - -# ifndef _WIN32_WINNT -# define _WIN32_WINNT VS2008_DEF_TARGET -# endif -# ifndef WINVER -# define WINVER VS2008_DEF_TARGET -# endif -# if (_WIN32_WINNT < VS2008_MIN_TARGET) || (WINVER < VS2008_MIN_TARGET) -# error VS2008 does not support build targets prior to Windows XP -# endif -# endif -#endif /* _MSC_VER */ - -#endif /* UNDER_CE */ - /* ---------------------------------------------------------------- */ /* HEADER FILES */ /* ---------------------------------------------------------------- */ -/* Define if you have the header file. */ -/* #define HAVE_ARPA_INET_H 1 */ - -#ifndef UNDER_CE - /* Define if you have the header file. */ -#define HAVE_FCNTL_H 1 /* exists on __MINGW32CE__ */ +#define HAVE_FCNTL_H 1 /* Define if you have the header file. */ -#define HAVE_IO_H 1 /* exists on __MINGW32CE__ */ +#define HAVE_IO_H 1 /* Define if you have the header file. */ #define HAVE_LOCALE_H 1 -#endif - -/* Define if you have the header file. */ -/* #define HAVE_NETDB_H 1 */ - -/* Define if you have the header file. */ -/* #define HAVE_NETINET_IN_H 1 */ - /* Define to 1 if you have the header file. */ -#ifndef UNDER_CE #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__) -#define HAVE_STDBOOL_H 1 /* exists on __MINGW32CE__ */ -#endif -#endif - -/* Define to 1 if you have the header file. */ -#if (defined(_MSC_VER) && (_MSC_VER >= 1600)) || defined(__MINGW32__) -#define HAVE_STDINT_H 1 +#define HAVE_STDBOOL_H 1 #endif /* Define if you have the header file. */ @@ -123,24 +51,12 @@ #define HAVE_SYS_PARAM_H 1 #endif -/* Define if you have the header file. */ -/* #define HAVE_SYS_SELECT_H 1 */ - -/* Define if you have the header file. */ -/* #define HAVE_SYS_SOCKIO_H 1 */ - /* Define if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define if you have the header file. */ #define HAVE_SYS_UTIME_H 1 -/* Define if you have the header file. */ -/* #define HAVE_TERMIO_H 1 */ - -/* Define if you have the header file. */ -/* #define HAVE_TERMIOS_H 1 */ - /* Define if you have the header file. */ #ifdef __MINGW32__ #define HAVE_UNISTD_H 1 @@ -159,10 +75,8 @@ #define STDC_HEADERS 1 /* Define to 1 if bool is an available type. */ -#ifndef UNDER_CE #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__) -#define HAVE_BOOL_T 1 /* exists on __MINGW32CE__ */ -#endif +#define HAVE_BOOL_T 1 #endif /* ---------------------------------------------------------------- */ @@ -172,11 +86,6 @@ /* Define if you have the closesocket function. */ #define HAVE_CLOSESOCKET 1 -/* Define if you have the ftruncate function. */ -#ifdef __MINGW32__ -#define HAVE_FTRUNCATE 1 -#endif - /* Define to 1 if you have the `getpeername' function. */ #define HAVE_GETPEERNAME 1 @@ -200,23 +109,12 @@ /* Define if you have the select function. */ #define HAVE_SELECT 1 -#ifndef UNDER_CE /* 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 -#endif - /* 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 @@ -244,9 +142,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 * @@ -259,11 +154,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 */ @@ -276,9 +166,7 @@ #endif /* Define to 1 if you have the signal function. */ -#ifndef UNDER_CE #define HAVE_SIGNAL 1 -#endif /* ---------------------------------------------------------------- */ /* TYPEDEF REPLACEMENTS */ @@ -303,9 +191,6 @@ /* Define to the size of `int', as computed by sizeof. */ #define SIZEOF_INT 4 -/* Define to the size of `long long', as computed by sizeof. */ -/* #define SIZEOF_LONG_LONG 8 */ - /* Define to the size of `long', as computed by sizeof. */ #define SIZEOF_LONG 4 @@ -323,20 +208,6 @@ /* COMPILER SPECIFIC */ /* ---------------------------------------------------------------- */ -/* Define to nothing if compiler does not support 'const' qualifier. */ -/* #define const */ - -/* Define to nothing if compiler does not support 'volatile' qualifier. */ -/* #define volatile */ - -/* Windows should not have HAVE_GMTIME_R defined */ -/* #undef HAVE_GMTIME_R */ - -/* 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 @@ -347,11 +218,9 @@ #endif /* Windows XP is required for freeaddrinfo, getaddrinfo */ -#ifndef UNDER_CE #define HAVE_FREEADDRINFO 1 #define HAVE_GETADDRINFO 1 #define HAVE_GETADDRINFO_THREADSAFE 1 -#endif /* ---------------------------------------------------------------- */ /* STRUCT RELATED */ @@ -370,46 +239,34 @@ /* LARGE FILE SUPPORT */ /* ---------------------------------------------------------------- */ -#ifndef UNDER_CE - -#if defined(_MSC_VER) || defined(__MINGW32__) -# define USE_WIN32_LARGE_FILES /* Number of bits in a file offset, on hosts where this is settable. */ -# ifdef __MINGW32__ -# ifndef _FILE_OFFSET_BITS -# define _FILE_OFFSET_BITS 64 -# endif -# endif +#ifdef __MINGW32__ +# undef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 #endif /* Define to the size of `off_t', as computed by sizeof. */ -#if defined(__MINGW32__) && \ - defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64) +#ifdef __MINGW32__ # define SIZEOF_OFF_T 8 #else # define SIZEOF_OFF_T 4 #endif -#endif /* UNDER_CE */ - /* ---------------------------------------------------------------- */ /* DNS RESOLVER SPECIALTY */ /* ---------------------------------------------------------------- */ /* - * Undefine both USE_ARES and USE_THREADS_WIN32 for synchronous DNS. + * Undefine both USE_ARES and USE_RESOLV_THREADED for synchronous DNS. */ -/* Define to enable c-ares asynchronous DNS lookups. */ -/* #define USE_ARES 1 */ - /* Default define to enable threaded asynchronous DNS lookups. */ #if !defined(USE_SYNC_DNS) && !defined(USE_ARES) && \ - !defined(USE_THREADS_WIN32) -# define USE_THREADS_WIN32 1 + !defined(USE_RESOLV_THREADED) +# define USE_RESOLV_THREADED 1 #endif -#if defined(USE_ARES) && defined(USE_THREADS_WIN32) +#if defined(USE_ARES) && defined(USE_RESOLV_THREADED) # error "Only one DNS lookup specialty may be defined at most" #endif @@ -417,24 +274,16 @@ /* LDAP SUPPORT */ /* ---------------------------------------------------------------- */ -#ifdef CURL_HAS_OPENLDAP_LDAPSDK -#undef USE_WIN32_LDAP -#define HAVE_LDAP_URL_PARSE 1 -#elif !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) -#undef HAVE_LDAP_URL_PARSE -#define HAVE_LDAP_SSL 1 +#ifndef CURL_WINDOWS_UWP +#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. */ -#ifndef UNDER_CE #define USE_UNIX_SOCKETS -#endif /* ---------------------------------------------------------------- */ /* ADDITIONAL DEFINITIONS */ @@ -442,51 +291,19 @@ /* Define cpu-machine-OS */ #ifndef CURL_OS -# ifdef UNDER_CE -# ifdef _M_ARM -# define CURL_OS "arm-pc-win32ce" -# else -# define CURL_OS "i386-pc-win32ce" -# endif -# else /* !UNDER_CE */ -# if defined(_M_IX86) || defined(__i386__) /* x86 (MSVC or gcc) */ -# define CURL_OS "i386-pc-win32" -# elif defined(_M_X64) || defined(__x86_64__) /* x86_64 (VS2005+ or gcc) */ -# define CURL_OS "x86_64-pc-win32" -# elif defined(_M_IA64) || defined(__ia64__) /* Itanium */ -# define CURL_OS "ia64-pc-win32" -# elif defined(_M_ARM_NT) || defined(__arm__) /* ARMv7-Thumb2 */ -# define CURL_OS "thumbv7a-pc-win32" -# elif defined(_M_ARM64) || defined(__aarch64__) /* ARM64 (Windows 10) */ -# define CURL_OS "aarch64-pc-win32" -# else -# define CURL_OS "unknown-pc-win32" -# endif -# endif /* UNDER_CE */ +# if defined(_M_IX86) || defined(__i386__) /* x86 (MSVC or gcc) */ +# define CURL_OS "i386-pc-win32" +# elif defined(_M_X64) || defined(__x86_64__) /* x86_64 (VS2005+ or gcc) */ +# define CURL_OS "x86_64-pc-win32" +# elif defined(_M_IA64) || defined(__ia64__) /* Itanium */ +# define CURL_OS "ia64-pc-win32" +# elif defined(_M_ARM_NT) || defined(__arm__) /* ARMv7-Thumb2 */ +# define CURL_OS "thumbv7a-pc-win32" +# elif defined(_M_ARM64) || defined(__aarch64__) /* ARM64 (Windows 10) */ +# define CURL_OS "aarch64-pc-win32" +# else +# define CURL_OS "unknown-pc-win32" +# endif #endif /* !CURL_OS */ -/* ---------------------------------------------------------------- */ -/* Windows CE */ -/* ---------------------------------------------------------------- */ - -#ifdef UNDER_CE - -#ifndef UNICODE -#define UNICODE -#endif - -#ifndef _UNICODE -#define _UNICODE -#endif - -#define CURL_DISABLE_FILE 1 -#define CURL_DISABLE_TELNET 1 -#define CURL_DISABLE_LDAP 1 - -#ifndef _MSC_VER -extern int stat(const char *path, struct stat *buffer); -#endif - -#endif /* UNDER_CE */ - #endif /* HEADER_CURL_CONFIG_WIN32_H */ diff --git a/lib/conncache.c b/lib/conncache.c index 1393bb565b..33fb68b124 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -22,40 +22,25 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "urldata.h" #include "url.h" #include "cfilters.h" #include "progress.h" #include "multiif.h" -#include "multi_ev.h" -#include "sendf.h" +#include "curl_trc.h" #include "cshutdn.h" #include "conncache.h" -#include "http_negotiate.h" -#include "http_ntlm.h" -#include "share.h" +#include "curl_share.h" #include "sigpipe.h" -#include "connect.h" -#include "select.h" -#include "curlx/strparse.h" -#include "uint-table.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" #define CPOOL_IS_LOCKED(c) ((c) && (c)->locked) -#define CPOOL_LOCK(c,d) \ +#define CPOOL_LOCK(c, d) \ do { \ - if((c)) { \ + if(c) { \ if(CURL_SHARE_KEEP_CONNECT((c)->share)) \ Curl_share_lock((d), CURL_LOCK_DATA_CONNECT, \ CURL_LOCK_ACCESS_SINGLE); \ @@ -64,9 +49,9 @@ } \ } while(0) -#define CPOOL_UNLOCK(c,d) \ +#define CPOOL_UNLOCK(c, d) \ do { \ - if((c)) { \ + if(c) { \ DEBUGASSERT((c)->locked); \ (c)->locked = FALSE; \ if(CURL_SHARE_KEEP_CONNECT((c)->share)) \ @@ -74,30 +59,23 @@ } \ } while(0) - /* A list of connections to the same destination. */ struct cpool_bundle { struct Curl_llist conns; /* connections in the bundle */ size_t dest_len; /* total length of destination, including NUL */ - char *dest[1]; /* destination of bundle, allocated to keep dest_len bytes */ + 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; - size_t dest_len = strlen(dest); + size_t dest_len = strlen(dest) + 1; - bundle = calloc(1, sizeof(*bundle) + dest_len); + bundle = curlx_calloc(1, sizeof(*bundle) + dest_len - 1); if(!bundle) return NULL; Curl_llist_init(&bundle->conns, NULL); - bundle->dest_len = dest_len + 1; + bundle->dest_len = dest_len; memcpy(bundle->dest, dest, bundle->dest_len); return bundle; } @@ -105,7 +83,7 @@ static struct cpool_bundle *cpool_bundle_create(const char *dest) static void cpool_bundle_destroy(struct cpool_bundle *bundle) { DEBUGASSERT(!Curl_llist_count(&bundle->conns)); - free(bundle); + curlx_free(bundle); } /* Add a connection to a bundle */ @@ -166,7 +144,6 @@ static struct connectdata *cpool_get_first(struct cpool *cpool) return NULL; } - static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool, struct connectdata *conn) { @@ -174,7 +151,6 @@ static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool, conn->destination, strlen(conn->destination) + 1); } - static void cpool_remove_bundle(struct cpool *cpool, struct cpool_bundle *bundle) { @@ -183,7 +159,6 @@ static void cpool_remove_bundle(struct cpool *cpool, Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len); } - static void cpool_remove_conn(struct cpool *cpool, struct connectdata *conn) { @@ -206,27 +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->bits.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); - connclose(conn, "kill all"); 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); } } @@ -266,8 +288,8 @@ void Curl_cpool_xfer_init(struct Curl_easy *data) } } -static struct cpool_bundle * -cpool_add_bundle(struct cpool *cpool, struct connectdata *conn) +static struct cpool_bundle *cpool_add_bundle(struct cpool *cpool, + struct connectdata *conn) { struct cpool_bundle *bundle; @@ -284,23 +306,22 @@ cpool_add_bundle(struct cpool *cpool, struct connectdata *conn) } static struct connectdata * -cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle) +cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle, + const struct curltime *pnow) { struct Curl_llist_node *curr; timediff_t highscore = -1; timediff_t score; - struct curltime now; struct connectdata *oldest_idle = NULL; struct connectdata *conn; - now = curlx_now(); curr = Curl_llist_head(&bundle->conns); while(curr) { conn = Curl_node_elem(curr); if(!CONN_INUSE(conn)) { /* Set higher score for the age passed since the connection was used */ - score = curlx_timediff(now, conn->lastused); + score = curlx_ptimediff_ms(pnow, &conn->lastused); if(score > highscore) { highscore = score; @@ -312,18 +333,17 @@ cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle) return oldest_idle; } -static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool) +static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool, + const struct curltime *pnow) { struct Curl_hash_iterator iter; struct Curl_llist_node *curr; struct Curl_hash_element *he; struct connectdata *oldest_idle = NULL; struct cpool_bundle *bundle; - struct curltime now; - timediff_t highscore =- 1; + timediff_t highscore = -1; timediff_t score; - now = curlx_now(); Curl_hash_start_iterate(&cpool->dest2bundle, &iter); for(he = Curl_hash_next_element(&iter); he; @@ -334,10 +354,10 @@ static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool) for(curr = Curl_llist_head(&bundle->conns); curr; curr = Curl_node_next(curr)) { conn = Curl_node_elem(curr); - if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only) + if(CONN_INUSE(conn) || conn->bits.close || conn->bits.connect_only) continue; /* Set higher score for the age passed since the connection was used */ - score = curlx_timediff(now, conn->lastused); + score = curlx_ptimediff_ms(pnow, &conn->lastused); if(score > highscore) { highscore = score; oldest_idle = conn; @@ -347,7 +367,6 @@ static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool) return oldest_idle; } - int Curl_cpool_check_limits(struct Curl_easy *data, struct connectdata *conn) { @@ -356,7 +375,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data, size_t dest_limit = 0; size_t total_limit = 0; size_t shutdowns; - int result = CPOOL_LIMIT_OK; + int res = CPOOL_LIMIT_OK; if(!cpool) return CPOOL_LIMIT_OK; @@ -376,7 +395,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data, bundle = cpool_find_bundle(cpool, conn); live = bundle ? Curl_llist_count(&bundle->conns) : 0; shutdowns = Curl_cshutdn_dest_count(data, conn->destination); - while((live + shutdowns) >= dest_limit) { + while((live + shutdowns) >= dest_limit) { if(shutdowns) { /* close one connection in shutdown right away, if we can */ if(!Curl_cshutdn_close_oldest(data, conn->destination)) @@ -388,14 +407,15 @@ int Curl_cpool_check_limits(struct Curl_easy *data, struct connectdata *oldest_idle = NULL; /* The bundle is full. Extract the oldest connection that may * be removed now, if there is one. */ - oldest_idle = cpool_bundle_get_oldest_idle(bundle); + oldest_idle = cpool_bundle_get_oldest_idle(bundle, + Curl_pgrs_now(data)); if(!oldest_idle) break; /* disconnect the old conn and continue */ - CURL_TRC_M(data, "Discarding connection #%" - FMT_OFF_T " from %zu to reach destination " - "limit of %zu", oldest_idle->connection_id, - Curl_llist_count(&bundle->conns), dest_limit); + CURL_TRC_M(data, "Discarding connection #%" FMT_OFF_T + " from %zu to reach destination limit of %zu", + oldest_idle->connection_id, + Curl_llist_count(&bundle->conns), dest_limit); Curl_conn_terminate(cpool->idata, oldest_idle, FALSE); /* in case the bundle was destroyed in disconnect, look it up again */ @@ -405,7 +425,7 @@ int Curl_cpool_check_limits(struct Curl_easy *data, shutdowns = Curl_cshutdn_dest_count(cpool->idata, conn->destination); } if((live + shutdowns) >= dest_limit) { - result = CPOOL_LIMIT_DEST; + res = CPOOL_LIMIT_DEST; goto out; } } @@ -419,7 +439,8 @@ int Curl_cpool_check_limits(struct Curl_easy *data, break; } else { - struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool); + struct connectdata *oldest_idle = + cpool_get_oldest_idle(cpool, Curl_pgrs_now(data)); if(!oldest_idle) break; /* disconnect the old conn and continue */ @@ -432,14 +453,14 @@ int Curl_cpool_check_limits(struct Curl_easy *data, shutdowns = Curl_cshutdn_count(cpool->idata); } if((cpool->num_conn + shutdowns) >= total_limit) { - result = CPOOL_LIMIT_TOTAL; + res = CPOOL_LIMIT_TOTAL; goto out; } } out: CPOOL_UNLOCK(cpool, cpool->idata); - return result; + return res; } CURLcode Curl_cpool_add(struct Curl_easy *data, @@ -532,13 +553,23 @@ static bool cpool_foreach(struct Curl_easy *data, bool Curl_cpool_conn_now_idle(struct Curl_easy *data, struct connectdata *conn) { - unsigned int maxconnects = !data->multi->maxconnects ? - (Curl_multi_xfers_running(data->multi) * 4) : data->multi->maxconnects; + unsigned int maxconnects; struct connectdata *oldest_idle = NULL; struct cpool *cpool = cpool_get_instance(data); bool kept = TRUE; - conn->lastused = curlx_now(); /* it was used up until now */ + if(!data || !data->multi) + return kept; + + if(!data->multi->maxconnects) { + unsigned int running = Curl_multi_xfers_running(data->multi); + maxconnects = (running <= UINT_MAX / 4) ? running * 4 : UINT_MAX; + } + else { + maxconnects = data->multi->maxconnects; + } + + conn->lastused = *Curl_pgrs_now(data); /* it was used up until now */ if(cpool && maxconnects) { /* may be called form a callback already under lock */ bool do_lock = !CPOOL_IS_LOCKED(cpool); @@ -548,7 +579,7 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data, infof(data, "Connection pool is full, closing the oldest of %zu/%u", cpool->num_conn, maxconnects); - oldest_idle = cpool_get_oldest_idle(cpool); + oldest_idle = cpool_get_oldest_idle(cpool, Curl_pgrs_now(data)); kept = (oldest_idle != conn); if(oldest_idle) { Curl_conn_terminate(data, oldest_idle, FALSE); @@ -569,7 +600,7 @@ bool Curl_cpool_find(struct Curl_easy *data, { struct cpool *cpool = cpool_get_instance(data); struct cpool_bundle *bundle; - bool result = FALSE; + bool found = FALSE; DEBUGASSERT(cpool); DEBUGASSERT(conn_cb); @@ -588,64 +619,17 @@ bool Curl_cpool_find(struct Curl_easy *data, curr = Curl_node_next(curr); if(conn_cb(conn, userdata)) { - result = TRUE; + found = TRUE; break; } } } if(done_cb) { - result = done_cb(result, userdata); + found = done_cb(userdata); } CPOOL_UNLOCK(cpool, 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(conn)); - 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); + return found; } void Curl_conn_terminate(struct Curl_easy *data, @@ -664,7 +648,7 @@ void Curl_conn_terminate(struct Curl_easy *data, * are other users of it */ if(CONN_INUSE(conn) && !aborted) { DEBUGASSERT(0); /* does this ever happen? */ - DEBUGF(infof(data, "Curl_disconnect when inuse: %u", CONN_ATTACHED(conn))); + DEBUGF(infof(data, "conn terminate when inuse: %u", conn->attached_xfers)); return; } @@ -681,7 +665,7 @@ void Curl_conn_terminate(struct Curl_easy *data, /* treat the connection as aborted in CONNECT_ONLY situations, * so no graceful shutdown is attempted. */ - if(conn->connect_only) + if(conn->bits.connect_only) aborted = TRUE; if(data->multi) { @@ -700,18 +684,24 @@ void Curl_conn_terminate(struct Curl_easy *data, CPOOL_UNLOCK(cpool, data); } - struct cpool_reaper_ctx { - struct curltime now; + size_t checked; + size_t reaped; }; static int cpool_reap_dead_cb(struct Curl_easy *data, struct connectdata *conn, void *param) { - struct cpool_reaper_ctx *rctx = param; - if((!CONN_INUSE(conn) && conn->bits.no_reuse) || - Curl_conn_seems_dead(conn, data, &rctx->now)) { + struct cpool_reaper_ctx *reaper = param; + bool terminate = !CONN_INUSE(conn) && conn->bits.no_reuse; + + if(!terminate) { + reaper->checked++; + terminate = Curl_conn_seems_dead(conn, data); + } + if(terminate) { /* stop the iteration here, pass back the connection that was pruned */ + reaper->reaped++; Curl_conn_terminate(data, conn, FALSE); return 1; } @@ -728,20 +718,20 @@ static int cpool_reap_dead_cb(struct Curl_easy *data, void Curl_cpool_prune_dead(struct Curl_easy *data) { struct cpool *cpool = cpool_get_instance(data); - struct cpool_reaper_ctx rctx; + struct cpool_reaper_ctx reaper; timediff_t elapsed; if(!cpool) return; - rctx.now = curlx_now(); + memset(&reaper, 0, sizeof(reaper)); CPOOL_LOCK(cpool, data); - elapsed = curlx_timediff(rctx.now, cpool->last_cleanup); + elapsed = curlx_ptimediff_ms(Curl_pgrs_now(data), &cpool->last_cleanup); if(elapsed >= 1000L) { - while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb)) + while(cpool_foreach(data, cpool, &reaper, cpool_reap_dead_cb)) ; - cpool->last_cleanup = rctx.now; + cpool->last_cleanup = *Curl_pgrs_now(data); } CPOOL_UNLOCK(cpool, data); } @@ -750,21 +740,20 @@ static int conn_upkeep(struct Curl_easy *data, struct connectdata *conn, void *param) { - struct curltime *now = param; - Curl_conn_upkeep(data, conn, now); + (void)param; + Curl_conn_upkeep(data, conn); return 0; /* continue iteration */ } -CURLcode Curl_cpool_upkeep(void *data) +CURLcode Curl_cpool_upkeep(struct Curl_easy *data) { struct cpool *cpool = cpool_get_instance(data); - struct curltime now = curlx_now(); if(!cpool) return CURLE_OK; CPOOL_LOCK(cpool, data); - cpool_foreach(data, cpool, &now, conn_upkeep); + cpool_foreach(data, cpool, NULL, conn_upkeep); CPOOL_UNLOCK(cpool, data); return CURLE_OK; } @@ -812,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; @@ -862,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); @@ -895,7 +883,7 @@ void Curl_cpool_print(struct cpool *cpool) if(!cpool) return; - fprintf(stderr, "=Bundle cache=\n"); + curl_mfprintf(stderr, "=Bundle cache=\n"); Curl_hash_start_iterate(cpool->dest2bundle, &iter); @@ -906,15 +894,15 @@ void Curl_cpool_print(struct cpool *cpool) bundle = he->ptr; - fprintf(stderr, "%s -", he->key); + curl_mfprintf(stderr, "%s -", he->key); curr = Curl_llist_head(bundle->conns); while(curr) { conn = Curl_node_elem(curr); - fprintf(stderr, " [%p %d]", (void *)conn, conn->refcount); + curl_mfprintf(stderr, " [%p %d]", (void *)conn, conn->refcount); curr = Curl_node_next(curr); } - fprintf(stderr, "\n"); + curl_mfprintf(stderr, "\n"); he = Curl_hash_next_element(&iter); } diff --git a/lib/conncache.h b/lib/conncache.h index a5f133344f..7cee4d4729 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -24,8 +24,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include #include "curlx/timeval.h" struct connectdata; @@ -49,7 +47,7 @@ void Curl_conn_terminate(struct Curl_easy *data, bool aborted); struct cpool { - /* the pooled connections, bundled per destination */ + /* the pooled connections, bundled per destination */ struct Curl_hash dest2bundle; size_t num_conn; curl_off_t next_connection_id; @@ -70,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`. */ @@ -86,8 +84,8 @@ CURLcode Curl_cpool_add(struct Curl_easy *data, /** * Return if the pool has reached its configured limits for adding - * the given connection. Will try to discard the oldest, idle - * connections to make space. + * the given connection. Try to discard the oldest, idle connections + * to make space. */ #define CPOOL_LIMIT_OK 0 #define CPOOL_LIMIT_DEST 1 @@ -100,7 +98,7 @@ typedef bool Curl_cpool_conn_match_cb(struct connectdata *conn, void *userdata); /* Act on the result of the find, may override it. */ -typedef bool Curl_cpool_done_match_cb(bool result, void *userdata); +typedef bool Curl_cpool_done_match_cb(void *userdata); /** * Find a connection in the pool matching `destination`. @@ -139,7 +137,7 @@ void Curl_cpool_prune_dead(struct Curl_easy *data); /** * Perform upkeep actions on connections in the transfer's pool. */ -CURLcode Curl_cpool_upkeep(void *data); +CURLcode Curl_cpool_upkeep(struct Curl_easy *data); typedef void Curl_cpool_conn_do_cb(struct connectdata *conn, struct Curl_easy *data, @@ -166,5 +164,4 @@ void Curl_cpool_do_locked(struct Curl_easy *data, /* Close all unused connections, prevent reuse of existing ones. */ void Curl_cpool_nw_changed(struct Curl_easy *data); - #endif /* HEADER_CURL_CONNCACHE_H */ diff --git a/lib/connect.c b/lib/connect.c index f0628d6206..85b97c37a0 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_NETINET_IN_H @@ -41,9 +40,6 @@ #ifdef HAVE_NETDB_H #include #endif -#ifdef HAVE_FCNTL_H -#include -#endif #ifdef HAVE_ARPA_INET_H #include #endif @@ -54,165 +50,142 @@ #endif #include "urldata.h" -#include "sendf.h" -#include "if2ip.h" +#include "curl_trc.h" #include "strerror.h" #include "cfilters.h" #include "connect.h" +#include "cf-dns.h" #include "cf-haproxy.h" #include "cf-https-connect.h" #include "cf-ip-happy.h" #include "cf-socket.h" -#include "select.h" -#include "url.h" /* for Curl_safefree() */ #include "multiif.h" -#include "sockaddr.h" /* required for Curl_sockaddr_storage */ #include "curlx/inet_ntop.h" -#include "curlx/inet_pton.h" -#include "vtls/vtls.h" /* for vtsl cfilters */ +#include "curlx/strparse.h" +#include "vtls/vtls.h" /* for vtls cfilters */ #include "progress.h" -#include "curlx/warnless.h" #include "conncache.h" #include "multihandle.h" -#include "share.h" #include "http_proxy.h" #include "socks.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - #if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR) -enum alpnid Curl_alpn2alpnid(const char *name, size_t len) +enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len) { if(len == 2) { - if(curl_strnequal(name, "h1", 2)) + if(!memcmp(name, "h1", 2)) return ALPN_h1; - if(curl_strnequal(name, "h2", 2)) + if(!memcmp(name, "h2", 2)) return ALPN_h2; - if(curl_strnequal(name, "h3", 2)) + if(!memcmp(name, "h3", 2)) return ALPN_h3; } else if(len == 8) { - if(curl_strnequal(name, "http/1.1", 8)) + if(!memcmp(name, "http/1.1", 8)) return ALPN_h1; } return ALPN_none; /* unknown, probably rubbish input */ } +enum alpnid Curl_str2alpnid(const struct Curl_str *cstr) +{ + return Curl_alpn2alpnid((const unsigned char *)curlx_str(cstr), + curlx_strlen(cstr)); +} + #endif /* - * Curl_timeleft() returns the amount of milliseconds left allowed for the + * Curl_timeleft_ms() returns the amount of milliseconds left allowed for the * 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 nowp timestamp to use for calculation, NULL to use curlx_now() - * @param duringconnect TRUE iff connect timeout is also taken into account. * @unittest: 1303 */ -timediff_t Curl_timeleft(struct Curl_easy *data, - struct curltime *nowp, - bool duringconnect) +timediff_t Curl_timeleft_now_ms(struct Curl_easy *data, + const struct curltime *pnow) { timediff_t timeleft_ms = 0; timediff_t ctimeleft_ms = 0; - struct curltime now; - /* 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 <= 0 && !duringconnect) - return 0; /* no timeout in place or checked, return "no limit" */ - - if(!nowp) { - now = curlx_now(); - nowp = &now; - } - - if(data->set.timeout > 0) { - timeleft_ms = data->set.timeout - - curlx_timediff(*nowp, data->progress.t_startop); - if(!timeleft_ms) - timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ - if(!duringconnect) - return timeleft_ms; /* no connect check, this is it */ - } - - if(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_timediff(*nowp, data->progress.t_startsingle); + 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; + 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 - + curlx_ptimediff_ms(pnow, &data->progress.t_startop); + if(!timeleft_ms) + timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ + } + + if(!ctimeleft_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) +{ + return Curl_timeleft_now_ms(data, Curl_pgrs_now(data)); } void Curl_shutdown_start(struct Curl_easy *data, int sockindex, - int timeout_ms, struct curltime *nowp) + int timeout_ms) { - struct curltime now; + struct connectdata *conn = data->conn; - DEBUGASSERT(data->conn); - if(!nowp) { - now = curlx_now(); - nowp = &now; - } - data->conn->shutdown.start[sockindex] = *nowp; - data->conn->shutdown.timeout_ms = (timeout_ms > 0) ? + DEBUGASSERT(conn); + conn->shutdown.start[sockindex] = *Curl_pgrs_now(data); + conn->shutdown.timeout_ms = (timeout_ms > 0) ? (timediff_t)timeout_ms : ((data->set.shutdowntimeout > 0) ? data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS); /* Set a timer, unless we operate on the admin handle */ - if(data->mid && (data->conn->shutdown.timeout_ms > 0)) - Curl_expire_ex(data, nowp, data->conn->shutdown.timeout_ms, - EXPIRE_SHUTDOWN); + 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 connectdata *conn, int sockindex, - struct curltime *nowp) +timediff_t Curl_shutdown_timeleft(struct Curl_easy *data, + struct connectdata *conn, + int sockindex) { - struct curltime now; timediff_t left_ms; if(!conn->shutdown.start[sockindex].tv_sec || (conn->shutdown.timeout_ms <= 0)) return 0; /* not started or no limits */ - if(!nowp) { - now = curlx_now(); - nowp = &now; - } left_ms = conn->shutdown.timeout_ms - - curlx_timediff(*nowp, conn->shutdown.start[sockindex]); + curlx_ptimediff_ms(Curl_pgrs_now(data), + &conn->shutdown.start[sockindex]); return left_ms ? left_ms : -1; } -timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn, - struct curltime *nowp) +timediff_t Curl_conn_shutdown_timeleft(struct Curl_easy *data, + struct connectdata *conn) { timediff_t left_ms = 0, ms; - struct curltime now; int i; for(i = 0; conn->shutdown.timeout_ms && (i < 2); ++i) { if(!conn->shutdown.start[i].tv_sec) continue; - if(!nowp) { - now = curlx_now(); - nowp = &now; - } - ms = Curl_shutdown_timeleft(conn, i, nowp); + ms = Curl_shutdown_timeleft(data, conn, i); if(ms && (!left_ms || ms < left_ms)) left_ms = ms; } @@ -227,63 +200,63 @@ 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 curlx_inet_ntop which sets errno on fail, not SOCKERRNO. */ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, - char *addr, int *port) + char *addr, uint16_t *port) { struct sockaddr_in *si = NULL; #ifdef USE_IPV6 struct sockaddr_in6 *si6 = NULL; #endif -#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX) +#ifdef USE_UNIX_SOCKETS struct sockaddr_un *su = NULL; #else (void)salen; #endif switch(sa->sa_family) { - case AF_INET: - si = (struct sockaddr_in *)(void *) sa; - if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) { - unsigned short us_port = ntohs(si->sin_port); - *port = us_port; - return TRUE; - } - break; -#ifdef USE_IPV6 - case AF_INET6: - si6 = (struct sockaddr_in6 *)(void *) sa; - if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, - MAX_IPADR_LEN)) { - unsigned short us_port = ntohs(si6->sin6_port); - *port = us_port; - return TRUE; - } - break; -#endif -#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX) - case AF_UNIX: - if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) { - su = (struct sockaddr_un*)sa; - msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); - } - else - addr[0] = 0; /* socket with no name */ - *port = 0; + case AF_INET: + si = (struct sockaddr_in *)(void *)sa; + if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) { + *port = ntohs(si->sin_port); return TRUE; + } + break; +#ifdef USE_IPV6 + case AF_INET6: + si6 = (struct sockaddr_in6 *)(void *)sa; + if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, MAX_IPADR_LEN)) { + *port = ntohs(si6->sin6_port); + return TRUE; + } + break; #endif - default: - break; +#ifdef USE_UNIX_SOCKETS + case AF_UNIX: + if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) { + su = (struct sockaddr_un *)sa; + curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); + } + else + addr[0] = 0; /* socket with no name */ + *port = 0; + return TRUE; +#endif + default: + break; } addr[0] = '\0'; *port = 0; - CURL_SETERRNO(SOCKEAFNOSUPPORT); + errno = SOCKEAFNOSUPPORT; return FALSE; } @@ -325,7 +298,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 ) @@ -335,15 +308,15 @@ 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); closeit = (ctrl == CONNCTRL_CONNECTION) || - ((ctrl == CONNCTRL_STREAM) && !is_multiplex); + ((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 */ } @@ -362,7 +335,7 @@ typedef enum { struct cf_setup_ctx { cf_setup_state state; int ssl_mode; - int transport; + uint8_t transport; }; static CURLcode cf_setup_connect(struct Curl_cfilter *cf, @@ -371,7 +344,6 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, { struct cf_setup_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - struct Curl_dns_entry *dns = data->state.dns[cf->sockindex]; if(cf->connected) { *done = TRUE; @@ -380,8 +352,6 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf, /* connect current sub-chain */ connect_sub_chain: - if(!dns) - return CURLE_FAILED_INIT; if(cf->next && !cf->next->connected) { result = Curl_conn_cf_connect(cf->next, data, done); @@ -411,8 +381,8 @@ connect_sub_chain: if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) { #ifdef USE_SSL - if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) - && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { + if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) && + !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { result = Curl_cf_ssl_proxy_insert_after(cf, data); if(result) return result; @@ -452,10 +422,10 @@ connect_sub_chain: if(ctx->state < CF_SETUP_CNNCT_SSL) { #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 */ + if((ctx->ssl_mode == CURL_CF_SSL_ENABLE || + (ctx->ssl_mode != CURL_CF_SSL_DISABLE && + 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; @@ -491,12 +461,10 @@ 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); + curlx_safefree(ctx); } - struct Curl_cftype Curl_cft_setup = { "SETUP", 0, @@ -517,7 +485,7 @@ struct Curl_cftype Curl_cft_setup = { static CURLcode cf_setup_create(struct Curl_cfilter **pcf, struct Curl_easy *data, - int transport, + uint8_t transport, int ssl_mode) { struct Curl_cfilter *cf = NULL; @@ -525,7 +493,7 @@ static CURLcode cf_setup_create(struct Curl_cfilter **pcf, CURLcode result = CURLE_OK; (void)data; - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -542,7 +510,7 @@ static CURLcode cf_setup_create(struct Curl_cfilter **pcf, out: *pcf = result ? NULL : cf; if(ctx) { - free(ctx); + curlx_free(ctx); } return result; } @@ -550,7 +518,7 @@ out: static CURLcode cf_setup_add(struct Curl_easy *data, struct connectdata *conn, int sockindex, - int transport, + uint8_t transport, int ssl_mode) { struct Curl_cfilter *cf; @@ -567,7 +535,7 @@ out: CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - int transport, + uint8_t transport, int ssl_mode) { struct Curl_cfilter *cf; @@ -589,17 +557,15 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, int ssl_mode) { CURLcode result = CURLE_OK; + uint8_t dns_queries; DEBUGASSERT(data); - DEBUGASSERT(conn->handler); - DEBUGASSERT(dns); - - Curl_resolv_unlink(data, &data->state.dns[sockindex]); - data->state.dns[sockindex] = dns; + DEBUGASSERT(conn->scheme); + DEBUGASSERT(!conn->cfilter[sockindex]); #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) @@ -615,9 +581,40 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, goto out; } + dns_queries = Curl_resolv_dns_queries(data, conn->ip_version); +#ifdef USE_HTTPSRR + if(sockindex == FIRSTSOCKET) + dns_queries |= CURL_DNSQ_HTTPS; +#endif + result = Curl_cf_dns_add(data, conn, sockindex, dns_queries, + conn->transport_wanted, dns); DEBUGASSERT(conn->cfilter[sockindex]); out: - if(result) - Curl_resolv_unlink(data, &data->state.dns[sockindex]); return result; } + +#ifdef USE_UNIX_SOCKETS +const char *Curl_conn_get_unix_path(struct connectdata *conn) +{ + const char *unix_path = conn->unix_domain_socket; + +#ifndef CURL_DISABLE_PROXY + if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name && + !strncmp(UNIX_SOCKET_PREFIX "/", + conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX))) + unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1; +#endif + + return unix_path; +} +#endif /* USE_UNIX_SOCKETS */ + +void Curl_conn_set_multiplex(struct connectdata *conn) +{ + if(!conn->bits.multiplex) { + conn->bits.multiplex = TRUE; + if(conn->attached_multi) { + Curl_multi_connchanged(conn->attached_multi); + } + } +} diff --git a/lib/connect.h b/lib/connect.h index 6a2487ff53..72e1ee7fcf 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -25,37 +25,38 @@ ***************************************************************************/ #include "curl_setup.h" -#include "curlx/nonblock.h" /* for curlx_nonblock() */ -#include "sockaddr.h" #include "curlx/timeval.h" struct Curl_dns_entry; struct ip_quadruple; +struct Curl_str; -enum alpnid Curl_alpn2alpnid(const char *name, size_t len); +enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len); +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(struct Curl_easy *data, - struct curltime *nowp, - 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); #define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */ #define DEFAULT_SHUTDOWN_TIMEOUT_MS (2 * 1000) void Curl_shutdown_start(struct Curl_easy *data, int sockindex, - int timeout_ms, struct curltime *nowp); + int timeout_ms); /* return how much time there is left to shutdown the connection at * sockindex. Returns 0 if there is no limit or shutdown has not started. */ -timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex, - struct curltime *nowp); +timediff_t Curl_shutdown_timeleft(struct Curl_easy *data, + struct connectdata *conn, + int sockindex); /* return how much time there is left to shutdown the connection. * Returns 0 if there is no limit or shutdown has not started. */ -timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn, - struct curltime *nowp); +timediff_t Curl_conn_shutdown_timeleft(struct Curl_easy *data, + struct connectdata *conn); void Curl_shutdown_clear(struct Curl_easy *data, int sockindex); @@ -72,10 +73,10 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, struct connectdata **connp); bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, - char *addr, int *port); + 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 @@ -91,25 +92,25 @@ 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) -#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 */ -#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) +#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 /* !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) #endif CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - int transport, + uint8_t transport, int ssl_mode); /** @@ -123,6 +124,18 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, struct Curl_dns_entry *dns, int ssl_mode); +/* Set conn to allow multiplexing. */ +void Curl_conn_set_multiplex(struct connectdata *conn); + +#ifdef USE_UNIX_SOCKETS +#ifndef CURL_DISABLE_PROXY +#define UNIX_SOCKET_PREFIX "localhost" +#endif +const char *Curl_conn_get_unix_path(struct connectdata *conn); +#else +#define Curl_conn_get_unix_path(c) NULL +#endif + extern struct Curl_cftype Curl_cft_setup; #endif /* HEADER_CURL_CONNECT_H */ diff --git a/lib/content_encoding.c b/lib/content_encoding.c index 3a0549c4b1..32d32241f4 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -21,25 +21,23 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #include "urldata.h" -#include -#include +#include "curlx/dynbuf.h" #ifdef HAVE_LIBZ #include #endif #ifdef HAVE_BROTLI -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG /* Ignore -Wvla warnings in brotli headers */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wvla" #endif #include -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG #pragma GCC diagnostic pop #endif #endif @@ -49,14 +47,8 @@ #endif #include "sendf.h" -#include "http.h" +#include "curl_trc.h" #include "content_encoding.h" -#include "strdup.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" #define CONTENT_ENCODING_DEFAULT "identity" @@ -92,24 +84,20 @@ struct zlib_writer { z_stream z; /* State structure for zlib. */ }; - -static voidpf -zalloc_cb(voidpf opaque, unsigned int items, unsigned int size) +static voidpf zalloc_cb(voidpf opaque, unsigned int items, unsigned int size) { (void)opaque; - /* not a typo, keep it calloc() */ - return (voidpf) calloc(items, size); + /* not a typo, keep it curlx_calloc() */ + return (voidpf)curlx_calloc(items, size); } -static void -zfree_cb(voidpf opaque, voidpf ptr) +static void zfree_cb(voidpf opaque, voidpf ptr) { (void)opaque; - free(ptr); + curlx_free(ptr); } -static CURLcode -process_zlib_error(struct Curl_easy *data, z_stream *z) +static CURLcode process_zlib_error(struct Curl_easy *data, z_stream *z) { if(z->msg) failf(data, "Error while processing content unencoding: %s", @@ -121,9 +109,8 @@ process_zlib_error(struct Curl_easy *data, z_stream *z) return CURLE_BAD_CONTENT_ENCODING; } -static CURLcode -exit_zlib(struct Curl_easy *data, - z_stream *z, zlibInitState *zlib_init, CURLcode result) +static CURLcode exit_zlib(struct Curl_easy *data, z_stream *z, + zlibInitState *zlib_init, CURLcode result) { if(*zlib_init != ZLIB_UNINIT) { if(inflateEnd(z) != Z_OK && result == CURLE_OK) @@ -134,8 +121,7 @@ exit_zlib(struct Curl_easy *data, return result; } -static CURLcode process_trailer(struct Curl_easy *data, - struct zlib_writer *zp) +static CURLcode process_trailer(struct Curl_easy *data, struct zlib_writer *zp) { z_stream *z = &zp->z; CURLcode result = CURLE_OK; @@ -162,7 +148,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, struct Curl_cwriter *writer, int type, zlibInitState started) { - struct zlib_writer *zp = (struct zlib_writer *) writer; + struct zlib_writer *zp = (struct zlib_writer *)writer; z_stream *z = &zp->z; /* zlib state structure */ uInt nread = z->avail_in; z_const Bytef *orig_in = z->next_in; @@ -182,7 +168,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, done = TRUE; /* (re)set buffer for decompressed output for every iteration */ - z->next_out = (Bytef *) zp->buffer; + z->next_out = (Bytef *)zp->buffer; z->avail_out = DECOMPRESS_BUFFER_SIZE; status = inflate(z, Z_BLOCK); @@ -207,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); @@ -243,17 +229,16 @@ static CURLcode inflate_stream(struct Curl_easy *data, return result; } - /* Deflate handler. */ static CURLcode deflate_do_init(struct Curl_easy *data, struct Curl_cwriter *writer) { - struct zlib_writer *zp = (struct zlib_writer *) writer; + struct zlib_writer *zp = (struct zlib_writer *)writer; z_stream *z = &zp->z; /* zlib state structure */ /* Initialize zlib */ - z->zalloc = (alloc_func) zalloc_cb; - z->zfree = (free_func) zfree_cb; + z->zalloc = (alloc_func)zalloc_cb; + z->zfree = (free_func)zfree_cb; if(inflateInit(z) != Z_OK) return process_zlib_error(data, z); @@ -265,7 +250,7 @@ static CURLcode deflate_do_write(struct Curl_easy *data, struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { - struct zlib_writer *zp = (struct zlib_writer *) writer; + struct zlib_writer *zp = (struct zlib_writer *)writer; z_stream *z = &zp->z; /* zlib state structure */ if(!(type & CLIENTWRITE_BODY) || !nbytes) @@ -285,7 +270,7 @@ static CURLcode deflate_do_write(struct Curl_easy *data, static void deflate_do_close(struct Curl_easy *data, struct Curl_cwriter *writer) { - struct zlib_writer *zp = (struct zlib_writer *) writer; + struct zlib_writer *zp = (struct zlib_writer *)writer; z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(data, z, &zp->zlib_init, CURLE_OK); @@ -300,17 +285,19 @@ static const struct Curl_cwtype deflate_encoding = { sizeof(struct zlib_writer) }; +/* + * Gzip handler. + */ -/* Gzip handler. */ static CURLcode gzip_do_init(struct Curl_easy *data, struct Curl_cwriter *writer) { - struct zlib_writer *zp = (struct zlib_writer *) writer; + struct zlib_writer *zp = (struct zlib_writer *)writer; z_stream *z = &zp->z; /* zlib state structure */ /* Initialize zlib */ - z->zalloc = (alloc_func) zalloc_cb; - z->zfree = (free_func) zfree_cb; + z->zalloc = (alloc_func)zalloc_cb; + z->zfree = (free_func)zfree_cb; if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) return process_zlib_error(data, z); @@ -323,7 +310,7 @@ static CURLcode gzip_do_write(struct Curl_easy *data, struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { - struct zlib_writer *zp = (struct zlib_writer *) writer; + struct zlib_writer *zp = (struct zlib_writer *)writer; z_stream *z = &zp->z; /* zlib state structure */ if(!(type & CLIENTWRITE_BODY) || !nbytes) @@ -344,7 +331,7 @@ static CURLcode gzip_do_write(struct Curl_easy *data, static void gzip_do_close(struct Curl_easy *data, struct Curl_cwriter *writer) { - struct zlib_writer *zp = (struct zlib_writer *) writer; + struct zlib_writer *zp = (struct zlib_writer *)writer; z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(data, z, &zp->zlib_init, CURLE_OK); @@ -386,12 +373,10 @@ static CURLcode brotli_map_error(BrotliDecoderErrorCode be) case BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS: case BROTLI_DECODER_ERROR_FORMAT_PADDING_1: case BROTLI_DECODER_ERROR_FORMAT_PADDING_2: -#ifdef BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY +#ifdef BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY /* brotli v1.1.0+ */ case BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY: #endif -#ifdef BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET case BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET: -#endif case BROTLI_DECODER_ERROR_INVALID_ARGUMENTS: return CURLE_BAD_CONTENT_ENCODING; case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES: @@ -410,7 +395,7 @@ static CURLcode brotli_map_error(BrotliDecoderErrorCode be) static CURLcode brotli_do_init(struct Curl_easy *data, struct Curl_cwriter *writer) { - struct brotli_writer *bp = (struct brotli_writer *) writer; + struct brotli_writer *bp = (struct brotli_writer *)writer; (void)data; bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL); @@ -421,8 +406,8 @@ static CURLcode brotli_do_write(struct Curl_easy *data, struct Curl_cwriter *writer, int type, const char *buf, size_t nbytes) { - struct brotli_writer *bp = (struct brotli_writer *) writer; - const uint8_t *src = (const uint8_t *) buf; + struct brotli_writer *bp = (struct brotli_writer *)writer; + const uint8_t *src = (const uint8_t *)buf; uint8_t *dst; size_t dstleft; CURLcode result = CURLE_OK; @@ -436,7 +421,7 @@ static CURLcode brotli_do_write(struct Curl_easy *data, while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) && result == CURLE_OK) { - dst = (uint8_t *) bp->buffer; + dst = (uint8_t *)bp->buffer; dstleft = DECOMPRESS_BUFFER_SIZE; r = BrotliDecoderDecompressStream(bp->br, &nbytes, &src, &dstleft, &dst, NULL); @@ -465,7 +450,7 @@ static CURLcode brotli_do_write(struct Curl_easy *data, static void brotli_do_close(struct Curl_easy *data, struct Curl_cwriter *writer) { - struct brotli_writer *bp = (struct brotli_writer *) writer; + struct brotli_writer *bp = (struct brotli_writer *)writer; (void)data; if(bp->br) { @@ -509,7 +494,7 @@ static void Curl_zstd_free(void *opaque, void *address) static CURLcode zstd_do_init(struct Curl_easy *data, struct Curl_cwriter *writer) { - struct zstd_writer *zp = (struct zstd_writer *) writer; + struct zstd_writer *zp = (struct zstd_writer *)writer; (void)data; @@ -531,7 +516,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data, const char *buf, size_t nbytes) { CURLcode result = CURLE_OK; - struct zstd_writer *zp = (struct zstd_writer *) writer; + struct zstd_writer *zp = (struct zstd_writer *)writer; ZSTD_inBuffer in; ZSTD_outBuffer out; size_t errorCode; @@ -568,7 +553,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data, static void zstd_do_close(struct Curl_easy *data, struct Curl_cwriter *writer) { - struct zstd_writer *zp = (struct zstd_writer *) writer; + struct zstd_writer *zp = (struct zstd_writer *)writer; (void)data; if(zp->zds) { @@ -615,47 +600,34 @@ 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 }; -/* Provide a list of comma-separated names of supported encodings. -*/ -void Curl_all_content_encodings(char *buf, size_t blen) +/* Return the list of comma-separated names of supported encodings. + */ +char *Curl_get_content_encodings(void) { - size_t len = 0; + struct dynbuf enc; const struct Curl_cwtype * const *cep; - const struct Curl_cwtype *ce; + CURLcode result = CURLE_OK; + curlx_dyn_init(&enc, 255); - DEBUGASSERT(buf); - DEBUGASSERT(blen); - buf[0] = 0; - - for(cep = general_unencoders; *cep; cep++) { - ce = *cep; - if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT)) - len += strlen(ce->name) + 2; - } - - if(!len) { - if(blen >= sizeof(CONTENT_ENCODING_DEFAULT)) - strcpy(buf, CONTENT_ENCODING_DEFAULT); - } - else if(blen > len) { - char *p = buf; - for(cep = general_unencoders; *cep; cep++) { - ce = *cep; - if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT)) { - strcpy(p, ce->name); - p += strlen(p); - *p++ = ','; - *p++ = ' '; - } + for(cep = general_unencoders; *cep && !result; cep++) { + const struct Curl_cwtype *ce = *cep; + if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT)) { + if(curlx_dyn_len(&enc)) + result = curlx_dyn_addn(&enc, ", ", 2); + if(!result) + result = curlx_dyn_add(&enc, ce->name); } - p[-2] = '\0'; } + if(!result && !curlx_dyn_len(&enc)) + result = curlx_dyn_add(&enc, CONTENT_ENCODING_DEFAULT); + + if(!result) + return curlx_dyn_ptr(&enc); + return NULL; } /* Deferred error dummy writer. */ @@ -677,12 +649,7 @@ static CURLcode error_do_write(struct Curl_easy *data, if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); - else { - char all[256]; - (void)Curl_all_content_encodings(all, sizeof(all)); - failf(data, "Unrecognized content encoding type. " - "libcurl understands %s content encodings.", all); - } + failf(data, "Unrecognized content encoding type"); return CURLE_BAD_CONTENT_ENCODING; } @@ -713,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; } } @@ -849,14 +816,9 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, return CURLE_NOT_BUILT_IN; } -void Curl_all_content_encodings(char *buf, size_t blen) +char *Curl_get_content_encodings(void) { - DEBUGASSERT(buf); - DEBUGASSERT(blen); - if(blen < sizeof(CONTENT_ENCODING_DEFAULT)) - buf[0] = 0; - else - strcpy(buf, CONTENT_ENCODING_DEFAULT); + return curlx_strdup(CONTENT_ENCODING_DEFAULT); } #endif /* CURL_DISABLE_HTTP */ diff --git a/lib/content_encoding.h b/lib/content_encoding.h index 1addf230bb..e84a739761 100644 --- a/lib/content_encoding.h +++ b/lib/content_encoding.h @@ -27,7 +27,8 @@ struct Curl_cwriter; -void Curl_all_content_encodings(char *buf, size_t blen); +/* returns an allocated string or NULL */ +char *Curl_get_content_encodings(void); CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, const char *enclist, int is_transfer); diff --git a/lib/cookie.c b/lib/cookie.c index 35d33268f9..1e21d1bf75 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -21,54 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -/*** - - -RECEIVING COOKIE INFORMATION -============================ - -Curl_cookie_init() - - Inits a cookie struct to store data in a local file. This is always - called before any cookies are set. - -Curl_cookie_add() - - Adds a cookie to the in-memory cookie jar. - - -SENDING COOKIE INFORMATION -========================== - -Curl_cookie_getlist() - - For a given host and path, return a linked list of cookies that - the client should send to the server if used now. The secure - boolean informs the cookie if a secure connection is achieved or - not. - - It shall only return cookies that have not expired. - -Example set of cookies: - - Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure - Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; - domain=.fidelity.com; path=/ftgw; secure - Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; - domain=.fidelity.com; path=/; secure - Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; - domain=.fidelity.com; path=/; secure - Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; - domain=.fidelity.com; path=/; secure - Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; - domain=.fidelity.com; path=/; secure - Set-cookie: - Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday, - 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure -****/ - - #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) @@ -76,28 +28,20 @@ Example set of cookies: #include "urldata.h" #include "cookie.h" #include "psl.h" -#include "sendf.h" +#include "curl_trc.h" #include "slist.h" -#include "share.h" +#include "curl_share.h" #include "strcase.h" +#include "curl_fopen.h" #include "curl_get_line.h" #include "curl_memrchr.h" #include "parsedate.h" -#include "rename.h" -#include "fopen.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "llist.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -static void strstore(char **str, const char *newstr, size_t len); - /* number of seconds in 400 days */ -#define COOKIES_MAXAGE (400*24*3600) +#define COOKIES_MAXAGE (400 * 24 * 3600) /* Make sure cookies never expire further away in time than 400 days into the future. (from RFC6265bis draft-19) @@ -111,19 +55,19 @@ static void cap_expires(time_t now, struct Cookie *co) timediff_t cap = now + COOKIES_MAXAGE; if(co->expires > cap) { cap += 30; - co->expires = (cap/60)*60; + co->expires = (cap / 60) * 60; } } } -static void freecookie(struct Cookie *co) +static void freecookie(struct Cookie *co, bool maintoo) { - free(co->domain); - free(co->path); - free(co->spath); - free(co->name); - free(co->value); - free(co); + curlx_free(co->domain); + curlx_free(co->path); + curlx_free(co->name); + curlx_free(co->value); + if(maintoo) + curlx_free(co); } static bool cookie_tailmatch(const char *cookie_domain, @@ -136,7 +80,7 @@ static bool cookie_tailmatch(const char *cookie_domain, return FALSE; if(!curl_strnequal(cookie_domain, - hostname + hostname_len-cookie_domain_len, + hostname + hostname_len - cookie_domain_len, cookie_domain_len)) return FALSE; @@ -279,57 +223,28 @@ static size_t cookiehash(const char * const domain) /* * cookie path sanitize */ -static char *sanitize_cookie_path(const char *cookie_path) +static char *sanitize_cookie_path(const char *cookie_path, size_t len) { - size_t len = strlen(cookie_path); - /* some sites send path attribute within '"'. */ - if(cookie_path[0] == '\"') { + if(len && (cookie_path[0] == '\"')) { cookie_path++; len--; + + if(len && (cookie_path[len - 1] == '\"')) + len--; } - if(len && (cookie_path[len - 1] == '\"')) - len--; /* RFC6265 5.2.4 The Path Attribute */ - if(cookie_path[0] != '/') + if(!len || (cookie_path[0] != '/')) /* Let cookie-path be the default-path. */ - return strdup("/"); + return curlx_strdup("/"); /* remove trailing slash when path is non-empty */ /* convert /hoge/ to /hoge */ if(len > 1 && cookie_path[len - 1] == '/') len--; - return Curl_memdup0(cookie_path, len); -} - -/* - * Load cookies from all given cookie files (CURLOPT_COOKIEFILE). - * - * NOTE: OOM or cookie parsing failures are ignored. - */ -void Curl_cookie_loadfiles(struct Curl_easy *data) -{ - struct curl_slist *list = data->state.cookielist; - if(list) { - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - while(list) { - struct CookieInfo *ci = - Curl_cookie_init(data, list->data, data->cookies, - data->set.cookiesession); - if(!ci) - /* - * Failure may be due to OOM or a bad cookie; both are ignored - * but only the first should be - */ - infof(data, "ignoring failed cookie_init for %s", list->data); - else - data->cookies = ci; - list = list->next; - } - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } + return curlx_memdup0(cookie_path, len); } /* @@ -341,15 +256,17 @@ void Curl_cookie_loadfiles(struct Curl_easy *data) * parsing in a last-wins scenario. The caller is responsible for checking * for OOM errors. */ -static void strstore(char **str, const char *newstr, size_t len) +static CURLcode strstore(char **str, const char *newstr, size_t len) { DEBUGASSERT(str); - free(*str); if(!len) { len++; newstr = ""; } - *str = Curl_memdup0(newstr, len); + *str = curlx_memdup0(newstr, len); + if(!*str) + return CURLE_OUT_OF_MEMORY; + return CURLE_OK; } /* @@ -370,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) @@ -391,7 +308,7 @@ static void remove_expired(struct CookieInfo *ci) if(co->expires) { if(co->expires < now) { Curl_node_remove(n); - freecookie(co); + freecookie(co, TRUE); ci->numcookies--; } else if(co->expires < ci->next_expiration) @@ -413,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) @@ -430,176 +347,186 @@ static bool bad_domain(const char *domain, size_t len) cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E - But Firefox and Chrome as of June 2022 accept space, comma and double-quotes + Yet, Firefox and Chrome as of June 2022 accept space, comma and double-quotes fine. The prime reason for filtering out control bytes is that some HTTP servers return 400 for requests that contain such. */ -static bool invalid_octets(const char *ptr) +static bool invalid_octets(const char *ptr, size_t len) { const unsigned char *p = (const unsigned char *)ptr; /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */ - while(*p) { + while(len && *p) { if(((*p != 9) && (*p < 0x20)) || (*p == 0x7f)) return TRUE; p++; + len--; } return FALSE; } -#define CERR_OK 0 -#define CERR_TOO_LONG 1 /* input line too long */ -#define CERR_TAB 2 /* in a wrong place */ -#define CERR_TOO_BIG 3 /* name/value too large */ -#define CERR_BAD 4 /* deemed incorrect */ -#define CERR_NO_SEP 5 /* semicolon problem */ -#define CERR_NO_NAME_VALUE 6 /* name or value problem */ -#define CERR_INVALID_OCTET 7 /* bad content */ -#define CERR_BAD_SECURE 8 /* secure in a bad place */ -#define CERR_OUT_OF_MEMORY 9 -#define CERR_NO_TAILMATCH 10 -#define CERR_COMMENT 11 /* a commented line */ -#define CERR_RANGE 12 /* expire range problem */ -#define CERR_FIELDS 13 /* incomplete netscape line */ -#ifdef USE_LIBPSL -#define CERR_PSL 14 /* a public suffix */ -#endif -#define CERR_LIVE_WINS 15 - /* The maximum length we accept a date string for the 'expire' keyword. The standard date formats are within the 30 bytes range. This adds an extra - margin just to make sure it realistically works with what is used out - there. + margin to make sure it realistically works with what is used out there. */ #define MAX_DATE_LENGTH 80 -static int -parse_cookie_header(struct Curl_easy *data, - struct Cookie *co, - struct CookieInfo *ci, - const char *ptr, - const char *domain, /* default domain */ - const char *path, /* full path used when this cookie is - set, used to get default path for - the cookie unless set */ - bool secure) /* TRUE if connection is over secure - origin */ +#define COOKIE_NAME 0 +#define COOKIE_VALUE 1 +#define COOKIE_DOMAIN 2 +#define COOKIE_PATH 3 + +#define COOKIE_PIECES 4 /* the list above */ + +static CURLcode storecookie(struct Cookie *co, struct Curl_str *cp, + const char *path, const char *domain) +{ + CURLcode result; + result = strstore(&co->name, curlx_str(&cp[COOKIE_NAME]), + curlx_strlen(&cp[COOKIE_NAME])); + if(!result) + result = strstore(&co->value, curlx_str(&cp[COOKIE_VALUE]), + curlx_strlen(&cp[COOKIE_VALUE])); + if(!result) { + size_t plen = 0; + if(curlx_strlen(&cp[COOKIE_PATH])) { + path = curlx_str(&cp[COOKIE_PATH]); + plen = curlx_strlen(&cp[COOKIE_PATH]); + } + else if(path) { + /* 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 */ + else + plen = strlen(path); + } + + if(path) { + co->path = sanitize_cookie_path(path, plen); + if(!co->path) + result = CURLE_OUT_OF_MEMORY; + } + } + if(!result) { + if(curlx_strlen(&cp[COOKIE_DOMAIN])) + result = strstore(&co->domain, curlx_str(&cp[COOKIE_DOMAIN]), + curlx_strlen(&cp[COOKIE_DOMAIN])); + else if(domain) { + /* no domain was given in the header line, set the default */ + co->domain = curlx_strdup(domain); + if(!co->domain) + result = CURLE_OUT_OF_MEMORY; + } + } + return result; +} + +/* this function return errors on OOM etc, not on plain cookie format + problems */ +static CURLcode parse_cookie_header( + struct Curl_easy *data, + struct Cookie *co, + struct CookieInfo *ci, + bool *okay, /* if the cookie was fine */ + const char *ptr, + const char *domain, /* default domain */ + const char *path, /* full path used when this cookie is + set, used to get default path for + the cookie unless set */ + bool secure) /* TRUE if connection is over secure origin */ { /* This line was read off an HTTP-header */ - time_t now; + time_t now = 0; size_t linelength = strlen(ptr); + CURLcode result = CURLE_OK; + struct Curl_str cookie[COOKIE_PIECES]; + *okay = FALSE; if(linelength > MAX_COOKIE_LINE) /* discard overly long lines at once */ - return CERR_TOO_LONG; + return CURLE_OK; - now = time(NULL); + /* memset instead of initializer because gcc 4.8.1 is silly */ + memset(cookie, 0, sizeof(cookie)); do { struct Curl_str name; struct Curl_str val; /* we have a = pair or a stand-alone word here */ if(!curlx_str_cspn(&ptr, &name, ";\t\r\n=")) { - bool done = FALSE; bool sep = FALSE; curlx_str_trimblanks(&name); if(!curlx_str_single(&ptr, '=')) { sep = TRUE; /* a '=' was used */ - if(!curlx_str_cspn(&ptr, &val, ";\r\n")) { + if(!curlx_str_cspn(&ptr, &val, ";\r\n")) curlx_str_trimblanks(&val); - /* Reject cookies with a TAB inside the value */ - if(memchr(curlx_str(&val), '\t', curlx_strlen(&val))) { - infof(data, "cookie contains TAB, dropping"); - return CERR_TAB; - } + /* Reject cookies with a TAB inside the value */ + if(curlx_strlen(&val) && + memchr(curlx_str(&val), '\t', curlx_strlen(&val))) { + infof(data, "cookie contains TAB, dropping"); + return CURLE_OK; } } - else { + else curlx_str_init(&val); - } - /* - * Check for too long individual name or contents, or too long - * combination of name + contents. Chrome and Firefox support 4095 or - * 4096 bytes combo - */ - if(curlx_strlen(&name) >= (MAX_NAME-1) || - curlx_strlen(&val) >= (MAX_NAME-1) || - ((curlx_strlen(&name) + curlx_strlen(&val)) > MAX_NAME)) { - infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", - curlx_strlen(&name), curlx_strlen(&val)); - return CERR_TOO_BIG; - } - - /* - * Check if we have a reserved prefix set before anything else, as we - * otherwise have to test for the prefix in both the cookie name and - * "the rest". Prefixes must start with '__' and end with a '-', so - * only test for names where that can possibly be true. - */ - if(!strncmp("__Secure-", curlx_str(&name), 9)) - co->prefix_secure = TRUE; - else if(!strncmp("__Host-", curlx_str(&name), 7)) - co->prefix_host = TRUE; - - /* - * Use strstore() below to properly deal with received cookie - * headers that have the same string property set more than once, - * and then we use the last one. - */ - - if(!co->name) { - /* The very first name/value pair is the actual cookie name */ - if(!sep) - /* Bad name/value pair. */ - return CERR_NO_SEP; - - strstore(&co->name, curlx_str(&name), curlx_strlen(&name)); - strstore(&co->value, curlx_str(&val), curlx_strlen(&val)); - done = TRUE; - if(!co->name || !co->value) - return CERR_NO_NAME_VALUE; - - if(invalid_octets(co->value) || invalid_octets(co->name)) { + if(!curlx_strlen(&cookie[COOKIE_NAME])) { + /* The first name/value pair is the actual cookie name */ + if(!sep || + /* Bad name/value pair. */ + invalid_octets(curlx_str(&name), curlx_strlen(&name)) || + invalid_octets(curlx_str(&val), curlx_strlen(&val)) || + !curlx_strlen(&name)) { infof(data, "invalid octets in name/value, cookie dropped"); - return CERR_INVALID_OCTET; + return CURLE_OK; } - } - else if(!curlx_strlen(&val)) { + /* - * this was a "=" with no content, and we must allow - * 'secure' and 'httponly' specified this weirdly + * Check for too long individual name or contents, or too long + * combination of name + contents. Chrome and Firefox support 4095 or + * 4096 bytes combo */ - done = TRUE; + if(curlx_strlen(&name) >= (MAX_NAME - 1) || + curlx_strlen(&val) >= (MAX_NAME - 1) || + ((curlx_strlen(&name) + curlx_strlen(&val)) > MAX_NAME)) { + infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", + curlx_strlen(&name), curlx_strlen(&val)); + return CURLE_OK; + } + + /* Check if we have a reserved prefix set. */ + if(!strncmp("__Secure-", curlx_str(&name), 9)) + co->prefix_secure = TRUE; + else if(!strncmp("__Host-", curlx_str(&name), 7)) + co->prefix_host = TRUE; + + cookie[COOKIE_NAME] = name; + cookie[COOKIE_VALUE] = val; + } + else if(!sep) { + /* + * this is a "" with no content + */ + /* * secure cookies are only allowed to be set when the connection is * using a secure protocol, or when the cookie is being set by * reading from file */ if(curlx_str_casecompare(&name, "secure")) { - if(secure || !ci->running) { + if(secure || !ci->running) co->secure = TRUE; - } else { - return CERR_BAD_SECURE; + infof(data, "skipped cookie because not 'secure'"); + return CURLE_OK; } } else if(curlx_str_casecompare(&name, "httponly")) co->httponly = TRUE; - else if(sep) - /* there was a '=' so we are not done parsing this field */ - done = FALSE; } - if(done) - ; else if(curlx_str_casecompare(&name, "path")) { - strstore(&co->path, curlx_str(&val), curlx_strlen(&val)); - if(!co->path) - return CERR_OUT_OF_MEMORY; - free(co->spath); /* if this is set again */ - co->spath = sanitize_cookie_path(co->path); - if(!co->spath) - return CERR_OUT_OF_MEMORY; + cookie[COOKIE_PATH] = val; } else if(curlx_str_casecompare(&name, "domain") && curlx_strlen(&val)) { bool is_ip; @@ -624,16 +551,13 @@ parse_cookie_header(struct Curl_easy *data, is_ip = Curl_host_is_ipnum(domain ? domain : curlx_str(&val)); - if(!domain - || (is_ip && !strncmp(curlx_str(&val), domain, - curlx_strlen(&val)) && - (curlx_strlen(&val) == strlen(domain))) - || (!is_ip && cookie_tailmatch(curlx_str(&val), + if(!domain || + (is_ip && + !strncmp(curlx_str(&val), domain, curlx_strlen(&val)) && + (curlx_strlen(&val) == strlen(domain))) || + (!is_ip && cookie_tailmatch(curlx_str(&val), curlx_strlen(&val), domain))) { - strstore(&co->domain, curlx_str(&val), curlx_strlen(&val)); - if(!co->domain) - return CERR_OUT_OF_MEMORY; - + cookie[COOKIE_DOMAIN] = val; if(!is_ip) co->tailmatch = TRUE; /* we always do that if the domain name was given */ @@ -645,12 +569,9 @@ parse_cookie_header(struct Curl_easy *data, */ infof(data, "skipped cookie with bad tailmatch domain: %s", curlx_str(&val)); - return CERR_NO_TAILMATCH; + return CURLE_OK; } } - else if(curlx_str_casecompare(&name, "version")) { - /* just ignore */ - } else if(curlx_str_casecompare(&name, "max-age") && curlx_strlen(&val)) { /* * Defined in RFC2109: @@ -666,6 +587,8 @@ parse_cookie_header(struct Curl_easy *data, if(*maxage == '\"') maxage++; rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX); + if(!now) + now = time(NULL); switch(rc) { case STRE_OVERFLOW: /* overflow, used max value */ @@ -687,80 +610,47 @@ parse_cookie_header(struct Curl_easy *data, } cap_expires(now, co); } - else if(curlx_str_casecompare(&name, "expires") && curlx_strlen(&val)) { - if(!co->expires && (curlx_strlen(&val) < MAX_DATE_LENGTH)) { - /* - * Let max-age have priority. - * - * If the date cannot get parsed for whatever reason, the cookie - * will be treated as a session cookie - */ - char dbuf[MAX_DATE_LENGTH + 1]; - time_t date = 0; - memcpy(dbuf, curlx_str(&val), curlx_strlen(&val)); - dbuf[curlx_strlen(&val)] = 0; - if(!Curl_getdate_capped(dbuf, &date)) { - if(!date) - date++; - co->expires = (curl_off_t)date; - } - else - co->expires = 0; - cap_expires(now, co); + else if(curlx_str_casecompare(&name, "expires") && curlx_strlen(&val) && + !co->expires && (curlx_strlen(&val) < MAX_DATE_LENGTH)) { + /* + * Let max-age have priority. + * + * If the date cannot get parsed for whatever reason, the cookie + * will be treated as a session cookie + */ + char dbuf[MAX_DATE_LENGTH + 1]; + time_t date = 0; + memcpy(dbuf, curlx_str(&val), curlx_strlen(&val)); + dbuf[curlx_strlen(&val)] = 0; + if(!Curl_getdate_capped(dbuf, &date)) { + if(!date) + date++; + co->expires = (curl_off_t)date; } + else + co->expires = 0; + if(!now) + now = time(NULL); + cap_expires(now, co); } - - /* - * Else, this is the second (or more) name we do not know about! - */ } + } while(!curlx_str_single(&ptr, ';')); - if(curlx_str_single(&ptr, ';')) - break; - } while(1); - - if(!co->domain && domain) { - /* no domain was given in the header line, set the default */ - co->domain = strdup(domain); - if(!co->domain) - return CERR_OUT_OF_MEMORY; + if(curlx_strlen(&cookie[COOKIE_NAME])) { + /* the header was fine, now store the data */ + result = storecookie(co, &cookie[0], path, domain); + if(!result) + *okay = TRUE; } - - if(!co->path && path) { - /* - * No path was given in the header line, set the default. - */ - const char *endslash = strrchr(path, '/'); - if(endslash) { - size_t pathlen = (endslash - path + 1); /* include end slash */ - co->path = Curl_memdup0(path, pathlen); - if(co->path) { - co->spath = sanitize_cookie_path(co->path); - if(!co->spath) - return CERR_OUT_OF_MEMORY; - } - else - return CERR_OUT_OF_MEMORY; - } - } - - /* - * If we did not get a cookie name, or a bad one, the this is an illegal - * line so bail out. - */ - if(!co->name) - return CERR_BAD; - - data->req.setcookies++; - return CERR_OK; + return result; } -static int -parse_netscape(struct Cookie *co, - struct CookieInfo *ci, - const char *lineptr, - bool secure) /* TRUE if connection is over secure - origin */ +static CURLcode parse_netscape(struct Cookie *co, + struct CookieInfo *ci, + bool *okay, + const char *lineptr, + bool secure) /* TRUE if connection is over + secure origin */ { /* * This line is NOT an HTTP header style line, we do offer support for @@ -769,6 +659,7 @@ parse_netscape(struct Cookie *co, const char *ptr, *next; int fields; size_t len; + *okay = FALSE; /* * In 2008, Internet Explorer introduced HTTP-only cookies to prevent XSS @@ -781,9 +672,9 @@ parse_netscape(struct Cookie *co, co->httponly = TRUE; } - if(lineptr[0]=='#') + if(lineptr[0] == '#') /* do not even try the comments */ - return CERR_COMMENT; + return CURLE_OK; /* * Now loop through the fields and init the struct we already have @@ -796,13 +687,13 @@ parse_netscape(struct Cookie *co, next = (ptr[len] == '\t' ? &ptr[len + 1] : NULL); switch(fields) { case 0: - if(ptr[0]=='.') { /* skip preceding dots */ + if(ptr[0] == '.') { /* skip preceding dots */ ptr++; len--; } - co->domain = Curl_memdup0(ptr, len); + co->domain = curlx_memdup0(ptr, len); if(!co->domain) - return CERR_OUT_OF_MEMORY; + return CURLE_OUT_OF_MEMORY; break; case 1: /* @@ -816,23 +707,17 @@ parse_netscape(struct Cookie *co, /* The file format allows the path field to remain not filled in */ if(strncmp("TRUE", ptr, len) && strncmp("FALSE", ptr, len)) { /* only if the path does not look like a boolean option! */ - co->path = Curl_memdup0(ptr, len); + co->path = sanitize_cookie_path(ptr, len); if(!co->path) - return CERR_OUT_OF_MEMORY; - else { - co->spath = sanitize_cookie_path(co->path); - if(!co->spath) - return CERR_OUT_OF_MEMORY; - } + return CURLE_OUT_OF_MEMORY; break; } - /* this does not look like a path, make one up! */ - co->path = strdup("/"); - if(!co->path) - return CERR_OUT_OF_MEMORY; - co->spath = strdup("/"); - if(!co->spath) - return CERR_OUT_OF_MEMORY; + else { + /* this does not look like a path, make one up! */ + co->path = curlx_strdup("/"); + if(!co->path) + return CURLE_OUT_OF_MEMORY; + } fields++; /* add a field and fall down to secure */ FALLTHROUGH(); case 3: @@ -841,17 +726,17 @@ parse_netscape(struct Cookie *co, if(secure || ci->running) co->secure = TRUE; else - return CERR_BAD_SECURE; + return CURLE_OK; } break; case 4: if(curlx_str_number(&ptr, &co->expires, CURL_OFF_T_MAX)) - return CERR_RANGE; + return CURLE_OK; break; case 5: - co->name = Curl_memdup0(ptr, len); + co->name = curlx_memdup0(ptr, len); if(!co->name) - return CERR_OUT_OF_MEMORY; + return CURLE_OUT_OF_MEMORY; else { /* For Netscape file format cookies we check prefix on the name */ if(curl_strnequal("__Secure-", co->name, 9)) @@ -861,32 +746,32 @@ parse_netscape(struct Cookie *co, } break; case 6: - co->value = Curl_memdup0(ptr, len); + co->value = curlx_memdup0(ptr, len); if(!co->value) - return CERR_OUT_OF_MEMORY; + return CURLE_OUT_OF_MEMORY; break; } } if(fields == 6) { /* we got a cookie with blank contents, fix it */ - co->value = strdup(""); + co->value = curlx_strdup(""); if(!co->value) - return CERR_OUT_OF_MEMORY; + return CURLE_OUT_OF_MEMORY; else fields++; } if(fields != 7) /* we did not find the sufficient number of fields */ - return CERR_FIELDS; + return CURLE_OK; - return CERR_OK; + *okay = TRUE; + return CURLE_OK; } -static int -is_public_suffix(struct Curl_easy *data, - struct Cookie *co, - const char *domain) +static bool is_public_suffix(struct Curl_easy *data, + struct Cookie *co, + const char *domain) { #ifdef USE_LIBPSL /* @@ -895,7 +780,7 @@ 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]; @@ -918,7 +803,7 @@ is_public_suffix(struct Curl_easy *data, if(!acceptable) { infof(data, "cookie '%s' dropped, domain '%s' must not " "set cookies for '%s'", co->name, domain, co->domain); - return CERR_PSL; + return TRUE; } } #else @@ -926,17 +811,17 @@ 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 CERR_OK; + return FALSE; } -static int -replace_existing(struct Curl_easy *data, - struct Cookie *co, - struct CookieInfo *ci, - bool secure, - bool *replacep) +/* returns TRUE when replaced */ +static bool replace_existing(struct Curl_easy *data, + struct Cookie *co, + struct CookieInfo *ci, + bool secure, + bool *replacep) { bool replace_old = FALSE; struct Curl_llist_node *replace_n = NULL; @@ -957,7 +842,7 @@ replace_existing(struct Curl_easy *data, matching_domains = TRUE; if(matching_domains && /* the domains were identical */ - clist->spath && co->spath && /* both have paths */ + clist->path && co->path && /* both have paths */ clist->secure && !co->secure && !secure) { size_t cllen; const char *sep = NULL; @@ -969,18 +854,18 @@ replace_existing(struct Curl_easy *data, * "/loginhelper" is ok. */ - DEBUGASSERT(clist->spath[0]); - if(clist->spath[0]) - sep = strchr(clist->spath + 1, '/'); + DEBUGASSERT(clist->path[0]); + if(clist->path[0]) + sep = strchr(clist->path + 1, '/'); if(sep) - cllen = sep - clist->spath; + cllen = sep - clist->path; else - cllen = strlen(clist->spath); + cllen = strlen(clist->path); - if(curl_strnequal(clist->spath, co->spath, cllen)) { + if(curl_strnequal(clist->path, co->path, cllen)) { infof(data, "cookie '%s' for domain '%s' dropped, would " "overlay an existing cookie", co->name, co->domain); - return CERR_BAD_SECURE; + return FALSE; } } } @@ -990,7 +875,7 @@ replace_existing(struct Curl_easy *data, if(clist->domain && co->domain) { if(curl_strequal(clist->domain, co->domain) && - (clist->tailmatch == co->tailmatch)) + (clist->tailmatch == co->tailmatch)) /* The domains are identical */ replace_old = TRUE; } @@ -1000,10 +885,10 @@ replace_existing(struct Curl_easy *data, if(replace_old) { /* the domains were identical */ - if(clist->spath && co->spath && - !curl_strequal(clist->spath, co->spath)) + if(clist->path && co->path && + !curl_strequal(clist->path, co->path)) replace_old = FALSE; - else if(!clist->spath != !co->spath) + else if(!clist->path != !co->path) replace_old = FALSE; } @@ -1014,7 +899,7 @@ replace_existing(struct Curl_easy *data, * was read from a file and thus is not "live". "live" cookies are * preferred so the new cookie is freed. */ - return CERR_LIVE_WINS; + return FALSE; } if(replace_old) replace_n = n; @@ -1030,10 +915,10 @@ replace_existing(struct Curl_easy *data, Curl_node_remove(replace_n); /* free the old cookie */ - freecookie(repl); + freecookie(repl, TRUE); } *replacep = replace_old; - return CERR_OK; + return TRUE; } /* @@ -1043,42 +928,40 @@ replace_existing(struct Curl_easy *data, * sometimes we get an IP-only hostname, and that might also be a numerical * IPv6 address. * - * Returns NULL on out of memory or invalid cookie. This is suboptimal, - * as they should be treated separately. */ -struct Cookie * -Curl_cookie_add(struct Curl_easy *data, - struct CookieInfo *ci, - bool httpheader, /* TRUE if HTTP header-style line */ - bool noexpire, /* if TRUE, skip remove_expired() */ - const char *lineptr, /* first character of the line */ - const char *domain, /* default domain */ - const char *path, /* full path used when this cookie is set, - used to get default path for the cookie - unless set */ - bool secure) /* TRUE if connection is over secure origin */ +CURLcode Curl_cookie_add( + struct Curl_easy *data, + struct CookieInfo *ci, + bool httpheader, /* TRUE if HTTP header-style line */ + bool noexpire, /* if TRUE, skip remove_expired() */ + const char *lineptr, /* first character of the line */ + const char *domain, /* default domain */ + const char *path, /* full path used when this cookie is set, used + to get default path for the cookie unless set */ + bool secure) /* TRUE if connection is over secure origin */ { + struct Cookie comem; struct Cookie *co; size_t myhash; - int rc; + CURLcode result; bool replaces = FALSE; + bool okay; DEBUGASSERT(data); DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */ if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT) - return NULL; + return CURLE_OK; /* silently ignore */ - /* First, alloc and init a new struct for it */ - co = calloc(1, sizeof(struct Cookie)); - if(!co) - return NULL; /* bail out if we are this low on memory */ + co = &comem; + memset(co, 0, sizeof(comem)); if(httpheader) - rc = parse_cookie_header(data, co, ci, lineptr, domain, path, secure); + result = parse_cookie_header(data, co, ci, &okay, + lineptr, domain, path, secure); else - rc = parse_netscape(co, ci, lineptr, secure); + result = parse_netscape(co, ci, &okay, lineptr, secure); - if(rc) + if(result || !okay) goto fail; if(co->prefix_secure && !co->secure) @@ -1090,7 +973,7 @@ Curl_cookie_add(struct Curl_easy *data, * The __Host- prefix requires the cookie to be secure, have a "/" path * and not have a domain set. */ - if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch) + if(co->secure && co->path && !strcmp(co->path, "/") && !co->tailmatch) ; else goto fail; @@ -1117,9 +1000,17 @@ Curl_cookie_add(struct Curl_easy *data, if(is_public_suffix(data, co, domain)) goto fail; - if(replace_existing(data, co, ci, secure, &replaces)) + if(!replace_existing(data, co, ci, secure, &replaces)) goto fail; + /* clone the stack struct into heap */ + co = curlx_memdup(&comem, sizeof(comem)); + if(!co) { + co = &comem; + result = CURLE_OUT_OF_MEMORY; + goto fail; /* bail out if we are this low on memory */ + } + /* add this cookie to the list */ myhash = cookiehash(co->domain); Curl_llist_append(&ci->cookielist[myhash], co, &co->node); @@ -1128,7 +1019,7 @@ Curl_cookie_add(struct Curl_easy *data, /* Only show this when NOT reading the cookies from a file */ infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, " "expire %" FMT_OFF_T, - replaces ? "Replaced":"Added", co->name, co->value, + replaces ? "Replaced" : "Added", co->name, co->value, co->domain, co->path, co->expires); if(!replaces) @@ -1141,12 +1032,14 @@ Curl_cookie_add(struct Curl_easy *data, if(co->expires && (co->expires < ci->next_expiration)) ci->next_expiration = co->expires; - return co; -fail: - freecookie(co); - return NULL; -} + if(httpheader) + data->req.setcookies++; + return result; +fail: + freecookie(co, FALSE); + return result; +} /* * Curl_cookie_init() @@ -1160,54 +1053,78 @@ fail: * Note that 'data' might be called as NULL pointer. If data is NULL, 'file' * will be ignored. * - * Returns NULL on out of memory. Invalid cookies are ignored. + * Returns NULL on out of memory. */ -struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, - const char *file, - struct CookieInfo *ci, - bool newsession) +struct CookieInfo *Curl_cookie_init(void) +{ + int i; + struct CookieInfo *ci = curlx_calloc(1, sizeof(struct CookieInfo)); + if(!ci) + return NULL; + + /* This does not use the destructor callback since we want to add + and remove to lists while keeping the cookie struct intact */ + for(i = 0; i < COOKIE_HASH_SIZE; i++) + Curl_llist_init(&ci->cookielist[i], NULL); + /* + * Initialize the next_expiration time to signal that we do not have enough + * information yet. + */ + ci->next_expiration = CURL_OFF_T_MAX; + + return ci; +} + +/* + * cookie_load() + * + * Reads cookies from a local file. This is always called before any cookies + * are set. If file is "-" then STDIN is read. + * + * If 'newsession' is TRUE, discard all "session cookies" on read from file. + * + */ +static CURLcode cookie_load(struct Curl_easy *data, const char *file, + struct CookieInfo *ci, bool newsession) { FILE *handle = NULL; + CURLcode result = CURLE_OK; + FILE *fp = NULL; + DEBUGASSERT(ci); + DEBUGASSERT(data); + DEBUGASSERT(file); - if(!ci) { - int i; - - /* we did not get a struct, create one */ - ci = calloc(1, sizeof(struct CookieInfo)); - if(!ci) - return NULL; /* failed to get memory */ - - /* This does not use the destructor callback since we want to add - and remove to lists while keeping the cookie struct intact */ - for(i = 0; i < COOKIE_HASH_SIZE; i++) - Curl_llist_init(&ci->cookielist[i], NULL); - /* - * Initialize the next_expiration time to signal that we do not have enough - * information yet. - */ - ci->next_expiration = CURL_OFF_T_MAX; - } ci->newsession = newsession; /* new session? */ + ci->running = FALSE; /* this is not running, this is init */ - if(data) { - FILE *fp = NULL; - if(file && *file) { - if(!strcmp(file, "-")) - fp = stdin; + if(file && *file) { + if(!strcmp(file, "-")) + fp = stdin; + else { + fp = curlx_fopen(file, "rb"); + if(!fp) + infof(data, "WARNING: failed to open cookie file \"%s\"", file); else { - fp = fopen(file, "rb"); - if(!fp) - infof(data, "WARNING: failed to open cookie file \"%s\"", file); + curlx_struct_stat stat; + if((curlx_fstat(fileno(fp), &stat) != -1) && S_ISDIR(stat.st_mode)) { + curlx_fclose(fp); + fp = NULL; + infof(data, "WARNING: cookie filename points to a directory: \"%s\"", + file); + } else handle = fp; } } + } - ci->running = FALSE; /* this is not running, this is init */ - if(fp) { - struct dynbuf buf; - curlx_dyn_init(&buf, MAX_COOKIE_LINE); - while(Curl_get_line(&buf, fp)) { + if(fp) { + struct dynbuf buf; + bool eof = FALSE; + curlx_dyn_init(&buf, MAX_COOKIE_LINE); + do { + result = Curl_get_line(&buf, fp, &eof); + if(!result) { const char *lineptr = curlx_dyn_ptr(&buf); bool headerline = FALSE; if(checkprefix("Set-Cookie:", lineptr)) { @@ -1217,24 +1134,55 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, curlx_str_passblanks(&lineptr); } - Curl_cookie_add(data, ci, headerline, TRUE, lineptr, NULL, NULL, TRUE); + result = Curl_cookie_add(data, ci, headerline, TRUE, lineptr, NULL, + NULL, TRUE); + /* File reading cookie failures are not propagated back to the + caller because there is no way to do that */ } - curlx_dyn_free(&buf); /* free the line buffer */ + } while(!result && !eof); + curlx_dyn_free(&buf); /* free the line buffer */ - /* - * Remove expired cookies from the hash. We must make sure to run this - * after reading the file, and not on every cookie. - */ - remove_expired(ci); + /* + * Remove expired cookies from the hash. We must make sure to run this + * after reading the file, and not on every cookie. + */ + remove_expired(ci); - if(handle) - fclose(handle); - } - data->state.cookie_engine = TRUE; + if(handle) + curlx_fclose(handle); } + data->state.cookie_engine = TRUE; ci->running = TRUE; /* now, we are running */ - return ci; + return result; +} + +/* + * Load cookies from all given cookie files (CURLOPT_COOKIEFILE). + */ +CURLcode Curl_cookie_loadfiles(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + struct curl_slist *list = data->state.cookielist; + if(list) { + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + if(!data->cookies) + data->cookies = Curl_cookie_init(); + if(!data->cookies) + result = CURLE_OUT_OF_MEMORY; + else { + data->state.cookie_engine = TRUE; + while(list) { + result = cookie_load(data, list->data, data->cookies, + (bool)data->set.cookiesession); + if(result) + break; + list = list->next; + } + } + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } + return result; } /* @@ -1291,7 +1239,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"); @@ -1306,12 +1254,13 @@ bool Curl_secure_context(struct connectdata *conn, const char *host) * * It shall only return cookies that have not expired. * - * Returns 0 when there is a list returned. Otherwise non-zero. + * 'okay' is TRUE when there is a list returned. */ -int Curl_cookie_getlist(struct Curl_easy *data, - struct connectdata *conn, - const char *host, - struct Curl_llist *list) +CURLcode Curl_cookie_getlist(struct Curl_easy *data, + struct connectdata *conn, + bool *okay, + const char *host, + struct Curl_llist *list) { size_t matches = 0; const bool is_ip = Curl_host_is_ipnum(host); @@ -1320,17 +1269,18 @@ int Curl_cookie_getlist(struct Curl_easy *data, const bool secure = Curl_secure_context(conn, host); struct CookieInfo *ci = data->cookies; const char *path = data->state.up.path; + CURLcode result = CURLE_OK; + *okay = FALSE; Curl_llist_init(list, NULL); if(!ci || !Curl_llist_count(&ci->cookielist[myhash])) - return 1; /* no cookie struct or no cookies in the struct */ + return CURLE_OK; /* no cookie struct or no cookies in the struct */ /* at first, remove expired cookies */ remove_expired(ci); - for(n = Curl_llist_head(&ci->cookielist[myhash]); - n; n = Curl_node_next(n)) { + for(n = Curl_llist_head(&ci->cookielist[myhash]); n; n = Curl_node_next(n)) { struct Cookie *co = Curl_node_elem(n); /* if the cookie requires we are secure we must only continue if we are! */ @@ -1340,7 +1290,7 @@ int Curl_cookie_getlist(struct Curl_easy *data, if(!co->domain || (co->tailmatch && !is_ip && cookie_tailmatch(co->domain, strlen(co->domain), host)) || - ((!co->tailmatch || is_ip) && curl_strequal(host, co->domain)) ) { + ((!co->tailmatch || is_ip) && curl_strequal(host, co->domain))) { /* * the right part of the host matches the domain stuff in the * cookie data @@ -1350,7 +1300,7 @@ int Curl_cookie_getlist(struct Curl_easy *data, * now check the left part of the path with the cookies path * requirement */ - if(!co->spath || pathmatch(co->spath, path) ) { + if(!co->path || pathmatch(co->path, path)) { /* * This is a match and we add it to the return-linked-list @@ -1370,16 +1320,18 @@ int 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; /* alloc an array and store all cookie pointers */ - array = malloc(sizeof(struct Cookie *) * matches); - if(!array) + array = curlx_malloc(sizeof(struct Cookie *) * matches); + if(!array) { + result = CURLE_OUT_OF_MEMORY; goto fail; + } n = Curl_llist_head(list); @@ -1395,15 +1347,16 @@ int Curl_cookie_getlist(struct Curl_easy *data, for(i = 0; i < matches; i++) Curl_llist_append(list, array[i], &array[i]->getnode); - free(array); /* remove the temporary data again */ + curlx_free(array); /* remove the temporary data again */ } - return 0; /* success */ + *okay = TRUE; + return CURLE_OK; /* success */ fail: /* failure, clear up the allocated chain and return NULL */ Curl_llist_destroy(list, NULL); - return 2; /* error */ + return result; /* error */ } /* @@ -1421,7 +1374,7 @@ void Curl_cookie_clearall(struct CookieInfo *ci) struct Cookie *c = Curl_node_elem(n); struct Curl_llist_node *e = Curl_node_next(n); Curl_node_remove(n); - freecookie(c); + freecookie(c, TRUE); n = e; } } @@ -1450,7 +1403,7 @@ void Curl_cookie_clearsess(struct CookieInfo *ci) e = Curl_node_next(n); /* in case the node is removed, get it early */ if(!curr->expires) { Curl_node_remove(n); - freecookie(curr); + freecookie(curr, TRUE); ci->numcookies--; } } @@ -1466,7 +1419,7 @@ void Curl_cookie_cleanup(struct CookieInfo *ci) { if(ci) { Curl_cookie_clearall(ci); - free(ci); /* free the base struct as well */ + curlx_free(ci); /* free the base struct as well */ } } @@ -1479,15 +1432,15 @@ void Curl_cookie_cleanup(struct CookieInfo *ci) */ static char *get_netscape_format(const struct Cookie *co) { - return aprintf( - "%s" /* httponly preamble */ - "%s%s\t" /* domain */ - "%s\t" /* tailmatch */ - "%s\t" /* path */ - "%s\t" /* secure */ - "%" FMT_OFF_T "\t" /* expires */ - "%s\t" /* name */ - "%s", /* value */ + return curl_maprintf( + "%s" /* httponly preamble */ + "%s%s\t" /* domain */ + "%s\t" /* tailmatch */ + "%s\t" /* path */ + "%s\t" /* secure */ + "%" FMT_OFF_T "\t" /* expires */ + "%s\t" /* name */ + "%s", /* value */ co->httponly ? "#HttpOnly_" : "", /* * Make sure all domains are prefixed with a dot if they allow @@ -1549,7 +1502,7 @@ static CURLcode cookie_output(struct Curl_easy *data, struct Cookie **array; struct Curl_llist_node *n; - array = calloc(1, sizeof(struct Cookie *) * ci->numcookies); + array = curlx_calloc(1, sizeof(struct Cookie *) * ci->numcookies); if(!array) { error = CURLE_OUT_OF_MEMORY; goto error; @@ -1557,8 +1510,7 @@ static CURLcode cookie_output(struct Curl_easy *data, /* only sort the cookies with a domain property */ for(i = 0; i < COOKIE_HASH_SIZE; i++) { - for(n = Curl_llist_head(&ci->cookielist[i]); n; - n = Curl_node_next(n)) { + for(n = Curl_llist_head(&ci->cookielist[i]); n; n = Curl_node_next(n)) { struct Cookie *co = Curl_node_elem(n); if(!co->domain) continue; @@ -1571,22 +1523,21 @@ static CURLcode cookie_output(struct Curl_easy *data, for(i = 0; i < nvalid; i++) { char *format_ptr = get_netscape_format(array[i]); if(!format_ptr) { - free(array); + curlx_free(array); error = CURLE_OUT_OF_MEMORY; goto error; } - fprintf(out, "%s\n", format_ptr); - free(format_ptr); + curl_mfprintf(out, "%s\n", format_ptr); + curlx_free(format_ptr); } - free(array); + curlx_free(array); } if(!use_stdout) { - fclose(out); + curlx_fclose(out); out = NULL; - if(tempstore && Curl_rename(tempstore, filename)) { - unlink(tempstore); + if(tempstore && curlx_rename(tempstore, filename)) { error = CURLE_WRITE_ERROR; goto error; } @@ -1597,13 +1548,16 @@ static CURLcode cookie_output(struct Curl_easy *data, * no need to inspect the error, any error case should have jumped into the * error block below. */ - free(tempstore); + curlx_free(tempstore); return CURLE_OK; error: if(out && !use_stdout) - fclose(out); - free(tempstore); + curlx_fclose(out); + if(tempstore) { + unlink(tempstore); + curlx_free(tempstore); + } return error; } @@ -1634,7 +1588,7 @@ static struct curl_slist *cookie_list(struct Curl_easy *data) } beg = Curl_slist_append_nodup(list, line); if(!beg) { - free(line); + curlx_free(line); curl_slist_free_all(list); return NULL; } @@ -1656,26 +1610,35 @@ struct curl_slist *Curl_cookie_list(struct Curl_easy *data) void Curl_flush_cookies(struct Curl_easy *data, bool cleanup) { - CURLcode res; + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + /* only save the cookie file if a transfer was started (cookies->running is + set), as otherwise the cookies were not completely initialized and there + might be cookie files that were not loaded so saving the file is the + wrong thing. */ + if(data->cookies) { + if(data->set.str[STRING_COOKIEJAR] && data->cookies->running) { + /* if we have a destination file for all the cookies to get dumped to */ + CURLcode result = cookie_output(data, data->cookies, + data->set.str[STRING_COOKIEJAR]); + if(result) + infof(data, "WARNING: failed to save cookies in %s: %s", + data->set.str[STRING_COOKIEJAR], curl_easy_strerror(result)); + } - if(data->set.str[STRING_COOKIEJAR]) { - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - - /* if we have a destination file for all the cookies to get dumped to */ - res = cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]); - if(res) - infof(data, "WARNING: failed to save cookies in %s: %s", - data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res)); - } - else { - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - } - - if(cleanup && (!data->share || (data->cookies != data->share->cookies))) { - Curl_cookie_cleanup(data->cookies); - data->cookies = NULL; + if(cleanup && (!data->share || (data->cookies != data->share->cookies))) { + Curl_cookie_cleanup(data->cookies); + data->cookies = NULL; + } } Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } +void Curl_cookie_run(struct Curl_easy *data) +{ + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + if(data->cookies) + data->cookies->running = TRUE; + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); +} + #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */ diff --git a/lib/cookie.h b/lib/cookie.h index 99aa20af7c..f66e0ef591 100644 --- a/lib/cookie.h +++ b/lib/cookie.h @@ -25,34 +25,31 @@ ***************************************************************************/ #include "curl_setup.h" -#include - #include "llist.h" struct Cookie { - struct Curl_llist_node node; /* for the main cookie list */ + struct Curl_llist_node node; /* for the main cookie list */ struct Curl_llist_node getnode; /* for getlist */ - char *name; /* = value */ - char *value; /* name = */ - char *path; /* path = which is in Set-Cookie: */ - char *spath; /* sanitized cookie path */ - char *domain; /* domain = */ - curl_off_t expires; /* expires = */ - unsigned int creationtime; /* time when the cookie was written */ - BIT(tailmatch); /* tail-match the domain name */ - BIT(secure); /* the 'secure' keyword was used */ - BIT(livecookie); /* updated from a server, not a stored file */ - BIT(httponly); /* the httponly directive is present */ - BIT(prefix_secure); /* secure prefix is set */ - BIT(prefix_host); /* host prefix is set */ + char *name; /* = value */ + char *value; /* name = */ + char *path; /* canonical path */ + char *domain; /* domain = */ + curl_off_t expires; /* expires = */ + unsigned int creationtime; /* time when the cookie was written */ + BIT(tailmatch); /* tail-match the domain name */ + BIT(secure); /* the 'secure' keyword was used */ + BIT(livecookie); /* updated from server, not a stored file */ + BIT(httponly); /* the httponly directive is present */ + BIT(prefix_secure); /* secure prefix is set */ + BIT(prefix_host); /* host prefix is set */ }; /* * Available cookie prefixes, as defined in * draft-ietf-httpbis-rfc6265bis-02 */ -#define COOKIE_PREFIX__SECURE (1<<0) -#define COOKIE_PREFIX__HOST (1<<1) +#define COOKIE_PREFIX__SECURE (1 << 0) +#define COOKIE_PREFIX__HOST (1 << 1) #define COOKIE_HASH_SIZE 63 @@ -60,9 +57,9 @@ struct CookieInfo { /* linked lists of cookies we know of */ struct Curl_llist cookielist[COOKIE_HASH_SIZE]; curl_off_t next_expiration; /* the next time at which expiration happens */ - unsigned int numcookies; /* number of cookies in the "jar" */ - unsigned int lastct; /* last creation-time used in the jar */ - BIT(running); /* state info, for cookie adding information */ + unsigned int numcookies; /* number of cookies in the "jar" */ + unsigned int lastct; /* last creation-time used in the jar */ + BIT(running); /* state info, for cookie adding information */ BIT(newsession); /* new session, discard session cookies on load */ }; @@ -113,30 +110,34 @@ struct connectdata; */ bool Curl_secure_context(struct connectdata *conn, const char *host); -struct Cookie *Curl_cookie_add(struct Curl_easy *data, - struct CookieInfo *c, bool header, - bool noexpiry, const char *lineptr, - const char *domain, const char *path, - bool secure); -int Curl_cookie_getlist(struct Curl_easy *data, struct connectdata *conn, - const char *host, struct Curl_llist *list); -void Curl_cookie_clearall(struct CookieInfo *cookies); -void Curl_cookie_clearsess(struct CookieInfo *cookies); +CURLcode Curl_cookie_add(struct Curl_easy *data, + 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 *ci); +void Curl_cookie_clearsess(struct CookieInfo *ci); #if defined(CURL_DISABLE_HTTP) || defined(CURL_DISABLE_COOKIES) -#define Curl_cookie_list(x) NULL -#define Curl_cookie_loadfiles(x) Curl_nop_stmt -#define Curl_cookie_init(x,y,z,w) NULL -#define Curl_cookie_cleanup(x) Curl_nop_stmt -#define Curl_flush_cookies(x,y) Curl_nop_stmt +#define Curl_cookie_list(x) NULL +#define Curl_cookie_loadfiles(x) CURLE_OK +#define Curl_cookie_init() NULL +#define Curl_cookie_run(x) Curl_nop_stmt +#define Curl_cookie_cleanup(x) Curl_nop_stmt +#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); -struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, - const char *file, struct CookieInfo *inc, - bool newsession); +void Curl_cookie_cleanup(struct CookieInfo *ci); +struct CookieInfo *Curl_cookie_init(void); struct curl_slist *Curl_cookie_list(struct Curl_easy *data); -void Curl_cookie_loadfiles(struct Curl_easy *data); +CURLcode Curl_cookie_loadfiles(struct Curl_easy *data) WARN_UNUSED_RESULT; +void Curl_cookie_run(struct Curl_easy *data); #endif #endif /* HEADER_CURL_COOKIE_H */ diff --git a/lib/cshutdn.c b/lib/cshutdn.c index 1c144c6025..27b4a9f0dd 100644 --- a/lib/cshutdn.c +++ b/lib/cshutdn.c @@ -22,45 +22,35 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "urldata.h" #include "url.h" #include "cfilters.h" #include "progress.h" #include "multiif.h" #include "multi_ev.h" -#include "sendf.h" +#include "curl_trc.h" #include "cshutdn.h" -#include "http_negotiate.h" -#include "http_ntlm.h" #include "sigpipe.h" #include "connect.h" #include "select.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - static void cshutdn_run_conn_handler(struct Curl_easy *data, struct connectdata *conn) { 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 */ @@ -69,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; @@ -86,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) { @@ -93,14 +87,14 @@ static void cshutdn_run_once(struct Curl_easy *data, return; } - if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET)) + if(!conn->bits.connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET)) r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1); else { r1 = CURLE_OK; done1 = TRUE; } - if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET)) + if(!conn->bits.connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET)) r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2); else { r2 = CURLE_OK; @@ -124,7 +118,6 @@ void Curl_cshutdn_run_once(struct Curl_easy *data, Curl_detach_connection(data); } - void Curl_cshutdn_terminate(struct Curl_easy *data, struct connectdata *conn, bool do_shutdown) @@ -187,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; @@ -232,15 +225,13 @@ out: return result; } - 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; struct connectdata *conn; - struct curltime *nowp = NULL; - struct curltime now; timediff_t next_expire_ms = 0, ms; bool done; @@ -249,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); @@ -260,11 +252,7 @@ static void cshutdn_perform(struct cshutdn *cshutdn, else { /* idata has one timer list, but maybe more than one connection. * Set EXPIRE_SHUTDOWN to the smallest time left for all. */ - if(!nowp) { - now = curlx_now(); - nowp = &now; - } - ms = Curl_conn_shutdown_timeleft(conn, nowp); + ms = Curl_conn_shutdown_timeleft(data, conn); if(ms && ms < next_expire_ms) next_expire_ms = ms; } @@ -272,30 +260,28 @@ static void cshutdn_perform(struct cshutdn *cshutdn, } if(next_expire_ms) - Curl_expire_ex(data, nowp, next_expire_ms, EXPIRE_SHUTDOWN); + Curl_expire_ex(data, next_expire_ms, EXPIRE_SHUTDOWN); } - static void cshutdn_terminate_all(struct cshutdn *cshutdn, struct Curl_easy *data, int timeout_ms) { - struct curltime started = curlx_now(); + 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 timespent; + 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"); @@ -303,14 +289,14 @@ static void cshutdn_terminate_all(struct cshutdn *cshutdn, } /* wait for activity, timeout or "nothing" */ - timespent = curlx_timediff(curlx_now(), started); - if(timespent >= (timediff_t)timeout_ms) { + spent_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &started); + if(spent_ms >= (timediff_t)timeout_ms) { CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, %s", - (timeout_ms > 0) ? "timeout" : "best effort done"); + (timeout_ms > 0) ? "timeout" : "best effort done"); break; } - remain_ms = timeout_ms - (int)timespent; + remain_ms = timeout_ms - (int)spent_ms; if(cshutdn_wait(cshutdn, data, remain_ms)) { CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, aborted"); break; @@ -327,10 +313,9 @@ 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, struct Curl_multi *multi) { @@ -341,13 +326,12 @@ int Curl_cshutdn_init(struct cshutdn *cshutdn, return 0; /* good */ } - void Curl_cshutdn_destroy(struct cshutdn *cshutdn, struct Curl_easy *data) { 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"); @@ -393,7 +377,6 @@ size_t Curl_cshutdn_dest_count(struct Curl_easy *data, return 0; } - static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn, struct Curl_easy *data, struct connectdata *conn) @@ -409,19 +392,17 @@ static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn, return mresult; } - void Curl_cshutdn_add(struct cshutdn *cshutdn, struct connectdata *conn, size_t conns_in_pool) { struct Curl_easy *data = cshutdn->multi->admin; - size_t max_total = (cshutdn->multi->max_total_connections > 0) ? - (size_t)cshutdn->multi->max_total_connections : 0; + size_t max_total = cshutdn->multi->max_total_connections; /* 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); @@ -442,40 +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 */ @@ -489,8 +441,7 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn, struct easy_pollset ps; Curl_pollset_init(&ps); - for(e = Curl_llist_head(&cshutdn->list); e; - e = Curl_node_next(e)) { + for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) { unsigned int i; struct connectdata *conn = Curl_node_elem(e); CURLcode result; @@ -504,20 +455,16 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn, continue; for(i = 0; i < ps.n; i++) { -#ifdef __DJGPP__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warith-conversion" -#endif + curl_socket_t sock = ps.sockets[i]; + if(!FDSET_SOCK(sock)) + continue; if(ps.actions[i] & CURL_POLL_IN) - FD_SET(ps.sockets[i], read_fd_set); + FD_SET(sock, 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 + FD_SET(sock, write_fd_set); if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) && - ((int)ps.sockets[i] > *maxfd)) - *maxfd = (int)ps.sockets[i]; + ((int)sock > *maxfd)) + *maxfd = (int)sock; } } Curl_pollset_cleanup(&ps); @@ -538,8 +485,7 @@ unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn, CURLcode result; Curl_pollset_init(&ps); - for(e = Curl_llist_head(&cshutdn->list); e; - e = Curl_node_next(e)) { + for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) { conn = Curl_node_elem(e); Curl_pollset_reset(&ps); Curl_attach_connection(data, conn); @@ -566,8 +512,7 @@ CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn, struct connectdata *conn; Curl_pollset_init(&ps); - for(e = Curl_llist_head(&cshutdn->list); e; - e = Curl_node_next(e)) { + for(e = Curl_llist_head(&cshutdn->list); e; e = Curl_node_next(e)) { conn = Curl_node_elem(e); Curl_pollset_reset(&ps); Curl_attach_connection(data, conn); diff --git a/lib/cshutdn.h b/lib/cshutdn.h index 510d5bf506..b2e83f3d1a 100644 --- a/lib/cshutdn.h +++ b/lib/cshutdn.h @@ -24,19 +24,16 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include -#include "curlx/timeval.h" - struct connectdata; struct Curl_easy; 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. + * Shortly attach/detach `data` to `conn` while doing so. * `done` will be set TRUE if any error was encountered or if * the connection was shut down completely. */ void Curl_cshutdn_run_once(struct Curl_easy *data, @@ -44,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 @@ -81,7 +78,7 @@ size_t Curl_cshutdn_dest_count(struct Curl_easy *data, bool Curl_cshutdn_close_oldest(struct Curl_easy *data, const char *destination); -/* Add a connection to have it shut down. Will terminate the oldest +/* Add a connection to have it shut down. Terminate the oldest * connection when total connection limit of multi is being reached. */ void Curl_cshutdn_add(struct cshutdn *cshutdn, struct connectdata *conn, @@ -101,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/lib/curl_addrinfo.c b/lib/curl_addrinfo.c index 22212ac86f..fd26c5f0bc 100644 --- a/lib/curl_addrinfo.c +++ b/lib/curl_addrinfo.c @@ -21,11 +21,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #ifdef HAVE_NETINET_IN_H # include #endif @@ -47,16 +44,11 @@ # include #endif -#include +#include /* for offsetof() */ #include "curl_addrinfo.h" #include "fake_addrinfo.h" #include "curlx/inet_pton.h" -#include "curlx/warnless.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" /* * Curl_freeaddrinfo() @@ -68,26 +60,24 @@ */ #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ - defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__) + 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) +void Curl_freeaddrinfo(struct Curl_addrinfo *cahead) { struct Curl_addrinfo *vqualifier canext; struct Curl_addrinfo *ca; for(ca = cahead; ca; ca = canext) { canext = ca->ai_next; - free(ca); + curlx_free(ca); } } - #ifdef HAVE_GETADDRINFO /* * Curl_getaddrinfo_ex() @@ -102,12 +92,10 @@ Curl_freeaddrinfo(struct Curl_addrinfo *cahead) * There should be no single call to system's getaddrinfo() in the * whole library, any such call should be 'routed' through this one. */ - -int -Curl_getaddrinfo_ex(const char *nodename, - const char *servname, - const struct addrinfo *hints, - struct Curl_addrinfo **result) +int Curl_getaddrinfo_ex(const char *nodename, + const char *servname, + const struct addrinfo *hints, + struct Curl_addrinfo **result) { const struct addrinfo *ai; struct addrinfo *aihead; @@ -127,8 +115,8 @@ 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 @@ -146,14 +134,14 @@ Curl_getaddrinfo_ex(const char *nodename, if((size_t)ai->ai_addrlen < ss_size) continue; - ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen); + ca = curlx_malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen); if(!ca) { error = EAI_MEMORY; break; } - /* copy each structure member individually, member ordering, */ - /* size, or padding might be different for each platform. */ + /* 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; @@ -180,7 +168,6 @@ Curl_getaddrinfo_ex(const char *nodename, if(calast) calast->ai_next = ca; calast = ca; - } /* destroy the addrinfo list */ @@ -212,7 +199,6 @@ Curl_getaddrinfo_ex(const char *nodename, } #endif /* HAVE_GETADDRINFO */ - /* * Curl_he2ai() * @@ -252,10 +238,8 @@ Curl_getaddrinfo_ex(const char *nodename, * * #define h_addr h_addr_list[0] */ - #if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) -struct Curl_addrinfo * -Curl_he2ai(const struct hostent *he, int port) +struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port) { struct Curl_addrinfo *ai; struct Curl_addrinfo *prevai = NULL; @@ -285,7 +269,7 @@ Curl_he2ai(const struct hostent *he, int port) ss_size = sizeof(struct sockaddr_in); /* allocate memory to hold the struct, the address and the name */ - ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + namelen); + ai = curlx_calloc(1, sizeof(struct Curl_addrinfo) + ss_size + namelen); if(!ai) { result = CURLE_OUT_OF_MEMORY; break; @@ -347,16 +331,15 @@ Curl_he2ai(const struct hostent *he, int port) #endif /* - * Curl_ip2addr() + * ip2addr() * * This function takes an Internet address, in binary form, as input parameter * along with its address family and the string version of the address, and it * returns a Curl_addrinfo chain filled in correctly with information for the * given address/host */ - -struct Curl_addrinfo * -Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port) +static CURLcode ip2addr(struct Curl_addrinfo **addrp, int af, + const void *inaddr, const char *hostname, int port) { struct Curl_addrinfo *ai; size_t addrsize; @@ -369,6 +352,7 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port) DEBUGASSERT(inaddr && hostname); namelen = strlen(hostname) + 1; + *addrp = NULL; if(af == AF_INET) addrsize = sizeof(struct sockaddr_in); @@ -377,12 +361,12 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port) addrsize = sizeof(struct sockaddr_in6); #endif else - return NULL; + return CURLE_BAD_FUNCTION_ARGUMENT; /* allocate memory to hold the struct, the address and the name */ - ai = calloc(1, sizeof(struct Curl_addrinfo) + addrsize + namelen); + ai = curlx_calloc(1, sizeof(struct Curl_addrinfo) + addrsize + namelen); if(!ai) - return NULL; + return CURLE_OUT_OF_MEMORY; /* put the address after the struct */ ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); /* then put the name after the address */ @@ -412,61 +396,81 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port) break; #endif } - - return ai; + *addrp = ai; + return CURLE_OK; } /* * Given an IPv4 or IPv6 dotted string address, this converts it to a proper * allocated Curl_addrinfo struct and returns it. */ -struct Curl_addrinfo *Curl_str2addr(char *address, int port) +CURLcode Curl_str2addr(const char *dotted, uint16_t 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 Curl_ip2addr(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, dotted, &in6) > 0) + /* This is a dotted IPv6 address ::1-style */ + return ip2addr(addrp, AF_INET6, &in6, dotted, port); + } +#endif + return CURLE_BAD_FUNCTION_ARGUMENT; /* bad input format */ +} + +bool Curl_is_ipv4addr(const char *address) +{ + struct in_addr in; + return (curlx_inet_pton(AF_INET, address, &in) > 0); +} + +bool Curl_is_ipaddr(const char *address) +{ + if(Curl_is_ipv4addr(address)) + return TRUE; #ifdef USE_IPV6 { struct in6_addr in6; if(curlx_inet_pton(AF_INET6, address, &in6) > 0) /* This is a dotted IPv6 address ::1-style */ - return Curl_ip2addr(AF_INET6, &in6, address, port); + return TRUE; } #endif - return NULL; /* bad input format */ + return FALSE; } #ifdef USE_UNIX_SOCKETS /** * Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo * struct initialized with this path. - * Set '*longpath' to TRUE if the error is a too long path. + * Returns CURLE_TOO_LARGE when path is too long. */ -struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, - bool abstract) +CURLcode Curl_unix2addr(const char *path, bool abstract, + struct Curl_addrinfo **paddr) { struct Curl_addrinfo *ai; struct sockaddr_un *sa_un; size_t path_len; - *longpath = FALSE; - - ai = calloc(1, sizeof(struct Curl_addrinfo) + sizeof(struct sockaddr_un)); - if(!ai) - return NULL; - ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); - - sa_un = (void *) ai->ai_addr; - sa_un->sun_family = AF_UNIX; + *paddr = NULL; /* sun_path must be able to store the null-terminated path */ path_len = strlen(path) + 1; - if(path_len > sizeof(sa_un->sun_path)) { - free(ai); - *longpath = TRUE; - return NULL; - } + if(path_len > sizeof(sa_un->sun_path)) + return CURLE_TOO_LARGE; + + ai = curlx_calloc(1, + sizeof(struct Curl_addrinfo) + sizeof(struct sockaddr_un)); + if(!ai) + return CURLE_OUT_OF_MEMORY; + + ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); + sa_un = (void *)ai->ai_addr; + sa_un->sun_family = AF_UNIX; ai->ai_family = AF_UNIX; ai->ai_socktype = SOCK_STREAM; /* assume reliable transport for HTTP */ @@ -479,11 +483,12 @@ struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, else memcpy(sa_un->sun_path, path, path_len); /* copy NUL byte */ - return ai; + *paddr = ai; + return CURLE_OK; } #endif -#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \ +#if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) && \ defined(HAVE_FREEADDRINFO) /* * curl_dbg_freeaddrinfo() @@ -492,10 +497,8 @@ struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, * family otherwise present in memdebug.c. I put these ones here since they * require a bunch of structs I did not want to include in memdebug.c */ - -void -curl_dbg_freeaddrinfo(struct addrinfo *freethis, - int line, const char *source) +void curl_dbg_freeaddrinfo(struct addrinfo *freethis, + int line, const char *source) { curl_dbg_log("ADDR %s:%d freeaddrinfo(%p)\n", source, line, (void *)freethis); @@ -507,16 +510,17 @@ curl_dbg_freeaddrinfo(struct addrinfo *freethis, if(env) r_freeaddrinfo(freethis); else + /* !checksrc! disable BANNEDFUNC 1 */ freeaddrinfo(freethis); } #else + /* !checksrc! disable BANNEDFUNC 1 */ 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() * @@ -524,13 +528,11 @@ curl_dbg_freeaddrinfo(struct addrinfo *freethis, * family otherwise present in memdebug.c. I put these ones here since they * require a bunch of structs I did not want to include in memdebug.c */ - -int -curl_dbg_getaddrinfo(const char *hostname, - const char *service, - const struct addrinfo *hints, - struct addrinfo **result, - int line, const char *source) +int curl_dbg_getaddrinfo(const char *hostname, + const char *service, + const struct addrinfo *hints, + struct addrinfo **result, + int line, const char *source) { #ifdef USE_LWIPSOCK int res = lwip_getaddrinfo(hostname, service, hints, result); @@ -540,20 +542,21 @@ curl_dbg_getaddrinfo(const char *hostname, if(env) res = r_getaddrinfo(hostname, service, hints, result); else + /* !checksrc! disable BANNEDFUNC 1 */ res = getaddrinfo(hostname, service, hints, result); #else + /* !checksrc! disable BANNEDFUNC 1 */ int res = getaddrinfo(hostname, service, hints, result); #endif if(res == 0) /* success */ - curl_dbg_log("ADDR %s:%d getaddrinfo() = %p\n", - source, line, (void *)*result); + curl_dbg_log("ADDR %s:%d getaddrinfo() = %p\n", source, line, + (void *)*result); else - curl_dbg_log("ADDR %s:%d getaddrinfo() failed\n", - source, line); + 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/lib/curl_addrinfo.h b/lib/curl_addrinfo.h index 2303e95e31..da2da872cf 100644 --- a/lib/curl_addrinfo.h +++ b/lib/curl_addrinfo.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_NETINET_IN_H @@ -39,7 +38,6 @@ #ifdef __VMS # include # include -# include #endif /* @@ -60,50 +58,47 @@ struct Curl_addrinfo { struct Curl_addrinfo *ai_next; }; -void -Curl_freeaddrinfo(struct Curl_addrinfo *cahead); +void Curl_freeaddrinfo(struct Curl_addrinfo *cahead); #ifdef HAVE_GETADDRINFO -int -Curl_getaddrinfo_ex(const char *nodename, - const char *servname, - const struct addrinfo *hints, - struct Curl_addrinfo **result); +int Curl_getaddrinfo_ex(const char *nodename, + const char *servname, + const struct addrinfo *hints, + struct Curl_addrinfo **result); #endif #if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) -struct Curl_addrinfo * -Curl_he2ai(const struct hostent *he, int port); +struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port); #endif -struct Curl_addrinfo * -Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port); - -struct Curl_addrinfo *Curl_str2addr(char *dotted, int port); +bool Curl_is_ipv4addr(const char *address); +bool Curl_is_ipaddr(const char *address); +CURLcode Curl_str2addr(const char *dotted, uint16_t port, + struct Curl_addrinfo **addrp); #ifdef USE_UNIX_SOCKETS -struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, - bool abstract); +CURLcode Curl_unix2addr(const char *path, bool abstract, + struct Curl_addrinfo **paddr); #endif -#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \ - defined(HAVE_FREEADDRINFO) -void -curl_dbg_freeaddrinfo(struct addrinfo *freethis, int line, const char *source); +#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) -int -curl_dbg_getaddrinfo(const char *hostname, const char *service, - const struct addrinfo *hints, struct addrinfo **result, - int line, const char *source); +#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, + const char *source); #endif #ifdef HAVE_GETADDRINFO #ifdef USE_RESOLVE_ON_IPS void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port); #else -#define Curl_addrinfo_set_port(x,y) +#define Curl_addrinfo_set_port(x, y) #endif #endif diff --git a/lib/curl_config.h.cmake b/lib/curl_config-cmake.h.in similarity index 89% rename from lib/curl_config.h.cmake rename to lib/curl_config-cmake.h.in index ca516710e4..41b0ddf073 100644 --- a/lib/curl_config.h.cmake +++ b/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 @@ -115,8 +118,8 @@ /* disables netrc parser */ #cmakedefine CURL_DISABLE_NETRC 1 -/* disables NTLM support */ -#cmakedefine CURL_DISABLE_NTLM 1 +/* enables NTLM support */ +#cmakedefine CURL_ENABLE_NTLM 1 /* disables date parsing */ #cmakedefine CURL_DISABLE_PARSEDATE 1 @@ -143,15 +146,15 @@ #cmakedefine CURL_DISABLE_SHUFFLE_DNS 1 /* disables SMB */ -#cmakedefine CURL_DISABLE_SMB 1 +#cmakedefine CURL_ENABLE_SMB 1 /* disables SMTP */ #cmakedefine CURL_DISABLE_SMTP 1 -/* disabled WebSockets */ +/* disabled WebSocket */ #cmakedefine CURL_DISABLE_WEBSOCKETS 1 -/* disables use of socketpair for curl_multi_poll */ +/* disables use of socketpair for curl_multi_poll() */ #cmakedefine CURL_DISABLE_SOCKETPAIR 1 /* disables TELNET */ @@ -249,9 +252,6 @@ /* Define to 1 if you have the fseeko declaration. */ #cmakedefine HAVE_DECL_FSEEKO 1 -/* Define to 1 if you have the ftruncate function. */ -#cmakedefine HAVE_FTRUNCATE 1 - /* Define to 1 if you have a working getaddrinfo function. */ #cmakedefine HAVE_GETADDRINFO 1 @@ -315,15 +315,18 @@ /* if you have the gssapi libraries */ #cmakedefine HAVE_GSSAPI 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_GSSAPI_GSSAPI_GENERIC_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_GSSAPI_GSSAPI_H 1 - /* if you have the GNU gssapi libraries */ #cmakedefine HAVE_GSSGNU 1 +/* 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 @@ -391,15 +394,12 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_LOCALE_H 1 -/* Define to 1 if the compiler supports the 'long long' data type. */ -#cmakedefine HAVE_LONGLONG 1 +/* Define to 1 if you have a working localtime_r function. */ +#cmakedefine HAVE_LOCALTIME_R 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 @@ -421,9 +421,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NET_IF_H 1 -/* if you have an old MIT gssapi library, lacking GSS_C_NT_HOSTBASED_SERVICE */ -#cmakedefine HAVE_OLD_GSSMIT 1 - /* Define to 1 if you have the `pipe' function. */ #cmakedefine HAVE_PIPE 1 @@ -445,9 +442,6 @@ /* Define to 1 if you have a working POSIX-style strerror_r function. */ #cmakedefine HAVE_POSIX_STRERROR_R 1 -/* Define to 1 if you have the header file */ -#cmakedefine HAVE_PTHREAD_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_PWD_H 1 @@ -472,9 +466,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 +478,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 @@ -511,9 +496,6 @@ /* Define to 1 if you have the sigsetjmp function or macro. */ #cmakedefine HAVE_SIGSETJMP 1 -/* Define to 1 if you have the `snprintf' function. */ -#cmakedefine HAVE_SNPRINTF 1 - /* Define to 1 if struct sockaddr_in6 has the sin6_scope_id member */ #cmakedefine HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 @@ -538,9 +520,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 +605,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 +619,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} @@ -661,14 +637,17 @@ ${SIZEOF_TIME_T_CODE} /* Define to 1 if you have the ANSI C header files. */ #cmakedefine STDC_HEADERS 1 +/* Define if you have POSIX pthreads */ +#cmakedefine HAVE_THREADS_POSIX 1 + /* Define if you want to enable c-ares support */ #cmakedefine USE_ARES 1 -/* Define if you want to enable POSIX threaded DNS lookup */ -#cmakedefine USE_THREADS_POSIX 1 +/* Define if you want to enable c-ares DNS lookup */ +#cmakedefine USE_RESOLV_ARES 1 -/* Define if you want to enable Win32 threaded DNS lookup */ -#cmakedefine USE_THREADS_WIN32 1 +/* Define if you want to enable threaded DNS lookup */ +#cmakedefine USE_RESOLV_THREADED 1 /* if GnuTLS is enabled */ #cmakedefine USE_GNUTLS 1 @@ -679,6 +658,9 @@ ${SIZEOF_TIME_T_CODE} /* if mbedTLS is enabled */ #cmakedefine USE_MBEDTLS 1 +/* if mbedTLS <4 has the mbedtls_des_crypt_ecb function. */ +#cmakedefine HAVE_MBEDTLS_DES_CRYPT_ECB 1 + /* if Rustls is enabled */ #cmakedefine USE_RUSTLS 1 @@ -691,24 +673,21 @@ ${SIZEOF_TIME_T_CODE} /* if wolfSSL has the wolfSSL_UseALPN function. */ #cmakedefine HAVE_WOLFSSL_USEALPN 1 -/* if wolfSSL has the wolfSSL_DES_ecb_encrypt function. */ -#cmakedefine HAVE_WOLFSSL_DES_ECB_ENCRYPT 1 - /* if wolfSSL has the wolfSSL_BIO_new function. */ #cmakedefine HAVE_WOLFSSL_BIO_NEW 1 /* if wolfSSL has the wolfSSL_BIO_set_shutdown function. */ #cmakedefine HAVE_WOLFSSL_BIO_SET_SHUTDOWN 1 +/* if wolfSSL has the wc_Des_EcbEncrypt function. */ +#cmakedefine HAVE_WC_DES_ECBENCRYPT 1 + /* if libssh is in use */ #cmakedefine USE_LIBSSH 1 /* if libssh2 is in use */ #cmakedefine USE_LIBSSH2 1 -/* if wolfssh is in use */ -#cmakedefine USE_WOLFSSH 1 - /* if libpsl is in use */ #cmakedefine USE_LIBPSL 1 @@ -721,9 +700,6 @@ ${SIZEOF_TIME_T_CODE} /* if AmiSSL is in use */ #cmakedefine USE_AMISSL 1 -/* if librtmp/rtmpdump is in use */ -#cmakedefine USE_LIBRTMP 1 - /* if GSASL is in use */ #cmakedefine USE_GSASL 1 @@ -733,41 +709,38 @@ ${SIZEOF_TIME_T_CODE} /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UV_H 1 +/* if libbacktrace is in use */ +#cmakedefine USE_BACKTRACE 1 + /* Define to 1 if you do not want the OpenSSL configuration to be loaded 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 -/* Define to 1 if you are building a Windows target with large file support. */ -#cmakedefine USE_WIN32_LARGE_FILES 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 */ @@ -791,6 +764,9 @@ ${SIZEOF_TIME_T_CODE} /* to enable Apple IDN */ #cmakedefine USE_APPLE_IDN 1 +/* to enable Apple OS-native certificate verification */ +#cmakedefine USE_APPLE_SECTRUST 1 + /* Define to 1 if OpenSSL has the SSL_CTX_set_srp_username function. */ #cmakedefine HAVE_OPENSSL_SRP 1 @@ -807,7 +783,10 @@ ${SIZEOF_TIME_T_CODE} #cmakedefine USE_ECH 1 /* Define to 1 if you have the wolfSSL_CTX_GenerateEchConfig function. */ -#cmakedefine HAVE_WOLFSSL_CTX_GENERATEECHCONFIG +#cmakedefine HAVE_WOLFSSL_CTX_GENERATEECHCONFIG 1 /* Define to 1 if you have the SSL_set1_ech_config_list function. */ -#cmakedefine HAVE_SSL_SET1_ECH_CONFIG_LIST +#cmakedefine HAVE_SSL_SET1_ECH_CONFIG_LIST 1 + +/* Define to 1 if OpenSSL has the DES_ecb_encrypt function. */ +#cmakedefine HAVE_DES_ECB_ENCRYPT 1 diff --git a/lib/curl_ctype.h b/lib/curl_ctype.h index 48c3c37c35..f3291ad818 100644 --- a/lib/curl_ctype.h +++ b/lib/curl_ctype.h @@ -24,19 +24,19 @@ * ***************************************************************************/ -#define ISLOWHEXALHA(x) (((x) >= 'a') && ((x) <= 'f')) -#define ISUPHEXALHA(x) (((x) >= 'A') && ((x) <= 'F')) +#define ISLOWHEXALPHA(x) (((x) >= 'a') && ((x) <= 'f')) +#define ISUPHEXALPHA(x) (((x) >= 'A') && ((x) <= 'F')) #define ISLOWCNTRL(x) ((unsigned char)(x) <= 0x1f) -#define IS7F(x) ((x) == 0x7f) +#define IS7F(x) ((x) == 0x7f) #define ISLOWPRINT(x) (((x) >= 9) && ((x) <= 0x0d)) #define ISPRINT(x) (ISLOWPRINT(x) || (((x) >= ' ') && ((x) <= 0x7e))) #define ISGRAPH(x) (ISLOWPRINT(x) || (((x) > ' ') && ((x) <= 0x7e))) -#define ISCNTRL(x) (ISLOWCNTRL(x) || IS7F(x)) -#define ISALPHA(x) (ISLOWER(x) || ISUPPER(x)) -#define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALHA(x) || ISUPHEXALHA(x)) +#define ISCNTRL(x) (ISLOWCNTRL(x) || IS7F(x)) +#define ISALPHA(x) (ISLOWER(x) || ISUPPER(x)) +#define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALPHA(x) || ISUPHEXALPHA(x)) #define ISODIGIT(x) (((x) >= '0') && ((x) <= '7')) #define ISALNUM(x) (ISDIGIT(x) || ISLOWER(x) || ISUPPER(x)) #define ISUPPER(x) (((x) >= 'A') && ((x) <= 'Z')) @@ -44,9 +44,9 @@ #define ISDIGIT(x) (((x) >= '0') && ((x) <= '9')) #define ISBLANK(x) (((x) == ' ') || ((x) == '\t')) #define ISSPACE(x) (ISBLANK(x) || (((x) >= 0xa) && ((x) <= 0x0d))) -#define ISURLPUNTCS(x) (((x) == '-') || ((x) == '.') || ((x) == '_') || \ - ((x) == '~')) +#define ISURLPUNTCS(x) \ + (((x) == '-') || ((x) == '.') || ((x) == '_') || ((x) == '~')) #define ISUNRESERVED(x) (ISALNUM(x) || ISURLPUNTCS(x)) -#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r') +#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r') #endif /* HEADER_CURL_CTYPE_H */ diff --git a/lib/curl_des.c b/lib/curl_des.c deleted file mode 100644 index a202dd3fe4..0000000000 --- a/lib/curl_des.c +++ /dev/null @@ -1,68 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Steve Holme, . - * - * 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(USE_CURL_NTLM_CORE) && \ - (defined(USE_GNUTLS) || \ - defined(USE_OS400CRYPTO) || \ - defined(USE_WIN32_CRYPTO)) - -#include "curl_des.h" - -/* - * Curl_des_set_odd_parity() - * - * This is used to apply odd parity to the given byte array. It is typically - * used by when a cryptography engine does not have its own version. - * - * The function is a port of the Java based oddParity() function over at: - * - * https://davenport.sourceforge.net/ntlm.html - * - * Parameters: - * - * bytes [in/out] - The data whose parity bits are to be adjusted for - * odd parity. - * len [out] - The length of the data. - */ -void Curl_des_set_odd_parity(unsigned char *bytes, size_t len) -{ - size_t i; - - for(i = 0; i < len; i++) { - unsigned char b = bytes[i]; - - bool needs_parity = (((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ - (b >> 4) ^ (b >> 3) ^ (b >> 2) ^ - (b >> 1)) & 0x01) == 0; - - if(needs_parity) - bytes[i] |= 0x01; - else - bytes[i] &= 0xfe; - } -} - -#endif diff --git a/lib/curl_des.h b/lib/curl_des.h deleted file mode 100644 index c50aaf45b1..0000000000 --- a/lib/curl_des.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef HEADER_CURL_DES_H -#define HEADER_CURL_DES_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Steve Holme, . - * - * 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(USE_CURL_NTLM_CORE) && \ - (defined(USE_GNUTLS) || \ - defined(USE_OS400CRYPTO) || \ - defined(USE_WIN32_CRYPTO)) - -/* Applies odd parity to the given byte array */ -void Curl_des_set_odd_parity(unsigned char *bytes, size_t length); - -#endif - -#endif /* HEADER_CURL_DES_H */ diff --git a/lib/curl_endian.c b/lib/curl_endian.c index d982e31269..864b411b68 100644 --- a/lib/curl_endian.c +++ b/lib/curl_endian.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #include "curl_endian.h" diff --git a/lib/curl_fnmatch.c b/lib/curl_fnmatch.c index 66b9739f3c..dde956c3ac 100644 --- a/lib/curl_fnmatch.c +++ b/lib/curl_fnmatch.c @@ -21,16 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #ifndef CURL_DISABLE_FTP -#include #include "curl_fnmatch.h" -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" #ifndef HAVE_FNMATCH @@ -243,7 +238,7 @@ static int setcharset(const unsigned char **p, unsigned char *charset) case CURLFNM_SCHS_RIGHTBRLEFTBR: if(c == ']') return SETCHARSET_OK; - state = CURLFNM_SCHS_DEFAULT; + state = CURLFNM_SCHS_DEFAULT; charset[c] = 1; (*p)++; break; @@ -256,8 +251,8 @@ fail: 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(;;) { @@ -361,7 +356,8 @@ int Curl_fnmatch(void *ptr, const char *pattern, const char *string) return loop((const unsigned char *)pattern, (const unsigned char *)string, 2); } -#else +#else /* HAVE_FNMATCH */ + #include /* * @unittest: 1307 @@ -384,7 +380,6 @@ int Curl_fnmatch(void *ptr, const char *pattern, const char *string) } /* not reached */ } +#endif /* !HAVE_FNMATCH */ -#endif - -#endif /* if FTP is disabled */ +#endif /* !CURL_DISABLE_FTP */ diff --git a/lib/curl_fnmatch.h b/lib/curl_fnmatch.h index b8c2a4353c..61d2a0c4e9 100644 --- a/lib/curl_fnmatch.h +++ b/lib/curl_fnmatch.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #define CURL_FNMATCH_MATCH 0 #define CURL_FNMATCH_NOMATCH 1 #define CURL_FNMATCH_FAIL 2 diff --git a/lib/fopen.c b/lib/curl_fopen.c similarity index 75% rename from lib/fopen.c rename to lib/curl_fopen.c index b28977317a..cc888f7616 100644 --- a/lib/fopen.c +++ b/lib/curl_fopen.c @@ -21,23 +21,14 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #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) -#ifdef HAVE_FCNTL_H -#include -#endif - #include "urldata.h" #include "rand.h" -#include "fopen.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "curl_fopen.h" /* The dirslash() function breaks a null-terminated pathname string into @@ -70,10 +61,10 @@ static char *dirslash(const char *path) n = strlen(path); if(n) { /* find the rightmost path separator, if any */ - while(n && !IS_SEP(path[n-1])) + while(n && !IS_SEP(path[n - 1])) --n; /* skip over all the path separators, if any */ - while(n && IS_SEP(path[n-1])) + while(n && IS_SEP(path[n - 1])) --n; } if(curlx_dyn_addn(&out, path, n)) @@ -97,24 +88,29 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, CURLcode result = CURLE_WRITE_ERROR; unsigned char randbuf[41]; char *tempstore = NULL; - struct_stat sb; +#ifndef _WIN32 + curlx_struct_stat sb; +#endif int fd = -1; char *dir = NULL; *tempname = NULL; - *fh = fopen(filename, FOPEN_WRITETEXT); +#ifndef _WIN32 + *fh = curlx_fopen(filename, FOPEN_WRITETEXT); if(!*fh) goto fail; - if( -#ifdef UNDER_CE - stat(filename, &sb) == -1 -#else - fstat(fileno(*fh), &sb) == -1 -#endif - || !S_ISREG(sb.st_mode)) { + if(curlx_fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) { return CURLE_OK; } - fclose(*fh); + curlx_fclose(*fh); +#ifdef HAVE_GETEUID + /* If the existing file is not owned by the user, do not inherit + * its permissions at the temp file created below. The permissions + * might be unsuitable for holding user private data. */ + if(sb.st_uid != geteuid()) + sb.st_mode = 0; +#endif +#endif /* !_WIN32 */ *fh = NULL; result = Curl_rand_alnum(data, randbuf, sizeof(randbuf)); @@ -125,8 +121,8 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, if(dir) { /* The temp filename should not end up too long for the target file system */ - tempstore = aprintf("%s%s.tmp", dir, randbuf); - free(dir); + tempstore = curl_maprintf("%s%s.tmp", dir, randbuf); + curlx_free(dir); } if(!tempstore) { @@ -135,16 +131,21 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, } result = CURLE_WRITE_ERROR; -#if (defined(ANDROID) || defined(__ANDROID__)) && \ +#ifdef _WIN32 + fd = curlx_open(tempstore, _O_WRONLY | _O_CREAT | _O_EXCL, + _S_IREAD | _S_IWRITE); +#elif (defined(ANDROID) || defined(__ANDROID__)) && \ (defined(__i386__) || defined(__arm__)) - fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, (mode_t)(0600|sb.st_mode)); + fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL, + (mode_t)(S_IRUSR | S_IWUSR | sb.st_mode)); #else - fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600|sb.st_mode); + fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL, + S_IRUSR | S_IWUSR | sb.st_mode); #endif if(fd == -1) goto fail; - *fh = fdopen(fd, FOPEN_WRITETEXT); + *fh = curlx_fdopen(fd, FOPEN_WRITETEXT); if(!*fh) goto fail; @@ -153,12 +154,12 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, fail: if(fd != -1) { - close(fd); + curlx_close(fd); unlink(tempstore); } - free(tempstore); + curlx_free(tempstore); return result; } -#endif /* ! disabled */ +#endif /* !disabled */ diff --git a/lib/fopen.h b/lib/curl_fopen.h similarity index 97% rename from lib/fopen.h rename to lib/curl_fopen.h index e3a919d073..bd402d4489 100644 --- a/lib/fopen.h +++ b/lib/curl_fopen.h @@ -23,6 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curlx/fopen.h" CURLcode Curl_fopen(struct Curl_easy *data, const char *filename, FILE **fh, char **tempname); diff --git a/lib/curl_get_line.c b/lib/curl_get_line.c index 4b1c6c3e09..6fcd043c93 100644 --- a/lib/curl_get_line.c +++ b/lib/curl_get_line.c @@ -21,74 +21,49 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #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" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -static int appendnl(struct dynbuf *buf) -{ - CURLcode result = curlx_dyn_addn(buf, "\n", 1); - if(result) - /* too long line or out of memory */ - return 0; /* error */ - return 1; /* all good */ -} /* - * Curl_get_line() makes sure to only return complete whole lines that end - * newlines. + * Curl_get_line() returns only complete whole lines that end with newline. + * When 'eof' is set TRUE, the last line has been read. */ -int Curl_get_line(struct dynbuf *buf, FILE *input) +CURLcode Curl_get_line(struct dynbuf *buf, FILE *input, bool *eof) { CURLcode result; char buffer[128]; curlx_dyn_reset(buf); while(1) { - char *b = fgets(buffer, sizeof(buffer), input); size_t rlen; + const char *b = fgets(buffer, sizeof(buffer), input); + if(!b && ferror(input)) + return CURLE_READ_ERROR; - if(b) { - rlen = strlen(b); - - if(!rlen) - break; + *eof = feof(input); + rlen = b ? strlen(b) : 0; + if(rlen) { result = curlx_dyn_addn(buf, b, rlen); if(result) /* too long line or out of memory */ - return 0; /* error */ - - else if(b[rlen-1] == '\n') - /* end of the line */ - return 1; /* all good */ - - else if(feof(input)) - /* append a newline */ - return appendnl(buf); - } - else { - rlen = curlx_dyn_len(buf); - if(rlen) { - b = curlx_dyn_ptr(buf); - - if(b[rlen-1] != '\n') - /* append a newline */ - return appendnl(buf); - - return 1; /* all good */ - } - else - break; + return result; } + /* now check the full line */ + rlen = curlx_dyn_len(buf); + b = curlx_dyn_ptr(buf); + if(rlen && (b[rlen - 1] == '\n')) + /* LF at end of the line */ + return CURLE_OK; /* all good */ + if(*eof) + /* append a newline */ + return curlx_dyn_addn(buf, "\n", 1); + /* otherwise get next line to append */ } - return 0; + /* UNREACHABLE */ } #endif /* if not disabled */ diff --git a/lib/curl_get_line.h b/lib/curl_get_line.h index d4877123f2..6b90ac4727 100644 --- a/lib/curl_get_line.h +++ b/lib/curl_get_line.h @@ -23,10 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curlx/dynbuf.h" /* Curl_get_line() returns complete lines that end with a newline. */ -int Curl_get_line(struct dynbuf *buf, FILE *input); +CURLcode Curl_get_line(struct dynbuf *buf, FILE *input, bool *eof); #endif /* HEADER_CURL_GET_LINE_H */ diff --git a/lib/curl_gethostname.c b/lib/curl_gethostname.c index c3fa864eff..f154c837f6 100644 --- a/lib/curl_gethostname.c +++ b/lib/curl_gethostname.c @@ -21,10 +21,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #include "curl_gethostname.h" +#include "curlx/strcopy.h" /* * Curl_gethostname() is a wrapper around gethostname() which allows @@ -60,9 +60,9 @@ int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen) const char *force_hostname = getenv("CURL_GETHOSTNAME"); if(force_hostname) { if(strlen(force_hostname) < (size_t)namelen) - strcpy(name, force_hostname); + curlx_strcopy(name, namelen, force_hostname, strlen(force_hostname)); else - return 1; /* can't do it */ + return 1; /* cannot do it */ err = 0; } else { @@ -93,5 +93,4 @@ int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen) return 0; #endif - } diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c index 92b867f775..650d1908d0 100644 --- a/lib/curl_gssapi.c +++ b/lib/curl_gssapi.c @@ -21,18 +21,33 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_GSSAPI #include "curl_gssapi.h" -#include "sendf.h" +#include "curl_trc.h" +#include "curlx/strcopy.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#ifdef DEBUGBUILD +#if defined(HAVE_GSSGNU) || !defined(_WIN32) +#define Curl_gss_alloc malloc /* freed via the GSS API gss_release_buffer() */ +#define Curl_gss_free free /* pair of the above */ +#define CURL_GSS_STUB +/* For correctness this would be required for all platforms, not only Windows, + but, as of v1.22.1, MIT Kerberos uses a special allocator only for Windows, + and the availability of 'gssapi/gssapi_alloc.h' is difficult to detect, + because GSS headers are not versioned, and there is also no other macro to + indicate 1.18+ vs. previous versions. On Windows we can use 'GSS_S_BAD_MIC'. + */ +#elif defined(_WIN32) && defined(GSS_S_BAD_MIC) /* MIT Kerberos 1.15+ */ +/* MIT Kerberos 1.10+ (Windows), 1.18+ (all platforms), missing from GNU GSS */ +#include +#define Curl_gss_alloc gssalloc_malloc +#define Curl_gss_free gssalloc_free +#define CURL_GSS_STUB +#endif +#endif /* DEBUGBUILD */ #ifdef __GNUC__ #define CURL_ALIGN8 __attribute__((aligned(8))) @@ -40,7 +55,7 @@ #define CURL_ALIGN8 #endif -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -52,7 +67,7 @@ gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = { 9, CURL_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") }; -#ifdef DEBUGBUILD +#ifdef CURL_GSS_STUB enum min_err_code { STUB_GSS_OK = 0, STUB_GSS_NO_MEMORY, @@ -78,20 +93,20 @@ struct stub_gss_ctx_id_t_desc { char creds[250]; }; -static OM_uint32 -stub_gss_init_sec_context(OM_uint32 *min, - gss_cred_id_t initiator_cred_handle, - struct stub_gss_ctx_id_t_desc **context, - gss_name_t target_name, - const gss_OID mech_type, - OM_uint32 req_flags, - OM_uint32 time_req, - const gss_channel_bindings_t input_chan_bindings, - gss_buffer_desc *input_token, - gss_OID *actual_mech_type, - gss_buffer_desc *output_token, - OM_uint32 *ret_flags, - OM_uint32 *time_rec) +static OM_uint32 stub_gss_init_sec_context( + OM_uint32 *min, + gss_cred_id_t initiator_cred_handle, + struct stub_gss_ctx_id_t_desc **context, + gss_name_t target_name, + const gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + const gss_channel_bindings_t input_chan_bindings, + gss_buffer_desc *input_token, + gss_OID *actual_mech_type, + gss_buffer_desc *output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec) { struct stub_gss_ctx_id_t_desc *ctx = NULL; @@ -139,7 +154,7 @@ stub_gss_init_sec_context(OM_uint32 *min, } /* Server response, either D (RA==) or C (Qw==) */ - if(((char *) input_token->value)[0] == 'D') { + if(((char *)input_token->value)[0] == 'D') { /* Done */ switch(ctx->sent) { case STUB_GSS_KRB5: @@ -155,7 +170,7 @@ stub_gss_init_sec_context(OM_uint32 *min, } } - if(((char *) input_token->value)[0] != 'C') { + if(((char *)input_token->value)[0] != 'C') { /* We only support Done or Continue */ *min = STUB_GSS_SERVER_ERR; return GSS_S_FAILURE; @@ -187,7 +202,7 @@ stub_gss_init_sec_context(OM_uint32 *min, return GSS_S_FAILURE; } - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { *min = STUB_GSS_NO_MEMORY; return GSS_S_FAILURE; @@ -204,20 +219,18 @@ stub_gss_init_sec_context(OM_uint32 *min, else if(ctx->have_ntlm) ctx->sent = STUB_GSS_NTLM1; else { - free(ctx); + curlx_free(ctx); *min = STUB_GSS_NO_MECH; return GSS_S_FAILURE; } - strcpy(ctx->creds, creds); + curlx_strcopy(ctx->creds, sizeof(ctx->creds), creds, strlen(creds)); ctx->flags = req_flags; } - /* To avoid memdebug macro replacement, wrap the name in parentheses to call - the original version. It is freed via the GSS API gss_release_buffer(). */ - token = (malloc)(length); + token = Curl_gss_alloc(length); if(!token) { - free(ctx); + curlx_free(ctx); *min = STUB_GSS_NO_MEMORY; return GSS_S_FAILURE; } @@ -230,30 +243,31 @@ stub_gss_init_sec_context(OM_uint32 *min, major_status = gss_display_name(&minor_status, target_name, &target_desc, &name_type); if(GSS_ERROR(major_status)) { - (free)(token); - free(ctx); + Curl_gss_free(token); + curlx_free(ctx); *min = STUB_GSS_NO_MEMORY; return GSS_S_FAILURE; } if(strlen(creds) + target_desc.length + 5 >= sizeof(ctx->creds)) { - (free)(token); - free(ctx); + Curl_gss_free(token); + curlx_free(ctx); *min = STUB_GSS_NO_MEMORY; return GSS_S_FAILURE; } /* Token format: creds:target:type:padding */ - used = msnprintf(token, length, "%s:%.*s:%d:", creds, - (int)target_desc.length, (const char *)target_desc.value, - ctx->sent); + used = curl_msnprintf(token, length, "%s:%.*s:%d:", creds, + (int)target_desc.length, + (const char *)target_desc.value, + ctx->sent); gss_release_buffer(&minor_status, &target_desc); } if(used >= length) { - (free)(token); - free(ctx); + Curl_gss_free(token); + curlx_free(ctx); *min = STUB_GSS_NO_MEMORY; return GSS_S_FAILURE; } @@ -269,10 +283,10 @@ stub_gss_init_sec_context(OM_uint32 *min, return GSS_S_CONTINUE_NEEDED; } -static OM_uint32 -stub_gss_delete_sec_context(OM_uint32 *min, - struct stub_gss_ctx_id_t_desc **context, - gss_buffer_t output_token) +static OM_uint32 stub_gss_delete_sec_context( + OM_uint32 *min, + struct stub_gss_ctx_id_t_desc **context, + gss_buffer_t output_token) { (void)output_token; @@ -288,13 +302,13 @@ stub_gss_delete_sec_context(OM_uint32 *min, return GSS_S_FAILURE; } - free(*context); + curlx_free(*context); *context = NULL; *min = 0; return GSS_S_COMPLETE; } -#endif /* DEBUGBUILD */ +#endif /* CURL_GSS_STUB */ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, OM_uint32 *minor_status, @@ -313,7 +327,7 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, req_flags |= GSS_C_MUTUAL_FLAG; if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_POLICY_FLAG) { -#ifdef GSS_C_DELEG_POLICY_FLAG +#ifdef GSS_C_DELEG_POLICY_FLAG /* MIT Kerberos 1.8+, missing from GNU GSS */ req_flags |= GSS_C_DELEG_POLICY_FLAG; #else infof(data, "WARNING: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not " @@ -324,7 +338,7 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG) req_flags |= GSS_C_DELEG_FLAG; -#ifdef DEBUGBUILD +#ifdef CURL_GSS_STUB if(getenv("CURL_STUB_GSS_CREDS")) return stub_gss_init_sec_context(minor_status, GSS_C_NO_CREDENTIAL, /* cred_handle */ @@ -339,7 +353,7 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, output_token, ret_flags, NULL /* time_rec */); -#endif /* DEBUGBUILD */ +#endif /* CURL_GSS_STUB */ return gss_init_sec_context(minor_status, GSS_C_NO_CREDENTIAL, /* cred_handle */ @@ -360,19 +374,21 @@ OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min, gss_ctx_id_t *context, gss_buffer_t output_token) { -#ifdef DEBUGBUILD +#ifdef CURL_GSS_STUB if(getenv("CURL_STUB_GSS_CREDS")) return stub_gss_delete_sec_context(min, (struct stub_gss_ctx_id_t_desc **)context, output_token); -#endif /* DEBUGBUILD */ +#endif /* CURL_GSS_STUB */ 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) { + char *buf, size_t len) +{ OM_uint32 maj_stat; OM_uint32 min_stat; OM_uint32 msg_ctx = 0; @@ -387,9 +403,9 @@ static size_t display_gss_error(OM_uint32 status, int type, &status_string); if(maj_stat == GSS_S_COMPLETE && status_string.length > 0) { if(GSS_LOG_BUFFER_LEN > len + status_string.length + 3) { - len += msnprintf(buf + len, GSS_LOG_BUFFER_LEN - len, - "%.*s. ", (int)status_string.length, - (char *)status_string.value); + len += curl_msnprintf(buf + len, GSS_LOG_BUFFER_LEN - len, + "%.*s. ", (int)status_string.length, + (char *)status_string.value); } } gss_release_buffer(&min_stat, &status_string); @@ -413,7 +429,7 @@ static size_t display_gss_error(OM_uint32 status, int type, void Curl_gss_log_error(struct Curl_easy *data, const char *prefix, OM_uint32 major, OM_uint32 minor) { - char buf[GSS_LOG_BUFFER_LEN]; + char buf[GSS_LOG_BUFFER_LEN] = ""; size_t len = 0; if(major != GSS_S_FAILURE) @@ -422,13 +438,10 @@ 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__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif diff --git a/lib/curl_gssapi.h b/lib/curl_gssapi.h index 2659f23460..fc3759ebcb 100644 --- a/lib/curl_gssapi.h +++ b/lib/curl_gssapi.h @@ -23,8 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #include "urldata.h" #ifdef HAVE_GSSAPI @@ -44,17 +44,21 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, OM_uint32 *ret_flags); OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min, - gss_ctx_id_t *context_handle, + gss_ctx_id_t *context, 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); - -/* Provide some definitions missing in old headers */ -#ifdef HAVE_OLD_GSSMIT -#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name -#define NCOMPAT 1 +#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 */ diff --git a/lib/curl_hmac.h b/lib/curl_hmac.h index 9675c6c542..301d44fee8 100644 --- a/lib/curl_hmac.h +++ b/lib/curl_hmac.h @@ -28,8 +28,6 @@ !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ defined(USE_LIBSSH2) || defined(USE_SSL) -#include - #define HMAC_MD5_LENGTH 16 typedef CURLcode (*HMAC_hinit)(void *context); @@ -48,7 +46,6 @@ struct HMAC_params { unsigned int resultlen; /* Result length (bytes). */ }; - /* HMAC computation context. */ struct HMAC_context { const struct HMAC_params *hash; /* Hash function definition. */ @@ -56,19 +53,18 @@ struct HMAC_context { void *hashctxt2; /* Hash function context 2. */ }; - /* Prototypes. */ 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, - const unsigned char *data, - unsigned int len); -int Curl_HMAC_final(struct HMAC_context *context, unsigned char *result); +void Curl_HMAC_update(struct HMAC_context *ctxt, + const unsigned char *data, + unsigned int len); +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 *data, const size_t datalen, + const unsigned char *data, size_t datalen, unsigned char *output); #endif diff --git a/lib/curl_krb5.h b/lib/curl_krb5.h deleted file mode 100644 index 574340fd3c..0000000000 --- a/lib/curl_krb5.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef HEADER_CURL_KRB5_H -#define HEADER_CURL_KRB5_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 - * - ***************************************************************************/ - -struct Curl_sec_client_mech { - const char *name; - size_t size; - int (*init)(void *); - int (*auth)(void *, struct Curl_easy *data, struct connectdata *); - void (*end)(void *); - int (*check_prot)(void *, int); - int (*encode)(void *, const void *, int, int, void **); - int (*decode)(void *, void *, int, int, struct connectdata *); -}; - -#define AUTH_OK 0 -#define AUTH_CONTINUE 1 -#define AUTH_ERROR 2 - -#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP) -void Curl_sec_conn_init(struct connectdata *); -void Curl_sec_conn_destroy(struct connectdata *); -int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, char *, - enum protection_level); -CURLcode Curl_sec_login(struct Curl_easy *, struct connectdata *); -int Curl_sec_request_prot(struct connectdata *conn, const char *level); -#else -#define Curl_sec_conn_init(x) Curl_nop_stmt -#define Curl_sec_conn_destroy(x) Curl_nop_stmt -#endif - -#endif /* HEADER_CURL_KRB5_H */ diff --git a/lib/curl_ldap.h b/lib/curl_ldap.h index 8a1d807ed9..0ccc1289e2 100644 --- a/lib/curl_ldap.h +++ b/lib/curl_ldap.h @@ -23,14 +23,8 @@ * 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 +void Curl_ldap_version(char *buf, size_t bufsz); -#endif #endif /* HEADER_CURL_LDAP_H */ diff --git a/lib/curl_md4.h b/lib/curl_md4.h index f103d38b41..05d1624974 100644 --- a/lib/curl_md4.h +++ b/lib/curl_md4.h @@ -23,9 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include #ifdef USE_CURL_NTLM_CORE diff --git a/lib/curl_md5.h b/lib/curl_md5.h index 16272c7591..8a0cc2623e 100644 --- a/lib/curl_md5.h +++ b/lib/curl_md5.h @@ -54,11 +54,11 @@ extern const struct MD5_params Curl_DIGEST_MD5; extern const struct HMAC_params Curl_HMAC_MD5; CURLcode Curl_md5it(unsigned char *output, const unsigned char *input, - const size_t len); + size_t len); 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/lib/curl_memory.h b/lib/curl_memory.h deleted file mode 100644 index 07ef111ce7..0000000000 --- a/lib/curl_memory.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef HEADER_CURL_MEMORY_H -#define HEADER_CURL_MEMORY_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 - * - ***************************************************************************/ - -/* - * Nasty internal details ahead... - * - * File curl_memory.h must be included by _all_ *.c source files - * that use memory related functions strdup, malloc, calloc, realloc - * or free, and given source file is used to build libcurl library. - * It should be included immediately before memdebug.h as the last files - * included to avoid undesired interaction with other memory function - * headers in dependent libraries. - * - * There is nearly no exception to above rule. All libcurl source - * files in 'lib' subdirectory as well as those living deep inside - * 'packages' subdirectories and linked together in order to build - * libcurl library shall follow it. - * - * File lib/strdup.c is an exception, given that it provides a strdup - * clone implementation while using malloc. Extra care needed inside - * this one. - * - * The need for curl_memory.h inclusion is due to libcurl's feature - * of allowing library user to provide memory replacement functions, - * memory callbacks, at runtime with curl_global_init_mem() - * - * Any *.c source file used to build libcurl library that does not - * include curl_memory.h and uses any memory function of the five - * mentioned above will compile without any indication, but it will - * trigger weird memory related issues at runtime. - * - */ - -#ifndef CURLDEBUG - -/* - * libcurl's 'memory tracking' system defines strdup, malloc, calloc, - * realloc and free, along with others, in memdebug.h in a different - * way although still using memory callbacks forward declared above. - * When using the 'memory tracking' system (CURLDEBUG defined) we do - * not define here the five memory functions given that definitions - * from memdebug.h are the ones that shall be used. - */ - -#undef strdup -#define strdup(ptr) Curl_cstrdup(ptr) -#undef malloc -#define malloc(size) Curl_cmalloc(size) -#undef calloc -#define calloc(nbelem,size) Curl_ccalloc(nbelem, size) -#undef realloc -#define realloc(ptr,size) Curl_crealloc(ptr, size) -#undef free -#define free(ptr) Curl_cfree(ptr) - -#ifdef _WIN32 -#undef _tcsdup -#ifdef UNICODE -#define _tcsdup(ptr) Curl_wcsdup(ptr) -#else -#define _tcsdup(ptr) Curl_cstrdup(ptr) -#endif -#endif /* _WIN32 */ - -#endif /* CURLDEBUG */ -#endif /* HEADER_CURL_MEMORY_H */ diff --git a/lib/curl_memrchr.c b/lib/curl_memrchr.c index 5b6a39c022..59ee176bc9 100644 --- a/lib/curl_memrchr.c +++ b/lib/curl_memrchr.c @@ -21,16 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "curl_memrchr.h" -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" #ifndef HAVE_MEMRCHR /* @@ -41,9 +34,7 @@ * backwards from the end of the n bytes pointed to by s instead of forward * from the beginning. */ - -void * -Curl_memrchr(const void *s, int c, size_t n) +void *Curl_memrchr(const void *s, int c, size_t n) { if(n > 0) { const unsigned char *p = s; diff --git a/lib/curl_memrchr.h b/lib/curl_memrchr.h index 3c7dda96ac..248368b25c 100644 --- a/lib/curl_memrchr.h +++ b/lib/curl_memrchr.h @@ -23,19 +23,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_MEMRCHR -#include #ifdef HAVE_STRINGS_H # include #endif #else /* HAVE_MEMRCHR */ void *Curl_memrchr(const void *s, int c, size_t n); -#define memrchr(x,y,z) Curl_memrchr((x),(y),(z)) +#define memrchr(x, y, z) Curl_memrchr(x, y, z) #endif /* HAVE_MEMRCHR */ diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c index e040db2ab6..4b2007bbad 100644 --- a/lib/curl_ntlm_core.c +++ b/lib/curl_ntlm_core.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef USE_CURL_NTLM_CORE @@ -38,10 +37,9 @@ 1. USE_OPENSSL 2. USE_WOLFSSL 3. USE_GNUTLS - 4. - - 5. USE_MBEDTLS - 6. USE_OS400CRYPTO - 7. USE_WIN32_CRYPTO + 4. USE_MBEDTLS + 5. USE_OS400CRYPTO + 6. USE_WIN32_CRYPTO This ensures that: - the same SSL branch gets activated throughout this source @@ -51,63 +49,38 @@ in NTLM type-3 messages. */ -#ifdef USE_OPENSSL - #include - #if !defined(OPENSSL_NO_DES) && !defined(OPENSSL_NO_DEPRECATED_3_0) - #define USE_OPENSSL_DES - #endif -#elif defined(USE_WOLFSSL) - #include - #ifndef NO_DES3 - #define USE_OPENSSL_DES - #endif -#endif +#if defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT) -#ifdef USE_OPENSSL_DES - -#ifdef USE_OPENSSL # include -# include -# include -# include # ifdef OPENSSL_IS_AWSLC /* for versions 1.2.0 to 1.30.1 */ # define DES_set_key_unchecked (void)DES_set_key # endif -# define DESKEY(x) &x -#else -# include -# include -# include -# include -# ifdef OPENSSL_COEXIST -# define DES_key_schedule WOLFSSL_DES_key_schedule -# define DES_cblock WOLFSSL_DES_cblock -# define DES_set_odd_parity wolfSSL_DES_set_odd_parity -# define DES_set_key wolfSSL_DES_set_key -# 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)) -# else -# define DESKEY(x) &x -# endif -#endif -#define DESKEYARG(x) *x +# define USE_OPENSSL_DES + +#elif defined(USE_WOLFSSL) && defined(HAVE_WC_DES_ECBENCRYPT) + +# include +# include +# define USE_WOLFSSL_DES #elif defined(USE_GNUTLS) - # include - -#elif defined(USE_MBEDTLS) - +# define USE_CURL_DES_SET_ODD_PARITY +#elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB) +# include +# if MBEDTLS_VERSION_NUMBER < 0x03020000 +# error "mbedTLS 3.2.0 or later required" +# endif # include - +# define USE_MBEDTLS_DES #elif defined(USE_OS400CRYPTO) # include "cipher.mih" /* mih/cipher */ +# define USE_CURL_DES_SET_ODD_PARITY #elif defined(USE_WIN32_CRYPTO) # include +# define USE_CURL_DES_SET_ODD_PARITY #else # error "cannot compile NTLM support without a crypto library with DES." -# define CURL_NTLM_NOT_SUPPORTED #endif #include "urldata.h" @@ -115,19 +88,50 @@ #include "curl_ntlm_core.h" #include "curl_md5.h" #include "curl_hmac.h" -#include "curlx/warnless.h" -#include "curl_endian.h" -#include "curl_des.h" #include "curl_md4.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "vauth/vauth.h" -#ifndef CURL_NTLM_NOT_SUPPORTED +#ifdef USE_CURL_DES_SET_ODD_PARITY /* -* Turns a 56-bit key into being 64-bit wide. -*/ + * curl_des_set_odd_parity() + * + * Copyright (C) Steve Holme, + * + * This is used to apply odd parity to the given byte array. It is typically + * used by when a cryptography engine does not have its own version. + * + * The function is a port of the Java based oddParity() function over at: + * + * https://davenport.sourceforge.net/ntlm.html + * + * Parameters: + * + * bytes [in/out] - The data whose parity bits are to be adjusted for + * odd parity. + * len [in] - The length of the data. + */ +static void curl_des_set_odd_parity(unsigned char *bytes, size_t len) +{ + size_t i; + + for(i = 0; i < len; i++) { + unsigned char b = bytes[i]; + + bool needs_parity = (((b >> 7) ^ (b >> 6) ^ (b >> 5) ^ + (b >> 4) ^ (b >> 3) ^ (b >> 2) ^ + (b >> 1)) & 0x01) == 0; + + if(needs_parity) + bytes[i] |= 0x01; + else + bytes[i] &= 0xfe; + } +} +#endif /* USE_CURL_DES_SET_ODD_PARITY */ + +/* + * Turns a 56-bit key into being 64-bit wide. + */ static void extend_key_56_to_64(const unsigned char *key_56, char *key) { key[0] = (char)key_56[0]; @@ -137,22 +141,20 @@ static void extend_key_56_to_64(const unsigned char *key_56, char *key) key[4] = (char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4)); key[5] = (char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5)); key[6] = (char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6)); - key[7] = (char) ((key_56[6] << 1) & 0xFF); + key[7] = (char)((key_56[6] << 1) & 0xFF); } -#endif #ifdef USE_OPENSSL_DES /* * Turns a 56-bit key into a 64-bit, odd parity key and sets the key. The * key schedule ks is also set. */ -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; /* Expand the 56-bit key to 64 bits */ - extend_key_56_to_64(key_56, (char *) &key); + extend_key_56_to_64(key_56, (char *)&key); /* Set the key parity to odd */ DES_set_odd_parity(&key); @@ -161,10 +163,20 @@ static void setup_des_key(const unsigned char *key_56, DES_set_key_unchecked(&key, ks); } -#elif defined(USE_GNUTLS) +#elif defined(USE_WOLFSSL_DES) +static void setup_des_key(const unsigned char *key_56, Des *des) +{ + byte key[8]; -static void setup_des_key(const unsigned char *key_56, - struct des_ctx *des) + /* Expand the 56-bit key to 64 bits */ + extend_key_56_to_64(key_56, (char *)key); + + /* Set the key */ + wc_Des_SetKey(des, key, NULL, 0); +} + +#elif defined(USE_GNUTLS) +static void setup_des_key(const unsigned char *key_56, struct des_ctx *des) { char key[8]; @@ -172,14 +184,13 @@ static void setup_des_key(const unsigned char *key_56, extend_key_56_to_64(key_56, key); /* Set the key parity to odd */ - Curl_des_set_odd_parity((unsigned char *) key, sizeof(key)); + curl_des_set_odd_parity((unsigned char *)key, sizeof(key)); /* Set the key */ - des_set_key(des, (const uint8_t *) key); + des_set_key(des, (const uint8_t *)key); } -#elif defined(USE_MBEDTLS) - +#elif defined(USE_MBEDTLS_DES) static bool encrypt_des(const unsigned char *in, unsigned char *out, const unsigned char *key_56) { @@ -190,16 +201,15 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, extend_key_56_to_64(key_56, key); /* Set the key parity to odd */ - mbedtls_des_key_set_parity((unsigned char *) key); + mbedtls_des_key_set_parity((unsigned char *)key); /* Perform the encryption */ mbedtls_des_init(&ctx); - mbedtls_des_setkey_enc(&ctx, (unsigned char *) key); + mbedtls_des_setkey_enc(&ctx, (unsigned char *)key); return mbedtls_des_crypt_ecb(&ctx, in, out) == 0; } #elif defined(USE_OS400CRYPTO) - static bool encrypt_des(const unsigned char *in, unsigned char *out, const unsigned char *key_56) { @@ -214,16 +224,15 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, extend_key_56_to_64(key_56, ctl.Crypto_Key); /* Set the key parity to odd */ - Curl_des_set_odd_parity((unsigned char *) ctl.Crypto_Key, ctl.Data_Len); + curl_des_set_odd_parity((unsigned char *)ctl.Crypto_Key, ctl.Data_Len); /* Perform the encryption */ - _CIPHER((_SPCPTR *) &out, &ctl, (_SPCPTR *) &in); + _CIPHER((_SPCPTR *)&out, &ctl, (_SPCPTR *)&in); return TRUE; } #elif defined(USE_WIN32_CRYPTO) - static bool encrypt_des(const unsigned char *in, unsigned char *out, const unsigned char *key_56) { @@ -252,10 +261,10 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, extend_key_56_to_64(key_56, blob.key); /* Set the key parity to odd */ - Curl_des_set_odd_parity((unsigned char *) blob.key, sizeof(blob.key)); + curl_des_set_odd_parity((unsigned char *)blob.key, sizeof(blob.key)); /* Import the key */ - if(!CryptImportKey(hprov, (BYTE *) &blob, sizeof(blob), 0, 0, &hkey)) { + if(!CryptImportKey(hprov, (BYTE *)&blob, sizeof(blob), 0, 0, &hkey)) { CryptReleaseContext(hprov, 0); return FALSE; @@ -272,13 +281,13 @@ 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 - * 8 byte plaintext is encrypted with each key and the resulting 24 - * bytes are stored in the results array. - */ +/* + * takes a 21 byte array and treats it as 3 56-bit DES keys. The + * 8 byte plaintext is encrypted with each key and the resulting 24 + * bytes are stored in the results array. + */ void Curl_ntlm_core_lm_resp(const unsigned char *keys, const unsigned char *plaintext, unsigned char *results) @@ -286,17 +295,25 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, #ifdef USE_OPENSSL_DES DES_key_schedule ks; - setup_des_key(keys, DESKEY(ks)); - DES_ecb_encrypt((DES_cblock*)CURL_UNCONST(plaintext), - (DES_cblock*)results, DESKEY(ks), DES_ENCRYPT); + setup_des_key(keys, &ks); + DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(plaintext), + (DES_cblock *)results, &ks, DES_ENCRYPT); - setup_des_key(keys + 7, DESKEY(ks)); - DES_ecb_encrypt((DES_cblock*)CURL_UNCONST(plaintext), - (DES_cblock*)(results + 8), DESKEY(ks), DES_ENCRYPT); + setup_des_key(keys + 7, &ks); + DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(plaintext), + (DES_cblock *)(results + 8), &ks, DES_ENCRYPT); - setup_des_key(keys + 14, DESKEY(ks)); - DES_ecb_encrypt((DES_cblock*)CURL_UNCONST(plaintext), - (DES_cblock*)(results + 16), DESKEY(ks), DES_ENCRYPT); + setup_des_key(keys + 14, &ks); + DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(plaintext), + (DES_cblock *)(results + 16), &ks, DES_ENCRYPT); +#elif defined(USE_WOLFSSL_DES) + Des des; + setup_des_key(keys, &des); + wc_Des_EcbEncrypt(&des, results, plaintext, DES_KEY_SIZE); + setup_des_key(keys + 7, &des); + wc_Des_EcbEncrypt(&des, results + 8, plaintext, DES_KEY_SIZE); + setup_des_key(keys + 14, &des); + wc_Des_EcbEncrypt(&des, results + 16, plaintext, DES_KEY_SIZE); #elif defined(USE_GNUTLS) struct des_ctx des; setup_des_key(keys, &des); @@ -305,7 +322,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) || 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); @@ -324,11 +341,9 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, unsigned char *lmbuffer /* 21 bytes */) { unsigned char pw[14]; -#ifndef CURL_NTLM_NOT_SUPPORTED static const unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */ }; -#endif size_t len = CURLMIN(strlen(password), 14); Curl_strntoupper((char *)pw, password, len); @@ -336,24 +351,29 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, { /* Create LanManager hashed password. */ - #ifdef USE_OPENSSL_DES DES_key_schedule ks; - setup_des_key(pw, DESKEY(ks)); + setup_des_key(pw, &ks); DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic), - (DES_cblock *)lmbuffer, DESKEY(ks), DES_ENCRYPT); + (DES_cblock *)lmbuffer, &ks, DES_ENCRYPT); - setup_des_key(pw + 7, DESKEY(ks)); + setup_des_key(pw + 7, &ks); DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic), - (DES_cblock *)(lmbuffer + 8), DESKEY(ks), DES_ENCRYPT); + (DES_cblock *)(lmbuffer + 8), &ks, DES_ENCRYPT); +#elif defined(USE_WOLFSSL_DES) + Des des; + setup_des_key(pw, &des); + wc_Des_EcbEncrypt(&des, lmbuffer, magic, DES_KEY_SIZE); + setup_des_key(pw + 7, &des); + wc_Des_EcbEncrypt(&des, lmbuffer + 8, magic, DES_KEY_SIZE); #elif defined(USE_GNUTLS) struct des_ctx des; setup_des_key(pw, &des); des_encrypt(&des, 8, lmbuffer, magic); setup_des_key(pw + 7, &des); des_encrypt(&des, 8, lmbuffer + 8, magic); -#elif defined(USE_MBEDTLS) || 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); @@ -371,24 +391,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'; + 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'; - } -} - -#endif /* !USE_WINDOWS_SSPI */ - /* * Set up nt hashed passwords * @unittest: 1600 @@ -399,9 +405,9 @@ CURLcode Curl_ntlm_core_mk_nt_hash(const char *password, size_t len = strlen(password); unsigned char *pw; CURLcode result; - if(len > SIZE_MAX/2) /* avoid integer overflow */ + if(len > SIZE_MAX / 2) /* avoid integer overflow */ return CURLE_OUT_OF_MEMORY; - pw = len ? malloc(len * 2) : (unsigned char *)strdup(""); + pw = len ? curlx_malloc(len * 2) : (unsigned char *)curlx_strdup(""); if(!pw) return CURLE_OUT_OF_MEMORY; @@ -412,7 +418,7 @@ CURLcode Curl_ntlm_core_mk_nt_hash(const char *password, if(!result) memset(ntbuffer + 16, 0, 21 - 16); - free(pw); + curlx_free(pw); return result; } @@ -420,7 +426,7 @@ CURLcode Curl_ntlm_core_mk_nt_hash(const char *password, #ifndef USE_WINDOWS_SSPI #define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00" -#define NTLMv2_BLOB_LEN (44 -16 + ntlm->target_info_len + 4) +#define NTLMv2_BLOB_LEN (44 - 16 + ntlm->target_info_len + 4) /* Timestamp in tenths of a microsecond since January 1, 1601 00:00:00 UTC. */ struct ms_filetime { @@ -433,20 +439,20 @@ static void time2filetime(struct ms_filetime *ft, time_t t) { #if SIZEOF_TIME_T > 4 t = (t + (curl_off_t)11644473600) * 10000000; - ft->dwLowDateTime = (unsigned int) (t & 0xFFFFFFFF); - ft->dwHighDateTime = (unsigned int) (t >> 32); + ft->dwLowDateTime = (unsigned int)(t & 0xFFFFFFFF); + ft->dwHighDateTime = (unsigned int)(t >> 32); #else unsigned int r, s; unsigned int i; - ft->dwLowDateTime = (unsigned int)t & 0xFFFFFFFF; + ft->dwLowDateTime = (unsigned int)(t & 0xFFFFFFFF); ft->dwHighDateTime = 0; -# ifndef HAVE_TIME_T_UNSIGNED +#ifndef HAVE_TIME_T_UNSIGNED /* Extend sign if needed. */ if(ft->dwLowDateTime & 0x80000000) ft->dwHighDateTime = ~(unsigned int)0; -# endif +#endif /* Bias seconds to Jan 1, 1601. 134774 days = 11644473600 seconds = 0x2B6109100 */ @@ -472,6 +478,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 */ @@ -489,7 +505,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen, return CURLE_OUT_OF_MEMORY; identity_len = (userlen + domlen) * 2; - identity = malloc(identity_len + 1); + identity = curlx_malloc(identity_len + 1); if(!identity) return CURLE_OUT_OF_MEMORY; @@ -499,7 +515,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen, result = Curl_hmacit(&Curl_HMAC_MD5, ntlmhash, 16, identity, identity_len, ntlmv2hash); - free(identity); + curlx_free(identity); return result; } @@ -521,26 +537,26 @@ 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) { -/* NTLMv2 response structure : ------------------------------------------------------------------------------- -0 HMAC MD5 16 bytes -------BLOB-------------------------------------------------------------------- -16 Signature 0x01010000 -20 Reserved long (0x00000000) -24 Timestamp LE, 64-bit signed value representing the number of - tenths of a microsecond since January 1, 1601. -32 Client Nonce 8 bytes -40 Unknown 4 bytes -44 Target Info N bytes (from the type-2 message) -44+N Unknown 4 bytes ------------------------------------------------------------------------------- -*/ + /* NTLMv2 response structure : + ----------------------------------------------------------------------------- + 0 HMAC MD5 16 bytes + ------BLOB------------------------------------------------------------------- + 16 Signature 0x01010000 + 20 Reserved long (0x00000000) + 24 Timestamp LE, 64-bit signed value representing the number of + tenths of a microsecond since January 1, 1601. + 32 Client Nonce 8 bytes + 40 Unknown 4 bytes + 44 Target Info N bytes (from the type-2 message) + 44+N Unknown 4 bytes + ----------------------------------------------------------------------------- + */ unsigned int len = 0; unsigned char *ptr = NULL; @@ -553,7 +569,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, #ifdef DEBUGBUILD char *force_timestamp = getenv("CURL_FORCETIME"); if(force_timestamp) - time2filetime(&tw, (time_t) 0); + time2filetime(&tw, (time_t)0); else #endif time2filetime(&tw, time(NULL)); @@ -562,19 +578,20 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, len = HMAC_MD5_LENGTH + NTLMv2_BLOB_LEN; /* Allocate the response */ - ptr = calloc(1, len); + ptr = curlx_calloc(1, len); if(!ptr) return CURLE_OUT_OF_MEMORY; /* Create the BLOB structure */ - msnprintf((char *)ptr + HMAC_MD5_LENGTH, NTLMv2_BLOB_LEN, - "%c%c%c%c" /* NTLMv2_BLOB_SIGNATURE */ - "%c%c%c%c" /* Reserved = 0 */ - "%c%c%c%c%c%c%c%c", /* Timestamp */ - NTLMv2_BLOB_SIGNATURE[0], NTLMv2_BLOB_SIGNATURE[1], - NTLMv2_BLOB_SIGNATURE[2], NTLMv2_BLOB_SIGNATURE[3], - 0, 0, 0, 0, - LONGQUARTET(tw.dwLowDateTime), LONGQUARTET(tw.dwHighDateTime)); + curl_msnprintf((char *)ptr + HMAC_MD5_LENGTH, NTLMv2_BLOB_LEN, + "%c%c%c%c" /* NTLMv2_BLOB_SIGNATURE */ + "%c%c%c%c" /* Reserved = 0 */ + "%c%c%c%c%c%c%c%c", /* Timestamp */ + NTLMv2_BLOB_SIGNATURE[0], NTLMv2_BLOB_SIGNATURE[1], + NTLMv2_BLOB_SIGNATURE[2], NTLMv2_BLOB_SIGNATURE[3], + 0, 0, 0, 0, + LONGQUARTET(tw.dwLowDateTime), + LONGQUARTET(tw.dwHighDateTime)); memcpy(ptr + 32, challenge_client, 8); if(ntlm->target_info_len) @@ -585,7 +602,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, result = Curl_hmacit(&Curl_HMAC_MD5, ntlmv2hash, HMAC_MD5_LENGTH, ptr + 8, NTLMv2_BLOB_LEN + 8, hmac_output); if(result) { - free(ptr); + curlx_free(ptr); return result; } @@ -613,10 +630,10 @@ 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, - unsigned char *lmresp) +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]; unsigned char hmac_output[16]; diff --git a/lib/curl_ntlm_core.h b/lib/curl_ntlm_core.h index 584f4a17d5..f96bf0ada5 100644 --- a/lib/curl_ntlm_core.h +++ b/lib/curl_ntlm_core.h @@ -23,13 +23,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef USE_CURL_NTLM_CORE -#include "vauth/vauth.h" - struct ntlmdata; /* Helpers to generate function byte arguments in little endian order */ @@ -58,16 +55,16 @@ 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, - unsigned char **ntresp, - unsigned int *ntresp_len); +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, - unsigned char *lmresp); +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/lib/curl_printf.h b/lib/curl_printf.h index 6e0fa1fa8d..3b1a5af3b4 100644 --- a/lib/curl_printf.h +++ b/lib/curl_printf.h @@ -24,41 +24,14 @@ * ***************************************************************************/ -#include - #define MERR_OK 0 #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[]; -#ifdef BUILDING_LIBCURL - -/* - * This header should be included by ALL code in libcurl that uses any - * *rintf() functions. - */ - -# undef printf -# undef fprintf -# undef msnprintf -# undef vprintf -# undef vfprintf -# undef mvsnprintf -# undef aprintf -# undef vaprintf -# define printf curl_mprintf -# define fprintf curl_mfprintf -# define msnprintf curl_msnprintf -# define vprintf curl_mvprintf -# define vfprintf curl_mvfprintf -# define mvsnprintf curl_mvsnprintf -# define aprintf curl_maprintf -# define vaprintf curl_mvaprintf - -#endif /* BUILDING_LIBCURL */ #endif /* HEADER_CURL_PRINTF_H */ diff --git a/lib/curl_range.c b/lib/curl_range.c index e9620a29b5..9bbafa40cf 100644 --- a/lib/curl_range.c +++ b/lib/curl_range.c @@ -21,20 +21,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include + #include "curl_range.h" -#include "sendf.h" +#include "curl_trc.h" #include "curlx/strparse.h" /* 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) { @@ -57,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/lib/curl_range.h b/lib/curl_range.h index 77679e2b94..97354677c2 100644 --- a/lib/curl_range.h +++ b/lib/curl_range.h @@ -23,8 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #include "urldata.h" CURLcode Curl_range(struct Curl_easy *data); diff --git a/lib/curl_rtmp.c b/lib/curl_rtmp.c deleted file mode 100644 index baee79fbee..0000000000 --- a/lib/curl_rtmp.c +++ /dev/null @@ -1,394 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * Copyright (C) Howard Chu, - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef USE_LIBRTMP - -#include "curl_rtmp.h" -#include "urldata.h" -#include "url.h" -#include "curlx/nonblock.h" /* for curlx_nonblock */ -#include "progress.h" /* for Curl_pgrsSetUploadSize */ -#include "transfer.h" -#include "curlx/warnless.h" -#include -#include - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if defined(_WIN32) && !defined(USE_LWIPSOCK) -#define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e) -#define SET_RCVTIMEO(tv,s) int tv = s*1000 -#elif defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD) -#define SET_RCVTIMEO(tv,s) int tv = s*1000 -#else -#define SET_RCVTIMEO(tv,s) struct timeval tv = {s,0} -#endif - -#define DEF_BUFTIME (2*60*60*1000) /* 2 hours */ - -/* 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; - (void)key; - (void)klen; - RTMP_Close(r); - RTMP_Free(r); -} - -static CURLcode rtmp_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - RTMP *r = RTMP_Alloc(); - if(!r || - Curl_conn_meta_set(conn, CURL_META_RTMP_CONN, r, rtmp_conn_dtor)) - return CURLE_OUT_OF_MEMORY; - - RTMP_Init(r); - RTMP_SetBufferMS(r, DEF_BUFTIME); - if(!RTMP_SetupURL(r, data->state.url)) { - RTMP_Free(r); - return CURLE_URL_MALFORMAT; - } - return CURLE_OK; -} - -static CURLcode rtmp_connect(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - SET_RCVTIMEO(tv, 10); - - if(!r) - return CURLE_FAILED_INIT; - - r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET]; - - /* We have to know if it is a write before we send the - * connect request packet - */ - if(data->state.upload) - r->Link.protocol |= RTMP_FEATURE_WRITE; - - /* For plain streams, use the buffer toggle trick to keep data flowing */ - if(!(r->Link.lFlags & RTMP_LF_LIVE) && - !(r->Link.protocol & RTMP_FEATURE_HTTP)) - r->Link.lFlags |= RTMP_LF_BUFX; - - (void)curlx_nonblock(r->m_sb.sb_socket, FALSE); - setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, - (char *)&tv, sizeof(tv)); - - if(!RTMP_Connect1(r, NULL)) - return CURLE_FAILED_INIT; - - /* Clients must send a periodic BytesReceived report to the server */ - r->m_bSendCounter = TRUE; - - *done = TRUE; - conn->recv[FIRSTSOCKET] = rtmp_recv; - conn->send[FIRSTSOCKET] = rtmp_send; - return CURLE_OK; -} - -static CURLcode rtmp_do(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - - if(!r || !RTMP_ConnectStream(r, 0)) - return CURLE_FAILED_INIT; - - if(data->state.upload) { - Curl_pgrsSetUploadSize(data, data->state.infilesize); - Curl_xfer_setup_send(data, FIRSTSOCKET); - } - else - Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); - *done = TRUE; - return CURLE_OK; -} - -static CURLcode rtmp_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - (void)data; - (void)status; - (void)premature; - - return CURLE_OK; -} - -static CURLcode rtmp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - (void)data; - (void)dead_connection; - if(r) - Curl_conn_meta_remove(conn, CURL_META_RTMP_CONN); - return CURLE_OK; -} - -static CURLcode rtmp_recv(struct Curl_easy *data, int sockindex, char *buf, - size_t len, size_t *pnread) -{ - struct connectdata *conn = data->conn; - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - CURLcode result = CURLE_OK; - ssize_t nread; - - (void)sockindex; - *pnread = 0; - if(!r) - return CURLE_FAILED_INIT; - - nread = RTMP_Read(r, buf, curlx_uztosi(len)); - if(nread < 0) { - if(r->m_read.status == RTMP_READ_COMPLETE || - r->m_read.status == RTMP_READ_EOF) { - data->req.size = data->req.bytecount; - } - else - result = CURLE_RECV_ERROR; - } - else - *pnread = (size_t)nread; - - return result; -} - -static CURLcode rtmp_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t len, bool eos, - size_t *pnwritten) -{ - struct connectdata *conn = data->conn; - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - ssize_t nwritten; - - (void)sockindex; - (void)eos; - *pnwritten = 0; - if(!r) - return CURLE_FAILED_INIT; - - nwritten = RTMP_Write(r, (const char *)buf, curlx_uztosi(len)); - if(nwritten < 0) - return CURLE_SEND_ERROR; - - *pnwritten = (size_t)nwritten; - return CURLE_OK; -} - -void Curl_rtmp_version(char *version, size_t len) -{ - char suff[2]; - if(RTMP_LIB_VERSION & 0xff) { - suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1; - suff[1] = '\0'; - } - else - suff[0] = '\0'; - - msnprintf(version, len, "librtmp/%d.%d%s", - RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff, - suff); -} - -#endif /* USE_LIBRTMP */ diff --git a/lib/curl_rtmp.h b/lib/curl_rtmp.h deleted file mode 100644 index 339d3a4384..0000000000 --- a/lib/curl_rtmp.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef HEADER_CURL_RTMP_H -#define HEADER_CURL_RTMP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Howard Chu, - * - * 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 - * - ***************************************************************************/ -#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 - -#endif /* HEADER_CURL_RTMP_H */ diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 8eb63fb949..60f085901f 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -32,29 +32,18 @@ * Draft LOGIN SASL Mechanism * ***************************************************************************/ - #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 #include "urldata.h" - #include "curlx/base64.h" -#include "curl_md5.h" #include "vauth/vauth.h" #include "cfilters.h" -#include "vtls/vtls.h" -#include "curl_hmac.h" #include "curl_sasl.h" -#include "curlx/warnless.h" -#include "sendf.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "curl_trc.h" /* Supported mechanisms */ static const struct { @@ -97,7 +86,7 @@ unsigned short Curl_sasl_decode_mech(const char *ptr, size_t maxlen, for(i = 0; mechtable[i].name; i++) { if(maxlen >= mechtable[i].len && - !memcmp(ptr, mechtable[i].name, mechtable[i].len)) { + curl_strnequal(ptr, mechtable[i].name, mechtable[i].len)) { if(len) *len = mechtable[i].len; @@ -194,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", @@ -238,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 = (const char *) Curl_bufref_ptr(out); + 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); @@ -268,7 +258,7 @@ static CURLcode build_message(struct SASL *sasl, struct bufref *msg) char *base64; size_t base64len; - result = curlx_base64_encode((const char *) Curl_bufref_ptr(msg), + result = curlx_base64_encode(Curl_bufref_uptr(msg), Curl_bufref_len(msg), &base64, &base64len); if(!result) Curl_bufref_set(msg, base64, base64len, curl_free); @@ -286,7 +276,7 @@ static CURLcode build_message(struct SASL *sasl, struct bufref *msg) bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data) { /* Have credentials been provided? */ - if(data->state.aptr.user) + if(data->conn->user[0]) return TRUE; /* EXTERNAL can authenticate without a username and/or password */ @@ -299,7 +289,6 @@ bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data) struct sasl_ctx { struct SASL *sasl; struct connectdata *conn; - const char *user; unsigned short enabledmechs; const char *mech; saslstate state1; @@ -325,8 +314,7 @@ static bool sasl_choose_external(struct Curl_easy *data, struct sasl_ctx *sctx) #ifdef USE_KERBEROS5 static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) { - if(sctx->user && - (sctx->enabledmechs & SASL_MECH_GSSAPI) && + if((sctx->enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() && Curl_auth_user_contains_domain(sctx->conn->user)) { const char *service = data->set.str[STRING_SERVICE_NAME] ? @@ -345,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; } @@ -360,8 +348,8 @@ static bool sasl_choose_gsasl(struct Curl_easy *data, struct sasl_ctx *sctx) struct gsasldata *gsasl; struct bufref nullmsg; - if(sctx->user && - (sctx->enabledmechs & (SASL_MECH_SCRAM_SHA_256|SASL_MECH_SCRAM_SHA_1))) { + if((sctx->enabledmechs & + (SASL_MECH_SCRAM_SHA_256 | SASL_MECH_SCRAM_SHA_1))) { gsasl = Curl_auth_gsasl_get(sctx->conn); if(!gsasl) { sctx->result = CURLE_OUT_OF_MEMORY; @@ -369,14 +357,14 @@ static bool sasl_choose_gsasl(struct Curl_easy *data, struct sasl_ctx *sctx) } if((sctx->enabledmechs & SASL_MECH_SCRAM_SHA_256) && - Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_256, - gsasl)) { + Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_256, + gsasl)) { sctx->mech = SASL_MECH_STRING_SCRAM_SHA_256; sctx->sasl->authused = SASL_MECH_SCRAM_SHA_256; } else if((sctx->enabledmechs & SASL_MECH_SCRAM_SHA_1) && - Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_1, - gsasl)) { + Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_1, + gsasl)) { sctx->mech = SASL_MECH_STRING_SCRAM_SHA_1; sctx->sasl->authused = SASL_MECH_SCRAM_SHA_1; } @@ -401,9 +389,7 @@ static bool sasl_choose_gsasl(struct Curl_easy *data, struct sasl_ctx *sctx) static bool sasl_choose_digest(struct Curl_easy *data, struct sasl_ctx *sctx) { (void)data; - if(!sctx->user) - return FALSE; - else if((sctx->enabledmechs & SASL_MECH_DIGEST_MD5) && + if((sctx->enabledmechs & SASL_MECH_DIGEST_MD5) && Curl_auth_is_digest_supported()) { sctx->mech = SASL_MECH_STRING_DIGEST_MD5; sctx->state1 = SASL_DIGESTMD5; @@ -423,17 +409,14 @@ static bool sasl_choose_digest(struct Curl_easy *data, struct sasl_ctx *sctx) #ifdef USE_NTLM static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) { - if(!sctx->user) - return FALSE; - else if((sctx->enabledmechs & SASL_MECH_NTLM) && - Curl_auth_is_ntlm_supported()) { + if((sctx->enabledmechs & SASL_MECH_NTLM) && + Curl_auth_is_ntlm_supported()) { const char *service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : sctx->sasl->params->service; const char *hostname; - int port; - Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); + Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, NULL); sctx->mech = SASL_MECH_STRING_NTLM; sctx->state1 = SASL_NTLM; @@ -457,10 +440,11 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) { - const char *oauth_bearer = data->set.str[STRING_BEARER]; + const char *oauth_bearer = + (!data->state.this_is_a_follow || data->set.allow_auth_to_other_hosts) ? + data->set.str[STRING_BEARER] : NULL; - if(sctx->user && oauth_bearer && - (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) { + if(oauth_bearer && (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) { const char *hostname; int port; Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); @@ -482,10 +466,11 @@ static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) static bool sasl_choose_oauth2(struct Curl_easy *data, struct sasl_ctx *sctx) { - const char *oauth_bearer = data->set.str[STRING_BEARER]; + const char *oauth_bearer = + (!data->state.this_is_a_follow || data->set.allow_auth_to_other_hosts) ? + data->set.str[STRING_BEARER] : NULL; - if(sctx->user && oauth_bearer && - (sctx->enabledmechs & SASL_MECH_XOAUTH2)) { + if(oauth_bearer && (sctx->enabledmechs & SASL_MECH_XOAUTH2)) { sctx->mech = SASL_MECH_STRING_XOAUTH2; sctx->state1 = SASL_OAUTH2; sctx->sasl->authused = SASL_MECH_XOAUTH2; @@ -501,7 +486,7 @@ static bool sasl_choose_oauth2(struct Curl_easy *data, struct sasl_ctx *sctx) static bool sasl_choose_plain(struct Curl_easy *data, struct sasl_ctx *sctx) { - if(sctx->user && (sctx->enabledmechs & SASL_MECH_PLAIN)) { + if(sctx->enabledmechs & SASL_MECH_PLAIN) { sctx->mech = SASL_MECH_STRING_PLAIN; sctx->state1 = SASL_PLAIN; sctx->sasl->authused = SASL_MECH_PLAIN; @@ -518,7 +503,7 @@ static bool sasl_choose_plain(struct Curl_easy *data, struct sasl_ctx *sctx) static bool sasl_choose_login(struct Curl_easy *data, struct sasl_ctx *sctx) { - if(sctx->user && (sctx->enabledmechs & SASL_MECH_LOGIN)) { + if(sctx->enabledmechs & SASL_MECH_LOGIN) { sctx->mech = SASL_MECH_STRING_LOGIN; sctx->state1 = SASL_LOGIN; sctx->state2 = SASL_LOGIN_PASSWD; @@ -548,7 +533,6 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, memset(&sctx, 0, sizeof(sctx)); sctx.sasl = sasl; sctx.conn = data->conn; - sctx.user = data->state.aptr.user; Curl_bufref_init(&sctx.resp); sctx.enabledmechs = sasl->authmechs & sasl->prefmech; sctx.state1 = SASL_STOP; @@ -710,7 +694,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_NTLM_TYPE2MSG: { /* Decode the type-2 message */ struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE); - result = !ntlm ? CURLE_FAILED_INIT : + result = !ntlm ? CURLE_OUT_OF_MEMORY : get_server_message(sasl, data, &serverdata); if(!result) result = Curl_auth_decode_ntlm_type2_message(data, &serverdata, ntlm); @@ -727,9 +711,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; } @@ -744,7 +728,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; @@ -812,10 +796,12 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_CANCEL: /* Remove the offending mechanism from the supported list */ - sasl->authmechs ^= sasl->authused; + sasl->authmechs &= (unsigned short)~sasl->authused; + sasl->authused = SASL_AUTH_NONE; + 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 */ @@ -848,7 +834,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, @@ -875,15 +861,15 @@ static void sasl_unchosen(struct Curl_easy *data, unsigned short mech, else { if(param_missing) infof(data, "SASL: %s is missing %s", mname, param_missing); - if(!data->state.aptr.user) + if(!data->conn->user[0]) 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 @@ -931,14 +917,14 @@ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data) CURL_SASL_DIGEST, TRUE, NULL); sasl_unchosen(data, SASL_MECH_NTLM, enabledmechs, CURL_SASL_NTLM, Curl_auth_is_ntlm_supported(), NULL); - sasl_unchosen(data, SASL_MECH_OAUTHBEARER, enabledmechs, TRUE, TRUE, + sasl_unchosen(data, SASL_MECH_OAUTHBEARER, enabledmechs, TRUE, TRUE, data->set.str[STRING_BEARER] ? NULL : "CURLOPT_XOAUTH2_BEARER"); - sasl_unchosen(data, SASL_MECH_XOAUTH2, enabledmechs, TRUE, TRUE, + sasl_unchosen(data, SASL_MECH_XOAUTH2, enabledmechs, TRUE, TRUE, 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/lib/curl_sasl.h b/lib/curl_sasl.h index 0674d56f86..8a97f52adc 100644 --- a/lib/curl_sasl.h +++ b/lib/curl_sasl.h @@ -23,9 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include - #include "bufref.h" struct Curl_easy; @@ -132,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 */ @@ -150,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/lib/curl_setup.h b/lib/curl_setup.h index 72c118affc..0d4a8fa577 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -46,7 +46,7 @@ fail. Fixed in 14.2.0_1. Disable the workaround if the fix is detected. */ #if defined(__APPLE__) && !defined(__clang__) && defined(__GNUC__) && \ defined(__has_attribute) -# if !defined(__has_feature) +# if !defined(__has_feature) /* Keep this PP check separate from others */ # define availability curl_pp_attribute_disabled # elif !__has_feature(attribute_availability) # define availability curl_pp_attribute_disabled @@ -75,26 +75,22 @@ #endif #endif -#if defined(__MINGW32__) && !defined(__MINGW32CE__) && \ +#if defined(__MINGW32__) && \ (!defined(__MINGW64_VERSION_MAJOR) || (__MINGW64_VERSION_MAJOR < 3)) #error "Building curl requires mingw-w64 3.0 or later" #endif -/* Visual Studio 2008 is the minimum Visual Studio version we support. +/* Visual Studio 2010 is the minimum Visual Studio version we support. Workarounds for older versions of Visual Studio have been removed. */ -#if defined(_MSC_VER) && (_MSC_VER < 1500) +#if defined(_MSC_VER) && (_MSC_VER < 1600) #error "Ancient versions of Visual Studio are no longer supported due to bugs." #endif #ifdef _MSC_VER /* Disable Visual Studio warnings: 4127 "conditional expression is constant" */ #pragma warning(disable:4127) -/* Avoid VS2005 and upper complaining about portable C functions. */ -#ifndef _CRT_NONSTDC_NO_DEPRECATE -#define _CRT_NONSTDC_NO_DEPRECATE /* for strdup(), write(), etc. */ -#endif -#ifndef _CRT_SECURE_NO_DEPRECATE -#define _CRT_SECURE_NO_DEPRECATE /* for fopen(), getenv(), etc. */ +#ifndef _CRT_SECURE_NO_WARNINGS +#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,14 +117,12 @@ # define CURL_WINDOWS_UWP # endif # endif -#endif -/* Avoid bogus format check warnings with mingw32ce gcc 4.4.0 in - C99 (-std=gnu99) mode */ -#if defined(__MINGW32CE__) && !defined(CURL_NO_FMT_CHECKS) && \ - (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) && \ - (defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 4)) -#define CURL_NO_FMT_CHECKS +/* 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 */ @@ -162,15 +157,23 @@ # 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, */ +/* behavior or generated code characteristics must be done here, */ /* as appropriate, before any system header file is included. It is */ /* also possible to have them defined in the config file included */ /* before this point. As a result of all this we frown inclusion of */ @@ -187,7 +190,6 @@ * AIX 4.3 and newer needs _THREAD_SAFE defined to build * proper reentrant code. Others may also need it. */ - #ifdef NEED_THREAD_SAFE # ifndef _THREAD_SAFE # define _THREAD_SAFE @@ -199,7 +201,6 @@ * things to appear in the system header files. Unixware needs it * to build proper reentrant code. Others may also need it. */ - #ifdef NEED_REENTRANT # ifndef _REENTRANT # define _REENTRANT @@ -219,14 +220,13 @@ /* ================================================================ */ /* Give calloc a chance to be dragging in early, so we do not redefine */ -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) +#ifdef HAVE_THREADS_POSIX # include #endif /* * Disable other protocols when http is the only one desired. */ - #ifdef HTTP_ONLY # ifndef CURL_DISABLE_DICT # define CURL_DISABLE_DICT @@ -258,9 +258,6 @@ # ifndef CURL_DISABLE_RTSP # define CURL_DISABLE_RTSP # endif -# ifndef CURL_DISABLE_SMB -# define CURL_DISABLE_SMB -# endif # ifndef CURL_DISABLE_SMTP # define CURL_DISABLE_SMTP # endif @@ -270,12 +267,14 @@ # ifndef CURL_DISABLE_TFTP # define CURL_DISABLE_TFTP # endif +# ifndef CURL_DISABLE_WEBSOCKETS +# define CURL_DISABLE_WEBSOCKETS +# endif #endif /* * When http is disabled rtsp is not supported. */ - #if defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_RTSP) # define CURL_DISABLE_RTSP #endif @@ -283,18 +282,40 @@ /* * 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 /* ================================================================ */ @@ -305,7 +326,6 @@ /* * OS/400 setup file includes some system headers. */ - #ifdef __OS400__ # include "setup-os400.h" #endif @@ -313,7 +333,6 @@ /* * VMS setup file includes some system headers. */ - #ifdef __VMS # include "setup-vms.h" #endif @@ -321,7 +340,6 @@ /* * Windows setup file includes some system headers. */ - #ifdef _WIN32 # include "setup-win32.h" #endif @@ -332,8 +350,8 @@ * Direct macros concatenation does not work because macros * are not expanded before direct concatenation. */ -#define CURL_CONC_MACROS_(A,B) A ## B -#define CURL_CONC_MACROS(A,B) CURL_CONC_MACROS_(A,B) +#define CURL_CONC_MACROS_(A, B) A ## B +#define CURL_CONC_MACROS(A, B) CURL_CONC_MACROS_(A, B) /* curl uses its own printf() function internally. It understands the GNU * format. Use this format, so that it matches the GNU format attribute we @@ -347,17 +365,14 @@ #endif /* based on logic in "curl/mprintf.h" */ - -#if (defined(__GNUC__) || defined(__clang__) || \ - defined(__IAR_SYSTEMS_ICC__)) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ +#if (defined(__GNUC__) || defined(__clang__) || \ + defined(__IAR_SYSTEMS_ICC__)) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ !defined(CURL_NO_FMT_CHECKS) #if defined(__MINGW32__) && !defined(__clang__) -#define CURL_PRINTF(fmt, arg) \ - __attribute__((format(gnu_printf, fmt, arg))) +#define CURL_PRINTF(fmt, arg) __attribute__((format(gnu_printf, fmt, arg))) #else -#define CURL_PRINTF(fmt, arg) \ - __attribute__((format(__printf__, fmt, arg))) +#define CURL_PRINTF(fmt, arg) __attribute__((format(__printf__, fmt, arg))) #endif #else #define CURL_PRINTF(fmt, arg) @@ -375,7 +390,7 @@ (defined(__GNUC__) && __GNUC__ <= 14)) && \ defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ !defined(__ENVIRONMENT_OS_VERSION_MIN_REQUIRED__) -#define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ \ +#define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ \ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ #endif @@ -422,7 +437,7 @@ # if !(defined(__NEWLIB__) || \ (defined(__CLIB2__) && defined(__THREAD_SAFE))) /* disable threaded resolver with clib2 - requires newlib or clib-ts */ -# undef USE_THREADS_POSIX +# undef USE_RESOLV_THREADED # endif # endif # include @@ -438,9 +453,9 @@ # ifdef __amigaos4__ int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout); -# define select(a,b,c,d,e) Curl_amiga_select(a,b,c,d,e) +# define select(a, b, c, d, e) Curl_amiga_select(a, b, c, d, e) # else -# define select(a,b,c,d,e) WaitSelect(a,b,c,d,e,0) +# define select(a, b, c, d, e) WaitSelect(a, b, c, d, e, 0) # endif /* must not use libc's fcntl() on bsdsocket.library sockfds! */ # undef HAVE_FCNTL @@ -462,11 +477,15 @@ #define USE_EVENTFD #endif +#ifdef SO_NOSIGPIPE +#define USE_SO_NOSIGPIPE +#endif + #include #include #ifdef __TANDEM /* for ns*-tandem-nsk systems */ -# if ! defined __LP64 +# ifndef __LP64 # include /* FLOSS is only used for 32-bit builds. */ # endif #endif @@ -475,8 +494,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 @@ -487,48 +517,20 @@ # endif # include # include -# ifdef USE_WIN32_LARGE_FILES - /* 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) -# undef stat -# define struct_stat struct _stati64 -# define LSEEK_ERROR (__int64)-1 -# else - /* Small file (<2Gb) support using Win32 functions. */ -# ifndef UNDER_CE -# undef lseek -# define lseek(fdes, offset, whence) _lseek(fdes, (long)offset, whence) -# define fstat(fdes, stp) _fstat(fdes, stp) -# define struct_stat struct _stat -# endif -# define LSEEK_ERROR (long)-1 -# endif -# ifndef UNDER_CE - int curlx_win32_stat(const char *path, struct_stat *buffer); - int curlx_win32_open(const char *filename, int oflag, ...); - FILE *curlx_win32_fopen(const char *filename, const char *mode); -# define stat(fname, stp) curlx_win32_stat(fname, stp) -# define open curlx_win32_open -# define CURL_FOPEN(fname, mode) curlx_win32_fopen(fname, mode) -# define fopen(fname, mode) CURL_FOPEN(fname, mode) -# endif + /* Large file (>2Gb) support using Win32 functions. */ +# 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 @@ -538,7 +540,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 */ @@ -547,9 +549,13 @@ #endif #if SIZEOF_CURL_SOCKET_T < 8 +#ifdef USE_WINSOCK +# define FMT_SOCKET_T "u" +#else # define FMT_SOCKET_T "d" -#elif defined(__MINGW32__) -# define FMT_SOCKET_T "zd" +#endif +#elif defined(USE_WINSOCK) +# define FMT_SOCKET_T "zu" #else # define FMT_SOCKET_T "qd" #endif @@ -589,21 +595,7 @@ #endif #define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - 1) -#if (SIZEOF_CURL_OFF_T != 8) -# error "curl_off_t must be exactly 64 bits" -#else - typedef unsigned CURL_TYPEOF_CURL_OFF_T curl_uint64_t; - typedef CURL_TYPEOF_CURL_OFF_T curl_int64_t; -# ifndef CURL_SUFFIX_CURL_OFF_TU -# error "CURL_SUFFIX_CURL_OFF_TU must be defined" -# endif -# define CURL_UINT64_SUFFIX CURL_SUFFIX_CURL_OFF_TU -# define CURL_UINT64_C(val) CURL_CONC_MACROS(val,CURL_UINT64_SUFFIX) -# define FMT_PRId64 CURL_FORMAT_CURL_OFF_T -# define FMT_PRIu64 CURL_FORMAT_CURL_OFF_TU -#endif - -#define FMT_OFF_T CURL_FORMAT_CURL_OFF_T +#define FMT_OFF_T CURL_FORMAT_CURL_OFF_T #define FMT_OFF_TU CURL_FORMAT_CURL_OFF_TU #if (SIZEOF_TIME_T == 4) @@ -642,10 +634,13 @@ #endif #endif +#if SIZEOF_LONG > SIZEOF_SIZE_T +#error "unexpected: 'long' is larger than 'size_t'" +#endif + /* * Arg 2 type for gethostname in case it has not been defined in config file. */ - #ifndef GETHOSTNAME_TYPE_ARG2 # ifdef USE_WINSOCK # define GETHOSTNAME_TYPE_ARG2 int @@ -655,10 +650,9 @@ #endif /* Below we define some functions. They should - 4. set the SIGALRM signal timeout 5. set dir/file naming defines - */ + */ #ifdef _WIN32 @@ -669,8 +663,8 @@ # ifdef MSDOS /* Watt-32 */ # include -# define select(n,r,w,x,t) select_s(n,r,w,x,t) -# define ioctl(x,y,z) ioctlsocket(x,y,(char *)(z)) +# define select(n, r, w, x, t) select_s(n, r, w, x, t) +# define ioctl(x, y, z) ioctlsocket(x, y, (char *)(z)) # include # undef word # undef byte @@ -686,6 +680,16 @@ #endif /* _WIN32 */ +/* We want to use mutex when available. */ +#if defined(HAVE_THREADS_POSIX) || defined(_WIN32) +#define USE_MUTEX +#endif + +/* threaded resolver is the only feature requiring threads. */ +#ifdef USE_RESOLV_THREADED +#define USE_THREADS +#endif + /* ---------------------------------------------------------------- */ /* resolver specialty compile-time defines */ /* CURLRES_* defines to use in the host*.c sources */ @@ -694,7 +698,6 @@ /* * Mutually exclusive CURLRES_* definitions. */ - #if defined(USE_IPV6) && defined(HAVE_GETADDRINFO) # define CURLRES_IPV6 #elif defined(USE_IPV6) && (defined(_WIN32) || defined(__CYGWIN__)) @@ -704,13 +707,11 @@ # define CURLRES_IPV4 #endif -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#ifdef USE_RESOLV_THREADED # define CURLRES_ASYNCH -# define CURLRES_THREADED -#elif defined(USE_ARES) +#elif defined(USE_RESOLV_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 @@ -730,20 +731,14 @@ #endif #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ - defined(USE_RUSTLS) + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_RUSTLS) #define USE_SSL /* SSL support has been enabled */ #endif #if defined(USE_OPENSSL) && defined(USE_WOLFSSL) -# include -# if LIBWOLFSSL_VERSION_HEX >= 0x05007006 -# ifndef OPENSSL_COEXIST -# define OPENSSL_COEXIST -# endif -# else -# error "OpenSSL can only coexist with wolfSSL v5.7.6 or upper" -# endif +#ifndef OPENSSL_COEXIST +#define OPENSSL_COEXIST +#endif #endif #if defined(USE_WOLFSSL) && defined(USE_GNUTLS) @@ -752,23 +747,24 @@ #endif /* Single point where USE_SPNEGO definition might be defined */ -#if !defined(CURL_DISABLE_NEGOTIATE_AUTH) && \ - (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) +#if !defined(CURL_DISABLE_NEGOTIATE_AUTH) && \ + (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) #define USE_SPNEGO #endif /* Single point where USE_KERBEROS5 definition might be defined */ -#if !defined(CURL_DISABLE_KERBEROS_AUTH) && \ - (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) +#if !defined(CURL_DISABLE_KERBEROS_AUTH) && \ + (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) #define USE_KERBEROS5 #endif /* Single point where USE_NTLM definition might be defined */ -#ifndef CURL_DISABLE_NTLM -# if defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ - defined(USE_GNUTLS) || \ - defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ - (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT)) +#ifdef CURL_ENABLE_NTLM +# if (defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)) || \ + defined(USE_GNUTLS) || \ + (defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)) || \ + defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ + (defined(USE_WOLFSSL) && defined(HAVE_WC_DES_ECBENCRYPT)) # define USE_CURL_NTLM_CORE # endif # if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI) @@ -776,10 +772,20 @@ # endif #endif -#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) || defined(USE_WOLFSSH) +#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) #define USE_SSH #endif +/* GCC <4.6 does not support '#pragma GCC diagnostic push' and does not support + 'pragma GCC diagnostic' inside functions. + Use CURL_HAVE_DIAG to guard the above in the curl codebase, instead of + defined(__GNUC__) || defined(__clang__). + */ +#if defined(__clang__) || (defined(__GNUC__) && \ + ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)))) +#define CURL_HAVE_DIAG +#endif + /* * Provide a mechanism to silence picky compilers, such as gcc 4.6+. * Parameters should of course normally not be unused, but for example when @@ -810,55 +816,373 @@ /* fallthrough attribute */ #ifndef FALLTHROUGH -#if (defined(__GNUC__) && __GNUC__ >= 7) || \ - (defined(__clang__) && __clang_major__ >= 10) +#if (defined(__GNUC__) && __GNUC__ >= 7) || \ + (defined(__clang__) && __clang_major__ >= 10) # define FALLTHROUGH() __attribute__((fallthrough)) #else -# define FALLTHROUGH() do {} while (0) +# define FALLTHROUGH() do {} while(0) #endif #endif /* - * Include macros and defines that should only be processed once. + * Inclusion of common header files. */ -#ifndef HEADER_CURL_SETUP_ONCE_H -#include "curl_setup_once.h" +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include #endif -#ifdef UNDER_CE -#define getenv curl_getenv /* Windows CE does not support getenv() */ -#define raise(s) ((void)(s)) -/* Terrible workarounds to make Windows CE compile */ -#define errno 0 -#define CURL_SETERRNO(x) ((void)(x)) -#define EINTR 4 -#define EAGAIN 11 -#define ENOMEM 12 -#define EACCES 13 -#define EEXIST 17 -#define EISDIR 21 -#define EINVAL 22 -#define ENOSPC 28 -#define strerror(x) "?" -#undef STDIN_FILENO -#define STDIN_FILENO 0 +#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_SETERRNO(x) (errno = (x)) +#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 + +/* + * 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 */ +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 +# define read(fd, buf, count) (ssize_t)_read(fd, buf, curlx_uztoui(count)) +# 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 /* * Definition of our NOP statement Object-like macro */ - #ifndef Curl_nop_stmt -#define Curl_nop_stmt do { } while(0) +#define Curl_nop_stmt do {} while(0) #endif /* * Ensure that Winsock and lwIP TCP/IP stacks are not mixed. */ - #if defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H) # if defined(SOCKET) || defined(USE_WINSOCK) # error "Winsock and lwIP TCP/IP stack definitions shall not coexist!" @@ -868,7 +1192,6 @@ /* * shutdown() flags for systems that do not define them */ - #ifndef SHUT_RD #define SHUT_RD 0x00 #endif @@ -904,18 +1227,29 @@ /* 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 */ #if defined(_WIN32) || defined(MSDOS) -#define FOPEN_READTEXT "rt" -#define FOPEN_WRITETEXT "wt" +#define FOPEN_READTEXT "rt" +#define FOPEN_WRITETEXT "wt" #define FOPEN_APPENDTEXT "at" #elif defined(__CYGWIN__) /* Cygwin has specific behavior we need to address when _WIN32 is not defined. @@ -924,18 +1258,18 @@ For write we want our output to have line endings of LF and be compatible with other Cygwin utilities. For read we want to handle input that may have line endings either CRLF or LF so 't' is appropriate. */ -#define FOPEN_READTEXT "rt" -#define FOPEN_WRITETEXT "w" +#define FOPEN_READTEXT "rt" +#define FOPEN_WRITETEXT "w" #define FOPEN_APPENDTEXT "a" #else -#define FOPEN_READTEXT "r" -#define FOPEN_WRITETEXT "w" +#define FOPEN_READTEXT "r" +#define FOPEN_WRITETEXT "w" #define FOPEN_APPENDTEXT "a" #endif /* for systems that do not detect this in configure */ #ifndef CURL_SA_FAMILY_T -# if defined(_WIN32) && !defined(UNDER_CE) +# ifdef USE_WINSOCK # define CURL_SA_FAMILY_T ADDRESS_FAMILY # elif defined(HAVE_SA_FAMILY_T) # define CURL_SA_FAMILY_T sa_family_t @@ -949,15 +1283,19 @@ endings either CRLF or LF so 't' is appropriate. /* Some convenience macros to get the larger/smaller value out of two given. We prefix with CURL to prevent name collisions. */ -#define CURLMAX(x,y) ((x)>(y)?(x):(y)) -#define CURLMIN(x,y) ((x)<(y)?(x):(y)) +#define CURLMAX(x, y) ((x) > (y) ? (x) : (y)) +#define CURLMIN(x, y) ((x) < (y) ? (x) : (y)) /* A convenience macro to provide both the string literal and the length of the string literal in one go, useful for functions that take "string,len" as their argument */ -#define STRCONST(x) x,sizeof(x)-1 +#define STRCONST(x) x, sizeof(x) - 1 -#define CURL_ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#define CURL_ARRAYSIZE(A) (sizeof(A) / sizeof((A)[0])) + +/* Buffer size for error messages retrieved via + curlx_strerror() and Curl_sspi_strerror() */ +#define STRERROR_LEN 256 #ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* only if not already done */ /* @@ -981,14 +1319,23 @@ extern curl_strdup_callback Curl_cstrdup; extern curl_calloc_callback Curl_ccalloc; /* - * Curl_safefree defined as a macro to allow MemoryTracking feature - * to log free() calls at same location where Curl_safefree is used. + * curlx_safefree() defined as a macro to allow MemoryTracking feature + * to log free() calls at same location where curlx_safefree() is used. * This macro also assigns NULL to given pointer when free'd. */ -#define Curl_safefree(ptr) \ - do { free((ptr)); (ptr) = NULL;} while(0) +#define curlx_safefree(ptr) \ + do { \ + curlx_free(ptr); \ + (ptr) = NULL; \ + } while(0) -#ifdef CURLDEBUG +#include /* for CURL_EXTERN, curl_socket_t, mprintf.h */ + +#ifdef DEBUGBUILD +#define CURL_MEMDEBUG +#endif + +#ifdef CURL_MEMDEBUG #ifdef __clang__ # define ALLOC_FUNC __attribute__((__malloc__)) # if __clang_major__ >= 4 @@ -1012,8 +1359,6 @@ extern curl_calloc_callback Curl_ccalloc; # define ALLOC_SIZE2(n, s) #endif -#include /* for CURL_EXTERN */ - extern FILE *curl_dbg_logfile; /* memory functions */ @@ -1042,7 +1387,8 @@ CURL_EXTERN void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source); CURL_EXTERN int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source); -CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, void *a, void *alen, +CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, + void *saddr, void *saddrlen, int line, const char *source); #ifdef HAVE_ACCEPT4 CURL_EXTERN curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr, @@ -1055,49 +1401,106 @@ CURL_EXTERN int curl_dbg_socketpair(int domain, int type, int protocol, int line, const char *source); #endif -/* send/receive sockets */ -CURL_EXTERN SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd, - SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf, - SEND_TYPE_ARG3 len, - SEND_TYPE_ARG4 flags, int line, - const char *source); -CURL_EXTERN RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, - RECV_TYPE_ARG2 buf, - RECV_TYPE_ARG3 len, - RECV_TYPE_ARG4 flags, int line, - const char *source); - /* FILE functions */ CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source); -CURL_EXTERN ALLOC_FUNC - FILE *curl_dbg_fopen(const char *file, const char *mode, - int line, const char *source); -CURL_EXTERN ALLOC_FUNC - FILE *curl_dbg_fdopen(int filedes, const char *mode, - int line, const char *source); +CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode, + int line, const char *source); +CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_freopen(const char *file, + const char *mode, FILE *fh, + int line, const char *source); +CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode, + int line, const char *source); -#define sclose(sockfd) curl_dbg_sclose(sockfd,__LINE__,__FILE__) -#define fake_sclose(sockfd) curl_dbg_mark_sclose(sockfd,__LINE__,__FILE__) +#define sclose(sockfd) curl_dbg_sclose(sockfd, __LINE__, __FILE__) +#define fake_sclose(sockfd) curl_dbg_mark_sclose(sockfd, __LINE__, __FILE__) -#define CURL_GETADDRINFO(host,serv,hint,res) \ +#define CURL_GETADDRINFO(host, serv, hint, res) \ curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__) #define CURL_FREEADDRINFO(data) \ curl_dbg_freeaddrinfo(data, __LINE__, __FILE__) - -#define CURL_ACCEPT(sock,addr,len) \ +#define CURL_SOCKET(domain, type, protocol) \ + 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, \ + __LINE__, __FILE__) +#endif +#define CURL_ACCEPT(sock, addr, len) \ curl_dbg_accept(sock, addr, len, __LINE__, __FILE__) +#ifdef HAVE_ACCEPT4 +#define CURL_ACCEPT4(sock, addr, len, flags) \ + 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 #define CURL_GETADDRINFO getaddrinfo #define CURL_FREEADDRINFO freeaddrinfo - +#define CURL_SOCKET socket +#ifdef HAVE_SOCKETPAIR +#define CURL_SOCKETPAIR socketpair +#endif #define CURL_ACCEPT accept +#ifdef HAVE_ACCEPT4 +#define CURL_ACCEPT4 accept4 +#endif -#endif /* CURLDEBUG */ +#endif /* CURL_MEMDEBUG */ + +/* Allocator macros */ + +#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__) +#define curlx_calloc(nbelem, size) \ + curl_dbg_calloc(nbelem, size, __LINE__, __FILE__) +#define curlx_realloc(ptr, size) \ + curl_dbg_realloc(ptr, size, __LINE__, __FILE__) +#define curlx_free(ptr) curl_dbg_free(ptr, __LINE__, __FILE__) + +#ifdef _WIN32 +#ifdef UNICODE +#define curlx_tcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) +#else +#define curlx_tcsdup curlx_strdup +#endif +#endif /* _WIN32 */ + +#else /* !CURL_MEMDEBUG */ + +#ifdef BUILDING_LIBCURL +#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 */ +#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 curlx_wcsdup +#else +#define curlx_tcsdup curlx_strdup +#endif +#endif /* _WIN32 */ + +#endif /* CURL_MEMDEBUG */ /* Some versions of the Android NDK is missing the declaration */ #if defined(HAVE_GETPWUID_R) && \ @@ -1117,9 +1520,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, #define USE_HTTP2 #endif -#if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \ - (defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)) || \ - defined(USE_QUICHE) +#if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE) #ifdef CURL_WITH_MULTI_SSL #error "MultiSSL combined with QUIC is not supported" @@ -1141,22 +1542,25 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, #endif #if defined(USE_UNIX_SOCKETS) && defined(_WIN32) -# ifndef UNIX_PATH_MAX - /* Replicating logic present in afunix.h - (distributed with newer Windows 10 SDK versions only) */ -# define UNIX_PATH_MAX 108 - /* !checksrc! disable TYPEDEFSTRUCT 1 */ - typedef struct sockaddr_un { - CURL_SA_FAMILY_T sun_family; - char sun_path[UNIX_PATH_MAX]; - } SOCKADDR_UN, *PSOCKADDR_UN; -# define WIN32_SOCKADDR_UN -# endif +/* Offered by mingw-w64 v10+. MS SDK 10.17763/~VS2017+. */ +#if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR >= 10) +# include +#elif !defined(UNIX_PATH_MAX) /* Replicate logic present in afunix.h */ +# define UNIX_PATH_MAX 108 +/* !checksrc! disable TYPEDEFSTRUCT 1 */ +typedef struct sockaddr_un { + CURL_SA_FAMILY_T sun_family; + char sun_path[UNIX_PATH_MAX]; +} SOCKADDR_UN, *PSOCKADDR_UN; +#endif #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+ */ @@ -1188,6 +1592,20 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, # define CURL_INLINE /* empty */ #endif -#endif /* HEADER_CURL_SETUP_H */ +/* Detect if compiler supports C99 variadic macros */ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ + defined(_MSC_VER) +#define CURL_HAVE_MACRO_VARARG +#endif -#include "curl_mem_undef.h" +#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/lib/curl_setup_once.h b/lib/curl_setup_once.h deleted file mode 100644 index 7a54760e16..0000000000 --- a/lib/curl_setup_once.h +++ /dev/null @@ -1,374 +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 -#ifndef UNDER_CE -#include -#endif - -#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)) -#elif defined(_WIN32) /* for VS2008 */ -# define CURL_UNCONST(p) ((void *)(ULONG_PTR)(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) && !defined(UNDER_CE) -#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/lib/curl_sha256.h b/lib/curl_sha256.h index caa5080b3f..6c48a8e176 100644 --- a/lib/curl_sha256.h +++ b/lib/curl_sha256.h @@ -24,11 +24,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" #if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ defined(USE_LIBSSH2) || defined(USE_SSL) -#include #include "curl_hmac.h" extern const struct HMAC_params Curl_HMAC_SHA256; @@ -37,8 +37,8 @@ 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, - const size_t len); +CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input, + size_t len); #endif diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index 7e9b223387..5f5a738b3f 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -21,35 +21,29 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256) #include "curl_sha512_256.h" -#include "curlx/warnless.h" /* The recommended order of the TLS backends: - * * OpenSSL - * * GnuTLS - * * wolfSSL - * * Schannel SSPI - * * mbedTLS - * * Rustls + * 1. USE_OPENSSL + * 2. USE_WOLFSSL + * 3. USE_GNUTLS + * 4. USE_MBEDTLS (TBD) + * 5. USE_RUSTLS (TBD) + * 6. USE_WIN32_CRYPTO (TBD) * Skip the backend if it does not support the required algorithm */ #ifdef USE_OPENSSL # include -# if (!defined(LIBRESSL_VERSION_NUMBER) && \ - OPENSSL_VERSION_NUMBER >= 0x10101000L) || \ - (defined(LIBRESSL_VERSION_NUMBER) && \ - LIBRESSL_VERSION_NUMBER >= 0x3080000fL) -# include -# if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA512) -# include -# define USE_OPENSSL_SHA512_256 1 -# define HAS_SHA512_256_IMPLEMENTATION 1 -# ifdef __NetBSD__ +# if !defined(LIBRESSL_VERSION_NUMBER) || \ + (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x3080000fL) +# include +# define USE_OPENSSL_SHA512_256 1 +# define HAS_SHA512_256_IMPLEMENTATION 1 +# ifdef __NetBSD__ /* Some NetBSD versions has a bug in SHA-512/256. * See https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 * The problematic versions: @@ -59,28 +53,33 @@ * 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. */ -# include -# if __NetBSD_Version__ < 904000000 || \ - (__NetBSD_Version__ >= 999000000 && \ - __NetBSD_Version__ < 1000000000) || \ - (__NetBSD_Version__ >= 1099000000 && \ - __NetBSD_Version__ < 1099001100) -# define NEED_NETBSD_SHA512_256_WORKAROUND 1 -# include -# endif + * the workaround reduces performance slightly. */ +# include +# if __NetBSD_Version__ < 904000000 || \ + (__NetBSD_Version__ >= 999000000 && \ + __NetBSD_Version__ < 1000000000) || \ + (__NetBSD_Version__ >= 1099000000 && \ + __NetBSD_Version__ < 1099001100) +# define NEED_NETBSD_SHA512_256_WORKAROUND 1 # endif # endif # endif #endif /* USE_OPENSSL */ +#if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_WOLFSSL) +# include +# ifndef WOLFSSL_NOSHA512_256 +# define USE_WOLFSSL_SHA512_256 1 +# define HAS_SHA512_256_IMPLEMENTATION 1 +# endif +#endif #if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS) # include # ifdef SHA512_256_DIGEST_SIZE # define USE_GNUTLS_SHA512_256 1 # endif -#endif /* ! HAS_SHA512_256_IMPLEMENTATION && USE_GNUTLS */ +#endif /* !HAS_SHA512_256_IMPLEMENTATION && USE_GNUTLS */ #ifdef USE_OPENSSL_SHA512_256 @@ -111,7 +110,7 @@ typedef EVP_MD_CTX *Curl_sha512_256_ctx; */ static CURLcode Curl_sha512_256_init(void *context) { - Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; *ctx = EVP_MD_CTX_create(); if(!*ctx) @@ -131,7 +130,6 @@ static CURLcode Curl_sha512_256_init(void *context) return CURLE_FAILED_INIT; } - /** * Process portion of bytes. * @@ -144,7 +142,7 @@ static CURLcode Curl_sha512_256_update(void *context, const unsigned char *data, size_t length) { - Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; if(!EVP_DigestUpdate(*ctx, data, length)) return CURLE_SSL_CIPHER; @@ -152,20 +150,19 @@ static CURLcode Curl_sha512_256_update(void *context, return CURLE_OK; } - /** * Finalise SHA-512/256 calculation, return digest. * * @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 */ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) { CURLcode ret; - Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; #ifdef NEED_NETBSD_SHA512_256_WORKAROUND /* Use a larger buffer to work around a bug in NetBSD: @@ -176,9 +173,9 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) if(ret == CURLE_OK) memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE); explicit_memset(tmp_digest, 0, sizeof(tmp_digest)); -#else /* ! NEED_NETBSD_SHA512_256_WORKAROUND */ +#else /* !NEED_NETBSD_SHA512_256_WORKAROUND */ ret = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; -#endif /* ! NEED_NETBSD_SHA512_256_WORKAROUND */ +#endif /* NEED_NETBSD_SHA512_256_WORKAROUND */ EVP_MD_CTX_destroy(*ctx); *ctx = NULL; @@ -186,6 +183,42 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) return ret; } +#elif defined(USE_WOLFSSL_SHA512_256) +#include + +#define CURL_SHA512_256_DIGEST_SIZE WC_SHA512_256_DIGEST_SIZE +#define CURL_SHA512_256_BLOCK_SIZE WC_SHA512_256_BLOCK_SIZE + +typedef struct wc_Sha512 Curl_sha512_256_ctx; + +static CURLcode Curl_sha512_256_init(void *ctx) +{ + if(wc_InitSha512_256(ctx)) + return CURLE_FAILED_INIT; + return CURLE_OK; +} + +static CURLcode Curl_sha512_256_update(void *ctx, + const unsigned char *data, + size_t length) +{ + do { + word32 ilen = (word32)CURLMIN(length, UINT_MAX); + if(wc_Sha512_256Update(ctx, data, ilen)) + return CURLE_SSL_CIPHER; + length -= ilen; + data += ilen; + } while(length); + return CURLE_OK; +} + +static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *ctx) +{ + if(wc_Sha512_256Final(ctx, digest)) + return CURLE_SSL_CIPHER; + return CURLE_OK; +} + #elif defined(USE_GNUTLS_SHA512_256) #define CURL_SHA512_256_BLOCK_SIZE SHA512_256_BLOCK_SIZE @@ -204,7 +237,7 @@ typedef struct sha512_256_ctx Curl_sha512_256_ctx; */ static CURLcode Curl_sha512_256_init(void *context) { - Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; /* Check whether the header and this file use the same numbers */ DEBUGASSERT(CURL_SHA512_256_DIGEST_LENGTH == CURL_SHA512_256_DIGEST_SIZE); @@ -214,7 +247,6 @@ static CURLcode Curl_sha512_256_init(void *context) return CURLE_OK; } - /** * Process portion of bytes. * @@ -227,7 +259,7 @@ static CURLcode Curl_sha512_256_update(void *context, const unsigned char *data, size_t length) { - Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; DEBUGASSERT((data != NULL) || (length == 0)); @@ -236,19 +268,17 @@ static CURLcode Curl_sha512_256_update(void *context, return CURLE_OK; } - /** * Finalise SHA-512/256 calculation, return digest. * * @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) +static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) { - Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context; + Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; sha512_256_digest(ctx, (size_t)CURL_SHA512_256_DIGEST_SIZE, (uint8_t *)digest); @@ -261,10 +291,7 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, /* ** This implementation of SHA-512/256 hash calculation was originally ** * * ** written by Evgeny Grin (Karlson2k) for GNU libmicrohttpd. ** * * ** The author ported the code to libcurl. The ported code is provided ** * - * ** under curl license. ** * - * ** This is a minimal version with minimal optimizations. Performance ** * - * ** can be significantly improved. Big-endian store and load macros ** * - * ** are obvious targets for optimization. ** */ + * ** under curl license. ** */ #ifdef __GNUC__ # if defined(__has_attribute) && defined(__STDC_VERSION__) @@ -288,32 +315,32 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, /* Bits manipulation macros and functions. Can be moved to other headers to reuse. */ -#define CURL_GET_64BIT_BE(ptr) \ - ( ((curl_uint64_t)(((const unsigned char*)(ptr))[0]) << 56) | \ - ((curl_uint64_t)(((const unsigned char*)(ptr))[1]) << 48) | \ - ((curl_uint64_t)(((const unsigned char*)(ptr))[2]) << 40) | \ - ((curl_uint64_t)(((const unsigned char*)(ptr))[3]) << 32) | \ - ((curl_uint64_t)(((const unsigned char*)(ptr))[4]) << 24) | \ - ((curl_uint64_t)(((const unsigned char*)(ptr))[5]) << 16) | \ - ((curl_uint64_t)(((const unsigned char*)(ptr))[6]) << 8) | \ - (curl_uint64_t)(((const unsigned char*)(ptr))[7]) ) +#define CURL_GET_64BIT_BE(ptr) \ + (((uint64_t)(((const uint8_t *)(ptr))[0]) << 56) | \ + ((uint64_t)(((const uint8_t *)(ptr))[1]) << 48) | \ + ((uint64_t)(((const uint8_t *)(ptr))[2]) << 40) | \ + ((uint64_t)(((const uint8_t *)(ptr))[3]) << 32) | \ + ((uint64_t)(((const uint8_t *)(ptr))[4]) << 24) | \ + ((uint64_t)(((const uint8_t *)(ptr))[5]) << 16) | \ + ((uint64_t)(((const uint8_t *)(ptr))[6]) << 8) | \ + (uint64_t)(((const uint8_t *)(ptr))[7])) -#define CURL_PUT_64BIT_BE(ptr,val) do { \ - ((unsigned char*)(ptr))[7]=(unsigned char)((curl_uint64_t)(val)); \ - ((unsigned char*)(ptr))[6]=(unsigned char)(((curl_uint64_t)(val)) >> 8); \ - ((unsigned char*)(ptr))[5]=(unsigned char)(((curl_uint64_t)(val)) >> 16); \ - ((unsigned char*)(ptr))[4]=(unsigned char)(((curl_uint64_t)(val)) >> 24); \ - ((unsigned char*)(ptr))[3]=(unsigned char)(((curl_uint64_t)(val)) >> 32); \ - ((unsigned char*)(ptr))[2]=(unsigned char)(((curl_uint64_t)(val)) >> 40); \ - ((unsigned char*)(ptr))[1]=(unsigned char)(((curl_uint64_t)(val)) >> 48); \ - ((unsigned char*)(ptr))[0]=(unsigned char)(((curl_uint64_t)(val)) >> 56); \ +#define CURL_PUT_64BIT_BE(ptr, val) \ + do { \ + ((uint8_t *)(ptr))[7] = (uint8_t)((uint64_t)(val)); \ + ((uint8_t *)(ptr))[6] = (uint8_t)(((uint64_t)(val)) >> 8); \ + ((uint8_t *)(ptr))[5] = (uint8_t)(((uint64_t)(val)) >> 16); \ + ((uint8_t *)(ptr))[4] = (uint8_t)(((uint64_t)(val)) >> 24); \ + ((uint8_t *)(ptr))[3] = (uint8_t)(((uint64_t)(val)) >> 32); \ + ((uint8_t *)(ptr))[2] = (uint8_t)(((uint64_t)(val)) >> 40); \ + ((uint8_t *)(ptr))[1] = (uint8_t)(((uint64_t)(val)) >> 48); \ + ((uint8_t *)(ptr))[0] = (uint8_t)(((uint64_t)(val)) >> 56); \ } while(0) /* Defined as a function. The macro version may duplicate the binary code * size as each argument is used twice, so if any calculation is used * as an argument, the calculation could be done twice. */ -static CURL_FORCEINLINE curl_uint64_t Curl_rotr64(curl_uint64_t value, - unsigned int bits) +static CURL_FORCEINLINE uint64_t Curl_rotr64(uint64_t value, unsigned int bits) { bits %= 64; if(bits == 0) @@ -344,7 +371,7 @@ static CURL_FORCEINLINE curl_uint64_t Curl_rotr64(curl_uint64_t value, * Size of the SHA-512/256 resulting digest in words. * This is the final digest size, not intermediate hash. */ -#define SHA512_256_DIGEST_SIZE_WORDS (SHA512_256_HASH_SIZE_WORDS / 2) +#define SHA512_256_DIGEST_SIZE_WORDS (SHA512_256_HASH_SIZE_WORDS / 2) /** * Size of the SHA-512/256 resulting digest in bytes @@ -378,22 +405,22 @@ struct Curl_sha512_256ctx { * compilers may automatically use fast load/store instruction for big * endian data on little endian machine. */ - curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS]; + uint64_t H[SHA512_256_HASH_SIZE_WORDS]; /** * SHA-512/256 input data buffer. The buffer is properly aligned. Smart * compilers may automatically use fast load/store instruction for big * endian data on little endian machine. */ - curl_uint64_t buffer[SHA512_256_BLOCK_SIZE_WORDS]; + uint64_t buffer[SHA512_256_BLOCK_SIZE_WORDS]; /** * The number of bytes, lower part */ - curl_uint64_t count; + uint64_t count; /** * The number of bits, high part. Unlike lower part, this counts the number * of bits, not bytes. */ - curl_uint64_t count_bits_hi; + uint64_t count_bits_hi; }; /** @@ -401,7 +428,6 @@ struct Curl_sha512_256ctx { */ typedef struct Curl_sha512_256ctx Curl_sha512_256_ctx; - /** * Initialise structure for SHA-512/256 calculation. * @@ -410,119 +436,117 @@ typedef struct Curl_sha512_256ctx Curl_sha512_256_ctx; */ static CURLcode Curl_sha512_256_init(void *context) { - struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context; + struct Curl_sha512_256ctx * const ctx = (struct Curl_sha512_256ctx *)context; /* Check whether the header and this file use the same numbers */ DEBUGASSERT(CURL_SHA512_256_DIGEST_LENGTH == CURL_SHA512_256_DIGEST_SIZE); - DEBUGASSERT(sizeof(curl_uint64_t) == 8); + DEBUGASSERT(sizeof(uint64_t) == 8); /* Initial hash values, see FIPS PUB 180-4 section 5.3.6.2 */ /* Values generated by "IV Generation Function" as described in * section 5.3.6 */ - ctx->H[0] = CURL_UINT64_C(0x22312194FC2BF72C); - ctx->H[1] = CURL_UINT64_C(0x9F555FA3C84C64C2); - ctx->H[2] = CURL_UINT64_C(0x2393B86B6F53B151); - ctx->H[3] = CURL_UINT64_C(0x963877195940EABD); - ctx->H[4] = CURL_UINT64_C(0x96283EE2A88EFFE3); - ctx->H[5] = CURL_UINT64_C(0xBE5E1E2553863992); - ctx->H[6] = CURL_UINT64_C(0x2B0199FC2C85B8AA); - ctx->H[7] = CURL_UINT64_C(0x0EB72DDC81C52CA2); + ctx->H[0] = UINT64_C(0x22312194FC2BF72C); + ctx->H[1] = UINT64_C(0x9F555FA3C84C64C2); + ctx->H[2] = UINT64_C(0x2393B86B6F53B151); + ctx->H[3] = UINT64_C(0x963877195940EABD); + ctx->H[4] = UINT64_C(0x96283EE2A88EFFE3); + ctx->H[5] = UINT64_C(0xBE5E1E2553863992); + ctx->H[6] = UINT64_C(0x2B0199FC2C85B8AA); + ctx->H[7] = UINT64_C(0x0EB72DDC81C52CA2); /* Initialise number of bytes and high part of number of bits. */ - ctx->count = CURL_UINT64_C(0); - ctx->count_bits_hi = CURL_UINT64_C(0); + ctx->count = UINT64_C(0); + ctx->count_bits_hi = UINT64_C(0); return CURLE_OK; } - /** * Base of the SHA-512/256 transformation. * Gets a full 128 bytes block of data and updates hash values; * @param H hash values * @param data the data buffer with #CURL_SHA512_256_BLOCK_SIZE bytes block */ -static -void Curl_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS], - const void *data) +static void Curl_sha512_256_transform(uint64_t H[SHA512_256_HASH_SIZE_WORDS], + const void *data) { /* Working variables, see FIPS PUB 180-4 section 6.7, 6.4. */ - curl_uint64_t a = H[0]; - curl_uint64_t b = H[1]; - curl_uint64_t c = H[2]; - curl_uint64_t d = H[3]; - curl_uint64_t e = H[4]; - curl_uint64_t f = H[5]; - curl_uint64_t g = H[6]; - curl_uint64_t h = H[7]; + uint64_t a = H[0]; + uint64_t b = H[1]; + uint64_t c = H[2]; + uint64_t d = H[3]; + uint64_t e = H[4]; + uint64_t f = H[5]; + uint64_t g = H[6]; + uint64_t h = H[7]; /* Data buffer, used as a cyclic buffer. See FIPS PUB 180-4 section 5.2.2, 6.7, 6.4. */ - curl_uint64_t W[16]; + uint64_t W[16]; /* 'Ch' and 'Maj' macro functions are defined with widely-used optimization. See FIPS PUB 180-4 formulae 4.8, 4.9. */ -#define Sha512_Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) ) -#define Sha512_Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) ) +#define Sha512_Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define Sha512_Maj(x, y, z) (((x) & (y)) ^ ((z) & ((x) ^ (y)))) /* 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; /* K constants array. See FIPS PUB 180-4 section 4.2.3 for K values. */ - static const curl_uint64_t K[80] = { - CURL_UINT64_C(0x428a2f98d728ae22), CURL_UINT64_C(0x7137449123ef65cd), - CURL_UINT64_C(0xb5c0fbcfec4d3b2f), CURL_UINT64_C(0xe9b5dba58189dbbc), - CURL_UINT64_C(0x3956c25bf348b538), CURL_UINT64_C(0x59f111f1b605d019), - CURL_UINT64_C(0x923f82a4af194f9b), CURL_UINT64_C(0xab1c5ed5da6d8118), - CURL_UINT64_C(0xd807aa98a3030242), CURL_UINT64_C(0x12835b0145706fbe), - CURL_UINT64_C(0x243185be4ee4b28c), CURL_UINT64_C(0x550c7dc3d5ffb4e2), - CURL_UINT64_C(0x72be5d74f27b896f), CURL_UINT64_C(0x80deb1fe3b1696b1), - CURL_UINT64_C(0x9bdc06a725c71235), CURL_UINT64_C(0xc19bf174cf692694), - CURL_UINT64_C(0xe49b69c19ef14ad2), CURL_UINT64_C(0xefbe4786384f25e3), - CURL_UINT64_C(0x0fc19dc68b8cd5b5), CURL_UINT64_C(0x240ca1cc77ac9c65), - CURL_UINT64_C(0x2de92c6f592b0275), CURL_UINT64_C(0x4a7484aa6ea6e483), - CURL_UINT64_C(0x5cb0a9dcbd41fbd4), CURL_UINT64_C(0x76f988da831153b5), - CURL_UINT64_C(0x983e5152ee66dfab), CURL_UINT64_C(0xa831c66d2db43210), - CURL_UINT64_C(0xb00327c898fb213f), CURL_UINT64_C(0xbf597fc7beef0ee4), - CURL_UINT64_C(0xc6e00bf33da88fc2), CURL_UINT64_C(0xd5a79147930aa725), - CURL_UINT64_C(0x06ca6351e003826f), CURL_UINT64_C(0x142929670a0e6e70), - CURL_UINT64_C(0x27b70a8546d22ffc), CURL_UINT64_C(0x2e1b21385c26c926), - CURL_UINT64_C(0x4d2c6dfc5ac42aed), CURL_UINT64_C(0x53380d139d95b3df), - CURL_UINT64_C(0x650a73548baf63de), CURL_UINT64_C(0x766a0abb3c77b2a8), - CURL_UINT64_C(0x81c2c92e47edaee6), CURL_UINT64_C(0x92722c851482353b), - CURL_UINT64_C(0xa2bfe8a14cf10364), CURL_UINT64_C(0xa81a664bbc423001), - CURL_UINT64_C(0xc24b8b70d0f89791), CURL_UINT64_C(0xc76c51a30654be30), - CURL_UINT64_C(0xd192e819d6ef5218), CURL_UINT64_C(0xd69906245565a910), - CURL_UINT64_C(0xf40e35855771202a), CURL_UINT64_C(0x106aa07032bbd1b8), - CURL_UINT64_C(0x19a4c116b8d2d0c8), CURL_UINT64_C(0x1e376c085141ab53), - CURL_UINT64_C(0x2748774cdf8eeb99), CURL_UINT64_C(0x34b0bcb5e19b48a8), - CURL_UINT64_C(0x391c0cb3c5c95a63), CURL_UINT64_C(0x4ed8aa4ae3418acb), - CURL_UINT64_C(0x5b9cca4f7763e373), CURL_UINT64_C(0x682e6ff3d6b2b8a3), - CURL_UINT64_C(0x748f82ee5defb2fc), CURL_UINT64_C(0x78a5636f43172f60), - CURL_UINT64_C(0x84c87814a1f0ab72), CURL_UINT64_C(0x8cc702081a6439ec), - CURL_UINT64_C(0x90befffa23631e28), CURL_UINT64_C(0xa4506cebde82bde9), - CURL_UINT64_C(0xbef9a3f7b2c67915), CURL_UINT64_C(0xc67178f2e372532b), - CURL_UINT64_C(0xca273eceea26619c), CURL_UINT64_C(0xd186b8c721c0c207), - CURL_UINT64_C(0xeada7dd6cde0eb1e), CURL_UINT64_C(0xf57d4f7fee6ed178), - CURL_UINT64_C(0x06f067aa72176fba), CURL_UINT64_C(0x0a637dc5a2c898a6), - CURL_UINT64_C(0x113f9804bef90dae), CURL_UINT64_C(0x1b710b35131c471b), - CURL_UINT64_C(0x28db77f523047d84), CURL_UINT64_C(0x32caab7b40c72493), - CURL_UINT64_C(0x3c9ebe0a15c9bebc), CURL_UINT64_C(0x431d67c49c100d4c), - CURL_UINT64_C(0x4cc5d4becb3e42b6), CURL_UINT64_C(0x597f299cfc657e2a), - CURL_UINT64_C(0x5fcb6fab3ad6faec), CURL_UINT64_C(0x6c44198c4a475817) + static const uint64_t K[80] = { + UINT64_C(0x428a2f98d728ae22), UINT64_C(0x7137449123ef65cd), + UINT64_C(0xb5c0fbcfec4d3b2f), UINT64_C(0xe9b5dba58189dbbc), + UINT64_C(0x3956c25bf348b538), UINT64_C(0x59f111f1b605d019), + UINT64_C(0x923f82a4af194f9b), UINT64_C(0xab1c5ed5da6d8118), + UINT64_C(0xd807aa98a3030242), UINT64_C(0x12835b0145706fbe), + UINT64_C(0x243185be4ee4b28c), UINT64_C(0x550c7dc3d5ffb4e2), + UINT64_C(0x72be5d74f27b896f), UINT64_C(0x80deb1fe3b1696b1), + UINT64_C(0x9bdc06a725c71235), UINT64_C(0xc19bf174cf692694), + UINT64_C(0xe49b69c19ef14ad2), UINT64_C(0xefbe4786384f25e3), + UINT64_C(0x0fc19dc68b8cd5b5), UINT64_C(0x240ca1cc77ac9c65), + UINT64_C(0x2de92c6f592b0275), UINT64_C(0x4a7484aa6ea6e483), + UINT64_C(0x5cb0a9dcbd41fbd4), UINT64_C(0x76f988da831153b5), + UINT64_C(0x983e5152ee66dfab), UINT64_C(0xa831c66d2db43210), + UINT64_C(0xb00327c898fb213f), UINT64_C(0xbf597fc7beef0ee4), + UINT64_C(0xc6e00bf33da88fc2), UINT64_C(0xd5a79147930aa725), + UINT64_C(0x06ca6351e003826f), UINT64_C(0x142929670a0e6e70), + UINT64_C(0x27b70a8546d22ffc), UINT64_C(0x2e1b21385c26c926), + UINT64_C(0x4d2c6dfc5ac42aed), UINT64_C(0x53380d139d95b3df), + UINT64_C(0x650a73548baf63de), UINT64_C(0x766a0abb3c77b2a8), + UINT64_C(0x81c2c92e47edaee6), UINT64_C(0x92722c851482353b), + UINT64_C(0xa2bfe8a14cf10364), UINT64_C(0xa81a664bbc423001), + UINT64_C(0xc24b8b70d0f89791), UINT64_C(0xc76c51a30654be30), + UINT64_C(0xd192e819d6ef5218), UINT64_C(0xd69906245565a910), + UINT64_C(0xf40e35855771202a), UINT64_C(0x106aa07032bbd1b8), + UINT64_C(0x19a4c116b8d2d0c8), UINT64_C(0x1e376c085141ab53), + UINT64_C(0x2748774cdf8eeb99), UINT64_C(0x34b0bcb5e19b48a8), + UINT64_C(0x391c0cb3c5c95a63), UINT64_C(0x4ed8aa4ae3418acb), + UINT64_C(0x5b9cca4f7763e373), UINT64_C(0x682e6ff3d6b2b8a3), + UINT64_C(0x748f82ee5defb2fc), UINT64_C(0x78a5636f43172f60), + UINT64_C(0x84c87814a1f0ab72), UINT64_C(0x8cc702081a6439ec), + UINT64_C(0x90befffa23631e28), UINT64_C(0xa4506cebde82bde9), + UINT64_C(0xbef9a3f7b2c67915), UINT64_C(0xc67178f2e372532b), + UINT64_C(0xca273eceea26619c), UINT64_C(0xd186b8c721c0c207), + UINT64_C(0xeada7dd6cde0eb1e), UINT64_C(0xf57d4f7fee6ed178), + UINT64_C(0x06f067aa72176fba), UINT64_C(0x0a637dc5a2c898a6), + UINT64_C(0x113f9804bef90dae), UINT64_C(0x1b710b35131c471b), + UINT64_C(0x28db77f523047d84), UINT64_C(0x32caab7b40c72493), + UINT64_C(0x3c9ebe0a15c9bebc), UINT64_C(0x431d67c49c100d4c), + UINT64_C(0x4cc5d4becb3e42b6), UINT64_C(0x597f299cfc657e2a), + UINT64_C(0x5fcb6fab3ad6faec), UINT64_C(0x6c44198c4a475817) }; /* One step of SHA-512/256 computation, @@ -535,38 +559,41 @@ void Curl_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS], * Note: 'wt' must be used exactly one time in this macro as macro for 'wt' calculation may change other data as well every time when 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)); } while (0) +#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); \ + } while(0) /* One step of SHA-512/256 computation with working variables rotation, see FIPS PUB 180-4 section 6.4.2 step 3. This macro version reassigns all working variables on each step. */ -#define SHA2STEP64RV(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \ - curl_uint64_t tmp_h_ = (vH); \ - SHA2STEP64((vA),(vB),(vC),(vD),(vE),(vF),(vG),tmp_h_,(kt),(wt)); \ - (vH) = (vG); \ - (vG) = (vF); \ - (vF) = (vE); \ - (vE) = (vD); \ - (vD) = (vC); \ - (vC) = (vB); \ - (vB) = (vA); \ - (vA) = tmp_h_; } while(0) +#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); \ + (vH) = (vG); \ + (vG) = (vF); \ + (vF) = (vE); \ + (vE) = (vD); \ + (vD) = (vC); \ + (vC) = (vB); \ + (vB) = (vA); \ + (vA) = tmp_h_; \ + } while(0) /* Get value of W(t) from input data buffer for 0 <= t <= 15, See FIPS PUB 180-4 section 6.2. 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 unsigned char*) (buf)) + (t) * SHA512_256_BYTES_IN_WORD) +#define SHA512_GET_W_FROM_DATA(buf, t) \ + 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 stored in the array of W elements. */ for(t = 0; t < 16; ++t) { - SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], \ + SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], W[t] = SHA512_GET_W_FROM_DATA(data, t)); } @@ -575,15 +602,15 @@ void Curl_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS], As only the last 16 'W' are used in calculations, it is possible to use 16 elements array of W as a cyclic buffer. Note: ((t-16) & 15) have same value as (t & 15) */ -#define Wgen(w,t) \ - (curl_uint64_t)( (w)[(t - 16) & 15] + sig1((w)[((t) - 2) & 15]) \ - + (w)[((t) - 7) & 15] + sig0((w)[((t) - 15) & 15]) ) +#define Wgen(w, t) \ + (uint64_t)((w)[((t) - 16) & 15] + sig1((w)[((t) - 2) & 15]) + \ + (w)[((t) - 7) & 15] + sig0((w)[((t) - 15) & 15])) /* During the last 64 steps, before making any calculation on each step, current W element is generated from other W elements of the cyclic buffer and the generated value is stored back in the cyclic buffer. */ for(t = 16; t < 80; ++t) { - SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], \ + SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], W[t & 15] = Wgen(W, t)); } } @@ -600,7 +627,6 @@ void Curl_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS], H[7] += h; } - /** * Process portion of bytes. * @@ -613,10 +639,10 @@ static CURLcode Curl_sha512_256_update(void *context, const unsigned char *data, size_t length) { - unsigned int bytes_have; /**< Number of bytes in the context buffer */ - struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context; + unsigned int bytes_have; /* Number of bytes in the context buffer */ + struct Curl_sha512_256ctx * const ctx = (struct Curl_sha512_256ctx *)context; /* the void pointer here is required to mute Intel compiler warning */ - void *const ctx_buf = ctx->buffer; + void * const ctx_buf = ctx->buffer; DEBUGASSERT((data != NULL) || (length == 0)); @@ -625,21 +651,19 @@ static CURLcode Curl_sha512_256_update(void *context, /* Note: (count & (CURL_SHA512_256_BLOCK_SIZE-1)) equals (count % CURL_SHA512_256_BLOCK_SIZE) for this block size. */ - bytes_have = (unsigned int) (ctx->count & (CURL_SHA512_256_BLOCK_SIZE - 1)); + bytes_have = (unsigned int)(ctx->count & (CURL_SHA512_256_BLOCK_SIZE - 1)); ctx->count += length; if(length > ctx->count) ctx->count_bits_hi += 1U << 3; /* Value wrap */ ctx->count_bits_hi += ctx->count >> 61; - ctx->count &= CURL_UINT64_C(0x1FFFFFFFFFFFFFFF); + ctx->count &= UINT64_C(0x1FFFFFFFFFFFFFFF); if(bytes_have) { unsigned int bytes_left = CURL_SHA512_256_BLOCK_SIZE - bytes_have; if(length >= bytes_left) { /* Combine new data with data in the buffer and process the full block. */ - memcpy(((unsigned char *) ctx_buf) + bytes_have, - data, - bytes_left); + memcpy((unsigned char *)ctx_buf + bytes_have, data, bytes_left); data += bytes_left; length -= bytes_left; Curl_sha512_256_transform(ctx->H, ctx->buffer); @@ -658,13 +682,12 @@ static CURLcode Curl_sha512_256_update(void *context, if(length) { /* Copy incomplete block of new data (if any) to the buffer. */ - memcpy(((unsigned char *) ctx_buf) + bytes_have, data, length); + memcpy((unsigned char *)ctx_buf + bytes_have, data, length); } return CURLE_OK; } - /** * Size of "length" insertion in bits. * See FIPS PUB 180-4 section 5.1.2. @@ -681,16 +704,16 @@ 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) { - struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context; - curl_uint64_t num_bits; /**< Number of processed bits */ - unsigned int bytes_have; /**< Number of bytes in the context buffer */ + struct Curl_sha512_256ctx * const ctx = (struct Curl_sha512_256ctx *)context; + uint64_t num_bits; /* Number of processed bits */ + unsigned int bytes_have; /* Number of bytes in the context buffer */ /* the void pointer here is required to mute Intel compiler warning */ - void *const ctx_buf = ctx->buffer; + void * const ctx_buf = ctx->buffer; /* Memorise the number of processed bits. The padding and other data added here during the postprocessing must @@ -699,7 +722,7 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) /* Note: (count & (CURL_SHA512_256_BLOCK_SIZE-1)) equals (count % CURL_SHA512_256_BLOCK_SIZE) for this block size. */ - bytes_have = (unsigned int) (ctx->count & (CURL_SHA512_256_BLOCK_SIZE - 1)); + bytes_have = (unsigned int)(ctx->count & (CURL_SHA512_256_BLOCK_SIZE - 1)); /* Input data must be padded with a single bit "1", then with zeros and the finally the length of data in bits must be added as the final bytes @@ -711,13 +734,13 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) predefined (0x80). */ /* Buffer always have space at least for one byte (as full buffers are processed when formed). */ - ((unsigned char *) ctx_buf)[bytes_have++] = 0x80U; + ((unsigned char *)ctx_buf)[bytes_have++] = 0x80U; if(CURL_SHA512_256_BLOCK_SIZE - bytes_have < SHA512_256_SIZE_OF_LEN_ADD) { /* No space in the current block to put the total length of message. Pad the current block with zeros and process it. */ if(bytes_have < CURL_SHA512_256_BLOCK_SIZE) - memset(((unsigned char *) ctx_buf) + bytes_have, 0, + memset((unsigned char *)ctx_buf + bytes_have, 0, CURL_SHA512_256_BLOCK_SIZE - bytes_have); /* Process the full block. */ Curl_sha512_256_transform(ctx->H, ctx->buffer); @@ -726,31 +749,29 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) } /* Pad the rest of the buffer with zeros. */ - memset(((unsigned char *) ctx_buf) + bytes_have, 0, + memset((unsigned char *)ctx_buf + bytes_have, 0, CURL_SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD - bytes_have); /* Put high part of number of bits in processed message and then lower part of number of bits as big-endian values. See FIPS PUB 180-4 section 5.1.2. */ /* Note: the target location is predefined and buffer is always aligned */ - CURL_PUT_64BIT_BE(((unsigned char *) ctx_buf) \ - + CURL_SHA512_256_BLOCK_SIZE \ - - SHA512_256_SIZE_OF_LEN_ADD, \ - ctx->count_bits_hi); - CURL_PUT_64BIT_BE(((unsigned char *) ctx_buf) \ - + CURL_SHA512_256_BLOCK_SIZE \ - - SHA512_256_SIZE_OF_LEN_ADD \ - + SHA512_256_BYTES_IN_WORD, \ - num_bits); + CURL_PUT_64BIT_BE((unsigned char *)ctx_buf + + CURL_SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD, + ctx->count_bits_hi); + CURL_PUT_64BIT_BE((unsigned char *)ctx_buf + + CURL_SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD + + SHA512_256_BYTES_IN_WORD, + num_bits); /* Process the full final block. */ Curl_sha512_256_transform(ctx->H, ctx->buffer); /* Put in BE mode the leftmost part of the hash as the final digest. See FIPS PUB 180-4 section 6.7. */ - 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)); @@ -760,7 +781,6 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) #endif /* Local SHA-512/256 code */ - /** * Compute SHA-512/256 hash for the given data in one function call * @param[out] output the pointer to put the hash @@ -778,7 +798,7 @@ CURLcode Curl_sha512_256it(unsigned char *output, const unsigned char *input, if(res != CURLE_OK) return res; - res = Curl_sha512_256_update(&ctx, (const void *) input, input_size); + res = Curl_sha512_256_update(&ctx, (const void *)input, input_size); if(res != CURLE_OK) { (void)Curl_sha512_256_finish(output, &ctx); diff --git a/lib/curl_sha512_256.h b/lib/curl_sha512_256.h index a84e77bc30..ddaf8be91d 100644 --- a/lib/curl_sha512_256.h +++ b/lib/curl_sha512_256.h @@ -23,10 +23,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" #if !defined(CURL_DISABLE_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256) -#include #include "curl_hmac.h" #define CURL_HAVE_SHA512_256 @@ -35,9 +35,8 @@ extern const struct HMAC_params Curl_HMAC_SHA512_256[1]; #define CURL_SHA512_256_DIGEST_LENGTH 32 -CURLcode -Curl_sha512_256it(unsigned char *output, const unsigned char *input, - size_t input_size); +CURLcode Curl_sha512_256it(unsigned char *output, const unsigned char *input, + size_t input_size); #endif /* !CURL_DISABLE_DIGEST_AUTH && !CURL_DISABLE_SHA512_256 */ diff --git a/lib/share.c b/lib/curl_share.c similarity index 55% rename from lib/share.c rename to lib/curl_share.c index d1ab55eb27..386a2b547d 100644 --- a/lib/share.c +++ b/lib/curl_share.c @@ -21,35 +21,64 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include #include "urldata.h" -#include "connect.h" -#include "share.h" -#include "psl.h" +#include "multiif.h" +#include "curl_threads.h" +#include "curl_share.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" #include "hsts.h" #include "url.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -CURLSH * -curl_share_init(void) +static void share_destroy(struct Curl_share *share) { - struct Curl_share *share = calloc(1, sizeof(struct Curl_share)); + if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) { + Curl_cpool_destroy(&share->cpool); + } + + Curl_dnscache_destroy(&share->dnscache); + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + Curl_cookie_cleanup(share->cookies); +#endif + +#ifndef CURL_DISABLE_HSTS + Curl_hsts_cleanup(&share->hsts); +#endif + +#ifdef USE_SSL + if(share->ssl_scache) { + Curl_ssl_scache_destroy(share->ssl_scache); + share->ssl_scache = NULL; + } +#endif + + Curl_psl_destroy(&share->psl); + Curl_close(&share->admin); + +#ifdef USE_MUTEX + Curl_mutex_destroy(&share->lock); +#endif + share->magic = 0; + curlx_free(share); +} + +CURLSH *curl_share_init(void) +{ + struct Curl_share *share = curlx_calloc(1, sizeof(struct Curl_share)); if(share) { share->magic = CURL_GOOD_SHARE; share->specifier |= (1 << CURL_LOCK_DATA_SHARE); +#ifdef USE_MUTEX + Curl_mutex_init(&share->lock); +#endif + share->ref_count = 1; Curl_dnscache_init(&share->dnscache, 23); share->admin = curl_easy_init(); if(!share->admin) { - free(share); + share_destroy(share); return NULL; } /* admin handles have mid 0 */ @@ -60,13 +89,106 @@ curl_share_init(void) share->admin->set.verbose = TRUE; #endif } - return share; } +static uint32_t share_ref_inc(struct Curl_share *share) +{ + uint32_t n; +#ifdef USE_MUTEX + Curl_mutex_acquire(&share->lock); + n = ++(share->ref_count); + share->has_been_shared = TRUE; + Curl_mutex_release(&share->lock); +#else + n = ++(share->ref_count); + share->has_been_shared = TRUE; +#endif + return n; +} + +static uint32_t share_ref_dec(struct Curl_share *share) +{ + uint32_t n; +#ifdef USE_MUTEX + Curl_mutex_acquire(&share->lock); + DEBUGASSERT(share->ref_count); + n = --(share->ref_count); + Curl_mutex_release(&share->lock); +#else + n = --(share->ref_count); +#endif + return n; +} + +static bool share_has_been_shared(struct Curl_share *share) +{ + bool was_shared; +#ifdef USE_MUTEX + Curl_mutex_acquire(&share->lock); + was_shared = share->has_been_shared; + Curl_mutex_release(&share->lock); +#else + was_shared = share->has_been_shared; +#endif + return was_shared; +} + +static bool share_lock_acquire(struct Curl_share *share, + struct Curl_easy *data) +{ + if(share->lockfunc && share->unlockfunc && + (data || share_has_been_shared(share))) { + share->lockfunc(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE, + share->clientdata); + return TRUE; + } + return FALSE; +} + +static void share_lock_release(struct Curl_share *share, + struct Curl_easy *data, + bool locked) +{ + if(locked) { + DEBUGASSERT(share->unlockfunc); + if(share->unlockfunc) + share->unlockfunc(data, CURL_LOCK_DATA_SHARE, share->clientdata); + } +} + +static bool share_in_use(struct Curl_share *share) +{ + bool in_use; +#ifdef USE_MUTEX + Curl_mutex_acquire(&share->lock); + in_use = (share->ref_count > 1); + Curl_mutex_release(&share->lock); +#else + bool locked = share_lock_acquire(share, NULL); + in_use = (share->ref_count > 1); + share_lock_release(share, NULL, locked); +#endif + return in_use; +} + +static void share_unlink(struct Curl_share **pshare, + struct Curl_easy *data, + bool locked) +{ + struct Curl_share *share = *pshare; + uint32_t n; + + *pshare = NULL; + n = share_ref_dec(share); + if(locked) + share_lock_release(share, data, locked); + if(!n) /* last reference gone */ + share_destroy(share); +} + #undef curl_share_setopt -CURLSHcode -curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) +CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) { va_list param; int type; @@ -79,10 +201,11 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) if(!GOOD_SHARE_HANDLE(share)) return CURLSHE_INVALID; - if(share->dirty) + if(share_in_use(share)) { /* do not allow setting options while one or more handles are already using this share */ return CURLSHE_IN_USE; + } va_start(param, option); @@ -98,11 +221,11 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) case CURL_LOCK_DATA_COOKIE: #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) if(!share->cookies) { - share->cookies = Curl_cookie_init(NULL, NULL, NULL, TRUE); + share->cookies = Curl_cookie_init(); if(!share->cookies) res = CURLSHE_NOMEM; } -#else /* CURL_DISABLE_HTTP */ +#else /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */ res = CURLSHE_NOT_BUILT_IN; #endif break; @@ -114,7 +237,7 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) if(!share->hsts) res = CURLSHE_NOMEM; } -#else /* CURL_DISABLE_HSTS */ +#else /* CURL_DISABLE_HSTS */ res = CURLSHE_NOT_BUILT_IN; #endif break; @@ -125,8 +248,8 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) /* There is no way (yet) for the application to configure the * session cache size, shared between many transfers. As for curl * itself, a high session count will impact startup time. Also, the - * scache is not optimized for several hundreds of peers. So, - * keep it at a reasonable level. */ + * scache is not optimized for several hundreds of peers. + * Keep it at a reasonable level. */ if(Curl_ssl_scache_create(25, 2, &share->ssl_scache)) res = CURLSHE_NOMEM; } @@ -158,7 +281,6 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) case CURLSHOPT_UNSHARE: /* this is a type this share will no longer share */ type = va_arg(param, int); - share->specifier &= ~(unsigned int)(1 << type); switch(type) { case CURL_LOCK_DATA_DNS: break; @@ -169,7 +291,7 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) Curl_cookie_cleanup(share->cookies); share->cookies = NULL; } -#else /* CURL_DISABLE_HTTP */ +#else /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */ res = CURLSHE_NOT_BUILT_IN; #endif break; @@ -179,7 +301,7 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) if(share->hsts) { Curl_hsts_cleanup(&share->hsts); } -#else /* CURL_DISABLE_HSTS */ +#else /* CURL_DISABLE_HSTS */ res = CURLSHE_NOT_BUILT_IN; #endif break; @@ -202,6 +324,8 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) res = CURLSHE_BAD_OPTION; break; } + if(!res) + share->specifier &= ~(unsigned int)(1 << type); break; case CURLSHOPT_LOCKFUNC: @@ -229,59 +353,23 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) return res; } -CURLSHcode -curl_share_cleanup(CURLSH *sh) +CURLSHcode curl_share_cleanup(CURLSH *sh) { struct Curl_share *share = sh; + bool locked; if(!GOOD_SHARE_HANDLE(share)) return CURLSHE_INVALID; - if(share->lockfunc) - share->lockfunc(NULL, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE, - share->clientdata); - - if(share->dirty) { - if(share->unlockfunc) - share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata); + if(share_in_use(share)) return CURLSHE_IN_USE; - } - - if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) { - Curl_cpool_destroy(&share->cpool); - } - - Curl_dnscache_destroy(&share->dnscache); - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - Curl_cookie_cleanup(share->cookies); -#endif - -#ifndef CURL_DISABLE_HSTS - Curl_hsts_cleanup(&share->hsts); -#endif - -#ifdef USE_SSL - if(share->ssl_scache) { - Curl_ssl_scache_destroy(share->ssl_scache); - share->ssl_scache = NULL; - } -#endif - - Curl_psl_destroy(&share->psl); - Curl_close(&share->admin); - - if(share->unlockfunc) - share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata); - share->magic = 0; - free(share); + locked = share_lock_acquire(share, NULL); + share_unlink(&share, NULL, locked); return CURLSHE_OK; } - -CURLSHcode -Curl_share_lock(struct Curl_easy *data, curl_lock_data type, - curl_lock_access accesstype) +CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, + curl_lock_access accesstype) { struct Curl_share *share = data->share; @@ -297,8 +385,7 @@ Curl_share_lock(struct Curl_easy *data, curl_lock_data type, return CURLSHE_OK; } -CURLSHcode -Curl_share_unlock(struct Curl_easy *data, curl_lock_data type) +CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type) { struct Curl_share *share = data->share; @@ -307,8 +394,79 @@ Curl_share_unlock(struct Curl_easy *data, curl_lock_data type) if(share->specifier & (unsigned int)(1 << type)) { if(share->unlockfunc) /* only call this if set! */ - share->unlockfunc (data, type, share->clientdata); + share->unlockfunc(data, type, share->clientdata); } return CURLSHE_OK; } + +CURLcode Curl_share_easy_unlink(struct Curl_easy *data) +{ + struct Curl_share *share = data->share; + + if(share) { + bool locked = share_lock_acquire(share, data); + + /* If data has a connection from this share, detach it. */ + if(data->conn && (share->specifier & (1 << CURL_LOCK_DATA_CONNECT))) + Curl_detach_connection(data); + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + if(share->cookies == data->cookies) + data->cookies = NULL; +#endif + +#ifndef CURL_DISABLE_HSTS + if(share->hsts == data->hsts) + data->hsts = NULL; +#endif +#ifdef USE_LIBPSL + if(&share->psl == data->psl) + data->psl = data->multi ? &data->multi->psl : NULL; +#endif + + share_unlink(&data->share, data, locked); + } + return CURLE_OK; +} + +CURLcode Curl_share_easy_link(struct Curl_easy *data, + struct Curl_share *share) +{ + if(data->share) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + + if(share) { + bool locked = share_lock_acquire(share, data); + + share_ref_inc(share); + data->share = share; + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + if(share->cookies) { + /* use shared cookie list, first free own one if any */ + Curl_cookie_cleanup(data->cookies); + /* enable cookies since we now use a share that uses cookies! */ + data->cookies = share->cookies; + } +#endif /* CURL_DISABLE_HTTP */ +#ifndef CURL_DISABLE_HSTS + if(share->hsts) { + /* first free the private one if any */ + Curl_hsts_cleanup(&data->hsts); + data->hsts = share->hsts; + } +#endif +#ifdef USE_LIBPSL + if(share->specifier & (1 << CURL_LOCK_DATA_PSL)) + data->psl = &share->psl; +#endif + + /* check for host cache not needed, + * it will be done by curl_easy_perform */ + share_lock_release(share, data, locked); + } + return CURLE_OK; +} diff --git a/lib/share.h b/lib/curl_share.h similarity index 66% rename from lib/share.h rename to lib/curl_share.h index 974c99dc20..69001be705 100644 --- a/lib/share.h +++ b/lib/curl_share.h @@ -23,9 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include + +#include "curl_threads.h" #include "cookie.h" #include "psl.h" #include "urldata.h" @@ -37,19 +37,29 @@ 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 { unsigned int magic; /* CURL_GOOD_SHARE */ unsigned int specifier; - volatile unsigned int dirty; + uint32_t ref_count; +#ifdef USE_MUTEX + /* do `ref_count` and `has_been_shared` checks using this mutex. */ + curl_mutex_t lock; + int has_been_shared; +#else + /* this only ever goes from FALSE -> TRUE once. We need to check + * this without being able to use the `lockfunc`. */ + volatile int has_been_shared; +#endif curl_lock_function lockfunc; curl_unlock_function unlockfunc; void *clientdata; struct Curl_easy *admin; + struct cpool cpool; struct Curl_dnscache dnscache; /* DNS cache */ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) @@ -66,13 +76,17 @@ 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 & \ - (1<share && \ + ((data)->share->specifier & \ + (1 << CURL_LOCK_DATA_SSL_SESSION))) + +CURLcode Curl_share_easy_unlink(struct Curl_easy *data); +CURLcode Curl_share_easy_link(struct Curl_easy *data, + struct Curl_share *share); #endif /* HEADER_CURL_SHARE_H */ diff --git a/lib/curl_sspi.c b/lib/curl_sspi.c index 635f560b68..3ea17621b1 100644 --- a/lib/curl_sspi.c +++ b/lib/curl_sspi.c @@ -21,21 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef USE_WINDOWS_SSPI -#include #include "curl_sspi.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "curlx/multibyte.h" -#include "system_win32.h" -#include "curlx/warnless.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" /* Pointer to SSPI dispatch table */ PSecurityFunctionTable Curl_pSecFn = NULL; @@ -62,11 +54,7 @@ CURLcode Curl_sspi_global_init(void) /* If security interface is not yet initialized try to do this */ if(!Curl_pSecFn) { /* Get pointer to Security Service Provider Interface dispatch table */ -#ifdef __MINGW32CE__ - Curl_pSecFn = InitSecurityInterfaceW(); -#else Curl_pSecFn = InitSecurityInterface(); -#endif if(!Curl_pSecFn) return CURLE_FAILED_INIT; } @@ -138,43 +126,54 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, } /* Setup the identity's user and length */ - dup_user.tchar_ptr = _tcsdup(user.tchar_ptr); + dup_user.tchar_ptr = curlx_tcsdup(user.tchar_ptr); if(!dup_user.tchar_ptr) { - curlx_unicodefree(useranddomain.tchar_ptr); + curlx_free(useranddomain.tchar_ptr); return CURLE_OUT_OF_MEMORY; } - identity->User = dup_user.tbyte_ptr; - identity->UserLength = curlx_uztoul(_tcslen(dup_user.tchar_ptr)); - dup_user.tchar_ptr = NULL; /* Setup the identity's domain and length */ - dup_domain.tchar_ptr = malloc(sizeof(TCHAR) * (domlen + 1)); + dup_domain.tchar_ptr = curlx_malloc(sizeof(TCHAR) * (domlen + 1)); if(!dup_domain.tchar_ptr) { - curlx_unicodefree(useranddomain.tchar_ptr); + curlx_free(dup_user.tchar_ptr); + curlx_free(useranddomain.tchar_ptr); + return CURLE_OUT_OF_MEMORY; + } + if(_tcsncpy_s(dup_domain.tchar_ptr, domlen + 1, domain.tchar_ptr, domlen)) { + curlx_free(dup_user.tchar_ptr); + curlx_free(dup_domain.tchar_ptr); + curlx_free(useranddomain.tchar_ptr); return CURLE_OUT_OF_MEMORY; } - _tcsncpy(dup_domain.tchar_ptr, domain.tchar_ptr, domlen); - *(dup_domain.tchar_ptr + domlen) = TEXT('\0'); - identity->Domain = dup_domain.tbyte_ptr; - identity->DomainLength = curlx_uztoul(domlen); - dup_domain.tchar_ptr = NULL; - curlx_unicodefree(useranddomain.tchar_ptr); + curlx_free(useranddomain.tchar_ptr); /* Setup the identity's password and length */ passwd.tchar_ptr = curlx_convert_UTF8_to_tchar(passwdp); - if(!passwd.tchar_ptr) + if(!passwd.tchar_ptr) { + curlx_free(dup_user.tchar_ptr); + curlx_free(dup_domain.tchar_ptr); return CURLE_OUT_OF_MEMORY; - dup_passwd.tchar_ptr = _tcsdup(passwd.tchar_ptr); + } + dup_passwd.tchar_ptr = curlx_tcsdup(passwd.tchar_ptr); if(!dup_passwd.tchar_ptr) { - curlx_unicodefree(passwd.tchar_ptr); + curlx_free(dup_user.tchar_ptr); + curlx_free(dup_domain.tchar_ptr); + curlx_free(passwd.tchar_ptr); return CURLE_OUT_OF_MEMORY; } identity->Password = dup_passwd.tbyte_ptr; identity->PasswordLength = curlx_uztoul(_tcslen(dup_passwd.tchar_ptr)); dup_passwd.tchar_ptr = NULL; - curlx_unicodefree(passwd.tchar_ptr); + curlx_free(passwd.tchar_ptr); + + identity->User = dup_user.tbyte_ptr; + identity->UserLength = curlx_uztoul(_tcslen(dup_user.tchar_ptr)); + dup_user.tchar_ptr = NULL; + identity->Domain = dup_domain.tbyte_ptr; + identity->DomainLength = curlx_uztoul(domlen); + dup_domain.tchar_ptr = NULL; /* Setup the identity's flags */ identity->Flags = (unsigned long) @@ -199,9 +198,9 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity) { if(identity) { - Curl_safefree(identity->User); - Curl_safefree(identity->Password); - Curl_safefree(identity->Domain); + curlx_safefree(identity->User); + curlx_safefree(identity->Password); + curlx_safefree(identity->Domain); } } diff --git a/lib/curl_sspi.h b/lib/curl_sspi.h index 8ecd81fdea..3779d51753 100644 --- a/lib/curl_sspi.h +++ b/lib/curl_sspi.h @@ -23,24 +23,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef USE_WINDOWS_SSPI -#include - -/* - * 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); @@ -59,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" @@ -70,225 +57,6 @@ extern PSecurityFunctionTable Curl_pSecFn; #define ISC_REQ_USE_HTTP_STYLE 0x01000000 #endif -#ifdef __MINGW32CE__ -#ifndef ISC_RET_REPLAY_DETECT -#define ISC_RET_REPLAY_DETECT 0x00000004 -#endif -#ifndef ISC_RET_SEQUENCE_DETECT -#define ISC_RET_SEQUENCE_DETECT 0x00000008 -#endif -#ifndef ISC_RET_CONFIDENTIALITY -#define ISC_RET_CONFIDENTIALITY 0x00000010 -#endif -#ifndef ISC_RET_ALLOCATED_MEMORY -#define ISC_RET_ALLOCATED_MEMORY 0x00000100 -#endif -#ifndef ISC_RET_STREAM -#define ISC_RET_STREAM 0x00008000 -#endif - -#ifndef SEC_E_INSUFFICIENT_MEMORY -#define SEC_E_INSUFFICIENT_MEMORY ((HRESULT)0x80090300L) -#endif -#ifndef SEC_E_INVALID_HANDLE -#define SEC_E_INVALID_HANDLE ((HRESULT)0x80090301L) -#endif -#ifndef SEC_E_UNSUPPORTED_FUNCTION -#define SEC_E_UNSUPPORTED_FUNCTION ((HRESULT)0x80090302L) -#endif -#ifndef SEC_E_TARGET_UNKNOWN -#define SEC_E_TARGET_UNKNOWN ((HRESULT)0x80090303L) -#endif -#ifndef SEC_E_INTERNAL_ERROR -#define SEC_E_INTERNAL_ERROR ((HRESULT)0x80090304L) -#endif -#ifndef SEC_E_SECPKG_NOT_FOUND -#define SEC_E_SECPKG_NOT_FOUND ((HRESULT)0x80090305L) -#endif -#ifndef SEC_E_NOT_OWNER -#define SEC_E_NOT_OWNER ((HRESULT)0x80090306L) -#endif -#ifndef SEC_E_CANNOT_INSTALL -#define SEC_E_CANNOT_INSTALL ((HRESULT)0x80090307L) -#endif -#ifndef SEC_E_INVALID_TOKEN -#define SEC_E_INVALID_TOKEN ((HRESULT)0x80090308L) -#endif -#ifndef SEC_E_CANNOT_PACK -#define SEC_E_CANNOT_PACK ((HRESULT)0x80090309L) -#endif -#ifndef SEC_E_QOP_NOT_SUPPORTED -#define SEC_E_QOP_NOT_SUPPORTED ((HRESULT)0x8009030AL) -#endif -#ifndef SEC_E_NO_IMPERSONATION -#define SEC_E_NO_IMPERSONATION ((HRESULT)0x8009030BL) -#endif -#ifndef SEC_E_LOGON_DENIED -#define SEC_E_LOGON_DENIED ((HRESULT)0x8009030CL) -#endif -#ifndef SEC_E_UNKNOWN_CREDENTIALS -#define SEC_E_UNKNOWN_CREDENTIALS ((HRESULT)0x8009030DL) -#endif -#ifndef SEC_E_NO_CREDENTIALS -#define SEC_E_NO_CREDENTIALS ((HRESULT)0x8009030EL) -#endif -#ifndef SEC_E_MESSAGE_ALTERED -#define SEC_E_MESSAGE_ALTERED ((HRESULT)0x8009030FL) -#endif -#ifndef SEC_E_OUT_OF_SEQUENCE -#define SEC_E_OUT_OF_SEQUENCE ((HRESULT)0x80090310L) -#endif -#ifndef SEC_E_NO_AUTHENTICATING_AUTHORITY -#define SEC_E_NO_AUTHENTICATING_AUTHORITY ((HRESULT)0x80090311L) -#endif -#ifndef SEC_E_BAD_PKGID -#define SEC_E_BAD_PKGID ((HRESULT)0x80090316L) -#endif -#ifndef SEC_E_CONTEXT_EXPIRED -#define SEC_E_CONTEXT_EXPIRED ((HRESULT)0x80090317L) -#endif -#ifndef SEC_E_INCOMPLETE_MESSAGE -#define SEC_E_INCOMPLETE_MESSAGE ((HRESULT)0x80090318L) -#endif -#ifndef SEC_E_INCOMPLETE_CREDENTIALS -#define SEC_E_INCOMPLETE_CREDENTIALS ((HRESULT)0x80090320L) -#endif -#ifndef SEC_E_BUFFER_TOO_SMALL -#define SEC_E_BUFFER_TOO_SMALL ((HRESULT)0x80090321L) -#endif -#ifndef SEC_E_WRONG_PRINCIPAL -#define SEC_E_WRONG_PRINCIPAL ((HRESULT)0x80090322L) -#endif -#ifndef SEC_E_TIME_SKEW -#define SEC_E_TIME_SKEW ((HRESULT)0x80090324L) -#endif -#ifndef SEC_E_UNTRUSTED_ROOT -#define SEC_E_UNTRUSTED_ROOT ((HRESULT)0x80090325L) -#endif -#ifndef SEC_E_ILLEGAL_MESSAGE -#define SEC_E_ILLEGAL_MESSAGE ((HRESULT)0x80090326L) -#endif -#ifndef SEC_E_CERT_UNKNOWN -#define SEC_E_CERT_UNKNOWN ((HRESULT)0x80090327L) -#endif -#ifndef SEC_E_CERT_EXPIRED -#define SEC_E_CERT_EXPIRED ((HRESULT)0x80090328L) -#endif -#ifndef SEC_E_ENCRYPT_FAILURE -#define SEC_E_ENCRYPT_FAILURE ((HRESULT)0x80090329L) -#endif -#ifndef SEC_E_DECRYPT_FAILURE -#define SEC_E_DECRYPT_FAILURE ((HRESULT)0x80090330L) -#endif -#ifndef SEC_E_ALGORITHM_MISMATCH -#define SEC_E_ALGORITHM_MISMATCH ((HRESULT)0x80090331L) -#endif -#ifndef SEC_E_SECURITY_QOS_FAILED -#define SEC_E_SECURITY_QOS_FAILED ((HRESULT)0x80090332L) -#endif -#ifndef SEC_E_UNFINISHED_CONTEXT_DELETED -#define SEC_E_UNFINISHED_CONTEXT_DELETED ((HRESULT)0x80090333L) -#endif -#ifndef SEC_E_NO_TGT_REPLY -#define SEC_E_NO_TGT_REPLY ((HRESULT)0x80090334L) -#endif -#ifndef SEC_E_NO_IP_ADDRESSES -#define SEC_E_NO_IP_ADDRESSES ((HRESULT)0x80090335L) -#endif -#ifndef SEC_E_WRONG_CREDENTIAL_HANDLE -#define SEC_E_WRONG_CREDENTIAL_HANDLE ((HRESULT)0x80090336L) -#endif -#ifndef SEC_E_CRYPTO_SYSTEM_INVALID -#define SEC_E_CRYPTO_SYSTEM_INVALID ((HRESULT)0x80090337L) -#endif -#ifndef SEC_E_MAX_REFERRALS_EXCEEDED -#define SEC_E_MAX_REFERRALS_EXCEEDED ((HRESULT)0x80090338L) -#endif -#ifndef SEC_E_MUST_BE_KDC -#define SEC_E_MUST_BE_KDC ((HRESULT)0x80090339L) -#endif -#ifndef SEC_E_STRONG_CRYPTO_NOT_SUPPORTED -#define SEC_E_STRONG_CRYPTO_NOT_SUPPORTED ((HRESULT)0x8009033AL) -#endif -#ifndef SEC_E_TOO_MANY_PRINCIPALS -#define SEC_E_TOO_MANY_PRINCIPALS ((HRESULT)0x8009033BL) -#endif -#ifndef SEC_E_NO_PA_DATA -#define SEC_E_NO_PA_DATA ((HRESULT)0x8009033CL) -#endif -#ifndef SEC_E_PKINIT_NAME_MISMATCH -#define SEC_E_PKINIT_NAME_MISMATCH ((HRESULT)0x8009033DL) -#endif -#ifndef SEC_E_SMARTCARD_LOGON_REQUIRED -#define SEC_E_SMARTCARD_LOGON_REQUIRED ((HRESULT)0x8009033EL) -#endif -#ifndef SEC_E_SHUTDOWN_IN_PROGRESS -#define SEC_E_SHUTDOWN_IN_PROGRESS ((HRESULT)0x8009033FL) -#endif -#ifndef SEC_E_KDC_INVALID_REQUEST -#define SEC_E_KDC_INVALID_REQUEST ((HRESULT)0x80090340L) -#endif -#ifndef SEC_E_KDC_UNABLE_TO_REFER -#define SEC_E_KDC_UNABLE_TO_REFER ((HRESULT)0x80090341L) -#endif -#ifndef SEC_E_KDC_UNKNOWN_ETYPE -#define SEC_E_KDC_UNKNOWN_ETYPE ((HRESULT)0x80090342L) -#endif -#ifndef SEC_E_UNSUPPORTED_PREAUTH -#define SEC_E_UNSUPPORTED_PREAUTH ((HRESULT)0x80090343L) -#endif -#ifndef SEC_E_DELEGATION_REQUIRED -#define SEC_E_DELEGATION_REQUIRED ((HRESULT)0x80090345L) -#endif -#ifndef SEC_E_BAD_BINDINGS -#define SEC_E_BAD_BINDINGS ((HRESULT)0x80090346L) -#endif -#ifndef SEC_E_MULTIPLE_ACCOUNTS -#define SEC_E_MULTIPLE_ACCOUNTS ((HRESULT)0x80090347L) -#endif -#ifndef SEC_E_NO_KERB_KEY -#define SEC_E_NO_KERB_KEY ((HRESULT)0x80090348L) -#endif -#ifndef SEC_E_CERT_WRONG_USAGE -#define SEC_E_CERT_WRONG_USAGE ((HRESULT)0x80090349L) -#endif -#ifndef SEC_E_DOWNGRADE_DETECTED -#define SEC_E_DOWNGRADE_DETECTED ((HRESULT)0x80090350L) -#endif -#ifndef SEC_E_SMARTCARD_CERT_REVOKED -#define SEC_E_SMARTCARD_CERT_REVOKED ((HRESULT)0x80090351L) -#endif -#ifndef SEC_E_ISSUING_CA_UNTRUSTED -#define SEC_E_ISSUING_CA_UNTRUSTED ((HRESULT)0x80090352L) -#endif -#ifndef SEC_E_REVOCATION_OFFLINE_C -#define SEC_E_REVOCATION_OFFLINE_C ((HRESULT)0x80090353L) -#endif -#ifndef SEC_E_PKINIT_CLIENT_FAILURE -#define SEC_E_PKINIT_CLIENT_FAILURE ((HRESULT)0x80090354L) -#endif -#ifndef SEC_E_SMARTCARD_CERT_EXPIRED -#define SEC_E_SMARTCARD_CERT_EXPIRED ((HRESULT)0x80090355L) -#endif -#ifndef SEC_E_NO_S4U_PROT_SUPPORT -#define SEC_E_NO_S4U_PROT_SUPPORT ((HRESULT)0x80090356L) -#endif -#ifndef SEC_E_CROSSREALM_DELEGATION_FAILURE -#define SEC_E_CROSSREALM_DELEGATION_FAILURE ((HRESULT)0x80090357L) -#endif -#ifndef SEC_E_REVOCATION_OFFLINE_KDC -#define SEC_E_REVOCATION_OFFLINE_KDC ((HRESULT)0x80090358L) -#endif -#ifndef SEC_E_ISSUING_CA_UNTRUSTED_KDC -#define SEC_E_ISSUING_CA_UNTRUSTED_KDC ((HRESULT)0x80090359L) -#endif -#ifndef SEC_E_KDC_CERT_EXPIRED -#define SEC_E_KDC_CERT_EXPIRED ((HRESULT)0x8009035AL) -#endif -#ifndef SEC_E_KDC_CERT_REVOKED -#define SEC_E_KDC_CERT_REVOKED ((HRESULT)0x8009035BL) -#endif -#endif /* __MINGW32CE__ */ /* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ #ifndef SEC_E_INVALID_PARAMETER #define SEC_E_INVALID_PARAMETER ((HRESULT)0x8009035DL) @@ -302,44 +70,11 @@ extern PSecurityFunctionTable Curl_pSecFn; #define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL) #endif -#ifdef __MINGW32CE__ -#ifndef SEC_I_CONTINUE_NEEDED -#define SEC_I_CONTINUE_NEEDED ((HRESULT)0x00090312L) -#endif -#ifndef SEC_I_COMPLETE_NEEDED -#define SEC_I_COMPLETE_NEEDED ((HRESULT)0x00090313L) -#endif -#ifndef SEC_I_COMPLETE_AND_CONTINUE -#define SEC_I_COMPLETE_AND_CONTINUE ((HRESULT)0x00090314L) -#endif -#ifndef SEC_I_LOCAL_LOGON -#define SEC_I_LOCAL_LOGON ((HRESULT)0x00090315L) -#endif -#ifndef SEC_I_CONTEXT_EXPIRED -#define SEC_I_CONTEXT_EXPIRED ((HRESULT)0x00090317L) -#endif -#ifndef SEC_I_INCOMPLETE_CREDENTIALS -#define SEC_I_INCOMPLETE_CREDENTIALS ((HRESULT)0x00090320L) -#endif -#ifndef SEC_I_RENEGOTIATE -#define SEC_I_RENEGOTIATE ((HRESULT)0x00090321L) -#endif -#ifndef SEC_I_NO_LSA_CONTEXT -#define SEC_I_NO_LSA_CONTEXT ((HRESULT)0x00090323L) -#endif -#endif /* __MINGW32CE__ */ - /* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ #ifndef SEC_I_SIGNATURE_NEEDED #define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL) #endif -#ifdef __MINGW32CE__ -#ifndef CRYPT_E_NOT_IN_REVOCATION_DATABASE -#define CRYPT_E_NOT_IN_REVOCATION_DATABASE ((HRESULT)0x80092014L) -#endif -#endif /* __MINGW32CE__ */ - /* * Definitions required from ntsecapi.h are directly provided below this point * to avoid including ntsecapi.h due to a conflict with OpenSSL's safestack.h diff --git a/lib/curl_threads.c b/lib/curl_threads.c index 2750f5ad9f..01041f63c3 100644 --- a/lib/curl_threads.c +++ b/lib/curl_threads.c @@ -21,25 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" - -#include - -#ifdef USE_THREADS_POSIX -# ifdef HAVE_PTHREAD_H -# include -# endif -#elif defined(USE_THREADS_WIN32) -# include -#endif - #include "curl_threads.h" -#include "curl_memory.h" -/* The last #include FILE should be: */ -#include "memdebug.h" +#include "curlx/timeval.h" -#ifdef USE_THREADS_POSIX +#ifdef USE_THREADS + +#ifdef HAVE_THREADS_POSIX struct Curl_actual_call { unsigned int (*func)(void *); @@ -52,32 +40,39 @@ static void *curl_thread_create_thunk(void *arg) unsigned int (*func)(void *) = ac->func; void *real_arg = ac->arg; - free(ac); + curlx_free(ac); (*func)(real_arg); return 0; } -curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T - (CURL_STDCALL *func) (void *), void *arg) +curl_thread_t Curl_thread_create( + CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg) { - curl_thread_t t = malloc(sizeof(pthread_t)); - struct Curl_actual_call *ac = malloc(sizeof(struct Curl_actual_call)); + curl_thread_t t = curlx_malloc(sizeof(pthread_t)); + struct Curl_actual_call *ac = NULL; + int rc; + + if(t) + ac = curlx_malloc(sizeof(struct Curl_actual_call)); if(!(ac && t)) goto err; ac->func = func; ac->arg = arg; - if(pthread_create(t, NULL, curl_thread_create_thunk, ac) != 0) + rc = pthread_create(t, NULL, curl_thread_create_thunk, ac); + if(rc) { + errno = rc; goto err; + } return t; err: - free(t); - free(ac); + curlx_free(t); + curlx_free(ac); return curl_thread_t_null; } @@ -85,7 +80,7 @@ void Curl_thread_destroy(curl_thread_t *hnd) { if(*hnd != curl_thread_t_null) { pthread_detach(**hnd); - free(*hnd); + curlx_free(*hnd); *hnd = curl_thread_t_null; } } @@ -94,67 +89,24 @@ int Curl_thread_join(curl_thread_t *hnd) { int ret = (pthread_join(**hnd, NULL) == 0); - free(*hnd); + curlx_free(*hnd); *hnd = curl_thread_t_null; return ret; } -/* do not use pthread_cancel if: - * - pthread_cancel seems to be absent - * - on FreeBSD, as we see hangers in CI testing - * - this is a -fsanitize=thread build - * (clang sanitizer reports false positive when functions to not return) - */ -#if defined(PTHREAD_CANCEL_ENABLE) && !defined(__FreeBSD__) -#if defined(__has_feature) -# if !__has_feature(thread_sanitizer) -#define USE_PTHREAD_CANCEL -# endif -#else /* __has_feature */ -#define USE_PTHREAD_CANCEL -#endif /* !__has_feature */ -#endif /* PTHREAD_CANCEL_ENABLE && !__FreeBSD__ */ +#elif defined(_WIN32) -int Curl_thread_cancel(curl_thread_t *hnd) +curl_thread_t Curl_thread_create( + CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg) { - (void)hnd; - if(*hnd != curl_thread_t_null) -#ifdef USE_PTHREAD_CANCEL - return pthread_cancel(**hnd); -#else - return 1; /* not supported */ -#endif - return 0; -} - -#elif defined(USE_THREADS_WIN32) - -curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T - (CURL_STDCALL *func) (void *), void *arg) -{ -#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE) - typedef HANDLE curl_win_thread_handle_t; -#else - typedef uintptr_t curl_win_thread_handle_t; -#endif - curl_thread_t t; - curl_win_thread_handle_t thread_handle; -#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE) - thread_handle = CreateThread(NULL, 0, func, arg, 0, NULL); -#else - thread_handle = _beginthreadex(NULL, 0, func, arg, 0, NULL); -#endif - t = (curl_thread_t)thread_handle; - if((t == 0) || (t == LongToHandle(-1L))) { -#ifdef UNDER_CE + curl_thread_t t = CreateThread(NULL, 0, func, arg, 0, NULL); + if(!t) { DWORD gle = GetLastError(); /* !checksrc! disable ERRNOVAR 1 */ - int err = (gle == ERROR_ACCESS_DENIED || - gle == ERROR_NOT_ENOUGH_MEMORY) ? - EACCES : EINVAL; - CURL_SETERRNO(err); -#endif + errno = (gle == ERROR_ACCESS_DENIED || + gle == ERROR_NOT_ENOUGH_MEMORY) ? + EACCES : EINVAL; return curl_thread_t_null; } return t; @@ -170,24 +122,93 @@ void Curl_thread_destroy(curl_thread_t *hnd) int Curl_thread_join(curl_thread_t *hnd) { -#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < _WIN32_WINNT_VISTA) - int ret = (WaitForSingleObject(*hnd, INFINITE) == WAIT_OBJECT_0); -#else int ret = (WaitForSingleObjectEx(*hnd, INFINITE, FALSE) == WAIT_OBJECT_0); -#endif Curl_thread_destroy(hnd); - return ret; } -int Curl_thread_cancel(curl_thread_t *hnd) +#else +#error neither HAVE_THREADS_POSIX nor _WIN32 defined +#endif +#endif /* USE_THREADS */ + +#ifdef USE_MUTEX + +#ifdef HAVE_THREADS_POSIX + +void Curl_cond_signal(pthread_cond_t *c) { - if(*hnd != curl_thread_t_null) { - return 1; /* not supported */ - } - return 0; + /* return code defined as always 0 */ + (void)pthread_cond_signal(c); } -#endif /* USE_THREADS_* */ +void Curl_cond_wait(pthread_cond_t *c, pthread_mutex_t *m) +{ + /* return code defined as always 0 */ + (void)pthread_cond_wait(c, m); +} + +CURLcode Curl_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, + uint32_t timeout_ms) +{ + struct curltime now; + struct timespec ts; + timediff_t usec; + int rc; + + /* POSIX expects an "absolute" time until the condition wait ends. + * We cannot use `curlx_now()` here that may run on some monotonic clock + * that will be most likely in the past, as far as POSIX abstime is + * concerned. */ +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; + (void)gettimeofday(&tv, NULL); + now.tv_sec = tv.tv_sec; + now.tv_usec = (int)tv.tv_usec; +#else + now.tv_sec = time(NULL); + now.tv_usec = 0; +#endif + + ts.tv_sec = now.tv_sec + (timeout_ms / 1000); + usec = now.tv_usec + ((timeout_ms % 1000) * 1000); + if(usec >= 1000000) { + ++ts.tv_sec; + usec %= 1000000; + } + ts.tv_nsec = (long)usec * 1000; + + rc = pthread_cond_timedwait(c, m, &ts); + if(rc == SOCKETIMEDOUT) + return CURLE_OPERATION_TIMEDOUT; + return rc ? CURLE_UNRECOVERABLE_POLL : CURLE_OK; +} + +#elif defined(_WIN32) + +void Curl_cond_signal(CONDITION_VARIABLE *c) +{ + WakeConditionVariable(c); +} + +void Curl_cond_wait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m) +{ + SleepConditionVariableCS(c, m, INFINITE); +} + +CURLcode Curl_cond_timedwait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m, + uint32_t timeout_ms) +{ + if(!SleepConditionVariableCS(c, m, (DWORD)timeout_ms)) { + DWORD err = GetLastError(); + return (err == ERROR_TIMEOUT) ? + CURLE_OPERATION_TIMEDOUT : CURLE_UNRECOVERABLE_POLL; + } + return CURLE_OK; +} +#else +#error neither HAVE_THREADS_POSIX nor _WIN32 defined +#endif +#endif /* USE_MUTEX */ diff --git a/lib/curl_threads.h b/lib/curl_threads.h index 115277c00e..081d155695 100644 --- a/lib/curl_threads.h +++ b/lib/curl_threads.h @@ -25,7 +25,9 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef USE_THREADS_POSIX +#ifdef USE_MUTEX +#ifdef HAVE_THREADS_POSIX +# define CURL_THREAD_RETURN_T unsigned int # define CURL_STDCALL # define curl_mutex_t pthread_mutex_t # define curl_thread_t pthread_t * @@ -34,54 +36,42 @@ # define Curl_mutex_acquire(m) pthread_mutex_lock(m) # define Curl_mutex_release(m) pthread_mutex_unlock(m) # define Curl_mutex_destroy(m) pthread_mutex_destroy(m) -#elif defined(USE_THREADS_WIN32) -# define CURL_STDCALL __stdcall +# define curl_cond_t pthread_cond_t +# define Curl_cond_init(c) pthread_cond_init(c, NULL) +# define Curl_cond_destroy(c) pthread_cond_destroy(c) +#elif defined(_WIN32) +# define CURL_THREAD_RETURN_T DWORD +# define CURL_STDCALL WINAPI # 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) +# define curl_cond_t CONDITION_VARIABLE +# define Curl_cond_init(c) InitializeConditionVariable(c) +# define Curl_cond_destroy(c) (void)(c) #else -# define CURL_STDCALL +#error neither HAVE_THREADS_POSIX nor _WIN32 defined #endif -#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE) -#define CURL_THREAD_RETURN_T DWORD -#else -#define CURL_THREAD_RETURN_T unsigned int -#endif +void Curl_cond_signal(curl_cond_t *c); +void Curl_cond_wait(curl_cond_t *c, curl_mutex_t *m); +/* Returns CURLE_OPERATION_TIMEDOUT on timeout */ +CURLcode Curl_cond_timedwait(curl_cond_t *c, curl_mutex_t *m, + uint32_t timeout_ms); +#endif /* USE_MUTEX */ -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#ifdef USE_THREADS -curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T - (CURL_STDCALL *func) (void *), void *arg); +curl_thread_t Curl_thread_create( + CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg); void Curl_thread_destroy(curl_thread_t *hnd); int Curl_thread_join(curl_thread_t *hnd); -int Curl_thread_cancel(curl_thread_t *hnd); - -#if defined(USE_THREADS_POSIX) && defined(PTHREAD_CANCEL_ENABLE) -#define Curl_thread_push_cleanup(a,b) pthread_cleanup_push(a,b) -#define Curl_thread_pop_cleanup() pthread_cleanup_pop(0) -#define Curl_thread_enable_cancel() \ - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) -#define Curl_thread_disable_cancel() \ - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) -#else -#define Curl_thread_push_cleanup(a,b) ((void)a,(void)b) -#define Curl_thread_pop_cleanup() Curl_nop_stmt -#define Curl_thread_enable_cancel() Curl_nop_stmt -#define Curl_thread_disable_cancel() Curl_nop_stmt -#endif - -#endif /* USE_THREADS_POSIX || USE_THREADS_WIN32 */ +#endif /* USE_THREADS */ #endif /* HEADER_CURL_THREADS_H */ diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 52671f4eae..c6115cf7f6 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -21,20 +21,16 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "curl_trc.h" #include "urldata.h" -#include "easyif.h" #include "cfilters.h" #include "multiif.h" +#include "cf-dns.h" #include "cf-socket.h" #include "connect.h" -#include "doh.h" #include "http2.h" #include "http_proxy.h" #include "cf-h1-proxy.h" @@ -42,15 +38,12 @@ #include "cf-haproxy.h" #include "cf-https-connect.h" #include "cf-ip-happy.h" +#include "progress.h" #include "socks.h" #include "curlx/strparse.h" #include "vtls/vtls.h" #include "vquic/vquic.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "curlx/strcopy.h" static void trc_write(struct Curl_easy *data, curl_infotype type, const char *ptr, size_t size) @@ -93,8 +86,8 @@ static struct curl_trc_feat Curl_trc_feat_ids = { CURL_LOG_LVL_NONE, }; #define CURL_TRC_IDS(data) \ - (Curl_trc_is_verbose(data) && \ - Curl_trc_feat_ids.log_level >= CURL_LOG_LVL_INFO) + (Curl_trc_is_verbose(data) && \ + Curl_trc_feat_ids.log_level >= CURL_LOG_LVL_INFO) static size_t trc_print_ids(struct Curl_easy *data, char *buf, size_t maxlen) { @@ -102,14 +95,14 @@ static size_t trc_print_ids(struct Curl_easy *data, char *buf, size_t maxlen) data->conn->connection_id : data->state.recent_conn_id; if(data->id >= 0) { if(cid >= 0) - return msnprintf(buf, maxlen, CURL_TRC_FMT_IDSDC, data->id, cid); + return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSDC, data->id, cid); else - return msnprintf(buf, maxlen, CURL_TRC_FMT_IDSD, data->id); + return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSD, data->id); } else if(cid >= 0) - return msnprintf(buf, maxlen, CURL_TRC_FMT_IDSC, cid); + return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSC, cid); else { - return msnprintf(buf, maxlen, "[x-x] "); + return curl_msnprintf(buf, maxlen, "[x-x] "); } } @@ -143,8 +136,8 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type, if(CURL_TRC_IDS(data) && (size < TRC_LINE_MAX)) { len = trc_print_ids(data, buf, TRC_LINE_MAX); - len += msnprintf(buf + len, TRC_LINE_MAX - len, "%.*s", - (int)size, ptr); + len += curl_msnprintf(buf + len, TRC_LINE_MAX - len, "%.*s", + (int)size, ptr); len = trc_end_buf(buf, len, TRC_LINE_MAX, FALSE); Curl_set_in_callback(data, TRUE); (void)(*data->set.fdebug)(data, type, buf, len, data->set.debugdata); @@ -189,10 +182,10 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...) size_t len; char error[CURL_ERROR_SIZE + 2]; va_start(ap, fmt); - len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap); + len = curl_mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap); if(data->set.errorbuffer && !data->state.errorbuf) { - strcpy(data->set.errorbuffer, error); + curlx_strcopy(data->set.errorbuffer, CURL_ERROR_SIZE, error, len); data->state.errorbuf = TRUE; /* wrote error string */ } error[len++] = '\n'; @@ -202,6 +195,42 @@ 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, +}; +#ifdef USE_THREADS +struct curl_trc_feat Curl_trc_feat_threads = { + "THREADS", + CURL_LOG_LVL_NONE, +}; +#endif +#endif + #ifndef CURL_DISABLE_VERBOSE_STRINGS static void trc_infof(struct Curl_easy *data, @@ -220,15 +249,15 @@ static void trc_infof(struct Curl_easy *data, if(CURL_TRC_IDS(data)) len += trc_print_ids(data, buf + len, TRC_LINE_MAX - len); if(feat) - len += msnprintf(buf + len, TRC_LINE_MAX - len, "[%s] ", feat->name); + len += curl_msnprintf(buf + len, TRC_LINE_MAX - len, "[%s] ", feat->name); if(opt_id) { if(opt_id_idx > 0) - len += msnprintf(buf + len, TRC_LINE_MAX - len, "[%s-%d] ", - opt_id, opt_id_idx); + len += curl_msnprintf(buf + len, TRC_LINE_MAX - len, "[%s-%d] ", + opt_id, opt_id_idx); else - len += msnprintf(buf + len, TRC_LINE_MAX - len, "[%s] ", opt_id); + len += curl_msnprintf(buf + len, TRC_LINE_MAX - len, "[%s] ", opt_id); } - len += mvsnprintf(buf + len, TRC_LINE_MAX - len, fmt, ap); + len += curl_mvsnprintf(buf + len, TRC_LINE_MAX - len, fmt, ap); len = trc_end_buf(buf, len, TRC_LINE_MAX, TRUE); trc_write(data, CURLINFO_TEXT, buf, len); } @@ -256,24 +285,20 @@ 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, -}; +void Curl_trc_feat_infof(struct Curl_easy *data, + struct curl_trc_feat *feat, + const char *fmt, ...) +{ + DEBUGASSERT(feat); + if(Curl_trc_ft_is_verbose(data, feat)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, feat, NULL, 0, fmt, ap); + va_end(ap); + } +} -static const char * const Curl_trc_timer_names[]={ +static const char * const Curl_trc_timer_names[] = { "100_TIMEOUT", "ASYNC_NAME", "CONNECTTIMEOUT", @@ -291,36 +316,47 @@ static const char * const Curl_trc_timer_names[]={ "SHUTDOWN", }; -const char *Curl_trc_timer_name(int tid) +static const char *trc_timer_name(int tid) { if((tid >= 0) && ((size_t)tid < CURL_ARRAYSIZE(Curl_trc_timer_names))) return Curl_trc_timer_names[(size_t)tid]; return "UNKNOWN?"; } -void Curl_trc_multi_timeouts(struct Curl_easy *data) +void Curl_trc_timer(struct Curl_easy *data, int tid, const char *fmt, ...) { - struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist); - if(e) { - struct curltime now = curlx_now(); - while(e) { - struct time_node *n = Curl_node_elem(e); - e = Curl_node_next(e); - CURL_TRC_M(data, "[TIMEOUT] %s expires in %" FMT_TIMEDIFF_T "ns", - CURL_TIMER_NAME(n->eid), - curlx_timediff_us(n->time, now)); + DEBUGASSERT(!strchr(fmt, '\n')); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_timer)) { + const char *tname = trc_timer_name(tid); + va_list ap; + va_start(ap, fmt); + trc_infof(data, &Curl_trc_feat_timer, tname, 0, fmt, ap); + va_end(ap); + } +} + +void Curl_trc_easy_timers(struct Curl_easy *data) +{ + if(CURL_TRC_TIMER_is_verbose(data)) { + struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist); + if(e) { + const struct curltime *pnow = Curl_pgrs_now(data); + while(e) { + struct time_node *n = Curl_node_elem(e); + e = Curl_node_next(e); + CURL_TRC_TIMER(data, n->eid, "expires in %" FMT_TIMEDIFF_T "ns", + curlx_ptimediff_us(&n->time, pnow)); + } } } } -static const char * const Curl_trc_mstate_names[]={ +static const char * const Curl_trc_mstate_names[] = { "INIT", "PENDING", "SETUP", "CONNECT", - "RESOLVING", "CONNECTING", - "TUNNELING", "PROTOCONNECT", "PROTOCONNECTING", "DO", @@ -441,6 +477,24 @@ void Curl_trc_ssls(struct Curl_easy *data, const char *fmt, ...) } #endif /* USE_SSL */ +#ifdef USE_SSH +struct curl_trc_feat Curl_trc_feat_ssh = { + "SSH", + CURL_LOG_LVL_NONE, +}; + +void Curl_trc_ssh(struct Curl_easy *data, const char *fmt, ...) +{ + DEBUGASSERT(!strchr(fmt, '\n')); + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssh)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, &Curl_trc_feat_ssh, NULL, 0, fmt, ap); + va_end(ap); + } +} +#endif /* USE_SSH */ + #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) struct curl_trc_feat Curl_trc_feat_ws = { "WS", @@ -459,11 +513,11 @@ 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_PROTOCOL (1<<(0)) -#define TRC_CT_NETWORK (1<<(1)) -#define TRC_CT_PROXY (1<<(2)) -#define TRC_CT_INTERNALS (1<<(3)) +#define TRC_CT_NONE 0 +#define TRC_CT_PROTOCOL (1 << 0) +#define TRC_CT_NETWORK (1 << 1) +#define TRC_CT_PROXY (1 << 2) +#define TRC_CT_INTERNALS (1 << 3) struct trc_feat_def { struct curl_trc_feat *feat; @@ -476,17 +530,22 @@ static struct trc_feat_def trc_feats[] = { { &Curl_trc_feat_read, TRC_CT_NONE }, { &Curl_trc_feat_write, TRC_CT_NONE }, { &Curl_trc_feat_dns, TRC_CT_NETWORK }, + { &Curl_trc_feat_timer, TRC_CT_NETWORK }, +#ifdef USE_THREADS + { &Curl_trc_feat_threads, TRC_CT_NONE }, +#endif #ifndef CURL_DISABLE_FTP { &Curl_trc_feat_ftp, TRC_CT_PROTOCOL }, #endif -#ifndef CURL_DISABLE_DOH -#endif #ifndef CURL_DISABLE_SMTP { &Curl_trc_feat_smtp, TRC_CT_PROTOCOL }, #endif #ifdef USE_SSL { &Curl_trc_feat_ssls, TRC_CT_NETWORK }, #endif +#ifdef USE_SSH + { &Curl_trc_feat_ssh, TRC_CT_PROTOCOL }, +#endif #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) { &Curl_trc_feat_ws, TRC_CT_PROTOCOL }, #endif @@ -498,6 +557,7 @@ struct trc_cft_def { }; static struct trc_cft_def trc_cfts[] = { + { &Curl_cft_dns, TRC_CT_NETWORK }, { &Curl_cft_tcp, TRC_CT_NETWORK }, { &Curl_cft_udp, TRC_CT_NETWORK }, { &Curl_cft_unix, TRC_CT_NETWORK }, @@ -550,7 +610,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; @@ -631,53 +691,75 @@ CURLcode Curl_trc_init(void) void Curl_infof(struct Curl_easy *data, const char *fmt, ...) { - (void)data; (void)fmt; + (void)data; + (void)fmt; } void Curl_trc_cf_infof(struct Curl_easy *data, const struct Curl_cfilter *cf, const char *fmt, ...) { - (void)data; (void)cf; (void)fmt; + (void)data; + (void)cf; + (void)fmt; } -struct curl_trc_feat; - void Curl_trc_multi(struct Curl_easy *data, const char *fmt, ...) { - (void)data; (void)fmt; + (void)data; + (void)fmt; } void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...) { - (void)data; (void)fmt; + (void)data; + (void)fmt; } void Curl_trc_dns(struct Curl_easy *data, const char *fmt, ...) { - (void)data; (void)fmt; + (void)data; + (void)fmt; +} + +void Curl_trc_timer(struct Curl_easy *data, int tid, const char *fmt, ...) +{ + (void)data; + (void)tid; + (void)fmt; } void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...) { - (void)data; (void)fmt; + (void)data; + (void)fmt; } #ifndef CURL_DISABLE_FTP void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...) { - (void)data; (void)fmt; + (void)data; + (void)fmt; } #endif #ifndef CURL_DISABLE_SMTP void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...) { - (void)data; (void)fmt; + (void)data; + (void)fmt; } #endif #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...) { - (void)data; (void)fmt; + (void)data; + (void)fmt; +} +#endif +#ifdef USE_SSH +void Curl_trc_ssh(struct Curl_easy *data, const char *fmt, ...) +{ + (void)data; + (void)fmt; } #endif #ifdef USE_SSL diff --git a/lib/curl_trc.h b/lib/curl_trc.h index fa0999250f..b4ae8e5314 100644 --- a/lib/curl_trc.h +++ b/lib/curl_trc.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - struct Curl_easy; struct Curl_cfilter; @@ -62,14 +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. */ @@ -86,7 +85,7 @@ void Curl_trc_multi(struct Curl_easy *data, const char *fmt, ...) CURL_PRINTF(2, 3); const char *Curl_trc_mstate_name(int state); const char *Curl_trc_timer_name(int tid); -void Curl_trc_multi_timeouts(struct Curl_easy *data); +void Curl_trc_easy_timers(struct Curl_easy *data); void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...) CURL_PRINTF(2, 3); @@ -94,6 +93,17 @@ void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...) CURL_PRINTF(2, 3); void Curl_trc_dns(struct Curl_easy *data, const char *fmt, ...) CURL_PRINTF(2, 3); +void Curl_trc_timer(struct Curl_easy *data, int tid, + const char *fmt, ...) CURL_PRINTF(3, 4); + +struct curl_trc_feat { + const char *name; + int log_level; +}; + +void Curl_trc_feat_infof(struct Curl_easy *data, + struct curl_trc_feat *feat, + const char *fmt, ...) CURL_PRINTF(3, 4); #ifndef CURL_DISABLE_FTP extern struct curl_trc_feat Curl_trc_feat_ftp; @@ -110,6 +120,11 @@ extern struct curl_trc_feat Curl_trc_feat_ssls; void Curl_trc_ssls(struct Curl_easy *data, const char *fmt, ...) CURL_PRINTF(2, 3); #endif +#ifdef USE_SSH +extern struct curl_trc_feat Curl_trc_feat_ssh; +void Curl_trc_ssh(struct Curl_easy *data, + const char *fmt, ...) CURL_PRINTF(2, 3); +#endif #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) extern struct curl_trc_feat Curl_trc_feat_ws; void Curl_trc_ws(struct Curl_easy *data, @@ -118,56 +133,156 @@ void Curl_trc_ws(struct Curl_easy *data, #define CURL_TRC_M_is_verbose(data) \ Curl_trc_ft_is_verbose(data, &Curl_trc_feat_multi) +#define CURL_TRC_DNS_is_verbose(data) \ + Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns) +#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) -#define infof(data, ...) \ - do { if(Curl_trc_is_verbose(data)) \ - Curl_infof(data, __VA_ARGS__); } while(0) -#define CURL_TRC_M(data, ...) \ - do { if(CURL_TRC_M_is_verbose(data)) \ - Curl_trc_multi(data, __VA_ARGS__); } while(0) -#define CURL_TRC_CF(data, cf, ...) \ - do { if(Curl_trc_cf_is_verbose(cf, data)) \ - Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0) -#define CURL_TRC_WRITE(data, ...) \ - do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) \ - Curl_trc_write(data, __VA_ARGS__); } while(0) -#define CURL_TRC_READ(data, ...) \ - do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) \ - Curl_trc_read(data, __VA_ARGS__); } while(0) -#define CURL_TRC_DNS(data, ...) \ - do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) \ - Curl_trc_dns(data, __VA_ARGS__); } while(0) +#if defined(CURL_HAVE_MACRO_VARARG) && !defined(CURL_DISABLE_VERBOSE_STRINGS) +#define infof(data, ...) \ + do { \ + if(Curl_trc_is_verbose(data)) \ + Curl_infof(data, __VA_ARGS__); \ + } while(0) +#define CURL_TRC_M(data, ...) \ + do { \ + if(CURL_TRC_M_is_verbose(data)) \ + Curl_trc_multi(data, __VA_ARGS__); \ + } while(0) +#define CURL_TRC_CF(data, cf, ...) \ + do { \ + if(Curl_trc_cf_is_verbose(cf, data)) \ + Curl_trc_cf_infof(data, cf, __VA_ARGS__); \ + } while(0) +#define CURL_TRC_WRITE(data, ...) \ + do { \ + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) \ + Curl_trc_write(data, __VA_ARGS__); \ + } while(0) +#define CURL_TRC_READ(data, ...) \ + do { \ + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) \ + Curl_trc_read(data, __VA_ARGS__); \ + } while(0) +#define CURL_TRC_DNS(data, ...) \ + do { \ + if(CURL_TRC_DNS_is_verbose(data)) \ + Curl_trc_dns(data, __VA_ARGS__); \ + } while(0) +#define CURL_TRC_TIMER(data, tid, ...) \ + do { \ + if(CURL_TRC_TIMER_is_verbose(data)) \ + Curl_trc_timer(data, tid, __VA_ARGS__); \ + } while(0) #ifndef CURL_DISABLE_FTP +#define CURL_TRC_FTP(data, ...) \ + do { \ + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) \ + Curl_trc_ftp(data, __VA_ARGS__); \ + } while(0) +#endif /* !CURL_DISABLE_FTP */ +#ifndef CURL_DISABLE_SMTP +#define CURL_TRC_SMTP(data, ...) \ + do { \ + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_smtp)) \ + Curl_trc_smtp(data, __VA_ARGS__); \ + } while(0) +#endif /* !CURL_DISABLE_SMTP */ +#ifdef USE_SSL +#define CURL_TRC_SSLS(data, ...) \ + do { \ + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssls)) \ + Curl_trc_ssls(data, __VA_ARGS__); \ + } while(0) +#endif /* USE_SSL */ +#ifdef USE_SSH +#define CURL_TRC_SSH(data, ...) \ + do { \ + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssh)) \ + Curl_trc_ssh(data, __VA_ARGS__); \ + } while(0) +#endif /* USE_SSH */ +#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) +#define CURL_TRC_WS(data, ...) \ + do { \ + if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) \ + Curl_trc_ws(data, __VA_ARGS__); \ + } while(0) +#endif /* !CURL_DISABLE_WEBSOCKETS && !CURL_DISABLE_HTTP */ + +#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 { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) \ - Curl_trc_ftp(data, __VA_ARGS__); } while(0) + do { \ + (void)(data); \ + } while(0) #endif /* !CURL_DISABLE_FTP */ #ifndef CURL_DISABLE_SMTP #define CURL_TRC_SMTP(data, ...) \ - do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_smtp)) \ - Curl_trc_smtp(data, __VA_ARGS__); } while(0) + do { \ + (void)(data); \ + } while(0) #endif /* !CURL_DISABLE_SMTP */ #ifdef USE_SSL #define CURL_TRC_SSLS(data, ...) \ - do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssls)) \ - Curl_trc_ssls(data, __VA_ARGS__); } while(0) + 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 { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) \ - Curl_trc_ws(data, __VA_ARGS__); } while(0) -#endif /* !CURL_DISABLE_WEBSOCKETS && !CURL_DISABLE_HTTP */ +#define CURL_TRC_WS(data, ...) \ + do { \ + (void)(data); \ + } while(0) +#endif -#else /* CURL_HAVE_C99 */ +#else /* !CURL_HAVE_MACRO_VARARG */ -#define infof Curl_infof -#define CURL_TRC_M Curl_trc_multi -#define CURL_TRC_CF Curl_trc_cf_infof +#define infof Curl_infof +#define CURL_TRC_M Curl_trc_multi +#define CURL_TRC_CF Curl_trc_cf_infof #define CURL_TRC_WRITE Curl_trc_write #define CURL_TRC_READ Curl_trc_read #define CURL_TRC_DNS Curl_trc_dns +#define CURL_TRC_TIMER Curl_trc_timer #ifndef CURL_DISABLE_FTP #define CURL_TRC_FTP Curl_trc_ftp @@ -178,51 +293,52 @@ void Curl_trc_ws(struct Curl_easy *data, #ifdef USE_SSL #define CURL_TRC_SSLS Curl_trc_ssls #endif +#ifdef USE_SSH +#define CURL_TRC_SSH Curl_trc_ssh +#endif #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) #define CURL_TRC_WS Curl_trc_ws #endif -#endif /* !CURL_HAVE_C99 */ - -struct curl_trc_feat { - const char *name; - int log_level; -}; - -#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; +#ifdef USE_THREADS +extern struct curl_trc_feat Curl_trc_feat_threads; +#endif +#endif -#define Curl_trc_is_verbose(data) \ - ((data) && (data)->set.verbose && \ - (!(data)->state.feat || \ - ((data)->state.feat->log_level >= CURL_LOG_LVL_INFO))) -#define Curl_trc_cf_is_verbose(cf, data) \ - (Curl_trc_is_verbose(data) && \ - (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO) +#ifndef CURL_DISABLE_VERBOSE_STRINGS +/* informational messages enabled */ +#define Curl_trc_is_verbose(data) \ + ((data) && (data)->set.verbose && \ + (!(data)->state.feat || \ + ((data)->state.feat->log_level >= CURL_LOG_LVL_INFO))) +#define Curl_trc_cf_is_verbose(cf, data) \ + (Curl_trc_is_verbose(data) && \ + (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO) #define Curl_trc_ft_is_verbose(data, ft) \ - (Curl_trc_is_verbose(data) && \ - (ft)->log_level >= CURL_LOG_LVL_INFO) + (Curl_trc_is_verbose(data) && \ + (ft)->log_level >= CURL_LOG_LVL_INFO) #define CURL_MSTATE_NAME(s) Curl_trc_mstate_name((int)(s)) -#define CURL_TIMER_NAME(t) Curl_trc_timer_name((int)(t)) -#define CURL_TRC_M_TIMEOUTS(data) \ - do { if(CURL_TRC_M_is_verbose(data)) \ - Curl_trc_multi_timeouts(data); } while(0) +#define CURL_TRC_EASY_TIMERS(data) \ + do { \ + if(CURL_TRC_TIMER_is_verbose(data)) \ + Curl_trc_easy_timers(data); \ + } while(0) #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_TIMER_NAME(x) ((void)(x), "-") -#define CURL_TRC_M_TIMEOUTS(x) Curl_nop_stmt - +#define CURL_TRC_EASY_TIMERS(x) Curl_nop_stmt #endif /* !CURL_DISABLE_VERBOSE_STRINGS */ #endif /* HEADER_CURL_TRC_H */ diff --git a/lib/curlx/.checksrc b/lib/curlx/.checksrc deleted file mode 100644 index 22ca8e0b53..0000000000 --- a/lib/curlx/.checksrc +++ /dev/null @@ -1,5 +0,0 @@ -banfunc snprintf -banfunc sscanf -banfunc strerror -banfunc strtol -banfunc vsnprint diff --git a/lib/curlx/base64.c b/lib/curlx/base64.c index dc6e9c001c..7f51576f50 100644 --- a/lib/curlx/base64.c +++ b/lib/curlx/base64.c @@ -24,33 +24,26 @@ /* Base64 encoding/decoding */ -#include "../curl_setup.h" +#include "curl_setup.h" -#include -#include "warnless.h" -#include "base64.h" - -/* The last 2 #include files should be in this order */ -#ifdef BUILDING_LIBCURL -#include "../curl_memory.h" -#endif -#include "../memdebug.h" +#include "curlx/base64.h" /* ---- Base64 Encoding/Decoding Table --- */ -const char Curl_base64encdec[]= +const char curlx_base64encdec[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648 section 5 */ -static const char base64url[]= +static const char base64url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; -static const unsigned char decodetable[] = -{ 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, - 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51 }; +static const unsigned char decodetable[] = { + 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, + 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 +}; /* * curlx_base64_decode() * @@ -66,7 +59,7 @@ static const unsigned char decodetable[] = * @unittest: 1302 */ CURLcode curlx_base64_decode(const char *src, - unsigned char **outptr, size_t *outlen) + uint8_t **outptr, size_t *outlen) { size_t srclen = 0; size_t padding = 0; @@ -103,7 +96,7 @@ CURLcode curlx_base64_decode(const char *src, rawlen = (numQuantums * 3) - padding; /* Allocate our buffer including room for a null-terminator */ - newstr = malloc(rawlen + 1); + newstr = curlx_malloc(rawlen + 1); if(!newstr) return CURLE_OUT_OF_MEMORY; @@ -156,7 +149,7 @@ CURLcode curlx_base64_decode(const char *src, pos += 3 - padding; } - /* Zero terminate */ + /* Null-terminate */ *pos = '\0'; /* Return the decoded data */ @@ -165,13 +158,13 @@ CURLcode curlx_base64_decode(const char *src, return CURLE_OK; bad: - free(newstr); + curlx_free(newstr); return CURLE_BAD_CONTENT_ENCODING; } static CURLcode base64_encode(const char *table64, - unsigned char padbyte, - const char *inputbuff, size_t insize, + uint8_t padbyte, + const uint8_t *inputbuff, size_t insize, char **outptr, size_t *outlen) { char *output; @@ -182,30 +175,30 @@ static CURLcode base64_encode(const char *table64, *outlen = 0; if(!insize) - insize = strlen(inputbuff); + return CURLE_OK; -#if SIZEOF_SIZE_T == 4 - if(insize > UINT_MAX/4) - return CURLE_OUT_OF_MEMORY; -#endif + /* safety precaution */ + DEBUGASSERT(insize <= CURL_MAX_BASE64_INPUT); + if(insize > CURL_MAX_BASE64_INPUT) + return CURLE_TOO_LARGE; - base64data = output = malloc((insize + 2) / 3 * 4 + 1); + base64data = output = curlx_malloc(((insize + 2) / 3 * 4) + 1); if(!output) return CURLE_OUT_OF_MEMORY; while(insize >= 3) { - *output++ = table64[ in[0] >> 2 ]; - *output++ = table64[ ((in[0] & 0x03) << 4) | (in[1] >> 4) ]; - *output++ = table64[ ((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6) ]; - *output++ = table64[ in[2] & 0x3F ]; + *output++ = table64[in[0] >> 2]; + *output++ = table64[((in[0] & 0x03) << 4) | (in[1] >> 4)]; + *output++ = table64[((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6)]; + *output++ = table64[in[2] & 0x3F]; insize -= 3; in += 3; } if(insize) { /* this is only one or two bytes now */ - *output++ = table64[ in[0] >> 2 ]; + *output++ = table64[in[0] >> 2]; if(insize == 1) { - *output++ = table64[ ((in[0] & 0x03) << 4) ]; + *output++ = table64[((in[0] & 0x03) << 4)]; if(padbyte) { *output++ = padbyte; *output++ = padbyte; @@ -213,14 +206,14 @@ static CURLcode base64_encode(const char *table64, } else { /* insize == 2 */ - *output++ = table64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4) ]; - *output++ = table64[ ((in[1] & 0x0F) << 2) ]; + *output++ = table64[((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4)]; + *output++ = table64[((in[1] & 0x0F) << 2)]; if(padbyte) *output++ = padbyte; } } - /* Zero terminate */ + /* Null-terminate */ *output = '\0'; /* Return the pointer to the new data (allocated memory) */ @@ -240,17 +233,15 @@ static CURLcode base64_encode(const char *table64, * encoded data. Size of encoded data is returned in variable pointed by * outlen. * - * Input length of 0 indicates input buffer holds a null-terminated string. - * * Returns CURLE_OK on success, otherwise specific error code. Function * output shall not be considered valid unless CURLE_OK is returned. * * @unittest: 1302 */ -CURLcode curlx_base64_encode(const char *inputbuff, size_t insize, +CURLcode curlx_base64_encode(const uint8_t *inputbuff, size_t insize, char **outptr, size_t *outlen) { - return base64_encode(Curl_base64encdec, '=', + return base64_encode(curlx_base64encdec, '=', inputbuff, insize, outptr, outlen); } @@ -269,7 +260,7 @@ CURLcode curlx_base64_encode(const char *inputbuff, size_t insize, * * @unittest: 1302 */ -CURLcode curlx_base64url_encode(const char *inputbuff, size_t insize, +CURLcode curlx_base64url_encode(const uint8_t *inputbuff, size_t insize, char **outptr, size_t *outlen) { return base64_encode(base64url, 0, inputbuff, insize, outptr, outlen); diff --git a/lib/curlx/base64.h b/lib/curlx/base64.h index 026f80e4d3..2e77814822 100644 --- a/lib/curlx/base64.h +++ b/lib/curlx/base64.h @@ -24,13 +24,17 @@ * ***************************************************************************/ -CURLcode curlx_base64_encode(const char *inputbuff, size_t insize, +CURLcode curlx_base64_encode(const uint8_t *inputbuff, size_t insize, char **outptr, size_t *outlen); -CURLcode curlx_base64url_encode(const char *inputbuff, size_t insize, +CURLcode curlx_base64url_encode(const uint8_t *inputbuff, size_t insize, char **outptr, size_t *outlen); CURLcode curlx_base64_decode(const char *src, - unsigned char **outptr, size_t *outlen); + uint8_t **outptr, size_t *outlen); -extern const char Curl_base64encdec[]; +extern const char curlx_base64encdec[]; + +/* maximum input length acceptable to base64 encode, here to catch and prevent + mistakes */ +#define CURL_MAX_BASE64_INPUT 16000000 #endif /* HEADER_CURL_BASE64_H */ diff --git a/lib/curlx/basename.c b/lib/curlx/basename.c new file mode 100644 index 0000000000..d2fd160ff2 --- /dev/null +++ b/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/src/tool_bname.h b/lib/curlx/basename.h similarity index 79% rename from src/tool_bname.h rename to lib/curlx/basename.h index d091c2231a..fb79fed805 100644 --- a/src/tool_bname.h +++ b/lib/curlx/basename.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_TOOL_BNAME_H -#define HEADER_CURL_TOOL_BNAME_H +#ifndef HEADER_CURLX_BASENAME_H +#define HEADER_CURLX_BASENAME_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -23,14 +23,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "tool_setup.h" +#include "curl_setup.h" #ifndef HAVE_BASENAME +char *curlx_basename(char *path); +#else -char *tool_basename(char *path); +#ifdef HAVE_LIBGEN_H +#include +#endif -#define basename(x) tool_basename((x)) +#define curlx_basename(x) basename(x) +#endif /* !HAVE_BASENAME */ -#endif /* HAVE_BASENAME */ - -#endif /* HEADER_CURL_TOOL_BNAME_H */ +#endif /* HEADER_CURLX_BASENAME_H */ diff --git a/lib/curlx/curlx.h b/lib/curlx/curlx.h deleted file mode 100644 index 9f7bd3a975..0000000000 --- a/lib/curlx/curlx.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef HEADER_CURL_CURLX_H -#define HEADER_CURL_CURLX_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 - * - ***************************************************************************/ - -/* - * Defines protos and includes all header files that provide the curlx_* - * functions. The curlx_* functions are not part of the libcurl API, but are - * stand-alone functions whose sources can be built and linked by apps if need - * be. - */ - -#include "binmode.h" -/* "binmode.h" provides macro CURLX_SET_BINMODE() */ - -#include "nonblock.h" -/* "nonblock.h" provides curlx_nonblock() */ - -#include "warnless.h" -/* "warnless.h" provides functions: - - curlx_ultous() - curlx_ultouc() - curlx_uztosi() -*/ - -#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() - curlx_unicodefree() -*/ - -#include "version_win32.h" -/* provides curlx_verify_windows_version() */ - -#include "strparse.h" -/* The curlx_str_* parsing functions */ - -#include "dynbuf.h" -/* The curlx_dyn_* 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 */ - -#endif /* HEADER_CURL_CURLX_H */ diff --git a/lib/curlx/dynbuf.c b/lib/curlx/dynbuf.c index 447203e42a..1e5ea5177d 100644 --- a/lib/curlx/dynbuf.c +++ b/lib/curlx/dynbuf.c @@ -21,14 +21,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" -#include "../curl_setup.h" -#include "dynbuf.h" -#include "../curl_printf.h" -#ifdef BUILDING_LIBCURL -#include "../curl_memory.h" -#endif -#include "../memdebug.h" +#include "curlx/dynbuf.h" +#include "curl_printf.h" #define MIN_FIRST_ALLOC 32 @@ -61,7 +57,7 @@ void curlx_dyn_free(struct dynbuf *s) { DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); - Curl_safefree(s->bufr); + curlx_safefree(s->bufr); s->leng = s->allc = 0; } @@ -106,9 +102,7 @@ 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 = realloc(s->bufr, a); + void *p = curlx_realloc(s->bufr, a); if(!p) { curlx_dyn_free(s); return CURLE_OUT_OF_MEMORY; @@ -140,7 +134,7 @@ void curlx_dyn_reset(struct dynbuf *s) /* * Specify the size of the tail to keep (number of bytes from the end of the - * buffer). The rest will be dropped. + * buffer). The rest is dropped. */ CURLcode curlx_dyn_tail(struct dynbuf *s, size_t trail) { @@ -160,7 +154,6 @@ CURLcode curlx_dyn_tail(struct dynbuf *s, size_t trail) s->bufr[s->leng] = 0; } return CURLE_OK; - } /* @@ -212,7 +205,7 @@ CURLcode curlx_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap) if(str) { CURLcode result = dyn_nappend(s, (const unsigned char *)str, strlen(str)); - free(str); + curl_free(str); return result; } /* If we failed, we cleanup the whole buffer and return error */ diff --git a/lib/curlx/dynbuf.h b/lib/curlx/dynbuf.h index 00ca047893..0cf4a2c576 100644 --- a/lib/curlx/dynbuf.h +++ b/lib/curlx/dynbuf.h @@ -24,15 +24,13 @@ * ***************************************************************************/ -#include - struct dynbuf { char *bufr; /* point to a null-terminated allocated buffer */ size_t leng; /* number of bytes *EXCLUDING* the null-terminator */ size_t allc; /* size of the current allocation */ size_t toobig; /* size limit for the buffer */ #ifdef DEBUGBUILD - int init; /* detect API usage mistakes */ + int init; /* detect API usage mistakes */ #endif }; @@ -55,31 +53,31 @@ size_t curlx_dyn_len(const struct dynbuf *s); /* returns 0 on success, -1 on error */ /* The implementation of this function exists in mprintf.c */ -int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save); +int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list args); /* Take the buffer out of the dynbuf. Caller has ownership and * dynbuf resets to initial state. */ char *curlx_dyn_take(struct dynbuf *s, size_t *plen); /* Dynamic buffer max sizes */ -#define MAX_DYNBUF_SIZE (SIZE_MAX/2) +#define MAX_DYNBUF_SIZE (SIZE_MAX / 2) #define DYN_DOH_RESPONSE 3000 #define DYN_DOH_CNAME 256 #define DYN_PAUSE_BUFFER (64 * 1024 * 1024) #define DYN_HAXPROXY 2048 -#define DYN_HTTP_REQUEST (1024*1024) +#define DYN_HTTP_REQUEST (1024 * 1024) #define DYN_APRINTF 8000000 -#define DYN_RTSP_REQ_HEADER (64*1024) -#define DYN_TRAILERS (64*1024) +#define DYN_RTSP_REQ_HEADER (64 * 1024) +#define DYN_TRAILERS (64 * 1024) #define DYN_PROXY_CONNECT_HEADERS 16384 #define DYN_QLOG_NAME 1024 #define DYN_H1_TRAILER 4096 -#define DYN_PINGPPONG_CMD (64*1024) -#define DYN_IMAP_CMD (64*1024) -#define DYN_MQTT_RECV (64*1024) +#define DYN_PINGPPONG_CMD (64 * 1024) +#define DYN_IMAP_CMD (64 * 1024) +#define DYN_MQTT_RECV (64 * 1024) #define DYN_MQTT_SEND 0xFFFFFFF -#define DYN_CRLFILE_SIZE (400*1024*1024) /* 400mb */ -#define DYN_CERTFILE_SIZE (100*1024) /* 100KiB */ -#define DYN_KEYFILE_SIZE (100*1024) /* 100KiB */ +#define DYN_CRLFILE_SIZE (400 * 1024 * 1024) /* 400MiB */ +#define DYN_CERTFILE_SIZE (100 * 1024) /* 100KiB */ +#define DYN_KEYFILE_SIZE (100 * 1024) /* 100KiB */ #endif diff --git a/lib/curlx/fopen.c b/lib/curlx/fopen.c new file mode 100644 index 0000000000..6733010468 --- /dev/null +++ b/lib/curlx/fopen.c @@ -0,0 +1,508 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "curlx/fopen.h" + +int curlx_fseek(void *stream, curl_off_t offset, int whence) +{ +#ifdef _WIN32 + return _fseeki64(stream, (__int64)offset, whence); +#elif defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO) + return fseeko(stream, (off_t)offset, whence); +#else + if(offset > LONG_MAX) + return -1; + return fseek(stream, (long)offset, whence); +#endif +} + +#ifdef _WIN32 + +#include /* for _SH_DENYNO */ + +#include "curlx/multibyte.h" +#include "curlx/timeval.h" + +#ifdef CURL_MEMDEBUG +/* + * Use system allocators to avoid infinite recursion when called by curl's + * memory tracker memdebug functions. + */ +#define CURLX_MALLOC(x) malloc(x) +#define CURLX_FREE(x) free(x) +#else +#define CURLX_MALLOC(x) curlx_malloc(x) +#define CURLX_FREE(x) curlx_free(x) +#endif + +#ifdef _UNICODE +static wchar_t *fn_convert_UTF8_to_wchar(const char *str_utf8) +{ + wchar_t *str_w = NULL; + + if(str_utf8) { + int str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + str_utf8, -1, NULL, 0); + if(str_w_len > 0) { + str_w = CURLX_MALLOC(str_w_len * sizeof(wchar_t)); + if(str_w) { + if(MultiByteToWideChar(CP_UTF8, 0, + str_utf8, -1, str_w, str_w_len) == 0) { + CURLX_FREE(str_w); + return NULL; + } + } + } + } + return str_w; +} +#endif + +/* declare GetFullPathNameW for mingw-w64 UWP builds targeting old windows */ +#if defined(CURL_WINDOWS_UWP) && defined(__MINGW32__) && \ + (_WIN32_WINNT < _WIN32_WINNT_WIN10) +WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR *); +#endif + +/* Fix excessive paths (paths that exceed MAX_PATH length of 260). + * + * This is a helper function to fix paths that would exceed the MAX_PATH + * limitation check done by Windows APIs. It does so by normalizing the passed + * in filename or path 'in' to its full canonical path, and if that path is + * longer than MAX_PATH then setting 'out' to "\\?\" prefix + that full path. + * + * For example 'in' filename255chars in current directory C:\foo\bar is + * fixed as \\?\C:\foo\bar\filename255chars for 'out' which tells Windows + * it is ok to access that filename even though the actual full path is longer + * than 260 chars. + * + * For non-Unicode builds this function may fail sometimes because only the + * Unicode versions of some Windows API functions can access paths longer than + * MAX_PATH, for example GetFullPathNameW which is used in this function. When + * the full path is then converted from Unicode to multibyte that fails if any + * directories in the path contain characters not in the current codepage. + */ +static bool fix_excessive_path(const TCHAR *in, TCHAR **out) +{ + size_t needed, count; + const wchar_t *in_w; + wchar_t *fbuf = NULL; + + /* MS documented "approximate" limit for the maximum path length */ + const size_t max_path_len = 32767; + +#ifndef _UNICODE + wchar_t *ibuf = NULL; + char *obuf = NULL; +#endif + + *out = NULL; + + /* skip paths already normalized */ + if(!_tcsncmp(in, _T("\\\\?\\"), 4)) + goto cleanup; + +#ifndef _UNICODE + /* convert multibyte input to unicode */ + if(mbstowcs_s(&needed, NULL, 0, in, 0)) + goto cleanup; + if(!needed || needed >= max_path_len) + goto cleanup; + ibuf = CURLX_MALLOC(needed * sizeof(wchar_t)); + if(!ibuf) + goto cleanup; + if(mbstowcs_s(&count, ibuf, needed, in, needed - 1)) + goto cleanup; + if(count != needed) + goto cleanup; + in_w = ibuf; +#else + in_w = in; +#endif + + /* GetFullPathNameW returns the normalized full path in unicode. It converts + forward slashes to backslashes, processes .. to remove directory segments, + etc. Unlike GetFullPathNameA it can process paths that exceed MAX_PATH. */ + needed = (size_t)GetFullPathNameW(in_w, 0, NULL, NULL); + if(!needed || needed > max_path_len) + goto cleanup; + /* skip paths that are not excessive and do not need modification */ + if(needed <= MAX_PATH) + goto cleanup; + fbuf = CURLX_MALLOC(needed * sizeof(wchar_t)); + if(!fbuf) + goto cleanup; + count = (size_t)GetFullPathNameW(in_w, (DWORD)needed, fbuf, NULL); + if(!count || count >= needed) + goto cleanup; + + /* prepend \\?\ or \\?\UNC\ to the excessively long path. + * + * c:\longpath ---> \\?\c:\longpath + * \\.\c:\longpath ---> \\?\c:\longpath + * \\?\c:\longpath ---> \\?\c:\longpath (unchanged) + * \\server\c$\longpath ---> \\?\UNC\server\c$\longpath + * + * https://learn.microsoft.com/dotnet/standard/io/file-path-formats + */ + if(!wcsncmp(fbuf, L"\\\\?\\", 4)) + ; /* do nothing */ + else if(!wcsncmp(fbuf, L"\\\\.\\", 4)) + fbuf[2] = '?'; + else if(!wcsncmp(fbuf, L"\\\\.", 3) || !wcsncmp(fbuf, L"\\\\?", 3)) { + /* Unexpected, not UNC. The formatting doc does not allow this AFAICT. */ + goto cleanup; + } + else { + wchar_t *temp; + + if(!wcsncmp(fbuf, L"\\\\", 2)) { + /* "\\?\UNC\" + full path without "\\" + null */ + needed = 8 + (count - 2) + 1; + if(needed > max_path_len) + goto cleanup; + + temp = CURLX_MALLOC(needed * sizeof(wchar_t)); + if(!temp) + goto cleanup; + + if(wcsncpy_s(temp, needed, L"\\\\?\\UNC\\", 8)) { + CURLX_FREE(temp); + goto cleanup; + } + if(wcscpy_s(temp + 8, needed, fbuf + 2)) { + CURLX_FREE(temp); + goto cleanup; + } + } + else { + /* "\\?\" + full path + null */ + needed = 4 + count + 1; + if(needed > max_path_len) + goto cleanup; + + temp = CURLX_MALLOC(needed * sizeof(wchar_t)); + if(!temp) + goto cleanup; + + if(wcsncpy_s(temp, needed, L"\\\\?\\", 4)) { + CURLX_FREE(temp); + goto cleanup; + } + if(wcscpy_s(temp + 4, needed, fbuf)) { + CURLX_FREE(temp); + goto cleanup; + } + } + + CURLX_FREE(fbuf); + fbuf = temp; + } + +#ifndef _UNICODE + /* convert unicode full path to multibyte output */ + if(wcstombs_s(&needed, NULL, 0, fbuf, 0)) + goto cleanup; + if(!needed || needed >= max_path_len) + goto cleanup; + obuf = CURLX_MALLOC(needed); + if(!obuf) + goto cleanup; + if(wcstombs_s(&count, obuf, needed, fbuf, needed - 1)) + goto cleanup; + if(count != needed) + goto cleanup; + *out = obuf; + obuf = NULL; +#else + *out = fbuf; + fbuf = NULL; +#endif + +cleanup: + CURLX_FREE(fbuf); +#ifndef _UNICODE + CURLX_FREE(ibuf); + CURLX_FREE(obuf); +#endif + return *out ? true : false; +} + +#ifndef CURL_WINDOWS_UWP +HANDLE curlx_CreateFile(const char *filename, + DWORD dwDesiredAccess, + DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, + DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile) +{ + HANDLE handle = INVALID_HANDLE_VALUE; + +#ifdef UNICODE + TCHAR *filename_t = curlx_convert_UTF8_to_wchar(filename); +#else + const TCHAR *filename_t = filename; +#endif + + if(filename_t) { + TCHAR *fixed = NULL; + const TCHAR *target = NULL; + + if(fix_excessive_path(filename_t, &fixed)) + target = fixed; + else + target = filename_t; + /* !checksrc! disable BANNEDFUNC 1 */ + handle = CreateFile(target, + dwDesiredAccess, + dwShareMode, + lpSecurityAttributes, + dwCreationDisposition, + dwFlagsAndAttributes, + hTemplateFile); + CURLX_FREE(fixed); +#ifdef UNICODE + curlx_free(filename_t); +#endif + } + + return handle; +} +#endif /* !CURL_WINDOWS_UWP */ + +int curlx_win32_open(const char *filename, int oflag, ...) +{ + int pmode = 0; + int res = -1; + TCHAR *fixed = NULL; + const TCHAR *target = NULL; + +#ifdef _UNICODE + wchar_t *filename_w = fn_convert_UTF8_to_wchar(filename); +#endif + + va_list param; + va_start(param, oflag); + if(oflag & O_CREAT) + pmode = va_arg(param, int); + va_end(param); + +#ifdef _UNICODE + if(filename_w) { + if(fix_excessive_path(filename_w, &fixed)) + target = fixed; + else + target = filename_w; + errno = _wsopen_s(&res, target, oflag, _SH_DENYNO, pmode); + CURLX_FREE(filename_w); + } + else + /* !checksrc! disable ERRNOVAR 1 */ + errno = EINVAL; +#else + if(fix_excessive_path(filename, &fixed)) + target = fixed; + else + target = filename; + errno = _sopen_s(&res, target, oflag, _SH_DENYNO, pmode); +#endif + + CURLX_FREE(fixed); + return res; +} + +FILE *curlx_win32_fopen(const char *filename, const char *mode) +{ + FILE *file = NULL; + TCHAR *fixed = NULL; + const TCHAR *target = NULL; + +#ifdef _UNICODE + wchar_t *filename_w = fn_convert_UTF8_to_wchar(filename); + wchar_t *mode_w = fn_convert_UTF8_to_wchar(mode); + if(filename_w && mode_w) { + if(fix_excessive_path(filename_w, &fixed)) + target = fixed; + else + target = filename_w; + file = _wfsopen(target, mode_w, _SH_DENYNO); + } + else + /* !checksrc! disable ERRNOVAR 1 */ + errno = EINVAL; + CURLX_FREE(filename_w); + CURLX_FREE(mode_w); +#else + if(fix_excessive_path(filename, &fixed)) + target = fixed; + else + target = filename; + file = _fsopen(target, mode, _SH_DENYNO); +#endif + + CURLX_FREE(fixed); + return file; +} + +#if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR < 5) +_CRTIMP errno_t __cdecl freopen_s(FILE **file, const char *filename, + const char *mode, FILE *stream); +#endif + +FILE *curlx_win32_freopen(const char *filename, const char *mode, FILE *fp) +{ + FILE *file = NULL; + TCHAR *fixed = NULL; + const TCHAR *target = NULL; + +#ifdef _UNICODE + wchar_t *filename_w = fn_convert_UTF8_to_wchar(filename); + wchar_t *mode_w = fn_convert_UTF8_to_wchar(mode); + if(filename_w && mode_w) { + if(fix_excessive_path(filename_w, &fixed)) + target = fixed; + else + target = filename_w; + errno = _wfreopen_s(&file, target, mode_w, fp); + } + else + /* !checksrc! disable ERRNOVAR 1 */ + errno = EINVAL; + CURLX_FREE(filename_w); + CURLX_FREE(mode_w); +#else + if(fix_excessive_path(filename, &fixed)) + target = fixed; + else + target = filename; + errno = freopen_s(&file, target, mode, fp); +#endif + + CURLX_FREE(fixed); + return file; +} + +int curlx_win32_stat(const char *path, curlx_struct_stat *buffer) +{ + int res = -1; + TCHAR *fixed = NULL; + const TCHAR *target = NULL; + +#ifdef _UNICODE + wchar_t *path_w = curlx_convert_UTF8_to_wchar(path); + if(path_w) { + if(fix_excessive_path(path_w, &fixed)) + target = fixed; + else + target = path_w; + res = _wstati64(target, buffer); + curlx_free(path_w); + } + else + /* !checksrc! disable ERRNOVAR 1 */ + errno = EINVAL; +#else + if(fix_excessive_path(path, &fixed)) + target = fixed; + else + target = path; + res = _stati64(target, buffer); +#endif + + CURLX_FREE(fixed); + return res; +} + +#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() does 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 */ + +#ifdef UNICODE + TCHAR *tchar_oldpath = curlx_convert_UTF8_to_wchar(oldpath); + TCHAR *tchar_newpath = curlx_convert_UTF8_to_wchar(newpath); +#else + const TCHAR *tchar_oldpath = oldpath; + const TCHAR *tchar_newpath = newpath; +#endif + + if(tchar_oldpath && tchar_newpath) { + const int max_wait_ms = 1000; + struct curltime start; + + TCHAR *oldpath_fixed = NULL; + TCHAR *newpath_fixed = NULL; + const TCHAR *target_oldpath; + const TCHAR *target_newpath; + + if(fix_excessive_path(tchar_oldpath, &oldpath_fixed)) + target_oldpath = oldpath_fixed; + else + target_oldpath = tchar_oldpath; + + if(fix_excessive_path(tchar_newpath, &newpath_fixed)) + target_newpath = newpath_fixed; + else + target_newpath = tchar_newpath; + + start = curlx_now(); + + for(;;) { + timediff_t diff; + /* !checksrc! disable BANNEDFUNC 1 */ + if(MoveFileEx(target_oldpath, target_newpath, + MOVEFILE_REPLACE_EXISTING)) { + res = 0; /* success */ + break; + } + diff = curlx_timediff_ms(curlx_now(), start); + if(diff < 0 || diff > max_wait_ms) { + break; + } + Sleep(1); + } + + CURLX_FREE(oldpath_fixed); + CURLX_FREE(newpath_fixed); + } + +#ifdef UNICODE + curlx_free(tchar_oldpath); + curlx_free(tchar_newpath); +#endif + + return res; +} +#endif + +#undef CURLX_MALLOC +#undef CURLX_FREE + +#endif /* _WIN32 */ diff --git a/lib/curlx/fopen.h b/lib/curlx/fopen.h new file mode 100644 index 0000000000..b64fbf6514 --- /dev/null +++ b/lib/curlx/fopen.h @@ -0,0 +1,87 @@ +#ifndef HEADER_CURLX_FOPEN_H +#define HEADER_CURLX_FOPEN_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "curlx/multibyte.h" + +#ifdef HAVE_FCNTL_H +#include /* for open() and attributes */ +#endif + +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, + DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, + 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 *fp); +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 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_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 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__) +#define curlx_fdopen(file, mode) \ + curl_dbg_fdopen(file, mode, __LINE__, __FILE__) +#define curlx_fclose(file) curl_dbg_fclose(file, __LINE__, __FILE__) +#else +#define curlx_fopen CURLX_FOPEN_LOW +#define curlx_freopen CURLX_FREOPEN_LOW +#define curlx_fdopen CURLX_FDOPEN_LOW +#define curlx_fclose fclose +#endif + +#endif /* HEADER_CURLX_FOPEN_H */ diff --git a/lib/curlx/inet_ntop.c b/lib/curlx/inet_ntop.c index 3f8bcbf366..803b9887ac 100644 --- a/lib/curlx/inet_ntop.c +++ b/lib/curlx/inet_ntop.c @@ -16,11 +16,7 @@ * * SPDX-License-Identifier: ISC */ -/* - * Original code by Paul Vixie. "curlified" by Gisle Vanem. - */ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef HAVE_INET_NTOP @@ -34,7 +30,9 @@ #include #endif -#include "inet_ntop.h" +#include "curlx/inet_ntop.h" +#include "curlx/snprintf.h" +#include "curlx/strcopy.h" #define IN6ADDRSZ 16 /* #define INADDRSZ 4 */ @@ -64,24 +62,23 @@ static char *inet_ntop4(const unsigned char *src, char *dst, size_t size) DEBUGASSERT(size >= 16); - /* this sprintf() does not overflow the buffer. Avoids snprintf to work more - widely. Avoids the msnprintf family to work as a curlx function. */ - (void)(sprintf)(tmp, "%d.%d.%d.%d", - ((int)((unsigned char)src[0])) & 0xff, - ((int)((unsigned char)src[1])) & 0xff, - ((int)((unsigned char)src[2])) & 0xff, - ((int)((unsigned char)src[3])) & 0xff); + /* this snprintf() does not overflow the buffer. */ + SNPRINTF(tmp, sizeof(tmp), "%d.%d.%d.%d", + ((int)((unsigned char)src[0])) & 0xff, + ((int)((unsigned char)src[1])) & 0xff, + ((int)((unsigned char)src[2])) & 0xff, + ((int)((unsigned char)src[3])) & 0xff); len = strlen(tmp); if(len == 0 || len >= size) { #ifdef USE_WINSOCK - CURL_SETERRNO(WSAEINVAL); + errno = WSAEINVAL; #else - CURL_SETERRNO(ENOSPC); + errno = ENOSPC; #endif return NULL; } - strcpy(dst, tmp); + curlx_strcopy(dst, size, tmp, len); return dst; } @@ -112,17 +109,18 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) */ memset(words, '\0', sizeof(words)); for(i = 0; i < IN6ADDRSZ; i++) - words[i/2] |= ((unsigned int)src[i] << ((1 - (i % 2)) << 3)); + words[i / 2] |= ((unsigned int)src[i] << ((1 - (i % 2)) << 3)); best.base = -1; - cur.base = -1; + cur.base = -1; best.len = 0; cur.len = 0; for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { if(words[i] == 0) { if(cur.base == -1) { - cur.base = i; cur.len = 1; + cur.base = i; + cur.len = 1; } else cur.len++; @@ -155,7 +153,7 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) /* Is this address an encapsulated IPv4? */ if(i == 6 && best.base == 0 && - (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { + (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { if(!inet_ntop4(src + 12, tp, sizeof(tmp) - (tp - tmp))) { return NULL; } @@ -163,12 +161,12 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) break; } else { - /* Lower-case digits. Can't use the set from mprintf.c since this + /* Lower-case digits. Cannot use the set from mprintf.c since this needs to work as a curlx function */ 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) @@ -183,19 +181,18 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) */ if(best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) *tp++ = ':'; - *tp++ = '\0'; - /* Check for overflow, copy, and we are done. - */ - if((size_t)(tp - tmp) > size) { + /* Check for overflow, copy, and we are done. */ + if((size_t)(tp - tmp) >= size) { #ifdef USE_WINSOCK - CURL_SETERRNO(WSAEINVAL); + errno = WSAEINVAL; #else - CURL_SETERRNO(ENOSPC); + errno = ENOSPC; #endif return NULL; } - strcpy(dst, tmp); + + curlx_strcopy(dst, size, tmp, tp - tmp); return dst; } @@ -218,8 +215,8 @@ char *curlx_inet_ntop(int af, const void *src, char *buf, size_t size) case AF_INET6: return inet_ntop6((const unsigned char *)src, buf, size); default: - CURL_SETERRNO(SOCKEAFNOSUPPORT); + errno = SOCKEAFNOSUPPORT; return NULL; } } -#endif /* HAVE_INET_NTOP */ +#endif /* HAVE_INET_NTOP */ diff --git a/lib/curlx/inet_ntop.h b/lib/curlx/inet_ntop.h index 490f49e8a1..ba8299fe4e 100644 --- a/lib/curlx/inet_ntop.h +++ b/lib/curlx/inet_ntop.h @@ -23,8 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef HAVE_INET_NTOP #ifdef HAVE_NETINET_IN_H @@ -37,15 +36,15 @@ #include #endif #ifdef __AMIGA__ -#define curlx_inet_ntop(af,addr,buf,size) \ - (char *)inet_ntop(af, CURL_UNCONST(addr), (unsigned char *)buf, \ +#define curlx_inet_ntop(af, src, buf, size) \ + (char *)inet_ntop(af, CURL_UNCONST(src), (unsigned char *)(buf), \ (curl_socklen_t)(size)) #else -#define curlx_inet_ntop(af,addr,buf,size) \ - inet_ntop(af, addr, buf, (curl_socklen_t)(size)) +#define curlx_inet_ntop(af, src, buf, size) \ + inet_ntop(af, src, buf, (curl_socklen_t)(size)) #endif #else -char *curlx_inet_ntop(int af, const void *addr, char *buf, size_t size); +char *curlx_inet_ntop(int af, const void *src, char *buf, size_t size); #endif /* HAVE_INET_NTOP */ #endif /* HEADER_CURL_INET_NTOP_H */ diff --git a/lib/curlx/inet_pton.c b/lib/curlx/inet_pton.c index d2b39ae9f1..7994f258fe 100644 --- a/lib/curlx/inet_pton.c +++ b/lib/curlx/inet_pton.c @@ -17,10 +17,7 @@ * * SPDX-License-Identifier: ISC */ - -#include "../curl_setup.h" -#include "../curl_ctype.h" -#include "strparse.h" +#include "curl_setup.h" #ifndef HAVE_INET_PTON @@ -34,7 +31,8 @@ #include #endif -#include "inet_pton.h" +#include "curlx/inet_pton.h" +#include "curlx/strparse.h" #define IN6ADDRSZ 16 #define INADDRSZ 4 @@ -54,42 +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: - CURL_SETERRNO(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. @@ -98,8 +61,7 @@ curlx_inet_pton(int af, const char *src, void *dst) * author: * Paul Vixie, 1996. */ -static int -inet_pton4(const char *src, unsigned char *dst) +static int inet_pton4(const char *src, unsigned char *dst) { int saw_digit, octets, ch; unsigned char tmp[INADDRSZ], *tp; @@ -108,7 +70,7 @@ 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'); @@ -138,8 +100,7 @@ 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. @@ -151,8 +112,7 @@ inet_pton4(const char *src, unsigned char *dst) * author: * Paul Vixie, 1996. */ -static int -inet_pton6(const char *src, unsigned char *dst) +static int inet_pton6(const char *src, unsigned char *dst) { unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp; const char *curtok; @@ -169,10 +129,10 @@ 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 |= Curl_hexval(ch); + val |= curlx_hexval(ch); if(++saw_xdigit > 4) return 0; continue; @@ -187,14 +147,14 @@ inet_pton6(const char *src, unsigned char *dst) } if(tp + INT16SZ > endp) return 0; - *tp++ = (unsigned char) ((val >> 8) & 0xff); - *tp++ = (unsigned char) (val & 0xff); + *tp++ = (unsigned char)((val >> 8) & 0xff); + *tp++ = (unsigned char)(val & 0xff); saw_xdigit = 0; val = 0; continue; } if(ch == '.' && ((tp + INADDRSZ) <= endp) && - inet_pton4(curtok, tp) > 0) { + inet_pton4(curtok, tp) > 0) { tp += INADDRSZ; saw_xdigit = 0; break; /* '\0' was seen by inet_pton4(). */ @@ -204,13 +164,13 @@ inet_pton6(const char *src, unsigned char *dst) if(saw_xdigit) { if(tp + INT16SZ > endp) return 0; - *tp++ = (unsigned char) ((val >> 8) & 0xff); - *tp++ = (unsigned char) (val & 0xff); + *tp++ = (unsigned char)((val >> 8) & 0xff); + *tp++ = (unsigned char)(val & 0xff); } if(colonp) { /* * Since some memmove()'s erroneously fail to handle - * overlapping regions, we will do the shift by hand. + * overlapping regions, we do the shift by hand. */ const ssize_t n = tp - colonp; ssize_t i; @@ -229,4 +189,33 @@ 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/lib/curlx/inet_pton.h b/lib/curlx/inet_pton.h index a9ad24c944..02ae7f2269 100644 --- a/lib/curlx/inet_pton.h +++ b/lib/curlx/inet_pton.h @@ -23,8 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef HAVE_INET_PTON #ifdef HAVE_NETINET_IN_H @@ -37,12 +36,14 @@ #include #endif #ifdef __AMIGA__ -#define curlx_inet_pton(x,y,z) inet_pton(x,(unsigned char *)CURL_UNCONST(y),z) +#define curlx_inet_pton(x, y, z) \ + inet_pton(x, (unsigned char *)CURL_UNCONST(y), z) #else -#define curlx_inet_pton(x,y,z) inet_pton(x,y,z) +#define curlx_inet_pton(x, y, z) \ + 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/lib/curlx/multibyte.c b/lib/curlx/multibyte.c index 30380275cc..715d2b8dc2 100644 --- a/lib/curlx/multibyte.c +++ b/lib/curlx/multibyte.c @@ -21,20 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" -/* - * This file is 'mem-include-scan' clean, which means its memory allocations - * are not tracked by the curl memory tracker memdebug, so they must not use - * `CURLDEBUG` macro replacements in memdebug.h for free, malloc, etc. To avoid - * these macro replacements, wrap the names in parentheses to call the original - * versions: `ptr = (malloc)(123)`, `(free)(ptr)`, etc. - */ +#if defined(_WIN32) && defined(UNICODE) -#include "../curl_setup.h" - -#ifdef _WIN32 - -#include "multibyte.h" +#include "curlx/multibyte.h" /* * MultiByte conversions using Windows kernel32 library. @@ -48,11 +39,11 @@ wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8) int str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str_utf8, -1, NULL, 0); if(str_w_len > 0) { - str_w = (malloc)(str_w_len * sizeof(wchar_t)); + str_w = curlx_malloc(str_w_len * sizeof(wchar_t)); if(str_w) { if(MultiByteToWideChar(CP_UTF8, 0, str_utf8, -1, str_w, str_w_len) == 0) { - (free)(str_w); + curlx_free(str_w); return NULL; } } @@ -70,11 +61,11 @@ char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w) int bytes = WideCharToMultiByte(CP_UTF8, 0, str_w, -1, NULL, 0, NULL, NULL); if(bytes > 0) { - str_utf8 = (malloc)(bytes); + str_utf8 = curlx_malloc(bytes); if(str_utf8) { if(WideCharToMultiByte(CP_UTF8, 0, str_w, -1, str_utf8, bytes, NULL, NULL) == 0) { - (free)(str_utf8); + curlx_free(str_utf8); return NULL; } } @@ -84,277 +75,4 @@ char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w) return str_utf8; } -#ifndef UNDER_CE - -/* declare GetFullPathNameW for mingw-w64 UWP builds targeting old windows */ -#if defined(CURL_WINDOWS_UWP) && defined(__MINGW32__) && \ - (_WIN32_WINNT < _WIN32_WINNT_WIN10) -WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR *); -#endif - -/* Fix excessive paths (paths that exceed MAX_PATH length of 260). - * - * This is a helper function to fix paths that would exceed the MAX_PATH - * limitation check done by Windows APIs. It does so by normalizing the passed - * in filename or path 'in' to its full canonical path, and if that path is - * longer than MAX_PATH then setting 'out' to "\\?\" prefix + that full path. - * - * For example 'in' filename255chars in current directory C:\foo\bar is - * fixed as \\?\C:\foo\bar\filename255chars for 'out' which will tell Windows - * it is ok to access that filename even though the actual full path is longer - * than 260 chars. - * - * For non-Unicode builds this function may fail sometimes because only the - * Unicode versions of some Windows API functions can access paths longer than - * MAX_PATH, for example GetFullPathNameW which is used in this function. When - * the full path is then converted from Unicode to multibyte that fails if any - * directories in the path contain characters not in the current codepage. - */ -static bool fix_excessive_path(const TCHAR *in, TCHAR **out) -{ - size_t needed, count; - const wchar_t *in_w; - wchar_t *fbuf = NULL; - - /* MS documented "approximate" limit for the maximum path length */ - const size_t max_path_len = 32767; - -#ifndef _UNICODE - wchar_t *ibuf = NULL; - char *obuf = NULL; -#endif - - *out = NULL; - - /* skip paths already normalized */ - if(!_tcsncmp(in, _T("\\\\?\\"), 4)) - goto cleanup; - -#ifndef _UNICODE - /* convert multibyte input to unicode */ - needed = mbstowcs(NULL, in, 0); - if(needed == (size_t)-1 || needed >= max_path_len) - goto cleanup; - ++needed; /* for NUL */ - ibuf = (malloc)(needed * sizeof(wchar_t)); - if(!ibuf) - goto cleanup; - count = mbstowcs(ibuf, in, needed); - if(count == (size_t)-1 || count >= needed) - goto cleanup; - in_w = ibuf; -#else - in_w = in; -#endif - - /* GetFullPathNameW returns the normalized full path in unicode. It converts - forward slashes to backslashes, processes .. to remove directory segments, - etc. Unlike GetFullPathNameA it can process paths that exceed MAX_PATH. */ - needed = (size_t)GetFullPathNameW(in_w, 0, NULL, NULL); - if(!needed || needed > max_path_len) - goto cleanup; - /* skip paths that are not excessive and do not need modification */ - if(needed <= MAX_PATH) - goto cleanup; - fbuf = (malloc)(needed * sizeof(wchar_t)); - if(!fbuf) - goto cleanup; - count = (size_t)GetFullPathNameW(in_w, (DWORD)needed, fbuf, NULL); - if(!count || count >= needed) - goto cleanup; - - /* prepend \\?\ or \\?\UNC\ to the excessively long path. - * - * c:\longpath ---> \\?\c:\longpath - * \\.\c:\longpath ---> \\?\c:\longpath - * \\?\c:\longpath ---> \\?\c:\longpath (unchanged) - * \\server\c$\longpath ---> \\?\UNC\server\c$\longpath - * - * https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats - */ - if(!wcsncmp(fbuf, L"\\\\?\\", 4)) - ; /* do nothing */ - else if(!wcsncmp(fbuf, L"\\\\.\\", 4)) - fbuf[2] = '?'; - else if(!wcsncmp(fbuf, L"\\\\.", 3) || !wcsncmp(fbuf, L"\\\\?", 3)) { - /* Unexpected, not UNC. The formatting doc doesn't allow this AFAICT. */ - goto cleanup; - } - else { - wchar_t *temp; - - if(!wcsncmp(fbuf, L"\\\\", 2)) { - /* "\\?\UNC\" + full path without "\\" + null */ - needed = 8 + (count - 2) + 1; - if(needed > max_path_len) - goto cleanup; - - temp = (malloc)(needed * sizeof(wchar_t)); - if(!temp) - goto cleanup; - - wcsncpy(temp, L"\\\\?\\UNC\\", 8); - wcscpy(temp + 8, fbuf + 2); - } - else { - /* "\\?\" + full path + null */ - needed = 4 + count + 1; - if(needed > max_path_len) - goto cleanup; - - temp = (malloc)(needed * sizeof(wchar_t)); - if(!temp) - goto cleanup; - - wcsncpy(temp, L"\\\\?\\", 4); - wcscpy(temp + 4, fbuf); - } - - (free)(fbuf); - fbuf = temp; - } - -#ifndef _UNICODE - /* convert unicode full path to multibyte output */ - needed = wcstombs(NULL, fbuf, 0); - if(needed == (size_t)-1 || needed >= max_path_len) - goto cleanup; - ++needed; /* for NUL */ - obuf = (malloc)(needed); - if(!obuf) - goto cleanup; - count = wcstombs(obuf, fbuf, needed); - if(count == (size_t)-1 || count >= needed) - goto cleanup; - *out = obuf; - obuf = NULL; -#else - *out = fbuf; - fbuf = NULL; -#endif - -cleanup: - (free)(fbuf); -#ifndef _UNICODE - (free)(ibuf); - (free)(obuf); -#endif - return *out ? true : false; -} - -int curlx_win32_open(const char *filename, int oflag, ...) -{ - int pmode = 0; - int result = -1; - TCHAR *fixed = NULL; - const TCHAR *target = NULL; - -#ifdef _UNICODE - wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename); -#endif - - va_list param; - va_start(param, oflag); - if(oflag & O_CREAT) - pmode = va_arg(param, int); - va_end(param); - -#ifdef _UNICODE - if(filename_w) { - if(fix_excessive_path(filename_w, &fixed)) - target = fixed; - else - target = filename_w; - result = _wopen(target, oflag, pmode); - curlx_unicodefree(filename_w); - } - else - /* !checksrc! disable ERRNOVAR 1 */ - CURL_SETERRNO(EINVAL); -#else - if(fix_excessive_path(filename, &fixed)) - target = fixed; - else - target = filename; - result = _open(target, oflag, pmode); -#endif - - (free)(fixed); - return result; -} - -FILE *curlx_win32_fopen(const char *filename, const char *mode) -{ - FILE *result = NULL; - TCHAR *fixed = NULL; - const TCHAR *target = NULL; - -#ifdef _UNICODE - wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename); - wchar_t *mode_w = curlx_convert_UTF8_to_wchar(mode); - if(filename_w && mode_w) { - if(fix_excessive_path(filename_w, &fixed)) - target = fixed; - else - target = filename_w; - result = _wfopen(target, mode_w); - } - else - /* !checksrc! disable ERRNOVAR 1 */ - CURL_SETERRNO(EINVAL); - curlx_unicodefree(filename_w); - curlx_unicodefree(mode_w); -#else - if(fix_excessive_path(filename, &fixed)) - target = fixed; - else - target = filename; - result = (fopen)(target, mode); -#endif - - (free)(fixed); - return result; -} - -int curlx_win32_stat(const char *path, struct_stat *buffer) -{ - int result = -1; - TCHAR *fixed = NULL; - const TCHAR *target = NULL; - -#ifdef _UNICODE - wchar_t *path_w = curlx_convert_UTF8_to_wchar(path); - if(path_w) { - if(fix_excessive_path(path_w, &fixed)) - target = fixed; - else - target = path_w; -#ifndef USE_WIN32_LARGE_FILES - result = _wstat(target, buffer); -#else - result = _wstati64(target, buffer); -#endif - curlx_unicodefree(path_w); - } - else - /* !checksrc! disable ERRNOVAR 1 */ - CURL_SETERRNO(EINVAL); -#else - if(fix_excessive_path(path, &fixed)) - target = fixed; - else - target = path; -#ifndef USE_WIN32_LARGE_FILES - result = _stat(target, buffer); -#else - result = _stati64(target, buffer); -#endif -#endif - - (free)(fixed); - return result; -} - -#endif /* UNDER_CE */ - -#endif /* _WIN32 */ +#endif /* _WIN32 && UNICODE */ diff --git a/lib/curlx/multibyte.h b/lib/curlx/multibyte.h index c60ce258c9..8863fb0249 100644 --- a/lib/curlx/multibyte.h +++ b/lib/curlx/multibyte.h @@ -23,38 +23,30 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef _WIN32 -/* MultiByte conversions using Windows kernel32 library. */ -wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8); -char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w); -#endif /* * Macros curlx_convert_UTF8_to_tchar(), curlx_convert_tchar_to_UTF8() - * and curlx_unicodefree() main purpose is to minimize the number of - * preprocessor conditional directives needed by code using these - * to differentiate Unicode from non-Unicode builds. + * main purpose is to minimize the number of preprocessor conditional + * directives needed by code using these to differentiate Unicode from + * non-Unicode builds. * * In the case of a non-Unicode build the tchar strings are char strings that * are duplicated via strdup and remain in whatever the passed in encoding is, * which is assumed to be UTF-8 but may be other encoding. Therefore the * significance of the conversion functions is primarily for Unicode builds. - * - * Allocated memory should be free'd with curlx_unicodefree(). - * - * Note: Because these are curlx functions their memory usage is not tracked - * by the curl memory tracker memdebug. you will notice that curlx - * function-like macros call free and strdup in parentheses, eg (strdup)(ptr), - * and that is to ensure that the curl memdebug override macros do not replace - * them. */ -#if defined(UNICODE) && defined(_WIN32) +#ifdef UNICODE -#define curlx_convert_UTF8_to_tchar(ptr) curlx_convert_UTF8_to_wchar((ptr)) -#define curlx_convert_tchar_to_UTF8(ptr) curlx_convert_wchar_to_UTF8((ptr)) +/* MultiByte conversions using Windows kernel32 library. */ +wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8); +char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w); + +#define curlx_convert_UTF8_to_tchar(ptr) curlx_convert_UTF8_to_wchar(ptr) +#define curlx_convert_tchar_to_UTF8(ptr) curlx_convert_wchar_to_UTF8(ptr) typedef union { unsigned short *tchar_ptr; @@ -63,10 +55,10 @@ typedef union { const unsigned short *const_tbyte_ptr; } xcharp_u; -#else +#else /* !UNICODE */ -#define curlx_convert_UTF8_to_tchar(ptr) (strdup)(ptr) -#define curlx_convert_tchar_to_UTF8(ptr) (strdup)(ptr) +#define curlx_convert_UTF8_to_tchar(ptr) curlx_strdup(ptr) +#define curlx_convert_tchar_to_UTF8(ptr) curlx_strdup(ptr) typedef union { char *tchar_ptr; @@ -75,9 +67,7 @@ typedef union { const unsigned char *const_tbyte_ptr; } xcharp_u; -#endif /* UNICODE && _WIN32 */ - -/* the purpose of this macro is to free() without being traced by memdebug */ -#define curlx_unicodefree(ptr) (free)(CURL_UNCONST(ptr)) +#endif /* UNICODE */ +#endif /* _WIN32 */ #endif /* HEADER_CURL_MULTIBYTE_H */ diff --git a/lib/curlx/nonblock.c b/lib/curlx/nonblock.c index b944f9546b..6f6458f2b3 100644 --- a/lib/curlx/nonblock.c +++ b/lib/curlx/nonblock.c @@ -21,8 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef HAVE_SYS_IOCTL_H #include @@ -36,7 +35,7 @@ #include #endif -#include "nonblock.h" +#include "curlx/nonblock.h" /* * curlx_nonblock() set the given socket to either blocking or non-blocking @@ -88,6 +87,6 @@ int curlx_nonblock(curl_socket_t sockfd, /* operate on this */ return setsockopt(sockfd, SOL_SOCKET, SO_NONBLOCK, &b, sizeof(b)); #else -# error "no non-blocking method was found/used/set" +#error "no non-blocking method was found/used/set" #endif } diff --git a/lib/curlx/nonblock.h b/lib/curlx/nonblock.h index 4a1a6151f2..ee2cd286a7 100644 --- a/lib/curlx/nonblock.h +++ b/lib/curlx/nonblock.h @@ -24,8 +24,6 @@ * ***************************************************************************/ -#include /* for curl_socket_t */ - int curlx_nonblock(curl_socket_t sockfd, /* operate on this */ int nonblock /* TRUE or FALSE */); diff --git a/lib/curlx/binmode.h b/lib/curlx/snprintf.c similarity index 62% rename from lib/curlx/binmode.h rename to lib/curlx/snprintf.c index 3f356edd59..911c42e6ad 100644 --- a/lib/curlx/binmode.h +++ b/lib/curlx/snprintf.c @@ -1,5 +1,3 @@ -#ifndef HEADER_CURL_TOOL_BINMODE_H -#define HEADER_CURL_TOOL_BINMODE_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -23,17 +21,29 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curlx/snprintf.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) -#else -# define CURLX_SET_BINMODE(stream) (void)setmode(fileno(stream), O_BINARY) -#endif -#else -# define CURLX_SET_BINMODE(stream) (void)stream; Curl_nop_stmt -#endif +#ifdef _WIN32 +#include -#endif /* HEADER_CURL_TOOL_BINMODE_H */ +/* 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); +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + /* !checksrc! disable BANNEDFUNC 1 */ + (void)vsnprintf(buf, maxlen, fmt, ap); +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic pop +#endif + buf[maxlen - 1] = 0; + va_end(ap); +} +#endif /* _WIN32 */ diff --git a/lib/curl_mem_undef.h b/lib/curlx/snprintf.h similarity index 67% rename from lib/curl_mem_undef.h rename to lib/curlx/snprintf.h index b72e529dde..d6260e4e39 100644 --- a/lib/curl_mem_undef.h +++ b/lib/curlx/snprintf.h @@ -1,3 +1,5 @@ +#ifndef HEADER_CURLX_SNPRINTF_H +#define HEADER_CURLX_SNPRINTF_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -21,35 +23,23 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" -/* Unset redefined system symbols. */ - -#undef strdup -#undef malloc -#undef calloc -#undef realloc -#undef free -#undef send -#undef recv +/* Raw snprintf() for curlx */ #ifdef _WIN32 -#undef _tcsdup +void curlx_win32_snprintf(char *buf, size_t maxlen, const char *fmt, ...) + CURL_PRINTF(3, 4); #endif -#undef socket -#ifdef HAVE_ACCEPT4 -#undef accept4 +#ifdef WITHOUT_LIBCURL /* when built for the test servers */ +#ifdef _WIN32 +#define SNPRINTF curlx_win32_snprintf +#else +#define SNPRINTF snprintf #endif -#ifdef HAVE_SOCKETPAIR -#undef socketpair -#endif - -#undef fopen -#ifdef CURL_FOPEN -#define fopen(fname, mode) CURL_FOPEN(fname, mode) -#endif -#undef fdopen -#undef fclose - -#undef HEADER_CURL_MEMORY_H -#undef HEADER_CURL_MEMDEBUG_H +#else /* !WITHOUT_LIBCURL */ +#include +#define SNPRINTF curl_msnprintf +#endif /* WITHOUT_LIBCURL */ +#endif /* HEADER_CURLX_SNPRINTF_H */ diff --git a/lib/curlx/strcopy.c b/lib/curlx/strcopy.c new file mode 100644 index 0000000000..3e58ea24f1 --- /dev/null +++ b/lib/curlx/strcopy.c @@ -0,0 +1,50 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "curlx/strcopy.h" + +/* + * curlx_strcopy() is a replacement for strcpy. + * + * Provide the target buffer @dest and size of the target buffer @dsize, If + * the source string @src with its *string length* @slen fits in the target + * buffer it is copied there - including storing a null terminator. + * + * If the target buffer is too small, the copy is not performed but if the + * target buffer has a non-zero size it gets a null terminator stored. + */ +void curlx_strcopy(char *dest, /* destination buffer */ + size_t dsize, /* size of target buffer */ + const char *src, /* source string */ + size_t slen) /* length of source string to copy */ +{ + DEBUGASSERT(slen < dsize); + if(slen < dsize) { + memcpy(dest, src, slen); + dest[slen] = 0; + } + else if(dsize) + dest[0] = 0; +} diff --git a/lib/curlx/strcopy.h b/lib/curlx/strcopy.h new file mode 100644 index 0000000000..d671149cb8 --- /dev/null +++ b/lib/curlx/strcopy.h @@ -0,0 +1,32 @@ +#ifndef HEADER_CURLX_STRCOPY_H +#define HEADER_CURLX_STRCOPY_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 + * + ***************************************************************************/ + +void curlx_strcopy(char *dest, + size_t dsize, /* size of target buffer */ + const char *src, + size_t slen); /* length of string to copy */ + +#endif /* HEADER_CURLX_STRCOPY_H */ diff --git a/lib/strdup.c b/lib/curlx/strdup.c similarity index 61% rename from lib/strdup.c rename to lib/curlx/strdup.c index 66fd7c60eb..3c967dbe0a 100644 --- a/lib/strdup.c +++ b/lib/curlx/strdup.c @@ -21,66 +21,39 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #ifdef _WIN32 #include #endif -#include "strdup.h" -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.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 = 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. @@ -88,9 +61,9 @@ 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 = malloc(length); + void *buffer = curlx_malloc(length); if(!buffer) return NULL; /* fail */ @@ -101,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. @@ -109,9 +82,9 @@ 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 = malloc(length + 1); + char *buf = (length < SIZE_MAX) ? curlx_malloc(length + 1) : NULL; if(!buf) return NULL; if(length) { @@ -121,26 +94,3 @@ void *Curl_memdup0(const char *src, size_t length) buf[length] = 0; return buf; } - -/*************************************************************************** - * - * Curl_saferealloc(ptr, size) - * - * Does a normal 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 = realloc(ptr, size); - if(size && !datap) - /* only free 'ptr' if size was non-zero */ - free(ptr); - return datap; -} diff --git a/lib/strdup.h b/lib/curlx/strdup.h similarity index 75% rename from lib/strdup.h rename to lib/curlx/strdup.h index 238a2611f6..0c4b14161e 100644 --- a/lib/strdup.h +++ b/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,14 +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/lib/curlx/strerr.c b/lib/curlx/strerr.c new file mode 100644 index 0000000000..b53173c578 --- /dev/null +++ b/lib/curlx/strerr.c @@ -0,0 +1,329 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef HAVE_STRERROR_R +# if (!defined(HAVE_POSIX_STRERROR_R) && \ + !defined(HAVE_GLIBC_STRERROR_R)) || \ + (defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)) +# error "strerror_r MUST be either POSIX, glibc style" +# endif +#endif + +#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 + * codes (WSAGetLastError) to error messages. + * Returns NULL if no error message was found for error code. + */ +static const char *get_winsock_error(int err, char *buf, size_t len) +{ + VERBOSE(const char *p); + + if(!len) + return NULL; + + *buf = '\0'; + +#ifndef CURLVERBOSE + (void)err; + return NULL; +#else + switch(err) { + case WSAEINTR: + p = "Call interrupted"; + break; + case WSAEBADF: + p = "Bad file"; + break; + case WSAEACCES: + p = "Bad access"; + break; + case WSAEFAULT: + p = "Bad argument"; + break; + case WSAEINVAL: + p = "Invalid arguments"; + break; + case WSAEMFILE: + p = "Out of file descriptors"; + break; + case WSAEWOULDBLOCK: + p = "Call would block"; + break; + case WSAEINPROGRESS: + case WSAEALREADY: + p = "Blocking call in progress"; + break; + case WSAENOTSOCK: + p = "Descriptor is not a socket"; + break; + case WSAEDESTADDRREQ: + p = "Need destination address"; + break; + case WSAEMSGSIZE: + p = "Bad message size"; + break; + case WSAEPROTOTYPE: + p = "Bad protocol"; + break; + case WSAENOPROTOOPT: + p = "Protocol option is unsupported"; + break; + case WSAEPROTONOSUPPORT: + p = "Protocol is unsupported"; + break; + case WSAESOCKTNOSUPPORT: + p = "Socket is unsupported"; + break; + case WSAEOPNOTSUPP: + p = "Operation not supported"; + break; + case WSAEAFNOSUPPORT: + p = "Address family not supported"; + break; + case WSAEPFNOSUPPORT: + p = "Protocol family not supported"; + break; + case WSAEADDRINUSE: + p = "Address already in use"; + break; + case WSAEADDRNOTAVAIL: + p = "Address not available"; + break; + case WSAENETDOWN: + p = "Network down"; + break; + case WSAENETUNREACH: + p = "Network unreachable"; + break; + case WSAENETRESET: + p = "Network has been reset"; + break; + case WSAECONNABORTED: + p = "Connection was aborted"; + break; + case WSAECONNRESET: + p = "Connection was reset"; + break; + case WSAENOBUFS: + p = "No buffer space"; + break; + case WSAEISCONN: + p = "Socket is already connected"; + break; + case WSAENOTCONN: + p = "Socket is not connected"; + break; + case WSAESHUTDOWN: + p = "Socket has been shut down"; + break; + case WSAETOOMANYREFS: + p = "Too many references"; + break; + case WSAETIMEDOUT: + p = "Timed out"; + break; + case WSAECONNREFUSED: + p = "Connection refused"; + break; + case WSAELOOP: + p = "Loop??"; + break; + case WSAENAMETOOLONG: + p = "Name too long"; + break; + case WSAEHOSTDOWN: + p = "Host down"; + break; + case WSAEHOSTUNREACH: + p = "Host unreachable"; + break; + case WSAENOTEMPTY: + p = "Not empty"; + break; + case WSAEPROCLIM: + p = "Process limit reached"; + break; + case WSAEUSERS: + p = "Too many users"; + break; + case WSAEDQUOT: + p = "Bad quota"; + break; + case WSAESTALE: + p = "Something is stale"; + break; + case WSAEREMOTE: + p = "Remote error"; + break; + case WSAEDISCON: + p = "Disconnected"; + break; + /* Extended Winsock errors */ + case WSASYSNOTREADY: + p = "Winsock library is not ready"; + break; + case WSANOTINITIALISED: + p = "Winsock library not initialised"; + break; + case WSAVERNOTSUPPORTED: + p = "Winsock version not supported"; + break; + + /* getXbyY() errors (already handled in herrmsg): + * Authoritative Answer: Host not found */ + case WSAHOST_NOT_FOUND: + p = "Host not found"; + break; + + /* Non-Authoritative: Host not found, or SERVERFAIL */ + case WSATRY_AGAIN: + p = "Host not found, try again"; + break; + + /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ + case WSANO_RECOVERY: + p = "Unrecoverable error in call to nameserver"; + break; + + /* Valid name, no data record of requested type */ + case WSANO_DATA: + p = "No data record of requested type"; + break; + + default: + return NULL; + } + curlx_strcopy(buf, len, p, strlen(p)); + return buf; +#endif +} +#endif /* USE_WINSOCK */ + +/* + * Our thread-safe and smart strerror() replacement. + * + * The 'err' argument passed in to this function MUST be a true errno number + * as reported on this system. We do no range checking on the number before + * we pass it to the "number-to-message" conversion function and there might + * be systems that do not do proper range checking in there themselves. + * + * We do not do range checking (on systems other than Windows) since there is + * no good reliable and portable way to do it. + * + * On Windows different types of error codes overlap. This function has an + * order of preference when trying to match error codes: + * CRT (errno), Winsock (WSAGetLastError), Windows API (GetLastError). + * + * It may be more correct to call one of the variant functions instead: + * Call Curl_sspi_strerror if the error code is definitely Windows SSPI. + * Call curlx_winapi_strerror if the error code is definitely Windows API. + */ +const char *curlx_strerror(int err, char *buf, size_t buflen) +{ +#ifdef _WIN32 + DWORD old_win_err = GetLastError(); +#endif + int old_errno = errno; + char *p; + + if(!buflen) + return NULL; + +#ifndef _WIN32 + DEBUGASSERT(err >= 0); +#endif + + *buf = '\0'; + +#ifdef _WIN32 + if((strerror_s(buf, buflen, err) || !strcmp(buf, "Unknown error")) && +#ifdef USE_WINSOCK + !get_winsock_error(err, buf, buflen) && +#endif + !curlx_get_winapi_error((DWORD)err, buf, buflen)) + SNPRINTF(buf, buflen, "Unknown error %d (%#x)", err, err); +#else /* !_WIN32 */ + +#if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R) + /* + * The POSIX-style strerror_r() may set errno to ERANGE if insufficient + * storage is supplied via 'strerrbuf' and 'buflen' to hold the generated + * message string, or EINVAL if 'errnum' is not a valid error number. + */ + if(strerror_r(err, buf, buflen) && + buflen > sizeof("Unknown error ") + 20) { + if(buf[0] == '\0') + SNPRINTF(buf, buflen, "Unknown error %d", err); + } +#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R) + /* + * The glibc-style strerror_r() only *might* use the buffer we pass to + * the function, but it always returns the error message as a pointer, + * so we must copy that string unconditionally (if non-NULL). + */ + { + char buffer[256]; + 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) + SNPRINTF(buf, buflen, "Unknown error %d", err); + } +#else + { + /* !checksrc! disable BANNEDFUNC 1 */ + const char *msg = strerror(err); + if(msg && buflen > 1) + SNPRINTF(buf, buflen, "%s", msg); + else if(buflen > sizeof("Unknown error ") + 20) + SNPRINTF(buf, buflen, "Unknown error %d", err); + } +#endif + +#endif /* _WIN32 */ + + /* strip trailing '\r\n' or '\n'. */ + p = strrchr(buf, '\n'); + if(p && (p - buf) >= 2) + *p = '\0'; + p = strrchr(buf, '\r'); + if(p && (p - buf) >= 1) + *p = '\0'; + + if(errno != old_errno) + errno = old_errno; + +#ifdef _WIN32 + if(old_win_err != GetLastError()) + SetLastError(old_win_err); +#endif + + return buf; +} diff --git a/lib/rename.h b/lib/curlx/strerr.h similarity index 87% rename from lib/rename.h rename to lib/curlx/strerr.h index 04440820c5..4413e6738c 100644 --- a/lib/rename.h +++ b/lib/curlx/strerr.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_RENAME_H -#define HEADER_CURL_RENAME_H +#ifndef HEADER_CURL_STRERR_H +#define HEADER_CURL_STRERR_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -24,6 +24,6 @@ * ***************************************************************************/ -int Curl_rename(const char *oldpath, const char *newpath); +const char *curlx_strerror(int err, char *buf, size_t buflen); -#endif /* HEADER_CURL_RENAME_H */ +#endif /* HEADER_CURL_STRERR_H */ diff --git a/lib/curlx/strparse.c b/lib/curlx/strparse.c index a29d8be2fd..7866b2087a 100644 --- a/lib/curlx/strparse.c +++ b/lib/curlx/strparse.c @@ -21,12 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "strparse.h" - -#ifndef WITHOUT_LIBCURL -#include /* for curl_strnequal() */ -#endif +#include "curlx/strparse.h" void curlx_str_init(struct Curl_str *out) { @@ -40,6 +35,15 @@ void curlx_str_assign(struct Curl_str *out, const char *str, size_t len) out->len = len; } +/* remove bytes from the end of the string, never remove more bytes than what + the string holds! */ +void curlx_str_trim(struct Curl_str *out, size_t len) +{ + DEBUGASSERT(out); + DEBUGASSERT(out->len >= len); + out->len -= len; +} + /* Get a word until the first DELIM or end of string. At least one byte long. return non-zero on error */ int curlx_str_until(const char **linep, struct Curl_str *out, @@ -66,8 +70,7 @@ int curlx_str_until(const char **linep, struct Curl_str *out, /* Get a word until the first space or end of string. At least one byte long. return non-zero on error */ -int curlx_str_word(const char **linep, struct Curl_str *out, - const size_t max) +int curlx_str_word(const char **linep, struct Curl_str *out, const size_t max) { return curlx_str_until(linep, out, max, ' '); } @@ -95,8 +98,7 @@ int curlx_str_untilnl(const char **linep, struct Curl_str *out, return STRE_OK; } - -/* Get a "quoted" word. No escaping possible. +/* Get a "quoted" word. Escaped quotes are supported. return non-zero on error */ int curlx_str_quotedword(const char **linep, struct Curl_str *out, const size_t max) @@ -110,6 +112,11 @@ int curlx_str_quotedword(const char **linep, struct Curl_str *out, return STRE_BEGQUOTE; s++; while(*s && (*s != '\"')) { + if(*s == '\\' && s[1]) { + s++; + if(++len > max) + return STRE_BIG; + } s++; if(++len > max) return STRE_BIG; @@ -141,13 +148,13 @@ 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) && Curl_hexasciitable[(x)-'0']) +#define valid_digit(x, m) \ + (((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 use the info */ -const unsigned char Curl_hexasciitable[] = { +const unsigned char curlx_hexasciitable[] = { 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* 0x30: 0 - 9 */ 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, /* 0x41: A - F */ @@ -173,18 +180,18 @@ static int str_num_base(const char **linep, curl_off_t *nump, curl_off_t max, if(max < base) { /* special-case low max scenario because check needs to be different */ do { - int n = Curl_hexval(*p++); - num = num * base + n; + int n = curlx_hexval(*p++); + num = (num * base) + n; if(num > max) return STRE_OVERFLOW; } while(valid_digit(*p, m)); } else { do { - int n = Curl_hexval(*p++); + 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/lib/curlx/strparse.h b/lib/curlx/strparse.h index 6a0bf28d6c..c7801b2cb3 100644 --- a/lib/curlx/strparse.h +++ b/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 @@ -44,8 +44,9 @@ struct Curl_str { void curlx_str_init(struct Curl_str *out); void curlx_str_assign(struct Curl_str *out, const char *str, size_t len); +void curlx_str_trim(struct Curl_str *out, size_t len); -#define curlx_str(x) ((x)->str) +#define curlx_str(x) ((x)->str) #define curlx_strlen(x) ((x)->len) /* Get a word until the first space @@ -62,7 +63,7 @@ int curlx_str_until(const char **linep, struct Curl_str *out, const size_t max, int curlx_str_untilnl(const char **linep, struct Curl_str *out, const size_t max); -/* Get a "quoted" word. No escaping possible. +/* Get a "quoted" word. Escaped quotes are supported. return non-zero on error */ int curlx_str_quotedword(const char **linep, struct Curl_str *out, const size_t max); @@ -98,7 +99,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); @@ -106,7 +108,7 @@ void curlx_str_passblanks(const char **linep); returns 10. THIS ONLY WORKS ON VALID HEXADECIMAL LETTER INPUT. Verify before calling this! */ -extern const unsigned char Curl_hexasciitable[]; -#define Curl_hexval(x) (unsigned char)(Curl_hexasciitable[(x) - '0'] & 0x0f) +extern const unsigned char curlx_hexasciitable[]; +#define curlx_hexval(x) (unsigned char)(curlx_hexasciitable[(x) - '0'] & 0x0f) #endif /* HEADER_CURL_STRPARSE_H */ diff --git a/lib/curlx/timediff.c b/lib/curlx/timediff.c index a90da961ab..2bf786941d 100644 --- a/lib/curlx/timediff.c +++ b/lib/curlx/timediff.c @@ -21,10 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "timediff.h" - -#include +#include "curlx/timediff.h" /* * Converts number of milliseconds into a timeval structure. @@ -84,5 +81,5 @@ struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms) */ timediff_t curlx_tvtoms(struct timeval *tv) { - return (tv->tv_sec*1000) + (timediff_t)(tv->tv_usec/1000); + return (tv->tv_sec * 1000) + (timediff_t)(tv->tv_usec / 1000); } diff --git a/lib/curlx/timediff.h b/lib/curlx/timediff.h index aa224381dd..1081c75d1c 100644 --- a/lib/curlx/timediff.h +++ b/lib/curlx/timediff.h @@ -23,8 +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/lib/curlx/timeval.c b/lib/curlx/timeval.c index bd8b9bcee2..2363e60839 100644 --- a/lib/curlx/timeval.c +++ b/lib/curlx/timeval.c @@ -21,89 +21,53 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "timeval.h" +#include "curlx/timeval.h" #ifdef _WIN32 -#include -#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 */ -struct curltime curlx_now(void) +void curlx_pnow(struct curltime *pnow) { - struct curltime now; - 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); - now.tv_sec = (time_t)(count.QuadPart / freq.QuadPart); - now.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 - - now.tv_sec = (time_t)(milliseconds / 1000); - now.tv_usec = (int)((milliseconds % 1000) * 1000); - } - return now; + 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) || \ +#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) || \ defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW) -struct curltime curlx_now(void) +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. - */ -#ifdef HAVE_GETTIMEOFDAY - struct timeval now; -#endif - struct curltime cnow; + * 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) + (HAVE_BUILTIN_AVAILABLE == 1) bool have_clock_gettime = FALSE; if(__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *)) have_clock_gettime = TRUE; @@ -111,61 +75,59 @@ struct curltime curlx_now(void) #ifdef HAVE_CLOCK_GETTIME_MONOTONIC_RAW if( -#if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ - (HAVE_BUILTIN_AVAILABLE == 1) +#if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ + (HAVE_BUILTIN_AVAILABLE == 1) have_clock_gettime && #endif (clock_gettime(CLOCK_MONOTONIC_RAW, &tsnow) == 0)) { - cnow.tv_sec = tsnow.tv_sec; - cnow.tv_usec = (int)(tsnow.tv_nsec / 1000); + pnow->tv_sec = tsnow.tv_sec; + pnow->tv_usec = (int)(tsnow.tv_nsec / 1000); } else #endif if( #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ - (HAVE_BUILTIN_AVAILABLE == 1) + (HAVE_BUILTIN_AVAILABLE == 1) have_clock_gettime && #endif (clock_gettime(CLOCK_MONOTONIC, &tsnow) == 0)) { - cnow.tv_sec = tsnow.tv_sec; - cnow.tv_usec = (int)(tsnow.tv_nsec / 1000); + pnow->tv_sec = tsnow.tv_sec; + 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; (void)gettimeofday(&now, NULL); - cnow.tv_sec = now.tv_sec; - cnow.tv_usec = (int)now.tv_usec; + pnow->tv_sec = now.tv_sec; + pnow->tv_usec = (int)now.tv_usec; } #else else { - cnow.tv_sec = time(NULL); - cnow.tv_usec = 0; + pnow->tv_sec = time(NULL); + pnow->tv_usec = 0; } #endif - return cnow; } #elif defined(HAVE_MACH_ABSOLUTE_TIME) -#include #include -struct curltime curlx_now(void) +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; - struct curltime cnow; uint64_t usecs; if(timebase.denom == 0) @@ -176,84 +138,136 @@ struct curltime curlx_now(void) usecs /= timebase.denom; usecs /= 1000; - cnow.tv_sec = usecs / 1000000; - cnow.tv_usec = (int)(usecs % 1000000); - - return cnow; + pnow->tv_sec = usecs / 1000000; + pnow->tv_usec = (int)(usecs % 1000000); } #elif defined(HAVE_GETTIMEOFDAY) -struct curltime curlx_now(void) +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; - struct curltime ret; (void)gettimeofday(&now, NULL); - ret.tv_sec = now.tv_sec; - ret.tv_usec = (int)now.tv_usec; - return ret; + pnow->tv_sec = now.tv_sec; + pnow->tv_usec = (int)now.tv_usec; } #else -struct curltime curlx_now(void) +void curlx_pnow(struct curltime *pnow) { /* - ** time() returns the value of time in seconds since the Epoch. - */ - struct curltime now; - now.tv_sec = time(NULL); - now.tv_usec = 0; - return now; + * time() returns the value of time in seconds since the Epoch. + */ + pnow->tv_sec = time(NULL); + pnow->tv_usec = 0; + if(!pnow->tv_sec) /* avoid a `now` fully zero */ + pnow->tv_usec = 1; } #endif +struct curltime curlx_now(void) +{ + struct curltime now; + curlx_pnow(&now); + return now; +} + /* * Returns: time difference in number of milliseconds. For too large diffs it * returns max value. * * @unittest: 1323 */ -timediff_t curlx_timediff(struct curltime newer, struct curltime older) +timediff_t curlx_ptimediff_ms(const struct curltime *newer, + const struct curltime *older) { - timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec; - if(diff >= (TIMEDIFF_T_MAX/1000)) + timediff_t diff = (timediff_t)newer->tv_sec - older->tv_sec; + if(diff >= (TIMEDIFF_T_MAX / 1000)) return TIMEDIFF_T_MAX; - else if(diff <= (TIMEDIFF_T_MIN/1000)) + 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); } /* * Returns: time difference in number of milliseconds, rounded up. * For too large diffs it returns max value. */ -timediff_t curlx_timediff_ceil(struct curltime newer, struct curltime older) +timediff_t curlx_timediff_ceil_ms(struct curltime newer, + struct curltime older) { - timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec; - if(diff >= (TIMEDIFF_T_MAX/1000)) + timediff_t diff = (timediff_t)newer.tv_sec - older.tv_sec; + if(diff >= (TIMEDIFF_T_MAX / 1000)) return TIMEDIFF_T_MAX; - else if(diff <= (TIMEDIFF_T_MIN/1000)) + 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); } /* * Returns: time difference in number of microseconds. For too large diffs it * returns max value. */ +timediff_t curlx_ptimediff_us(const struct curltime *newer, + const struct curltime *older) +{ + timediff_t diff = (timediff_t)newer->tv_sec - older->tv_sec; + if(diff >= (TIMEDIFF_T_MAX / 1000000)) + return TIMEDIFF_T_MAX; + else if(diff <= (TIMEDIFF_T_MIN / 1000000)) + return TIMEDIFF_T_MIN; + return (diff * 1000000) + newer->tv_usec - older->tv_usec; +} + timediff_t curlx_timediff_us(struct curltime newer, struct curltime older) { - timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec; - if(diff >= (TIMEDIFF_T_MAX/1000000)) - 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 curlx_ptimediff_us(&newer, &older); +} + +#if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 3) +#include /* for _gmtime32_s(), _gmtime64_s() */ +#ifdef _USE_32BIT_TIME_T +#define gmtime_s _gmtime32_s +#else +#define gmtime_s _gmtime64_s +#endif +#endif + +/* + * curlx_gmtime() is a gmtime() replacement for portability. Do not use + * the gmtime_s(), gmtime_r() or gmtime() functions anywhere else but here. + */ +CURLcode curlx_gmtime(time_t intime, struct tm *store) +{ +#ifdef _WIN32 + if(gmtime_s(store, &intime)) /* thread-safe */ + return CURLE_BAD_FUNCTION_ARGUMENT; +#elif defined(HAVE_GMTIME_R) + const struct tm *tm; + tm = gmtime_r(&intime, store); /* thread-safe */ + if(!tm) + return CURLE_BAD_FUNCTION_ARGUMENT; +#else + const struct tm *tm; + /* !checksrc! disable BANNEDFUNC 1 */ + tm = gmtime(&intime); /* not thread-safe */ + if(tm) + *store = *tm; /* copy the pointed struct to the local copy */ + else + return CURLE_BAD_FUNCTION_ARGUMENT; +#endif + + return CURLE_OK; } diff --git a/lib/curlx/timeval.h b/lib/curlx/timeval.h index 1f8fe5e8ad..c01f95d87d 100644 --- a/lib/curlx/timeval.h +++ b/lib/curlx/timeval.h @@ -23,10 +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 */ @@ -39,6 +38,7 @@ void curlx_now_init(void); #endif struct curltime curlx_now(void); +void curlx_pnow(struct curltime *pnow); /* * Make sure that the first argument (newer) is the more recent time and older @@ -46,7 +46,9 @@ struct curltime curlx_now(void); * * Returns: the time difference in number of milliseconds. */ -timediff_t curlx_timediff(struct curltime newer, struct curltime older); +timediff_t curlx_timediff_ms(struct curltime newer, struct curltime older); +timediff_t curlx_ptimediff_ms(const struct curltime *newer, + const struct curltime *older); /* * Make sure that the first argument (newer) is the more recent time and older @@ -54,7 +56,8 @@ timediff_t curlx_timediff(struct curltime newer, struct curltime older); * * Returns: the time difference in number of milliseconds, rounded up. */ -timediff_t curlx_timediff_ceil(struct curltime newer, struct curltime older); +timediff_t curlx_timediff_ceil_ms(struct curltime newer, + struct curltime older); /* * Make sure that the first argument (newer) is the more recent time and older @@ -63,5 +66,9 @@ timediff_t curlx_timediff_ceil(struct curltime newer, struct curltime older); * Returns: the time difference in number of microseconds. */ timediff_t curlx_timediff_us(struct curltime newer, struct curltime older); +timediff_t curlx_ptimediff_us(const struct curltime *newer, + const struct curltime *older); + +CURLcode curlx_gmtime(time_t intime, struct tm *store); #endif /* HEADER_CURL_TIMEVAL_H */ diff --git a/lib/curlx/version_win32.c b/lib/curlx/version_win32.c index 8d0af68fcf..296a38d924 100644 --- a/lib/curlx/version_win32.c +++ b/lib/curlx/version_win32.c @@ -21,19 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef _WIN32 -#include -#include "version_win32.h" -#include "warnless.h" - -/* The last 2 #include files should be in this order */ -#include "../curl_memory.h" -#include "../memdebug.h" +#include "curlx/version_win32.h" +#ifndef CURL_WINDOWS_UWP /* This Unicode version struct works for VerifyVersionInfoW (OSVERSIONINFOEXW) and RtlVerifyVersionInfo (RTLOSVERSIONINFOEXW) */ struct OUR_OSVERSIONINFOEXW { @@ -50,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() * @@ -111,12 +123,6 @@ bool curlx_verify_windows_version(const unsigned int majorVersion, /* we are always running on PLATFORM_WINNT */ matched = FALSE; } -#elif defined(UNDER_CE) - (void)majorVersion; - (void)minorVersion; - (void)buildVersion; - (void)platform; - (void)condition; #else ULONGLONG cm = 0; struct OUR_OSVERSIONINFOEXW osver; @@ -128,17 +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) { - pRtlVerifyVersionInfo = CURLX_FUNCTION_CAST(RTLVERIFYVERSIONINFO_FN, - (GetProcAddress(GetModuleHandleA("ntdll"), "RtlVerifyVersionInfo"))); - onetime = FALSE; - } - switch(condition) { case VERSION_LESS_THAN: majorCondition = VER_LESS; @@ -209,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); @@ -228,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/lib/curlx/version_win32.h b/lib/curlx/version_win32.h index 471100a66f..c4a1c0f758 100644 --- a/lib/curlx/version_win32.h +++ b/lib/curlx/version_win32.h @@ -23,8 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef _WIN32 @@ -44,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/lib/curlx/wait.c b/lib/curlx/wait.c index 4e10a8297a..e50a0f0af6 100644 --- a/lib/curlx/wait.c +++ b/lib/curlx/wait.c @@ -21,15 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef HAVE_SELECT #error "We cannot compile without select() support." #endif -#include - #ifdef HAVE_SYS_SELECT_H #include #elif defined(HAVE_UNISTD_H) @@ -40,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 is returned 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 @@ -74,7 +71,7 @@ int curlx_wait_ms(timediff_t timeout_ms) /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */ #if TIMEDIFF_T_MAX >= ULONG_MAX if(timeout_ms >= ULONG_MAX) - timeout_ms = ULONG_MAX-1; + timeout_ms = ULONG_MAX - 1; /* do not use ULONG_MAX, because that is equal to INFINITE */ #endif Sleep((DWORD)timeout_ms); diff --git a/lib/curlx/wait.h b/lib/curlx/wait.h index 208bc20acc..9b9c27386e 100644 --- a/lib/curlx/wait.h +++ b/lib/curlx/wait.h @@ -23,8 +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/lib/curlx/warnless.c b/lib/curlx/warnless.c index bb636a9327..5fc36ac9a8 100644 --- a/lib/curlx/warnless.c +++ b/lib/curlx/warnless.c @@ -21,22 +21,21 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" -#include "warnless.h" +#include "curlx/warnless.h" #if defined(__INTEL_COMPILER) && defined(__unix__) #ifdef HAVE_NETINET_IN_H -# include +#include #endif #ifdef HAVE_ARPA_INET_H -# include +#include #endif #endif /* __INTEL_COMPILER && __unix__ */ -#include - #define CURL_MASK_UCHAR ((unsigned char)~0) #define CURL_MASK_USHORT ((unsigned short)~0) @@ -50,243 +49,293 @@ #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 -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +#pragma warning(push) +#pragma warning(disable:810) /* conversion may lose significant bits */ #endif - DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_UCHAR); - return (unsigned char)(ulnum & (unsigned long) CURL_MASK_UCHAR); + DEBUGASSERT(ulnum <= (unsigned long)CURL_MASK_UCHAR); + return (unsigned char)(ulnum & (unsigned long)CURL_MASK_UCHAR); #ifdef __INTEL_COMPILER -# pragma warning(pop) +#pragma warning(pop) #endif } /* -** unsigned size_t to signed int -*/ - + * unsigned size_t to signed int + */ int curlx_uztosi(size_t uznum) { #ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +#pragma warning(push) +#pragma warning(disable:810) /* conversion may lose significant bits */ #endif - DEBUGASSERT(uznum <= (size_t) CURL_MASK_SINT); - return (int)(uznum & (size_t) CURL_MASK_SINT); + DEBUGASSERT(uznum <= (size_t)CURL_MASK_SINT); + return (int)(uznum & (size_t)CURL_MASK_SINT); #ifdef __INTEL_COMPILER -# pragma warning(pop) +#pragma warning(pop) #endif } /* -** unsigned size_t to unsigned long -*/ - + * unsigned size_t to unsigned long + */ unsigned long curlx_uztoul(size_t uznum) { #ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +#pragma warning(push) +#pragma warning(disable:810) /* conversion may lose significant bits */ #endif #if ULONG_MAX < SIZE_MAX - DEBUGASSERT(uznum <= (size_t) CURL_MASK_ULONG); + DEBUGASSERT(uznum <= (size_t)CURL_MASK_ULONG); #endif - return (unsigned long)(uznum & (size_t) CURL_MASK_ULONG); + return (unsigned long)(uznum & (size_t)CURL_MASK_ULONG); #ifdef __INTEL_COMPILER -# pragma warning(pop) +#pragma warning(pop) #endif } /* -** unsigned size_t to unsigned int -*/ - + * unsigned size_t to unsigned int + */ unsigned int curlx_uztoui(size_t uznum) { #ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +#pragma warning(push) +#pragma warning(disable:810) /* conversion may lose significant bits */ #endif #if UINT_MAX < SIZE_MAX - DEBUGASSERT(uznum <= (size_t) CURL_MASK_UINT); + DEBUGASSERT(uznum <= (size_t)CURL_MASK_UINT); #endif - return (unsigned int)(uznum & (size_t) CURL_MASK_UINT); + return (unsigned int)(uznum & (size_t)CURL_MASK_UINT); #ifdef __INTEL_COMPILER -# pragma warning(pop) +#pragma warning(pop) #endif } /* -** signed long to signed int -*/ - + * signed long to signed int + */ int curlx_sltosi(long slnum) { #ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +#pragma warning(push) +#pragma warning(disable:810) /* conversion may lose significant bits */ #endif DEBUGASSERT(slnum >= 0); #if INT_MAX < LONG_MAX - DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_SINT); + DEBUGASSERT((unsigned long)slnum <= (unsigned long)CURL_MASK_SINT); #endif - return (int)(slnum & (long) CURL_MASK_SINT); + return (int)(slnum & (long)CURL_MASK_SINT); #ifdef __INTEL_COMPILER -# pragma warning(pop) +#pragma warning(pop) #endif } /* -** signed long to unsigned int -*/ - + * signed long to unsigned int + */ unsigned int curlx_sltoui(long slnum) { #ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +#pragma warning(push) +#pragma warning(disable:810) /* conversion may lose significant bits */ #endif DEBUGASSERT(slnum >= 0); #if UINT_MAX < LONG_MAX - DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_UINT); + DEBUGASSERT((unsigned long)slnum <= (unsigned long)CURL_MASK_UINT); #endif - return (unsigned int)(slnum & (long) CURL_MASK_UINT); + return (unsigned int)(slnum & (long)CURL_MASK_UINT); #ifdef __INTEL_COMPILER -# pragma warning(pop) +#pragma warning(pop) #endif } /* -** signed long to unsigned short -*/ - + * signed long to unsigned short + */ unsigned short curlx_sltous(long slnum) { #ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +#pragma warning(push) +#pragma warning(disable:810) /* conversion may lose significant bits */ #endif DEBUGASSERT(slnum >= 0); - DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_USHORT); - return (unsigned short)(slnum & (long) CURL_MASK_USHORT); + DEBUGASSERT((unsigned long)slnum <= (unsigned long)CURL_MASK_USHORT); + return (unsigned short)(slnum & (long)CURL_MASK_USHORT); #ifdef __INTEL_COMPILER -# pragma warning(pop) +#pragma warning(pop) #endif } /* -** unsigned size_t to signed ssize_t -*/ - + * unsigned size_t to signed ssize_t + */ ssize_t curlx_uztosz(size_t uznum) { #ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +#pragma warning(push) +#pragma warning(disable:810) /* conversion may lose significant bits */ #endif - DEBUGASSERT(uznum <= (size_t) CURL_MASK_SSIZE_T); - return (ssize_t)(uznum & (size_t) CURL_MASK_SSIZE_T); + DEBUGASSERT(uznum <= (size_t)CURL_MASK_SSIZE_T); + return (ssize_t)(uznum & (size_t)CURL_MASK_SSIZE_T); #ifdef __INTEL_COMPILER -# pragma warning(pop) +#pragma warning(pop) #endif } /* -** 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 -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +#pragma warning(push) +#pragma warning(disable:810) /* conversion may lose significant bits */ #endif DEBUGASSERT(sonum >= 0); - return (size_t)(sonum & (curl_off_t) CURL_MASK_USIZE_T); + return (size_t)(sonum & (curl_off_t)CURL_MASK_USIZE_T); #ifdef __INTEL_COMPILER -# pragma warning(pop) +#pragma warning(pop) #endif } /* -** signed ssize_t to signed int -*/ - + * signed ssize_t to signed int + */ int curlx_sztosi(ssize_t sznum) { #ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +#pragma warning(push) +#pragma warning(disable:810) /* conversion may lose significant bits */ #endif DEBUGASSERT(sznum >= 0); #if INT_MAX < SSIZE_MAX - DEBUGASSERT((size_t) sznum <= (size_t) CURL_MASK_SINT); + DEBUGASSERT((size_t)sznum <= (size_t)CURL_MASK_SINT); #endif - return (int)(sznum & (ssize_t) CURL_MASK_SINT); + return (int)(sznum & (ssize_t)CURL_MASK_SINT); #ifdef __INTEL_COMPILER -# pragma warning(pop) +#pragma warning(pop) #endif } /* -** unsigned int to unsigned short -*/ - + * unsigned int to unsigned short + */ unsigned short curlx_uitous(unsigned int uinum) { #ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +#pragma warning(push) +#pragma warning(disable:810) /* conversion may lose significant bits */ #endif - DEBUGASSERT(uinum <= (unsigned int) CURL_MASK_USHORT); - return (unsigned short) (uinum & (unsigned int) CURL_MASK_USHORT); + DEBUGASSERT(uinum <= (unsigned int)CURL_MASK_USHORT); + return (unsigned short)(uinum & (unsigned int)CURL_MASK_USHORT); #ifdef __INTEL_COMPILER -# pragma warning(pop) +#pragma warning(pop) #endif } /* -** signed int to unsigned size_t -*/ - + * signed int to unsigned size_t + */ size_t curlx_sitouz(int sinum) { #ifdef __INTEL_COMPILER -# pragma warning(push) -# pragma warning(disable:810) /* conversion may lose significant bits */ +#pragma warning(push) +#pragma warning(disable:810) /* conversion may lose significant bits */ #endif DEBUGASSERT(sinum >= 0); - return (size_t) sinum; + return (size_t)sinum; #ifdef __INTEL_COMPILER -# pragma warning(pop) +#pragma warning(pop) #endif } + +size_t curlx_uitouz(unsigned int uinum) +{ + return (size_t)uinum; +} + +size_t curlx_sotouz_range(curl_off_t sonum, size_t uzmin, size_t uzmax) +{ + if(sonum < 0) + return uzmin; +#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T + if(sonum > SIZE_MAX) + return uzmax; +#endif + return CURLMIN(CURLMAX((size_t)sonum, uzmin), uzmax); +} + +bool curlx_sztouz(ssize_t sznum, size_t *puznum) +{ + if(sznum < 0) { + *puznum = 0; + return FALSE; + } + *puznum = (size_t)sznum; + return TRUE; +} + +bool curlx_sotouz_fits(curl_off_t sonum, size_t *puznum) +{ + if(sonum < 0) { + *puznum = 0; + return FALSE; + } +#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T + if(sonum > SIZE_MAX) { + *puznum = 0; + return FALSE; + } +#endif + *puznum = (size_t)sonum; + return TRUE; +} + +bool curlx_sltouz(long slnum, size_t *puznum) +{ + if(slnum < 0) { + *puznum = 0; + return FALSE; + } + /* We error in curl_setup.h if SIZEOF_LONG > SIZEOF_SIZE_T */ + *puznum = (size_t)slnum; + return TRUE; +} + +curl_off_t curlx_uztoso(size_t uznum) +{ +#if SIZEOF_SIZE_T >= SIZEOF_CURL_OFF_T + if(uznum > (size_t)CURL_OFF_T_MAX) + return CURL_OFF_T_MAX; +#endif + return (curl_off_t)uznum; +} diff --git a/lib/curlx/warnless.h b/lib/curlx/warnless.h index 0a0c608073..3d0341e418 100644 --- a/lib/curlx/warnless.h +++ b/lib/curlx/warnless.h @@ -24,14 +24,8 @@ * ***************************************************************************/ -#include "../curl_setup.h" - -#ifdef USE_WINSOCK -#include /* for curl_socket_t */ -#endif - #define CURLX_FUNCTION_CAST(target_type, func) \ - (target_type)(void (*) (void))(func) + (target_type)(void (*)(void))(func) unsigned char curlx_ultouc(unsigned long ulnum); @@ -57,11 +51,24 @@ unsigned short curlx_uitous(unsigned int uinum); size_t curlx_sitouz(int sinum); -#ifdef _WIN32 -#undef read -#define read(fd, buf, count) (ssize_t)_read(fd, buf, curlx_uztoui(count)) -#undef write -#define write(fd, buf, count) (ssize_t)_write(fd, buf, curlx_uztoui(count)) -#endif +size_t curlx_uitouz(unsigned int uinum); + +/* Convert a curl_off_t to fit into size_t interval [uzmin, uzmax]. + * values outside this interval give the lower/upper bound. */ +size_t curlx_sotouz_range(curl_off_t sonum, size_t uzmin, size_t uzmax); + +/* Convert a size_t to curl_off_t, return CURL_OFF_T_MAX if too large. */ +curl_off_t curlx_uztoso(size_t uznum); + +/* Convert a ssize_t to size_t, return FALSE if negative and set 0 */ +bool curlx_sztouz(ssize_t sznum, size_t *puznum); + +/* Convert a curl_off_t to size_t, return FALSE if negative or + * too large and set 0 */ +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 slnum, size_t *puznum); #endif /* HEADER_CURL_WARNLESS_H */ diff --git a/lib/curlx/winapi.c b/lib/curlx/winapi.c index 6069424bec..f025ca48c9 100644 --- a/lib/curlx/winapi.c +++ b/lib/curlx/winapi.c @@ -21,78 +21,53 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" /* * curlx_winapi_strerror: - * Variant of Curl_strerror if the error code is definitely Windows API. + * Variant of curlx_strerror if the error code is definitely Windows API. */ #ifdef _WIN32 -#include "winapi.h" +#include "curlx/winapi.h" +#include "curlx/snprintf.h" +#include "curlx/strcopy.h" -#ifdef BUILDING_LIBCURL -#include -#define SNPRINTF curl_msnprintf -#else -/* when built for the test servers */ - -/* adjust for old MSVC */ -#if defined(_MSC_VER) && (_MSC_VER < 1900) -# define SNPRINTF _snprintf -#else -#define SNPRINTF snprintf -#endif - -#endif /* !BUILDING_LIBCURL */ - -#ifdef _WIN32 -/* This is a helper function for Curl_strerror that converts Windows API error +/* This is a helper function for curlx_strerror that converts Windows API error * codes (GetLastError) to error messages. * Returns NULL if no error message was found for error code. */ -const char *curlx_get_winapi_error(int err, char *buf, size_t buflen) +const char *curlx_get_winapi_error(DWORD err, char *buf, size_t buflen) { char *p; - wchar_t wbuf[256]; 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). - FormatMessageW -> wcstombs is used for Windows CE compatibility. */ - if(FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS), NULL, (DWORD)err, - LANG_NEUTRAL, wbuf, CURL_ARRAYSIZE(wbuf), NULL)) { - size_t written = wcstombs(buf, wbuf, buflen - 1); - if(written != (size_t)-1) - buf[written] = '\0'; - else - *buf = '\0'; + output to the user's terminal, it is likely done with functions which + expect the local codepage (eg fprintf, failf, infof). */ + 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'; + if(p > buf && *(p - 1) == '\r') + *(p - 1) = '\0'; else *p = '\0'; } return *buf ? buf : NULL; } -#endif /* _WIN32 */ const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen) { -#ifdef _WIN32 DWORD old_win_err = GetLastError(); -#endif int old_errno = errno; if(!buflen) @@ -100,35 +75,31 @@ const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen) *buf = '\0'; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(!curlx_get_winapi_error((int)err, buf, buflen)) { +#ifdef CURLVERBOSE + if(!curlx_get_winapi_error(err, buf, buflen)) { #if defined(__GNUC__) && __GNUC__ >= 7 #pragma GCC diagnostic push #pragma GCC diagnostic warning "-Wformat-truncation=1" #endif /* some GCC compilers cause false positive warnings if we allow this warning */ - SNPRINTF(buf, buflen, "Unknown error %lu (0x%08lX)", err, err); + SNPRINTF(buf, buflen, "Unknown error %lu (0x%08lx)", err, err); #if defined(__GNUC__) && __GNUC__ >= 7 #pragma GCC diagnostic pop #endif - } #else { const char *txt = (err == ERROR_SUCCESS) ? "No error" : "Error"; - if(strlen(txt) < buflen) - strcpy(buf, txt); + curlx_strcopy(buf, buflen, txt, strlen(txt)); } #endif if(errno != old_errno) - CURL_SETERRNO(old_errno); + errno = old_errno; -#ifdef _WIN32 if(old_win_err != GetLastError()) SetLastError(old_win_err); -#endif return buf; } diff --git a/lib/curlx/winapi.h b/lib/curlx/winapi.h index 76ddcc53b8..d30f5efa13 100644 --- a/lib/curlx/winapi.h +++ b/lib/curlx/winapi.h @@ -26,7 +26,7 @@ #ifdef _WIN32 #define WINAPI_ERROR_LEN 100 -const char *curlx_get_winapi_error(int err, char *buf, size_t buflen); +const char *curlx_get_winapi_error(DWORD err, char *buf, size_t buflen); const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen); #endif diff --git a/lib/cw-out.c b/lib/cw-out.c index 6d988a00bf..f841725e46 100644 --- a/lib/cw-out.c +++ b/lib/cw-out.c @@ -21,25 +21,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "urldata.h" #include "cfilters.h" -#include "headers.h" #include "multiif.h" #include "sendf.h" +#include "curl_trc.h" #include "transfer.h" #include "cw-out.h" #include "cw-pause.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - +#include "progress.h" /** * OVERALL DESIGN of this client writer @@ -86,7 +78,7 @@ struct cw_out_buf { static struct cw_out_buf *cw_out_buf_create(cw_out_type otype) { - struct cw_out_buf *cwbuf = calloc(1, sizeof(*cwbuf)); + struct cw_out_buf *cwbuf = curlx_calloc(1, sizeof(*cwbuf)); if(cwbuf) { cwbuf->type = otype; curlx_dyn_init(&cwbuf->b, DYN_PAUSE_BUFFER); @@ -98,7 +90,7 @@ static void cw_out_buf_free(struct cw_out_buf *cwbuf) { if(cwbuf) { curlx_dyn_free(&cwbuf->b); - free(cwbuf); + curlx_free(cwbuf); } } @@ -109,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) { @@ -206,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); @@ -214,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; } @@ -227,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; } @@ -250,10 +228,10 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx, curl_write_callback wcb = NULL; void *wcb_data; size_t max_write, min_write; - size_t wlen, nwritten; - CURLcode result; + size_t wlen, nwritten = 0; + CURLcode result = CURLE_OK; - /* If we errored once, we do not invoke the client callback again */ + /* If we errored once, we do not invoke the client callback again */ if(ctx->errored) return CURLE_WRITE_ERROR; @@ -275,10 +253,15 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx, if(!flush_all && blen < min_write) break; wlen = max_write ? CURLMIN(blen, max_write) : blen; - result = cw_out_cb_write(ctx, data, wcb, wcb_data, otype, - buf, wlen, &nwritten); + if(otype == CW_OUT_BODY) + result = Curl_pgrs_deliver_check(data, wlen); + if(!result) + result = cw_out_cb_write(ctx, data, wcb, wcb_data, otype, + buf, wlen, &nwritten); if(result) return result; + if(otype == CW_OUT_BODY) + Curl_pgrs_deliver_inc(data, nwritten); *pconsumed += nwritten; blen -= nwritten; buf += nwritten; @@ -303,6 +286,7 @@ static CURLcode cw_out_buf_flush(struct cw_out_ctx *ctx, &consumed); if(result && (result != CURLE_AGAIN)) return result; + result = CURLE_OK; if(consumed) { if(consumed == curlx_dyn_len(&cwbuf->b)) { @@ -371,8 +355,8 @@ static CURLcode cw_out_append(struct cw_out_ctx *ctx, } /* if we do not have a buffer, or it is of another type, make a new one. - * And for CW_OUT_HDS always make a new one, so we "replay" headers - * exactly as they came in */ + * For CW_OUT_HDS always make a new one, so we "replay" headers exactly + * as they came in */ if(!ctx->buf || (ctx->buf->type != otype) || (otype == CW_OUT_HDS)) { struct cw_out_buf *cwbuf = cw_out_buf_create(otype); if(!cwbuf) @@ -404,7 +388,7 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx, /* still have buffered data, append and flush */ result = cw_out_append(ctx, data, otype, buf, blen); if(result) - return result; + goto out; result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all); if(result) goto out; @@ -453,7 +437,7 @@ static CURLcode cw_out_write(struct Curl_easy *data, return result; } - if(type & (CLIENTWRITE_HEADER|CLIENTWRITE_INFO)) { + if(type & (CLIENTWRITE_HEADER | CLIENTWRITE_INFO)) { result = cw_out_do_write(ctx, data, CW_OUT_HDS, flush_all, buf, blen); if(result) return result; @@ -462,6 +446,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; @@ -472,7 +465,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/lib/cw-out.h b/lib/cw-out.h index 89b9985bb5..7de6524bc5 100644 --- a/lib/cw-out.h +++ b/lib/cw-out.h @@ -23,10 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include "sendf.h" +struct Curl_easy; /** * The client writer type "cw-out" that does the actual writing to diff --git a/lib/cw-pause.c b/lib/cw-pause.c index 9b9554c551..ec611879d1 100644 --- a/lib/cw-pause.c +++ b/lib/cw-pause.c @@ -21,29 +21,20 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "urldata.h" #include "bufq.h" #include "cfilters.h" -#include "headers.h" -#include "multiif.h" #include "sendf.h" +#include "curl_trc.h" #include "cw-pause.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - /* 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; @@ -53,12 +44,12 @@ struct cw_pause_buf { static struct cw_pause_buf *cw_pause_buf_create(int type, size_t buflen) { - struct cw_pause_buf *cwbuf = calloc(1, sizeof(*cwbuf)); + struct cw_pause_buf *cwbuf = curlx_calloc(1, sizeof(*cwbuf)); if(cwbuf) { cwbuf->type = type; if(type & CLIENTWRITE_BODY) Curl_bufq_init2(&cwbuf->b, CW_PAUSE_BUF_CHUNK, 1, - (BUFQ_OPT_SOFT_LIMIT|BUFQ_OPT_NO_SPARES)); + (BUFQ_OPT_SOFT_LIMIT | BUFQ_OPT_NO_SPARES)); else Curl_bufq_init(&cwbuf->b, buflen, 1); } @@ -69,7 +60,7 @@ static void cw_pause_buf_free(struct cw_pause_buf *cwbuf) { if(cwbuf) { Curl_bufq_free(&cwbuf->b); - free(cwbuf); + curlx_free(cwbuf); } } @@ -79,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) { @@ -229,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/lib/cw-pause.h b/lib/cw-pause.h index 2aa1a499cd..544cbfa577 100644 --- a/lib/cw-pause.h +++ b/lib/cw-pause.h @@ -23,10 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include "sendf.h" +struct Curl_easy; /** * The client writer type "cw-pause" that buffers writes for @@ -36,5 +35,4 @@ extern const struct Curl_cwtype Curl_cwt_pause; CURLcode Curl_cw_pause_flush(struct Curl_easy *data); - #endif /* HEADER_CURL_CW_PAUSE_H */ diff --git a/lib/dict.c b/lib/dict.c index 819584f284..7b83c6cff1 100644 --- a/lib/dict.c +++ b/lib/dict.c @@ -21,8 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" +#include "urldata.h" +#include "dict.h" #ifndef CURL_DISABLE_DICT @@ -52,62 +53,17 @@ #include #endif -#include "urldata.h" -#include #include "transfer.h" -#include "sendf.h" +#include "curl_trc.h" #include "escape.h" -#include "progress.h" -#include "dict.h" -#include "curl_printf.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -#define DICT_MATCH "/MATCH:" -#define DICT_MATCH2 "/M:" -#define DICT_MATCH3 "/FIND:" -#define DICT_DEFINE "/DEFINE:" +#define DICT_MATCH "/MATCH:" +#define DICT_MATCH2 "/M:" +#define DICT_MATCH3 "/FIND:" +#define DICT_DEFINE "/DEFINE:" #define DICT_DEFINE2 "/D:" #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) { @@ -144,7 +100,7 @@ static CURLcode sendf(struct Curl_easy *data, const char *fmt, ...) char *sptr; va_list ap; va_start(ap, fmt); - s = vaprintf(fmt, ap); /* returns an allocated string */ + s = curl_mvaprintf(fmt, ap); /* returns an allocated string */ va_end(ap); if(!s) return CURLE_OUT_OF_MEMORY; /* failure */ @@ -160,9 +116,9 @@ static CURLcode sendf(struct Curl_easy *data, const char *fmt, ...) if(result) break; - Curl_debug(data, CURLINFO_DATA_OUT, sptr, (size_t)bytes_written); + Curl_debug(data, CURLINFO_DATA_OUT, sptr, bytes_written); - if((size_t)bytes_written != write_len) { + if(bytes_written != write_len) { /* if not all was written at once, we must advance the pointer, decrease the size left and try again! */ write_len -= bytes_written; @@ -172,7 +128,7 @@ static CURLcode sendf(struct Curl_easy *data, const char *fmt, ...) break; } - free(s); /* free the output string */ + curlx_free(s); /* free the output string */ return result; } @@ -197,9 +153,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) if(result) return result; - if(curl_strnequal(path, DICT_MATCH, sizeof(DICT_MATCH)-1) || - curl_strnequal(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) || - curl_strnequal(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) { + if(curl_strnequal(path, DICT_MATCH, sizeof(DICT_MATCH) - 1) || + curl_strnequal(path, DICT_MATCH2, sizeof(DICT_MATCH2) - 1) || + curl_strnequal(path, DICT_MATCH3, sizeof(DICT_MATCH3) - 1)) { word = strchr(path, ':'); if(word) { @@ -244,9 +200,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) } Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); } - else if(curl_strnequal(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) || - curl_strnequal(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) || - curl_strnequal(path, DICT_DEFINE3, sizeof(DICT_DEFINE3)-1)) { + else if(curl_strnequal(path, DICT_DEFINE, sizeof(DICT_DEFINE) - 1) || + curl_strnequal(path, DICT_DEFINE2, sizeof(DICT_DEFINE2) - 1) || + curl_strnequal(path, DICT_DEFINE3, sizeof(DICT_DEFINE3) - 1)) { word = strchr(path, ':'); if(word) { @@ -310,8 +266,32 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) } error: - free(eword); - free(path); + curlx_free(eword); + curlx_free(path); return result; } + +/* + * DICT protocol + */ +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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* CURL_DISABLE_DICT */ diff --git a/lib/dict.h b/lib/dict.h index ba9a92719b..5bdfcff816 100644 --- a/lib/dict.h +++ b/lib/dict.h @@ -23,9 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #ifndef CURL_DISABLE_DICT -extern const struct Curl_handler Curl_handler_dict; +extern const struct Curl_protocol Curl_protocol_dict; #endif #endif /* HEADER_CURL_DICT_H */ diff --git a/lib/dllmain.c b/lib/dllmain.c index 7ac457ae05..f715b6d301 100644 --- a/lib/dllmain.c +++ b/lib/dllmain.c @@ -21,24 +21,18 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef USE_OPENSSL #include #endif -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - /* DllMain() must only be defined for Windows DLL builds. */ #if defined(_WIN32) && !defined(CURL_STATICLIB) #if defined(USE_OPENSSL) && \ !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) && \ - !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L + !defined(LIBRESSL_VERSION_NUMBER) #define PREVENT_OPENSSL_MEMLEAK #endif @@ -65,6 +59,6 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) } return TRUE; } -#endif /* OpenSSL */ +#endif /* USE_OPENSSL (non-fork) */ -#endif /* DLL build */ +#endif /* _WIN32 && !CURL_STATICLIB */ diff --git a/lib/dnscache.c b/lib/dnscache.c new file mode 100644 index 0000000000..2129d283cc --- /dev/null +++ b/lib/dnscache.c @@ -0,0 +1,873 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETINET_IN6_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef __VMS +#include +#include +#endif + +#include "urldata.h" +#include "curl_addrinfo.h" +#include "curl_share.h" +#include "curl_trc.h" +#include "dnscache.h" +#include "hash.h" +#include "hostip.h" +#include "httpsrr.h" +#include "progress.h" +#include "rand.h" +#include "strcase.h" +#include "curlx/inet_ntop.h" +#include "curlx/inet_pton.h" +#include "curlx/strcopy.h" +#include "curlx/strparse.h" + +#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */ + +#define MAX_DNS_CACHE_SIZE 29999 + +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); +} + +/* + * Create a hostcache id string for the provided host + port, to be used by + * the DNS caching. Without alloc. Return length of the id string. + */ +static size_t create_dnscache_id(const char *name, + size_t nlen, /* 0 or actual name length */ + uint16_t port, char *ptr, size_t buflen) +{ + size_t len = nlen ? nlen : strlen(name); + DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN); + if(len > (buflen - 7)) + len = buflen - 7; + /* store and lower case the name */ + Curl_strntolower(ptr, name, len); + return curl_msnprintf(&ptr[len], 7, ":%u", port) + len; +} + +struct dnscache_prune_data { + struct curltime now; + timediff_t oldest_ms; /* oldest time in cache not pruned. */ + timediff_t max_age_ms; +}; + +/* + * This function is set as a callback to be called for every entry in the DNS + * cache when we want to prune old unused entries. + * + * Returning non-zero means remove the entry, return 0 to keep it in the + * cache. + */ +static int dnscache_entry_is_stale(void *datap, void *hc) +{ + struct dnscache_prune_data *prune = (struct dnscache_prune_data *)datap; + struct Curl_dns_entry *dns = (struct Curl_dns_entry *)hc; + + if(dns->timestamp.tv_sec || dns->timestamp.tv_usec) { + /* get age in milliseconds */ + timediff_t age = curlx_ptimediff_ms(&prune->now, &dns->timestamp); + if(!dns->addr) + age *= 2; /* negative entries age twice as fast */ + if(age >= prune->max_age_ms) + return TRUE; + if(age > prune->oldest_ms) + prune->oldest_ms = age; + } + return FALSE; +} + +/* + * Prune the DNS cache. This assumes that a lock has already been taken. + * Returns the 'age' of the oldest still kept entry - in milliseconds. + */ +static timediff_t dnscache_prune(struct Curl_hash *hostcache, + timediff_t cache_timeout_ms, + struct curltime now) +{ + struct dnscache_prune_data user; + + user.max_age_ms = cache_timeout_ms; + user.now = now; + user.oldest_ms = 0; + + Curl_hash_clean_with_criterium(hostcache, + (void *)&user, + dnscache_entry_is_stale); + + return user.oldest_ms; +} + +static struct Curl_dnscache *dnscache_get(struct Curl_easy *data) +{ + if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) + return &data->share->dnscache; + if(data->multi) + return &data->multi->dnscache; + return NULL; +} + +static void dnscache_lock(struct Curl_easy *data, + struct Curl_dnscache *dnscache) +{ + if(data->share && dnscache == &data->share->dnscache) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); +} + +static void dnscache_unlock(struct Curl_easy *data, + struct Curl_dnscache *dnscache) +{ + if(data->share && dnscache == &data->share->dnscache) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); +} + +/* + * Library-wide function for pruning the DNS cache. This function takes and + * returns the appropriate locks. + */ +void Curl_dnscache_prune(struct Curl_easy *data) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + /* the timeout may be set -1 (forever) */ + timediff_t timeout_ms = data->set.dns_cache_timeout_ms; + + if(!dnscache || (timeout_ms == -1)) + /* NULL hostcache means we cannot do it */ + return; + + dnscache_lock(data, dnscache); + + do { + /* Remove outdated and unused entries from the hostcache */ + timediff_t oldest_ms = + dnscache_prune(&dnscache->entries, timeout_ms, *Curl_pgrs_now(data)); + + if(Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE) + /* prune the ones over half this age */ + timeout_ms = oldest_ms / 2; + else + break; + + /* if the cache size is still too big, use the oldest age as new prune + limit */ + } while(timeout_ms); + + dnscache_unlock(data, dnscache); +} + +void Curl_dnscache_clear(struct Curl_easy *data) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + if(dnscache) { + dnscache_lock(data, dnscache); + Curl_hash_clean(&dnscache->entries); + dnscache_unlock(data, dnscache); + } +} + +/* lookup address, returns entry if found and not stale */ +static CURLcode fetch_addr(struct Curl_easy *data, + struct Curl_dnscache *dnscache, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + struct Curl_dns_entry **pdns) +{ + struct Curl_dns_entry *dns = NULL; + char entry_id[MAX_HOSTCACHE_LEN]; + size_t entry_len; + CURLcode result = CURLE_OK; + + *pdns = NULL; + if(!dnscache) + return CURLE_OK; + + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(hostname, 0, port, + entry_id, sizeof(entry_id)); + + /* See if it is already in our dns cache */ + dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); + + /* No entry found in cache, check if we might have a wildcard entry */ + if(!dns && data->state.wildcard_resolve) { + entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id)); + + /* See if it is already in our dns cache */ + dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); + } + + if(dns && (data->set.dns_cache_timeout_ms != -1)) { + /* See whether the returned entry is stale. Done before we release lock */ + struct dnscache_prune_data user; + + user.now = *Curl_pgrs_now(data); + user.max_age_ms = data->set.dns_cache_timeout_ms; + user.oldest_ms = 0; + + if(dnscache_entry_is_stale(&user, dns)) { + infof(data, "Hostname in DNS cache was stale, zapped"); + dns = NULL; /* the memory deallocation is being handled by the hash */ + Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); + } + } + + if(dns) { + if((dns->dns_queries & dns_queries) != dns_queries) { + /* The entry does not cover all wanted DNS queries, a miss. */ + dns = NULL; + } + else if(!(dns->dns_responses & dns_queries)) { + /* The entry has no responses for the wanted DNS queries. */ + CURL_TRC_DNS(data, "cache entry does not have type=%s addresses", + Curl_resolv_query_str(dns_queries)); + dns = NULL; + result = CURLE_COULDNT_RESOLVE_HOST; + } + } + + if(dns && !dns->addr) { /* negative entry */ + dns = NULL; + result = CURLE_COULDNT_RESOLVE_HOST; + } + *pdns = dns; + return result; +} + +/* + * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache. + * + * Curl_resolv() checks initially and multi_runsingle() checks each time + * it discovers the handle in the state WAITRESOLVE whether the hostname + * has already been resolved and the address has already been stored in + * the DNS cache. This short circuits waiting for a lot of pending + * lookups for the same hostname requested by different handles. + * + * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. + * + * The returned data *MUST* be "released" with Curl_dns_entry_unlink() after + * use, or we will leak memory! + */ +CURLcode Curl_dnscache_get(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + struct Curl_dns_entry **pentry) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + struct Curl_dns_entry *dns = NULL; + CURLcode result = CURLE_OK; + + dnscache_lock(data, dnscache); + result = fetch_addr(data, dnscache, dns_queries, hostname, port, &dns); + if(!result && dns) + dns->refcount++; /* we pass out a reference */ + else if(result) { + DEBUGASSERT(!dns); + dns = NULL; + } + dnscache_unlock(data, dnscache); + + *pentry = dns; + return result; +} + +#ifndef CURL_DISABLE_SHUFFLE_DNS +/* + * Return # of addresses in a Curl_addrinfo struct + */ +static int num_addresses(const struct Curl_addrinfo *addr) +{ + int i = 0; + while(addr) { + addr = addr->ai_next; + i++; + } + return i; +} + +/* + * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo' + * struct by re-linking its linked list. + * + * The addr argument should be the address of a pointer to the head node of a + * `Curl_addrinfo` list and it will be modified to point to the new head after + * shuffling. + * + * Not declared static only to make it easy to use in a unit test! + * + * @unittest: 1608 + */ +UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, + struct Curl_addrinfo **addr); +UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, + struct Curl_addrinfo **addr) +{ + CURLcode result = CURLE_OK; + const int num_addrs = num_addresses(*addr); + + if(num_addrs > 1) { + struct Curl_addrinfo **nodes; + CURL_TRC_DNS(data, "Shuffling %i addresses", num_addrs); + + nodes = curlx_malloc(num_addrs * sizeof(*nodes)); + if(nodes) { + int i; + unsigned int *rnd; + const size_t rnd_size = num_addrs * sizeof(*rnd); + + /* build a plain array of Curl_addrinfo pointers */ + nodes[0] = *addr; + for(i = 1; i < num_addrs; i++) { + nodes[i] = nodes[i - 1]->ai_next; + } + + rnd = curlx_malloc(rnd_size); + if(rnd) { + /* Fisher-Yates shuffle */ + if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) { + struct Curl_addrinfo *swap_tmp; + for(i = num_addrs - 1; i > 0; i--) { + swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)]; + nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i]; + nodes[i] = swap_tmp; + } + + /* relink list in the new order */ + for(i = 1; i < num_addrs; i++) { + nodes[i - 1]->ai_next = nodes[i]; + } + + nodes[num_addrs - 1]->ai_next = NULL; + *addr = nodes[0]; + } + curlx_free(rnd); + } + else + result = CURLE_OUT_OF_MEMORY; + curlx_free(nodes); + } + else + result = CURLE_OUT_OF_MEMORY; + } + return result; +} +#endif + +static bool dnscache_ai_has_family(struct Curl_addrinfo *ai, + int ai_family) +{ + for(; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) + return TRUE; + } + return FALSE; +} + +static struct Curl_dns_entry *dnscache_entry_create( + struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr1, + struct Curl_addrinfo **paddr2, + const char *hostname, + size_t hostlen, + uint16_t port, + bool permanent) +{ + struct Curl_dns_entry *dns = NULL; + + /* Create a new cache entry, struct already has the hostname NUL */ + dns = curlx_calloc(1, sizeof(struct Curl_dns_entry) + hostlen); + if(!dns) + goto out; + + dns->refcount = 1; /* the cache has the first reference */ + dns->dns_queries = dns_queries; + dns->port = port; + if(hostlen) + memcpy(dns->hostname, hostname, hostlen); + + if(permanent) { + dns->timestamp.tv_sec = 0; /* an entry that never goes stale */ + dns->timestamp.tv_usec = 0; /* an entry that never goes stale */ + } + else { + dns->timestamp = *Curl_pgrs_now(data); + } + + /* Take the given address lists into the entry */ + if(paddr1 && *paddr1) { + dns->addr = *paddr1; + *paddr1 = NULL; + } + if(paddr2 && *paddr2) { + struct Curl_addrinfo **phead = &dns->addr; + while(*phead) + phead = &(*phead)->ai_next; + *phead = *paddr2; + *paddr2 = NULL; + } + + if((dns_queries & CURL_DNSQ_A) && + dnscache_ai_has_family(dns->addr, PF_INET)) + dns->dns_responses |= CURL_DNSQ_A; + +#ifdef USE_IPV6 + if((dns_queries & CURL_DNSQ_AAAA) && + dnscache_ai_has_family(dns->addr, PF_INET6)) + dns->dns_responses |= CURL_DNSQ_AAAA; +#endif /* USE_IPV6 */ + +#ifndef CURL_DISABLE_SHUFFLE_DNS + /* shuffle addresses if requested */ + if(data->set.dns_shuffle_addresses && dns->addr) { + CURLcode result = Curl_shuffle_addr(data, &dns->addr); + if(result) { + /* free without lock, we are the sole owner */ + dnscache_entry_free(dns); + dns = NULL; + goto out; + } + } +#else + (void)data; +#endif + +out: + if(paddr1 && *paddr1) { + Curl_freeaddrinfo(*paddr1); + *paddr1 = NULL; + } + if(paddr2 && *paddr2) { + Curl_freeaddrinfo(*paddr2); + *paddr2 = NULL; + } + return dns; +} + +struct Curl_dns_entry *Curl_dnscache_mk_entry(struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + const char *hostname, + uint16_t port) +{ + return dnscache_entry_create(data, dns_queries, paddr, NULL, hostname, + hostname ? strlen(hostname) : 0, + port, FALSE); +} + +struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr1, + struct Curl_addrinfo **paddr2, + const char *hostname, + uint16_t port) +{ + return dnscache_entry_create(data, dns_queries, paddr1, paddr2, hostname, + hostname ? strlen(hostname) : 0, + port, FALSE); +} + +#ifdef USE_HTTPSRR +void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, + struct Curl_https_rrinfo *hinfo) +{ + /* only do this when this is the only reference */ + DEBUGASSERT(dns->refcount == 1); + /* it should have been in the queries */ + DEBUGASSERT(dns->dns_queries & CURL_DNSQ_HTTPS); + if(dns->hinfo) { + Curl_httpsrr_cleanup(dns->hinfo); + curlx_free(dns->hinfo); + } + dns->hinfo = hinfo; + dns->dns_responses |= CURL_DNSQ_HTTPS; +} +#endif /* USE_HTTPSRR */ + +static struct Curl_dns_entry *dnscache_add_addr(struct Curl_easy *data, + struct Curl_dnscache *dnscache, + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + const char *hostname, + size_t hlen, + uint16_t port, + bool permanent) +{ + char entry_id[MAX_HOSTCACHE_LEN]; + size_t entry_len; + struct Curl_dns_entry *dns; + struct Curl_dns_entry *dns2; + + dns = dnscache_entry_create(data, dns_queries, paddr, NULL, + hostname, hlen, port, permanent); + if(!dns) + return NULL; + + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(hostname, hlen, port, + entry_id, sizeof(entry_id)); + + /* Store the resolved data in our DNS cache. */ + dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1, + (void *)dns); + if(!dns2) { + dnscache_entry_free(dns); + return NULL; + } + + dns = dns2; + dns->refcount++; /* mark entry as in-use */ + return dns; +} + +CURLcode Curl_dnscache_add(struct Curl_easy *data, + struct Curl_dns_entry *entry) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + char id[MAX_HOSTCACHE_LEN]; + size_t idlen; + + if(!dnscache) + return CURLE_FAILED_INIT; + /* Create an entry id, based upon the hostname and port */ + idlen = create_dnscache_id(entry->hostname, 0, entry->port, id, sizeof(id)); + + /* Store the resolved data in our DNS cache and up ref count */ + dnscache_lock(data, dnscache); + if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) { + dnscache_unlock(data, dnscache); + return CURLE_OUT_OF_MEMORY; + } + entry->refcount++; + dnscache_unlock(data, dnscache); + return CURLE_OK; +} + +CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, + uint8_t dns_queries, + const char *host, + uint16_t port) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + struct Curl_dns_entry *dns; + DEBUGASSERT(dnscache); + if(!dnscache) + return CURLE_FAILED_INIT; + + dnscache_lock(data, dnscache); + + /* put this new host in the cache */ + dns = dnscache_add_addr(data, dnscache, dns_queries, NULL, + host, strlen(host), port, FALSE); + if(dns) { + /* release the returned reference; the cache itself will keep the + * entry alive: */ + dns->refcount--; + dnscache_unlock(data, dnscache); + CURL_TRC_DNS(data, "cache negative name resolve for %s:%d type=%s", + host, port, Curl_resolv_query_str(dns_queries)); + return CURLE_OK; + } + dnscache_unlock(data, dnscache); + return CURLE_OUT_OF_MEMORY; +} + +struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data, + struct Curl_dns_entry *dns) +{ + if(!dns) + return NULL; + else { + struct Curl_dnscache *dnscache = dnscache_get(data); + dnscache_lock(data, dnscache); + dns->refcount++; + dnscache_unlock(data, dnscache); + return dns; + } +} + +/* + * Curl_dns_entry_unlink() releases a reference to the given cached DNS entry. + * When the reference count reaches 0, the entry is destroyed. It is important + * that only one unlink is made for each Curl_resolv() call. + * + * May be called with 'data' == NULL for global cache. + */ +void Curl_dns_entry_unlink(struct Curl_easy *data, + struct Curl_dns_entry **pdns) +{ + if(*pdns) { + struct Curl_dnscache *dnscache = dnscache_get(data); + struct Curl_dns_entry *dns = *pdns; + *pdns = NULL; + dnscache_lock(data, dnscache); + dns->refcount--; + if(dns->refcount == 0) + dnscache_entry_free(dns); + dnscache_unlock(data, dnscache); + } +} + +static void dnscache_entry_dtor(void *entry) +{ + struct Curl_dns_entry *dns = (struct Curl_dns_entry *)entry; + DEBUGASSERT(dns && (dns->refcount > 0)); + dns->refcount--; + if(dns->refcount == 0) + dnscache_entry_free(dns); +} + +/* + * Curl_dnscache_init() inits a new DNS cache. + */ +void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size) +{ + Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare, + dnscache_entry_dtor); +} + +void Curl_dnscache_destroy(struct Curl_dnscache *dns) +{ + Curl_hash_destroy(&dns->entries); +} + +CURLcode Curl_loadhostpairs(struct Curl_easy *data) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + struct curl_slist *hostp; + + if(!dnscache) + return CURLE_FAILED_INIT; + + /* Default is no wildcard found */ + data->state.wildcard_resolve = FALSE; + + for(hostp = data->state.resolve; hostp; hostp = hostp->next) { + char entry_id[MAX_HOSTCACHE_LEN]; + const char *host = hostp->data; + struct Curl_str source; + if(!host) + continue; + if(*host == '-') { + curl_off_t num = 0; + size_t entry_len; + host++; + if(!curlx_str_single(&host, '[')) { + if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') || + curlx_str_single(&host, ']') || + curlx_str_single(&host, ':')) + continue; + } + else { + if(curlx_str_until(&host, &source, 4096, ':') || + curlx_str_single(&host, ':')) { + continue; + } + } + + if(!curlx_str_number(&host, &num, 0xffff)) { + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(curlx_str(&source), + curlx_strlen(&source), (uint16_t)num, + entry_id, sizeof(entry_id)); + dnscache_lock(data, dnscache); + /* delete entry, ignore if it did not exist */ + Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); + dnscache_unlock(data, dnscache); + } + } + else { + struct Curl_dns_entry *dns; + struct Curl_addrinfo *head = NULL, *tail = NULL; + size_t entry_len; + char address[64]; + curl_off_t tmpofft = 0; + uint16_t port = 0; + bool permanent = TRUE; + bool error = TRUE; + VERBOSE(const char *addresses = NULL); + + if(*host == '+') { + host++; + permanent = FALSE; + } + if(!curlx_str_single(&host, '[')) { + if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') || + curlx_str_single(&host, ']')) + continue; + } + else { + if(curlx_str_until(&host, &source, 4096, ':')) + continue; + } + if(curlx_str_single(&host, ':') || + curlx_str_number(&host, &tmpofft, 0xffff) || + curlx_str_single(&host, ':')) + goto err; + port = (uint16_t)tmpofft; + + VERBOSE(addresses = host); + + /* start the address section */ + while(*host) { + struct Curl_str target; + struct Curl_addrinfo *ai; + CURLcode result; + + if(!curlx_str_single(&host, '[')) { + if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') || + curlx_str_single(&host, ']')) + goto err; + } + else { + if(curlx_str_until(&host, &target, 4096, ',')) { + if(curlx_str_single(&host, ',')) + goto err; + /* survive nothing but a comma */ + continue; + } + } +#ifndef USE_IPV6 + if(memchr(curlx_str(&target), ':', curlx_strlen(&target))) { + infof(data, "Ignoring resolve address '%.*s', missing IPv6 support.", + (int)curlx_strlen(&target), curlx_str(&target)); + if(curlx_str_single(&host, ',')) + goto err; + continue; + } +#endif + + if(curlx_strlen(&target) >= sizeof(address)) + goto err; + + memcpy(address, curlx_str(&target), curlx_strlen(&target)); + address[curlx_strlen(&target)] = '\0'; + + result = Curl_str2addr(address, port, &ai); + if(result) { + infof(data, "Resolve address '%s' found illegal", address); + goto err; + } + + if(tail) { + tail->ai_next = ai; + tail = tail->ai_next; + } + else { + head = tail = ai; + } + if(curlx_str_single(&host, ',')) + break; + } + + if(!head) + goto err; + + error = FALSE; +err: + if(error) { + failf(data, "Could not parse CURLOPT_RESOLVE entry '%s'", hostp->data); + Curl_freeaddrinfo(head); + return CURLE_SETOPT_OPTION_SYNTAX; + } + + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source), + port, entry_id, sizeof(entry_id)); + + dnscache_lock(data, dnscache); + + /* See if it is already in our dns cache */ + dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); + + if(dns) { + infof(data, "RESOLVE %.*s:%u - old addresses discarded", + (int)curlx_strlen(&source), + curlx_str(&source), port); + /* delete old entry, there are two reasons for this + 1. old entry may have different addresses. + 2. even if entry with correct addresses is already in the cache, + but if it is close to expire, then by the time next http + request is made, it can get expired and pruned because old + entry is not necessarily marked as permanent. + 3. when adding a non-permanent entry, we want it to remove and + replace an existing permanent entry. + 4. when adding a non-permanent entry, we want it to get a "fresh" + timeout that starts _now_. */ + + Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); + } + + /* put this new host in the cache, an overridy for ALL dns queries */ + dns = dnscache_add_addr(data, dnscache, CURL_DNSQ_ALL, + &head, curlx_str(&source), + curlx_strlen(&source), port, permanent); + if(dns) + /* release the returned reference; the cache itself will keep the + * entry alive: */ + dns->refcount--; + + dnscache_unlock(data, dnscache); + + if(!dns) + return CURLE_OUT_OF_MEMORY; + + infof(data, "Added %.*s:%u:%s to DNS cache%s", + (int)curlx_strlen(&source), curlx_str(&source), port, addresses, + permanent ? "" : " (non-permanent)"); + + /* Wildcard hostname */ + if(curlx_str_casecompare(&source, "*")) { + infof(data, "RESOLVE *:%u using wildcard", port); + data->state.wildcard_resolve = TRUE; + } + } + } + data->state.resolve = NULL; /* dealt with now */ + + return CURLE_OK; +} diff --git a/lib/dnscache.h b/lib/dnscache.h new file mode 100644 index 0000000000..ebe25f6dd4 --- /dev/null +++ b/lib/dnscache.h @@ -0,0 +1,143 @@ +#ifndef HEADER_CURL_DNSCACHE_H +#define HEADER_CURL_DNSCACHE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "hash.h" +#include "curlx/timeval.h" + +struct addrinfo; +struct hostent; +struct Curl_easy; +struct connectdata; +struct easy_pollset; +struct Curl_https_rrinfo; +struct Curl_multi; + +struct Curl_dns_entry { + struct Curl_addrinfo *addr; +#ifdef USE_HTTPSRR + struct Curl_https_rrinfo *hinfo; +#endif + /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */ + struct curltime timestamp; + /* reference counter, entry is freed on reaching 0 */ + uint32_t refcount; + /* hostname port number that resolved to addr. */ + uint16_t port; + uint8_t dns_queries; /* CURL_DNSQ_* type of queries performed for this */ + uint8_t dns_responses; /* CURL_DNSQ_* type this entry has responses for */ + /* hostname that resolved to addr. may be NULL (Unix domain sockets). */ + char hostname[1]; +}; + +/* + * Create a `Curl_dns_entry` with a reference count of 1. + * Use `Curl_dns_entry_unlink()` to release your hold on it. + * + * The call takes ownership of `paddr`, 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, + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + const char *hostname, + uint16_t port); + +struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr1, + struct Curl_addrinfo **paddr2, + const char *hostname, + uint16_t port); + +#ifdef USE_HTTPSRR +void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, + struct Curl_https_rrinfo *hinfo); +#endif /* USE_HTTPSRR */ + +/* Increase the ref counter and return it for storing in another place. + * May be called with NULL, in which case it returns NULL. */ +struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data, + struct Curl_dns_entry *dns); + +/* unlink a dns entry, frees all resources if it was the last reference. + * Always clears `*pdns`` */ +void Curl_dns_entry_unlink(struct Curl_easy *data, + struct Curl_dns_entry **pdns); + +struct Curl_dnscache { + struct Curl_hash entries; +}; + +/* init a new dns cache */ +void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size); + +void Curl_dnscache_destroy(struct Curl_dnscache *dns); + +/* prune old entries from the DNS cache */ +void Curl_dnscache_prune(struct Curl_easy *data); + +/* clear the DNS cache */ +void Curl_dnscache_clear(struct Curl_easy *data); + +/* + * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache. + * + * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. + * + * The returned data *MUST* be "released" with Curl_dns_entry_unlink() after + * use, or we will leak memory! + * Returns CURLE_OK or CURLE_COULDNT_RESOLVE_HOST when a negative + * entry was in the cache. + */ +CURLcode Curl_dnscache_get(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + struct Curl_dns_entry **pentry); + +/* + * Curl_dnscache_addr() adds `entry` to the cache, increasing its + * reference count on success. + */ +CURLcode Curl_dnscache_add(struct Curl_easy *data, + struct Curl_dns_entry *entry); + +/* Store a "negative" entry for host:port, e.g. remember that + * it could not be resolved. */ +CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, + uint8_t dns_queries, + const char *host, + uint16_t port); + +/* + * Populate the cache with specified entries from CURLOPT_RESOLVE. + */ +CURLcode Curl_loadhostpairs(struct Curl_easy *data); + +#endif /* HEADER_CURL_DNSCACHE_H */ diff --git a/lib/doh.c b/lib/doh.c index 030b026fe2..f152401862 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifndef CURL_DISABLE_DOH @@ -29,27 +28,23 @@ #include "urldata.h" #include "curl_addrinfo.h" #include "doh.h" - -#include "sendf.h" +#include "curl_trc.h" +#include "httpsrr.h" #include "multiif.h" #include "url.h" -#include "share.h" -#include "curlx/base64.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" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - #define DNS_CLASS_IN 0x01 -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static const char * const errors[]={ +static void doh_close(struct Curl_easy *data, + struct Curl_resolv_async *async); + +#ifdef CURLVERBOSE +static const char * const errors[] = { "", "Bad label", "Out of range", @@ -73,14 +68,19 @@ static const char *doh_strerror(DOHcode code) return "bad error code"; } -#endif /* !CURL_DISABLE_VERBOSE_STRINGS */ +#endif /* CURLVERBOSE */ /* @unittest 1655 */ UNITTEST DOHcode doh_req_encode(const char *host, DNStype dnstype, - unsigned char *dnsp, /* buffer */ + unsigned char *dnsp, /* buffer */ size_t len, /* buffer size */ + size_t *olen); /* output length */ +UNITTEST DOHcode doh_req_encode(const char *host, + DNStype dnstype, + unsigned char *dnsp, /* buffer */ + size_t len, /* buffer size */ size_t *olen) /* output length */ { const size_t hostlen = strlen(host); @@ -112,7 +112,7 @@ UNITTEST DOHcode doh_req_encode(const char *host, size_t expected_len; DEBUGASSERT(hostlen); expected_len = 12 + 1 + hostlen + 4; - if(host[hostlen-1]!='.') + if(host[hostlen - 1] != '.') expected_len++; if(expected_len > DOH_MAX_DNSREQ_SIZE) @@ -121,7 +121,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 | */ @@ -137,7 +137,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 @@ -159,11 +159,11 @@ 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 */ + /* 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++ = '\0'; /* upper 8 bit CLASS */ + *dnsp++ = '\0'; /* upper 8-bit CLASS */ *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */ *olen = dnsp - orig; @@ -174,8 +174,8 @@ UNITTEST DOHcode doh_req_encode(const char *host, return DOH_OK; } -static size_t -doh_probe_write_cb(char *contents, size_t size, size_t nmemb, void *userp) +static size_t doh_probe_write_cb(char *contents, size_t size, size_t nmemb, + void *userp) { size_t realsize = size * nmemb; struct Curl_easy *data = userp; @@ -189,7 +189,7 @@ doh_probe_write_cb(char *contents, size_t size, size_t nmemb, void *userp) 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 @@ -209,56 +209,66 @@ 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 /* called from multi when a sub transfer, e.g. doh probe, is done. - * This looks up the the probe response at its meta CURL_EZM_DOH_PROBE + * This looks up the probe response at its meta CURL_EZM_DOH_PROBE * and copies the response body over to the struct at the master's * meta at CURL_EZM_DOH_MASTER. */ static void doh_probe_done(struct Curl_easy *data, struct Curl_easy *doh, CURLcode result) { - struct doh_probes *dohp = data->state.async.doh; - DEBUGASSERT(dohp); - if(dohp) { - struct doh_request *doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE); - int i; + struct Curl_resolv_async *async = NULL; + struct doh_probes *dohp = NULL; + struct doh_request *doh_req = NULL; + int i; - for(i = 0; i < DOH_SLOT_COUNT; ++i) { - if(dohp->probe_resp[i].probe_mid == doh->mid) - break; - } - if(i >= DOH_SLOT_COUNT) { - failf(data, "unknown sub request done"); - return; - } + doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE); + if(!doh_req) { + DEBUGASSERT(0); + return; + } - dohp->pending--; - infof(doh, "a DoH request is completed, %u to go", dohp->pending); - dohp->probe_resp[i].result = result; - /* We expect either the meta data still to exist or the sub request - * to have already failed. */ - DEBUGASSERT(doh_req || result); - if(doh_req) { - if(!result) { - dohp->probe_resp[i].dnstype = doh_req->dnstype; - result = curlx_dyn_addn(&dohp->probe_resp[i].body, - curlx_dyn_ptr(&doh_req->resp_body), - curlx_dyn_len(&doh_req->resp_body)); - curlx_dyn_free(&doh_req->resp_body); - } - Curl_meta_remove(doh, CURL_EZM_DOH_PROBE); - } + async = Curl_async_get(data, doh_req->resolv_id); + if(!async) { + CURL_TRC_DNS(data, "[%u] ignoring outdated DoH response", + doh_req->resolv_id); + return; + } + dohp = async->doh; - if(result) - infof(doh, "DoH request %s", curl_easy_strerror(result)); + for(i = 0; i < DOH_SLOT_COUNT; ++i) { + if(dohp->probe_resp[i].probe_mid == doh->mid) + break; + } + /* We really should have found the slot where to store the response */ + if(i >= DOH_SLOT_COUNT) { + DEBUGASSERT(0); + failf(data, "DoH: unknown sub request done"); + return; + } - if(!dohp->pending) { - /* DoH completed, run the transfer picking up the results */ - Curl_multi_mark_dirty(data); - } + dohp->pending--; + infof(doh, "a DoH request is completed, %u to go", dohp->pending); + dohp->probe_resp[i].result = result; + /* We expect either the meta data still to exist or the sub request + * to have already failed. */ + if(!result) { + dohp->probe_resp[i].dnstype = doh_req->dnstype; + result = curlx_dyn_addn(&dohp->probe_resp[i].body, + curlx_dyn_ptr(&doh_req->resp_body), + curlx_dyn_len(&doh_req->resp_body)); + curlx_dyn_free(&doh_req->resp_body); + } + Curl_meta_remove(doh, CURL_EZM_DOH_PROBE); + + if(result) + infof(doh, "DoH request %s", curl_easy_strerror(result)); + + if(!dohp->pending) { + /* DoH completed, run the transfer picking up the results */ + Curl_multi_mark_dirty(data); } } @@ -270,11 +280,11 @@ static void doh_probe_dtor(void *key, size_t klen, void *e) struct doh_request *doh_req = e; curl_slist_free_all(doh_req->req_hds); curlx_dyn_free(&doh_req->resp_body); - free(e); + curlx_free(e); } } -#define ERROR_CHECK_SETOPT(x,y) \ +#define ERROR_CHECK_SETOPT(x, y) \ do { \ result = curl_easy_setopt((CURL *)doh, x, y); \ if(result && \ @@ -287,7 +297,8 @@ static CURLcode doh_probe_run(struct Curl_easy *data, DNStype dnstype, const char *host, const char *url, CURLM *multi, - unsigned int *pmid) + uint32_t resolv_id, + uint32_t *pmid) { struct Curl_easy *doh = NULL; CURLcode result = CURLE_OK; @@ -295,11 +306,12 @@ static CURLcode doh_probe_run(struct Curl_easy *data, struct doh_request *doh_req; DOHcode d; - *pmid = UINT_MAX; + *pmid = UINT32_MAX; - doh_req = calloc(1, sizeof(*doh_req)); + doh_req = curlx_calloc(1, sizeof(*doh_req)); if(!doh_req) return CURLE_OUT_OF_MEMORY; + doh_req->resolv_id = resolv_id; doh_req->dnstype = dnstype; curlx_dyn_init(&doh_req->resp_body, DYN_DOH_RESPONSE); @@ -312,8 +324,8 @@ static CURLcode doh_probe_run(struct Curl_easy *data, goto error; } - timeout_ms = Curl_timeleft(data, NULL, TRUE); - if(timeout_ms <= 0) { + timeout_ms = Curl_timeleft_ms(data); + if(timeout_ms < 0) { result = CURLE_OPERATION_TIMEDOUT; goto error; } @@ -332,9 +344,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); @@ -351,7 +361,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); #else /* in debug mode, also allow http */ - ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS); + ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); #endif ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms); ERROR_CHECK_SETOPT(CURLOPT_SHARE, (CURLSH *)data->share); @@ -363,11 +373,11 @@ static CURLcode doh_probe_run(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L); ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, - data->set.doh_verifyhost ? 2L : 0L); + data->set.doh_verifyhost ? 2L : 0L); ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, - data->set.doh_verifypeer ? 1L : 0L); + data->set.doh_verifypeer ? 1L : 0L); ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, - data->set.doh_verifystatus ? 1L : 0L); + data->set.doh_verifystatus ? 1L : 0L); /* Inherit *some* SSL options from the user's transfer. This is a best-guess as to which options are needed for compatibility. #3661 @@ -377,21 +387,20 @@ static CURLcode doh_probe_run(struct Curl_easy *data, options should be added to check doh proxy insecure separately, CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER. */ + doh->set.ssl.custom_cafile = data->set.ssl.custom_cafile; + doh->set.ssl.custom_capath = data->set.ssl.custom_capath; + doh->set.ssl.custom_cablob = data->set.ssl.custom_cablob; if(data->set.str[STRING_SSL_CAFILE]) { - ERROR_CHECK_SETOPT(CURLOPT_CAINFO, - data->set.str[STRING_SSL_CAFILE]); + ERROR_CHECK_SETOPT(CURLOPT_CAINFO, data->set.str[STRING_SSL_CAFILE]); } if(data->set.blobs[BLOB_CAINFO]) { - ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB, - data->set.blobs[BLOB_CAINFO]); + ERROR_CHECK_SETOPT(CURLOPT_CAINFO_BLOB, data->set.blobs[BLOB_CAINFO]); } if(data->set.str[STRING_SSL_CAPATH]) { - ERROR_CHECK_SETOPT(CURLOPT_CAPATH, - data->set.str[STRING_SSL_CAPATH]); + ERROR_CHECK_SETOPT(CURLOPT_CAPATH, data->set.str[STRING_SSL_CAPATH]); } if(data->set.str[STRING_SSL_CRLFILE]) { - ERROR_CHECK_SETOPT(CURLOPT_CRLFILE, - data->set.str[STRING_SSL_CRLFILE]); + ERROR_CHECK_SETOPT(CURLOPT_CRLFILE, data->set.str[STRING_SSL_CRLFILE]); } if(data->set.ssl.certinfo) ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L); @@ -439,15 +448,12 @@ error: } /* - * Curl_doh() resolves a name using DoH. It resolves a name and returns a - * 'Curl_addrinfo *' with the address information. + * Curl_doh() starts a name resolve using DoH. It resolves a name and returns + * a 'Curl_addrinfo *' with the address information. */ -struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - int *waitp) +CURLcode Curl_doh(struct Curl_easy *data, + struct Curl_resolv_async *async) { CURLcode result = CURLE_OK; struct doh_probes *dohp = NULL; @@ -455,49 +461,45 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, size_t i; DEBUGASSERT(conn); - DEBUGASSERT(!data->state.async.doh); - if(data->state.async.doh) - Curl_doh_cleanup(data); - - data->state.async.done = FALSE; - data->state.async.port = port; - data->state.async.ip_version = ip_version; - data->state.async.hostname = strdup(hostname); - if(!data->state.async.hostname) - return NULL; + DEBUGASSERT(!async->doh); + DEBUGASSERT(async->hostname[0]); + if(async->doh) + Curl_doh_cleanup(data, async); /* start clean, consider allocating this struct on demand */ - data->state.async.doh = dohp = calloc(1, sizeof(struct doh_probes)); + async->doh = dohp = curlx_calloc(1, sizeof(struct doh_probes)); if(!dohp) - return NULL; + return CURLE_OUT_OF_MEMORY; for(i = 0; i < DOH_SLOT_COUNT; ++i) { - dohp->probe_resp[i].probe_mid = UINT_MAX; + dohp->probe_resp[i].probe_mid = UINT32_MAX; curlx_dyn_init(&dohp->probe_resp[i].body, DYN_DOH_RESPONSE); } conn->bits.doh = TRUE; - dohp->host = data->state.async.hostname; - dohp->port = data->state.async.port; + dohp->host = async->hostname; + dohp->port = async->port; /* We are making sub easy handles and want to be called back when * one is done. */ data->sub_xfer_done = doh_probe_done; /* create IPv4 DoH request */ - result = doh_probe_run(data, CURL_DNS_TYPE_A, - hostname, data->set.str[STRING_DOH], - data->multi, - &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid); - if(result) - goto error; - dohp->pending++; + if(async->dns_queries & CURL_DNSQ_A) { + result = doh_probe_run(data, CURL_DNS_TYPE_A, + async->hostname, data->set.str[STRING_DOH], + data->multi, async->id, + &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid); + if(result) + goto error; + dohp->pending++; + } #ifdef USE_IPV6 - if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { + if(async->dns_queries & CURL_DNSQ_AAAA) { /* create IPv6 DoH request */ result = doh_probe_run(data, CURL_DNS_TYPE_AAAA, - hostname, data->set.str[STRING_DOH], - data->multi, + async->hostname, data->set.str[STRING_DOH], + data->multi, async->id, &dohp->probe_resp[DOH_SLOT_IPV6].probe_mid); if(result) goto error; @@ -506,30 +508,29 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, #endif #ifdef USE_HTTPSRR - if(conn->handler->protocol & PROTO_FAMILY_HTTP) { - /* Only use HTTPS RR for HTTP(S) transfers */ + if(async->dns_queries & CURL_DNSQ_HTTPS) { char *qname = NULL; - if(port != PORT_HTTPS) { - qname = aprintf("_%d._https.%s", port, hostname); + if(async->port != PORT_HTTPS) { + qname = curl_maprintf("_%d._https.%s", async->port, async->hostname); if(!qname) goto error; } result = doh_probe_run(data, CURL_DNS_TYPE_HTTPS, - qname ? qname : hostname, data->set.str[STRING_DOH], - data->multi, + qname ? qname : async->hostname, + data->set.str[STRING_DOH], data->multi, + async->id, &dohp->probe_resp[DOH_SLOT_HTTPS_RR].probe_mid); - free(qname); + curlx_free(qname); if(result) goto error; dohp->pending++; } #endif - *waitp = TRUE; /* this never returns synchronously */ - return NULL; + return CURLE_OK; error: - Curl_doh_cleanup(data); - return NULL; + Curl_doh_cleanup(data, async); + return result; } static DOHcode doh_skipqname(const unsigned char *doh, size_t dohlen, @@ -606,7 +607,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; @@ -674,7 +675,7 @@ static DOHcode doh_rdata(const unsigned char *doh, struct dohentry *d) { /* RDATA - - A (TYPE 1): 4 bytes + - A (TYPE 1): 4 bytes - AAAA (TYPE 28): 16 bytes - NS (TYPE 2): N bytes - HTTPS (TYPE 65): N bytes */ @@ -704,15 +705,16 @@ 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; } +UNITTEST void de_init(struct dohentry *de); UNITTEST void de_init(struct dohentry *de) { int i; @@ -722,7 +724,10 @@ UNITTEST void de_init(struct dohentry *de) curlx_dyn_init(&de->cname[i], DYN_DOH_CNAME); } - +UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, + size_t dohlen, + DNStype dnstype, + struct dohentry *d); UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, size_t dohlen, DNStype dnstype, @@ -759,7 +764,7 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, ancount = doh_get16bit(doh, 6); while(ancount) { - unsigned short class; + unsigned short dnsclass; unsigned int ttl; rc = doh_skipqname(doh, dohlen, &index); @@ -770,17 +775,17 @@ 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; if(dohlen < (index + 2)) return DOH_DNS_OUT_OF_RANGE; - class = doh_get16bit(doh, index); - if(DNS_CLASS_IN != class) + dnsclass = doh_get16bit(doh, index); + if(DNS_CLASS_IN != dnsclass) return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */ index += 2; @@ -816,7 +821,7 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, if(dohlen < (index + 8)) return DOH_DNS_OUT_OF_RANGE; - index += 2 + 2 + 4; /* type, class and ttl */ + index += 2 + 2 + 4; /* type, dnsclass and ttl */ if(dohlen < (index + 2)) return DOH_DNS_OUT_OF_RANGE; @@ -838,7 +843,7 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, if(dohlen < (index + 8)) return DOH_DNS_OUT_OF_RANGE; - index += 2 + 2 + 4; /* type, class and ttl */ + index += 2 + 2 + 4; /* type, dnsclass and ttl */ if(dohlen < (index + 2)) return DOH_DNS_OUT_OF_RANGE; @@ -856,7 +861,7 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, #ifdef USE_HTTTPS if((type != CURL_DNS_TYPE_NS) && !d->numcname && !d->numaddr && - !d->numhttps_rrs) + !d->numhttps_rrs) #else if((type != CURL_DNS_TYPE_NS) && !d->numcname && !d->numaddr) #endif @@ -866,7 +871,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) { @@ -887,8 +892,9 @@ static void doh_show(struct Curl_easy *data, len = sizeof(buffer) - len; for(j = 0; j < 16; j += 2) { size_t l; - msnprintf(ptr, len, "%s%02x%02x", j ? ":" : "", d->addr[i].ip.v6[j], - d->addr[i].ip.v6[j + 1]); + curl_msnprintf(ptr, len, "%s%02x%02x", j ? ":" : "", + d->addr[i].ip.v6[j], + d->addr[i].ip.v6[j + 1]); l = strlen(ptr); len -= l; ptr += l; @@ -898,20 +904,19 @@ static void doh_show(struct Curl_easy *data, } #ifdef USE_HTTPSRR for(i = 0; i < d->numhttps_rrs; i++) { -# ifdef DEBUGBUILD - doh_print_buf(data, "DoH HTTPS", - d->https_rrs[i].val, d->https_rrs[i].len); -# else +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) + doh_print_buf(data, "DoH HTTPS", d->https_rrs[i].val, d->https_rrs[i].len); +#else infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len); -# endif - } #endif + } +#endif /* USE_HTTPSRR */ for(i = 0; i < d->numcname; i++) { infof(data, "CNAME: %s", curlx_dyn_ptr(&d->cname[i])); } } #else -#define doh_show(x,y) +#define doh_show(x, y) #endif /* @@ -963,7 +968,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname, addrtype = AF_INET; } - ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen); + ai = curlx_calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen); if(!ai) { result = CURLE_OUT_OF_MEMORY; break; @@ -995,7 +1000,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; @@ -1004,7 +1009,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 @@ -1022,24 +1027,25 @@ 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) { - case CURL_DNS_TYPE_A: - return "A"; - case CURL_DNS_TYPE_AAAA: - return "AAAA"; + case CURL_DNS_TYPE_A: + return "A"; + case CURL_DNS_TYPE_AAAA: + return "AAAA"; #ifdef USE_HTTPSRR - case CURL_DNS_TYPE_HTTPS: - return "HTTPS"; + case CURL_DNS_TYPE_HTTPS: + return "HTTPS"; #endif - default: - return "unknown"; + default: + return "unknown"; } } #endif +UNITTEST void de_cleanup(struct dohentry *d); UNITTEST void de_cleanup(struct dohentry *d) { int i = 0; @@ -1048,7 +1054,7 @@ UNITTEST void de_cleanup(struct dohentry *d) } #ifdef USE_HTTPSRR for(i = 0; i < d->numhttps_rrs; i++) - Curl_safefree(d->https_rrs[i].val); + curlx_safefree(d->https_rrs[i].val); #endif } @@ -1062,11 +1068,11 @@ UNITTEST void de_cleanup(struct dohentry *d) * @return is 1 for success, error otherwise * * The encoding here is defined in - * https://tools.ietf.org/html/rfc1035#section-3.1 + * 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. (that is why it is an "unsigned char + * **" :-) */ static CURLcode doh_decode_rdata_name(const unsigned char **buf, size_t *remaining, char **dnsname) @@ -1111,11 +1117,10 @@ static CURLcode doh_decode_rdata_name(const unsigned char **buf, return CURLE_OK; } +/* @unittest 1658 */ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, const unsigned char *cp, size_t len, struct Curl_https_rrinfo **hrr); - -/* @unittest 1658 */ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, const unsigned char *cp, size_t len, struct Curl_https_rrinfo **hrr) @@ -1127,10 +1132,11 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, CURLcode result = CURLE_OUT_OF_MEMORY; size_t olen; + (void)data; *hrr = NULL; if(len <= 2) return CURLE_BAD_FUNCTION_ARGUMENT; - lhrr = calloc(1, sizeof(struct Curl_https_rrinfo)); + lhrr = curlx_calloc(1, sizeof(struct Curl_https_rrinfo)); if(!lhrr) return CURLE_OUT_OF_MEMORY; lhrr->priority = doh_get16bit(cp, 0); @@ -1144,7 +1150,6 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, result = CURLE_WEIRD_SERVER_REPLY; goto err; } - lhrr->port = -1; /* until set */ while(len >= 4) { pcode = doh_get16bit(cp, 0); plen = doh_get16bit(cp, 2); @@ -1154,9 +1159,10 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, result = CURLE_WEIRD_SERVER_REPLY; goto err; } - result = Curl_httpsrr_set(data, lhrr, pcode, cp, plen); + result = Curl_httpsrr_set(lhrr, pcode, cp, plen); if(result) goto err; + Curl_httpsrr_trace(data, lhrr); cp += plen; len -= plen; expected_min_pcode = pcode + 1; @@ -1166,11 +1172,11 @@ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, return CURLE_OK; err: Curl_httpsrr_cleanup(lhrr); - Curl_safefree(lhrr); + curlx_safefree(lhrr); return result; } -#ifdef DEBUGBUILD +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) UNITTEST void doh_print_httpsrr(struct Curl_easy *data, struct Curl_https_rrinfo *hrr); @@ -1178,8 +1184,7 @@ UNITTEST void doh_print_httpsrr(struct Curl_easy *data, struct Curl_https_rrinfo *hrr) { DEBUGASSERT(hrr); - infof(data, "HTTPS RR: priority %d, target: %s", - hrr->priority, hrr->target); + infof(data, "HTTPS RR: priority %d, target: %s", hrr->priority, hrr->target); if(hrr->alpns[0] != ALPN_none) infof(data, "HTTPS RR: alpns %u %u %u %u", hrr->alpns[0], hrr->alpns[1], hrr->alpns[2], hrr->alpns[3]); @@ -1207,37 +1212,35 @@ UNITTEST void doh_print_httpsrr(struct Curl_easy *data, } else infof(data, "HTTPS RR: no ipv6hints"); - return; } # endif #endif -CURLcode Curl_doh_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dnsp) +CURLcode Curl_doh_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns) { - CURLcode result; - struct doh_probes *dohp = data->state.async.doh; - *dnsp = NULL; /* defaults to no response */ + struct doh_probes *dohp = async->doh; + CURLcode result = CURLE_OK; + struct dohentry de; + + *pdns = NULL; /* defaults to no response */ if(!dohp) return CURLE_OUT_OF_MEMORY; - if(dohp->probe_resp[DOH_SLOT_IPV4].probe_mid == UINT_MAX && - dohp->probe_resp[DOH_SLOT_IPV6].probe_mid == UINT_MAX) { + if(dohp->probe_resp[DOH_SLOT_IPV4].probe_mid == UINT32_MAX && + dohp->probe_resp[DOH_SLOT_IPV6].probe_mid == UINT32_MAX) { failf(data, "Could not DoH-resolve: %s", dohp->host); return CONN_IS_PROXIED(data->conn) ? CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; } else if(!dohp->pending) { DOHcode rc[DOH_SLOT_COUNT]; - struct dohentry de; int slot; - /* Clear any result the might still be there */ - Curl_resolv_unlink(data, &data->state.async.dns); - memset(rc, 0, sizeof(rc)); /* remove DoH handles from multi handle and close them */ - Curl_doh_close(data); + doh_close(data, async); /* parse the responses, create the struct and return it! */ de_init(&de); for(slot = 0; slot < DOH_SLOT_COUNT; slot++) { @@ -1247,87 +1250,89 @@ 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 */ if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) { /* we have an address, of one kind or other */ struct Curl_dns_entry *dns; struct Curl_addrinfo *ai; - if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) { CURL_TRC_DNS(data, "hostname: %s", dohp->host); doh_show(data, &de); } result = doh2ai(&de, dohp->host, dohp->port, &ai); - if(result) { - de_cleanup(&de); - return result; - } + if(result) + goto error; /* we got a response, create a dns entry. */ - 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 - if(de.numhttps_rrs > 0 && result == CURLE_OK) { - struct Curl_https_rrinfo *hrr = NULL; - result = doh_resp_decode_httpsrr(data, de.https_rrs->val, - de.https_rrs->len, &hrr); - if(result) { - infof(data, "Failed to decode HTTPS RR"); - return result; - } - infof(data, "Some HTTPS RR to process"); -# ifdef DEBUGBUILD - doh_print_httpsrr(data, hrr); -# endif - dns->hinfo = hrr; - } -#endif - /* and add the entry to the cache */ - data->state.async.dns = dns; - result = Curl_dnscache_add(data, dns); - *dnsp = data->state.async.dns; + dns = Curl_dnscache_mk_entry(data, async->dns_queries, + &ai, dohp->host, dohp->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto error; } - } /* address processing done */ - /* All done */ - data->state.async.done = TRUE; - de_cleanup(&de); - Curl_doh_cleanup(data); - return result; + /* Now add and HTTPSRR information if we have */ +#ifdef USE_HTTPSRR + if(de.numhttps_rrs > 0 && result == CURLE_OK) { + struct Curl_https_rrinfo *hrr = NULL; + result = doh_resp_decode_httpsrr(data, de.https_rrs->val, + de.https_rrs->len, &hrr); + if(result) { + infof(data, "Failed to decode HTTPS RR"); + Curl_dns_entry_unlink(data, &dns); + goto error; + } + infof(data, "Some HTTPS RR to process"); +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) + doh_print_httpsrr(data, hrr); +#endif + Curl_dns_entry_set_https_rr(dns, hrr); + } +#endif /* USE_HTTPSRR */ + + /* and add the entry to the cache */ + result = Curl_dnscache_add(data, dns); + *pdns = dns; + } /* address processing done */ + else { + result = CONN_IS_PROXIED(data->conn) ? CURLE_COULDNT_RESOLVE_PROXY : + CURLE_COULDNT_RESOLVE_HOST; + } } /* !dohp->pending */ + else + /* wait for pending DoH transactions to complete */ + return CURLE_AGAIN; - /* else wait for pending DoH transactions to complete */ - return CURLE_OK; +error: + de_cleanup(&de); + Curl_doh_cleanup(data, async); + return result; } -void Curl_doh_close(struct Curl_easy *data) +static void doh_close(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct doh_probes *doh = data->state.async.doh; + struct doh_probes *doh = async ? async->doh : NULL; if(doh && data->multi) { struct Curl_easy *probe_data; - unsigned int mid; + uint32_t mid; size_t slot; for(slot = 0; slot < DOH_SLOT_COUNT; slot++) { mid = doh->probe_resp[slot].probe_mid; - if(mid == UINT_MAX) + if(mid == UINT32_MAX) continue; - doh->probe_resp[slot].probe_mid = UINT_MAX; + doh->probe_resp[slot].probe_mid = UINT32_MAX; /* should have been called before data is removed from multi handle */ DEBUGASSERT(data->multi); - probe_data = data->multi ? Curl_multi_get_easy(data->multi, mid) : - NULL; + probe_data = data->multi ? Curl_multi_get_easy(data->multi, mid) : NULL; if(!probe_data) { DEBUGF(infof(data, "Curl_doh_close: xfer for mid=%u not found!", doh->probe_resp[slot].probe_mid)); @@ -1341,16 +1346,17 @@ void Curl_doh_close(struct Curl_easy *data) } } -void Curl_doh_cleanup(struct Curl_easy *data) +void Curl_doh_cleanup(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct doh_probes *dohp = data->state.async.doh; + struct doh_probes *dohp = async->doh; if(dohp) { int i; - Curl_doh_close(data); + doh_close(data, async); for(i = 0; i < DOH_SLOT_COUNT; ++i) { curlx_dyn_free(&dohp->probe_resp[i].body); } - Curl_safefree(data->state.async.doh); + curlx_safefree(async->doh); } } diff --git a/lib/doh.h b/lib/doh.h index 3fd7de2c1c..428b230a58 100644 --- a/lib/doh.h +++ b/lib/doh.h @@ -23,26 +23,23 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "urldata.h" -#include "curl_addrinfo.h" -#ifdef USE_HTTPSRR -# include -# include "httpsrr.h" -#endif -#ifndef CURL_DISABLE_DOH +/* enums outside of the #ifdef to make the types work in unitprotos.h even on + builds without DoH support */ + +struct Curl_resolv_async; 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 */ @@ -59,6 +56,10 @@ typedef enum { CURL_DNS_TYPE_HTTPS = 65 } DNStype; +struct dohentry; /* forward-declare for non-DoH builds */ + +#ifndef CURL_DISABLE_DOH + enum doh_slot_num { /* Explicit values for first two symbols so as to match hard-coded * constants in existing code @@ -92,11 +93,12 @@ struct doh_request { struct curl_slist *req_hds; struct dynbuf resp_body; size_t req_body_len; + uint32_t resolv_id; /* id of the resolve operation */ DNStype dnstype; }; struct doh_response { - unsigned int probe_mid; + uint32_t probe_mid; struct dynbuf body; DNStype dnstype; CURLcode result; @@ -107,25 +109,22 @@ struct doh_response { struct doh_probes { struct doh_response probe_resp[DOH_SLOT_COUNT]; unsigned int pending; /* still outstanding probes */ - int port; + uint16_t port; const char *host; }; /* - * Curl_doh() resolve a name using DoH (DNS-over-HTTPS). It resolves a name - * and returns a 'Curl_addrinfo *' with the address information. + * Curl_doh() starts a name resolve using DoH (DNS-over-HTTPS). It resolves a + * name and returns a 'Curl_addrinfo *' with the address information. */ +CURLcode Curl_doh(struct Curl_easy *data, + struct Curl_resolv_async *async); -struct Curl_addrinfo *Curl_doh(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - int *waitp); +CURLcode Curl_doh_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns); -CURLcode Curl_doh_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns); - -#define DOH_MAX_ADDR 24 +#define DOH_MAX_ADDR 24 #define DOH_MAX_CNAME 4 #define DOH_MAX_HTTPS 4 @@ -164,27 +163,15 @@ struct dohentry { #endif }; -void Curl_doh_close(struct Curl_easy *data); -void Curl_doh_cleanup(struct Curl_easy *data); +void Curl_doh_cleanup(struct Curl_easy *data, + struct Curl_resolv_async *async); +#define Curl_doh_wanted(d) (!!(d)->set.doh) -#ifdef UNITTESTS -UNITTEST DOHcode doh_req_encode(const char *host, - DNStype dnstype, - unsigned char *dnsp, /* buffer */ - size_t len, /* buffer size */ - size_t *olen); /* output length */ -UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, - size_t dohlen, - DNStype dnstype, - struct dohentry *d); -UNITTEST void de_init(struct dohentry *d); -UNITTEST void de_cleanup(struct dohentry *d); -#endif - -#else /* if DoH is disabled */ -#define Curl_doh(a,b,c,d,e) NULL -#define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST -#endif +#else /* CURL_DISABLE_DOH */ +#define Curl_doh(a, b) NULL +#define Curl_doh_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST +#define Curl_doh_wanted(d) FALSE +#endif /* !CURL_DISABLE_DOH */ #endif /* HEADER_CURL_DOH_H */ diff --git a/lib/dynhds.c b/lib/dynhds.c index dcb9193a8b..2930e45b52 100644 --- a/lib/dynhds.c +++ b/lib/dynhds.c @@ -21,34 +21,24 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #include "dynhds.h" #include "strcase.h" -/* The last 3 #include files should be in this order */ -#ifdef USE_NGHTTP2 -#include -#include -#endif /* USE_NGHTTP2 */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - -static struct dynhds_entry * -entry_new(const char *name, size_t namelen, - const char *value, size_t valuelen, int opts) +static struct dynhds_entry *entry_new(const char *name, size_t namelen, + const char *value, size_t valuelen, + int opts) { struct dynhds_entry *e; char *p; DEBUGASSERT(name); DEBUGASSERT(value); - e = calloc(1, sizeof(*e) + namelen + valuelen + 2); + e = curlx_calloc(1, sizeof(*e) + namelen + valuelen + 2); if(!e) return NULL; - e->name = p = ((char *)e) + sizeof(*e); + e->name = p = (char *)e + sizeof(*e); memcpy(p, name, namelen); e->namelen = namelen; e->value = p += namelen + 1; /* leave a \0 at the end of name */ @@ -59,33 +49,9 @@ entry_new(const char *name, size_t namelen, return e; } -static struct dynhds_entry * -entry_append(struct dynhds_entry *e, - const char *value, size_t valuelen) -{ - struct dynhds_entry *e2; - size_t valuelen2 = e->valuelen + 1 + valuelen; - char *p; - - DEBUGASSERT(value); - e2 = calloc(1, sizeof(*e) + e->namelen + valuelen2 + 2); - if(!e2) - return NULL; - e2->name = p = ((char *)e2) + sizeof(*e2); - memcpy(p, e->name, e->namelen); - e2->namelen = e->namelen; - e2->value = p += e->namelen + 1; /* leave a \0 at the end of name */ - memcpy(p, e->value, e->valuelen); - p += e->valuelen; - p[0] = ' '; - memcpy(p + 1, value, valuelen); - e2->valuelen = valuelen2; - return e2; -} - static void entry_free(struct dynhds_entry *e) { - free(e); + curlx_free(e); } void Curl_dynhds_init(struct dynhds *dynhds, size_t max_entries, @@ -110,7 +76,7 @@ void Curl_dynhds_free(struct dynhds *dynhds) entry_free(dynhds->hds[i]); } } - Curl_safefree(dynhds->hds); + curlx_safefree(dynhds->hds); dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0; } @@ -175,7 +141,7 @@ CURLcode Curl_dynhds_add(struct dynhds *dynhds, if(dynhds->strs_len + namelen + valuelen > dynhds->max_strs_size) return CURLE_OUT_OF_MEMORY; -entry = entry_new(name, namelen, value, valuelen, dynhds->opts); + entry = entry_new(name, namelen, value, valuelen, dynhds->opts); if(!entry) goto out; @@ -186,13 +152,13 @@ entry = entry_new(name, namelen, value, valuelen, dynhds->opts); if(dynhds->max_entries && nallc > dynhds->max_entries) nallc = dynhds->max_entries; - nhds = calloc(nallc, sizeof(struct dynhds_entry *)); + nhds = curlx_calloc(nallc, sizeof(struct dynhds_entry *)); if(!nhds) goto out; if(dynhds->hds) { memcpy(nhds, dynhds->hds, dynhds->hds_len * sizeof(struct dynhds_entry *)); - Curl_safefree(dynhds->hds); + curlx_safefree(dynhds->hds); } dynhds->hds = nhds; dynhds->hds_allc = nallc; @@ -226,48 +192,26 @@ CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds, if(!line || !line_len) return CURLE_OK; - if((line[0] == ' ') || (line[0] == '\t')) { - struct dynhds_entry *e, *e2; - /* header continuation, yikes! */ - if(!dynhds->hds_len) - return CURLE_BAD_FUNCTION_ARGUMENT; - - while(line_len && ISBLANK(line[0])) { - ++line; - --line_len; - } - if(!line_len) - return CURLE_BAD_FUNCTION_ARGUMENT; - e = dynhds->hds[dynhds->hds_len-1]; - e2 = entry_append(e, line, line_len); - if(!e2) - return CURLE_OUT_OF_MEMORY; - dynhds->hds[dynhds->hds_len-1] = e2; - entry_free(e); - return CURLE_OK; + p = memchr(line, ':', line_len); + if(!p) + return CURLE_BAD_FUNCTION_ARGUMENT; + name = line; + namelen = p - line; + p++; /* move past the colon */ + for(i = namelen + 1; i < line_len; ++i, ++p) { + if(!ISBLANK(*p)) + break; } - else { - p = memchr(line, ':', line_len); - if(!p) - return CURLE_BAD_FUNCTION_ARGUMENT; - name = line; - namelen = p - line; - p++; /* move past the colon */ - for(i = namelen + 1; i < line_len; ++i, ++p) { - if(!ISBLANK(*p)) - break; - } - value = p; - valuelen = line_len - i; + value = p; + valuelen = line_len - i; - p = memchr(value, '\r', valuelen); - if(!p) - p = memchr(value, '\n', valuelen); - if(p) - valuelen = (size_t)(p - value); + p = memchr(value, '\r', valuelen); + if(!p) + p = memchr(value, '\n', valuelen); + if(p) + valuelen = (size_t)(p - value); - return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); - } + return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); } CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line) @@ -278,19 +222,31 @@ CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line) #ifdef UNITTESTS /* used by unit2602.c */ -bool Curl_dynhds_contains(struct dynhds *dynhds, - const char *name, size_t namelen) +/** + * Return TRUE iff one or more headers with the given name exist. + */ +UNITTEST bool Curl_dynhds_contains(struct dynhds *dynhds, + const char *name, size_t namelen); +UNITTEST bool Curl_dynhds_contains(struct dynhds *dynhds, + const char *name, size_t namelen) { return !!Curl_dynhds_get(dynhds, name, namelen); } -bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name) +UNITTEST bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name); +UNITTEST bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name) { return Curl_dynhds_contains(dynhds, name, strlen(name)); } -size_t Curl_dynhds_count_name(struct dynhds *dynhds, - const char *name, size_t namelen) +/** + * Return how often the given name appears in `dynhds`. + * Names are case-insensitive. + */ +UNITTEST size_t Curl_dynhds_count_name(struct dynhds *dynhds, + const char *name, size_t namelen); +UNITTEST size_t Curl_dynhds_count_name(struct dynhds *dynhds, + const char *name, size_t namelen) { size_t n = 0; if(dynhds->hds_len) { @@ -304,21 +260,26 @@ size_t Curl_dynhds_count_name(struct dynhds *dynhds, return n; } -size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name) +/** + * Return how often the given null-terminated name appears in `dynhds`. + * Names are case-insensitive. + */ +UNITTEST size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, + const char *name); +UNITTEST size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, + const char *name) { return Curl_dynhds_count_name(dynhds, name, strlen(name)); } -CURLcode Curl_dynhds_set(struct dynhds *dynhds, - const char *name, size_t namelen, - const char *value, size_t valuelen) -{ - Curl_dynhds_remove(dynhds, name, namelen); - return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); -} - -size_t Curl_dynhds_remove(struct dynhds *dynhds, - const char *name, size_t namelen) +/** + * Remove all entries with the given name. + * Returns number of entries removed. + */ +UNITTEST size_t Curl_dynhds_remove(struct dynhds *dynhds, + const char *name, size_t namelen); +UNITTEST size_t Curl_dynhds_remove(struct dynhds *dynhds, + const char *name, size_t namelen) { size_t n = 0; if(dynhds->hds_len) { @@ -343,12 +304,29 @@ size_t Curl_dynhds_remove(struct dynhds *dynhds, return n; } -size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name) +/** + * Set the give header name and value, replacing any entries with + * the same name. The header is added at the end of all (remaining) + * entries. + */ +UNITTEST CURLcode Curl_dynhds_set(struct dynhds *dynhds, + const char *name, size_t namelen, + const char *value, size_t valuelen); +UNITTEST CURLcode Curl_dynhds_set(struct dynhds *dynhds, + const char *name, size_t namelen, + const char *value, size_t valuelen) +{ + Curl_dynhds_remove(dynhds, name, namelen); + return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); +} + +UNITTEST size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name); +UNITTEST size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name) { return Curl_dynhds_remove(dynhds, name, strlen(name)); } -#endif +#endif /* UNITTESTS */ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf) { @@ -374,7 +352,7 @@ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf) nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount) { - nghttp2_nv *nva = calloc(1, sizeof(nghttp2_nv) * dynhds->hds_len); + nghttp2_nv *nva = curlx_calloc(1, sizeof(nghttp2_nv) * dynhds->hds_len); size_t i; *pcount = 0; diff --git a/lib/dynhds.h b/lib/dynhds.h index e533dcc369..d5337eb353 100644 --- a/lib/dynhds.h +++ b/lib/dynhds.h @@ -25,7 +25,6 @@ ***************************************************************************/ #include "curl_setup.h" -#include #include "curlx/dynbuf.h" struct dynbuf; @@ -51,7 +50,7 @@ struct dynhds { int opts; }; -#define DYNHDS_OPT_NONE (0) +#define DYNHDS_OPT_NONE 0 #define DYNHDS_OPT_LOWERCASE (1 << 0) /** @@ -95,48 +94,6 @@ struct dynhds_entry *Curl_dynhds_get(struct dynhds *dynhds, const char *name, size_t namelen); struct dynhds_entry *Curl_dynhds_cget(struct dynhds *dynhds, const char *name); -#ifdef UNITTESTS -/* used by unit2602.c */ - -/** - * Return TRUE iff one or more headers with the given name exist. - */ -bool Curl_dynhds_contains(struct dynhds *dynhds, - const char *name, size_t namelen); -bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name); - -/** - * Return how often the given name appears in `dynhds`. - * Names are case-insensitive. - */ -size_t Curl_dynhds_count_name(struct dynhds *dynhds, - const char *name, size_t namelen); - -/** - * Return how often the given null-terminated name appears in `dynhds`. - * Names are case-insensitive. - */ -size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name); - -/** - * Remove all entries with the given name. - * Returns number of entries removed. - */ -size_t Curl_dynhds_remove(struct dynhds *dynhds, - const char *name, size_t namelen); -size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name); - - -/** - * Set the give header name and value, replacing any entries with - * the same name. The header is added at the end of all (remaining) - * entries. - */ -CURLcode Curl_dynhds_set(struct dynhds *dynhds, - const char *name, size_t namelen, - const char *value, size_t valuelen); -#endif - CURLcode Curl_dynhds_cset(struct dynhds *dynhds, const char *name, const char *value); @@ -155,29 +112,26 @@ 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); /** * Add the headers to the given `dynbuf` in HTTP/1.1 format with - * cr+lf line endings. Will NOT output a last empty line. + * CR+LF line endings. Does NOT output a last empty line. */ 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/lib/easy.c b/lib/easy.c index b9b8d52754..b2096fb8d3 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_NETINET_IN_H @@ -45,7 +44,6 @@ #endif #include "urldata.h" -#include #include "transfer.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" @@ -53,38 +51,32 @@ #include "url.h" #include "getinfo.h" #include "hostip.h" -#include "share.h" -#include "strdup.h" -#include "progress.h" +#include "curlx/strdup.h" #include "easyif.h" #include "multiif.h" +#include "multi_ev.h" #include "select.h" #include "cfilters.h" -#include "sendf.h" /* for failf function prototype */ +#include "sendf.h" +#include "curl_trc.h" #include "connect.h" /* for Curl_getconnectinfo */ #include "slist.h" #include "mime.h" #include "amigaos.h" #include "macos.h" -#include "curlx/warnless.h" #include "curlx/wait.h" #include "sigpipe.h" #include "vssh/ssh.h" #include "setopt.h" #include "http_digest.h" #include "system_win32.h" -#include "http2.h" #include "curlx/dynbuf.h" +#include "bufref.h" #include "altsvc.h" #include "hsts.h" #include "easy_lock.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - /* true globals -- for curl_global_init() and curl_global_cleanup() */ static unsigned int initialized; static long easy_init_flags; @@ -92,7 +84,7 @@ static long easy_init_flags; #ifdef GLOBAL_INIT_IS_THREADSAFE static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT; -#define global_init_lock() curl_simple_lock_lock(&s_lock) +#define global_init_lock() curl_simple_lock_lock(&s_lock) #define global_init_unlock() curl_simple_lock_unlock(&s_lock) #else @@ -102,19 +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 UNDER_CE -#define system_strdup _strdup -#elif !defined(HAVE_STRDUP) -#define system_strdup Curl_strdup -#else -#define system_strdup strdup -#endif - #if defined(_MSC_VER) && defined(_DLL) # pragma warning(push) # pragma warning(disable:4232) /* MSVC extension, dllimport identity */ @@ -127,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) @@ -152,47 +131,47 @@ 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; } if(Curl_trc_init()) { - DEBUGF(fprintf(stderr, "Error: Curl_trc_init failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: Curl_trc_init failed\n")); goto fail; } if(!Curl_ssl_init()) { - DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: Curl_ssl_init failed\n")); goto fail; } if(!Curl_vquic_init()) { - DEBUGF(fprintf(stderr, "Error: Curl_vquic_init failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: Curl_vquic_init failed\n")); goto fail; } if(Curl_win32_init(flags)) { - DEBUGF(fprintf(stderr, "Error: win32_init failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: win32_init failed\n")); goto fail; } if(Curl_amiga_init()) { - DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: Curl_amiga_init failed\n")); goto fail; } if(Curl_macos_init()) { - DEBUGF(fprintf(stderr, "Error: Curl_macos_init failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: Curl_macos_init failed\n")); goto fail; } if(Curl_async_global_init()) { - DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: resolver_global_init failed\n")); goto fail; } if(Curl_ssh_init()) { - DEBUGF(fprintf(stderr, "Error: Curl_ssh_init failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: Curl_ssh_init failed\n")); goto fail; } @@ -201,7 +180,7 @@ static CURLcode global_init(long flags, bool memoryfuncs) #ifdef DEBUGBUILD if(getenv("CURL_GLOBAL_INIT")) /* alloc data that will leak if *cleanup() is not called! */ - leakpointer = malloc(1); + leakpointer = curlx_malloc(1); #endif return CURLE_OK; @@ -211,7 +190,6 @@ fail: return CURLE_FAILED_INIT; } - /** * curl_global_init() globally initializes curl given a bitwise set of the * different features of what to initialize. @@ -300,7 +278,7 @@ void curl_global_cleanup(void) Curl_ssh_cleanup(); #ifdef DEBUGBUILD - free(leakpointer); + curlx_free(leakpointer); #endif easy_init_flags = 0; @@ -361,17 +339,17 @@ CURL *curl_easy_init(void) result = global_init(CURL_GLOBAL_DEFAULT, TRUE); if(result) { /* something in the global init failed, return nothing */ - DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: curl_global_init failed\n")); global_init_unlock(); return NULL; } } 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(fprintf(stderr, "Error: Curl_open failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: Curl_open failed\n")); return NULL; } @@ -407,14 +385,13 @@ static int events_timer(CURLM *multi, /* multi handle */ struct events *ev = userp; (void)multi; #if DEBUG_EV_POLL - fprintf(stderr, "events_timer: set timeout %ldms\n", timeout_ms); + curl_mfprintf(stderr, "events_timer: set timeout %ldms\n", timeout_ms); #endif ev->ms = timeout_ms; ev->msbump = TRUE; return 0; } - /* poll2cselect * * convert from poll() bit definitions to libcurl's CURL_CSELECT_* ones @@ -431,7 +408,6 @@ static int poll2cselect(int pollmask) return omask; } - /* socketcb2poll * * convert from libcurl' CURL_POLL_* bit definitions to poll()'s @@ -481,17 +457,16 @@ static int events_socket(CURL *easy, /* easy handle */ prev->next = nxt; else ev->list = nxt; - free(m); + curlx_free(m); infof(data, "socket cb: socket %" FMT_SOCKET_T " REMOVED", s); } else { /* The socket 's' is already being monitored, update the activity mask. Convert from libcurl bitmask to the poll one. */ m->socket.events = socketcb2poll(what); - infof(data, "socket cb: socket %" FMT_SOCKET_T - " UPDATED as %s%s", s, - (what&CURL_POLL_IN) ? "IN" : "", - (what&CURL_POLL_OUT) ? "OUT" : ""); + infof(data, "socket cb: socket %" FMT_SOCKET_T " UPDATED as %s%s", s, + (what & CURL_POLL_IN) ? "IN" : "", + (what & CURL_POLL_OUT) ? "OUT" : ""); } break; } @@ -507,7 +482,7 @@ static int events_socket(CURL *easy, /* easy handle */ DEBUGASSERT(0); } else { - m = malloc(sizeof(struct socketmonitor)); + m = curlx_malloc(sizeof(struct socketmonitor)); if(m) { m->next = ev->list; m->socket.fd = s; @@ -515,8 +490,8 @@ static int events_socket(CURL *easy, /* easy handle */ m->socket.revents = 0; ev->list = m; infof(data, "socket cb: socket %" FMT_SOCKET_T " ADDED as %s%s", s, - (what&CURL_POLL_IN) ? "IN" : "", - (what&CURL_POLL_OUT) ? "OUT" : ""); + (what & CURL_POLL_IN) ? "IN" : "", + (what & CURL_POLL_OUT) ? "OUT" : ""); } else return CURLE_OUT_OF_MEMORY; @@ -526,7 +501,6 @@ static int events_socket(CURL *easy, /* easy handle */ return 0; } - /* * events_setup() * @@ -559,7 +533,7 @@ static unsigned int populate_fds(struct pollfd *fds, struct events *ev) f->events = m->socket.events; f->revents = 0; #if DEBUG_EV_POLL - fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); + curl_mfprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); #endif f++; numfds++; @@ -579,19 +553,19 @@ static CURLcode poll_fds(struct events *ev, if(numfds) { /* wait for activity or timeout */ #if DEBUG_EV_POLL - fprintf(stderr, "poll(numfds=%u, timeout=%ldms)\n", numfds, ev->ms); + curl_mfprintf(stderr, "poll(numfds=%u, timeout=%ldms)\n", numfds, ev->ms); #endif *pollrc = Curl_poll(fds, numfds, ev->ms); #if DEBUG_EV_POLL - fprintf(stderr, "poll(numfds=%u, timeout=%ldms) -> %d\n", - numfds, ev->ms, *pollrc); + curl_mfprintf(stderr, "poll(numfds=%u, timeout=%ldms) -> %d\n", + numfds, ev->ms, *pollrc); #endif if(*pollrc < 0) return CURLE_UNRECOVERABLE_POLL; } else { #if DEBUG_EV_POLL - fprintf(stderr, "poll, but no fds, wait timeout=%ldms\n", ev->ms); + curl_mfprintf(stderr, "poll, but no fds, wait timeout=%ldms\n", ev->ms); #endif *pollrc = 0; if(ev->ms > 0) @@ -607,18 +581,18 @@ static CURLcode poll_fds(struct events *ev, static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) { bool done = FALSE; - CURLMcode mcode = CURLM_OK; + CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; while(!done) { CURLMsg *msg; struct pollfd fds[4]; int pollrc; - struct curltime before; + struct curltime start; const unsigned int numfds = populate_fds(fds, ev); /* get the time stamp to use to figure out how long poll takes */ - before = curlx_now(); + curlx_pnow(&start); result = poll_fds(ev, fds, numfds, &pollrc); if(result) @@ -629,9 +603,11 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) if(!pollrc) { /* timeout! */ ev->ms = 0; - /* fprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); */ - mcode = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, - &ev->running_handles); +#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); } else { /* here pollrc is > 0 */ @@ -645,31 +621,30 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) /* sending infof "randomly" to the first easy handle */ infof(multi->admin, "call curl_multi_socket_action(socket " "%" FMT_SOCKET_T ")", (curl_socket_t)fds[i].fd); - mcode = curl_multi_socket_action(multi, fds[i].fd, act, - &ev->running_handles); + mresult = curl_multi_socket_action(multi, fds[i].fd, act, + &ev->running_handles); } } - if(!ev->msbump && ev->ms >= 0) { /* If nothing updated the timeout, we decrease it by the spent time. * If it was updated, it has the new timeout time stored already. */ - timediff_t timediff = curlx_timediff(curlx_now(), before); - if(timediff > 0) { + timediff_t spent_ms = curlx_timediff_ms(curlx_now(), start); + if(spent_ms > 0) { #if DEBUG_EV_POLL - fprintf(stderr, "poll timeout %ldms not updated, decrease by " - "time spent %ldms\n", ev->ms, (long)timediff); + curl_mfprintf(stderr, "poll timeout %ldms not updated, decrease by " + "time spent %ldms\n", ev->ms, (long)spent_ms); #endif - if(timediff > ev->ms) + if(spent_ms > ev->ms) ev->ms = 0; else - ev->ms -= (long)timediff; + ev->ms -= (long)spent_ms; } } } - if(mcode) + if(mresult) return CURLE_URL_MALFORMAT; /* we do not really care about the "msgs_in_queue" value returned in the @@ -684,7 +659,6 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) return result; } - /* easy_events() * * Runs a transfer in a blocking manner using the events-based API @@ -693,7 +667,7 @@ static CURLcode easy_events(struct Curl_multi *multi) { /* this struct is made static to allow it to be used after this function returns and curl_multi_remove_handle() is called */ - static struct events evs = {-1, FALSE, 0, NULL, 0}; + static struct events evs = { -1, FALSE, 0, NULL, 0 }; /* if running event-based, do some further multi inits */ events_setup(multi, &evs); @@ -708,19 +682,19 @@ static CURLcode easy_events(struct Curl_multi *multi) static CURLcode easy_transfer(struct Curl_multi *multi) { bool done = FALSE; - CURLMcode mcode = CURLM_OK; + CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; - while(!done && !mcode) { + while(!done && !mresult) { int still_running = 0; - mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL); + mresult = curl_multi_poll(multi, NULL, 0, 1000, NULL); - if(!mcode) - mcode = curl_multi_perform(multi, &still_running); + if(!mresult) + mresult = curl_multi_perform(multi, &still_running); /* only read 'still_running' if curl_multi_perform() return OK */ - if(!mcode && !still_running) { + if(!mresult && !still_running) { int rc; CURLMsg *msg = curl_multi_info_read(multi, &rc); if(msg) { @@ -731,8 +705,8 @@ static CURLcode easy_transfer(struct Curl_multi *multi) } /* Make sure to return some kind of error if there was a multi problem */ - if(mcode) { - result = (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY : + if(mresult) { + result = (mresult == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY : /* The other multi errors should never happen, so return something suitably generic */ CURLE_BAD_FUNCTION_ARGUMENT; @@ -741,7 +715,6 @@ static CURLcode easy_transfer(struct Curl_multi *multi) return result; } - /* * easy_perform() is the internal interface that performs a blocking * transfer as previously setup. @@ -751,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. @@ -762,9 +735,9 @@ static CURLcode easy_transfer(struct Curl_multi *multi) static CURLcode easy_perform(struct Curl_easy *data, bool events) { struct Curl_multi *multi; - CURLMcode mcode; + CURLMcode mresult; CURLcode result = CURLE_OK; - SIGPIPE_VARIABLE(pipe_st); + struct Curl_sigpipe_ctx sigpipe_ctx; if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -806,14 +779,15 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) if(multi->in_callback) return CURLE_RECURSIVE_API_CALL; - /* Copy the MAXCONNECTS option to the multi handle */ + /* Copy relevant easy options to the multi handle */ curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects); + curl_multi_setopt(multi, CURLMOPT_QUICK_EXIT, (long)data->set.quick_exit); data->multi_easy = NULL; /* pretend it does not exist */ - mcode = curl_multi_add_handle(multi, data); - if(mcode) { + mresult = curl_multi_add_handle(multi, data); + if(mresult) { curl_multi_cleanup(multi); - if(mcode == CURLM_OUT_OF_MEMORY) + if(mresult == CURLM_OUT_OF_MEMORY) return CURLE_OUT_OF_MEMORY; return CURLE_FAILED_INIT; } @@ -821,8 +795,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); @@ -831,7 +805,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; @@ -841,9 +815,9 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) * curl_easy_perform() is the external interface that performs a blocking * transfer as previously setup. */ -CURLcode curl_easy_perform(CURL *data) +CURLcode curl_easy_perform(CURL *curl) { - return easy_perform(data, FALSE); + return easy_perform(curl, FALSE); } #ifdef DEBUGBUILD @@ -851,9 +825,9 @@ CURLcode curl_easy_perform(CURL *data) * curl_easy_perform_ev() is the external interface that performs a blocking * transfer using the event-based API internally. */ -CURLcode curl_easy_perform_ev(struct Curl_easy *data) +CURLcode curl_easy_perform_ev(struct Curl_easy *easy) { - return easy_perform(data, TRUE); + return easy_perform(easy, TRUE); } #endif @@ -861,14 +835,14 @@ CURLcode curl_easy_perform_ev(struct Curl_easy *data) * curl_easy_cleanup() is the external interface to cleaning/freeing the given * easy handle. */ -void curl_easy_cleanup(CURL *ptr) +void curl_easy_cleanup(CURL *curl) { - struct Curl_easy *data = ptr; + struct Curl_easy *data = curl; 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); } } @@ -877,12 +851,16 @@ void curl_easy_cleanup(CURL *ptr) * information from a performed transfer and similar. */ #undef curl_easy_getinfo -CURLcode curl_easy_getinfo(CURL *data, CURLINFO info, ...) +CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) { + struct Curl_easy *data = curl; va_list arg; void *paramp; CURLcode result; + if(!GOOD_EASY_HANDLE(data)) + return CURLE_BAD_FUNCTION_ARGUMENT; + va_start(arg, info); paramp = va_arg(arg, void *); @@ -901,8 +879,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 *)); @@ -926,19 +905,30 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) i = STRING_COPYPOSTFIELDS; if(src->set.str[i]) { if(src->set.postfieldsize == -1) - dst->set.str[i] = strdup(src->set.str[i]); + 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; @@ -949,7 +939,7 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) static void dupeasy_meta_freeentry(void *p) { (void)p; - /* Will always be FALSE. Cannot use a 0 assert here since compilers + /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ DEBUGASSERT(p == NULL); @@ -960,14 +950,14 @@ static void dupeasy_meta_freeentry(void *p) * given input easy handle. The returned handle will be a new working handle * with all options set exactly as the input source handle. */ -CURL *curl_easy_duphandle(CURL *d) +CURL *curl_easy_duphandle(CURL *curl) { - struct Curl_easy *data = d; + struct Curl_easy *data = curl; struct Curl_easy *outcurl = NULL; if(!GOOD_EASY_HANDLE(data)) goto fail; - outcurl = calloc(1, sizeof(struct Curl_easy)); + outcurl = curlx_calloc(1, sizeof(struct Curl_easy)); if(!outcurl) goto fail; @@ -981,14 +971,16 @@ CURL *curl_easy_duphandle(CURL *d) Curl_hash_init(&outcurl->meta_hash, 23, Curl_hash_str, curlx_str_key_compare, dupeasy_meta_freeentry); curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); + Curl_bufref_init(&outcurl->state.url); + Curl_bufref_init(&outcurl->state.referer); Curl_netrc_init(&outcurl->state.netrc); /* the connection pool is setup on demand */ outcurl->state.lastconnect_id = -1; outcurl->state.recent_conn_id = -1; outcurl->id = -1; - outcurl->mid = UINT_MAX; - outcurl->master_mid = UINT_MAX; + outcurl->mid = UINT32_MAX; + outcurl->master_mid = UINT32_MAX; #ifndef CURL_DISABLE_HTTP Curl_llist_init(&outcurl->state.httphdrs, NULL); @@ -1007,10 +999,10 @@ CURL *curl_easy_duphandle(CURL *d) if(data->cookies && data->state.cookie_engine) { /* If cookies are enabled in the parent handle, we enable them in the clone as well! */ - outcurl->cookies = Curl_cookie_init(outcurl, NULL, outcurl->cookies, - data->set.cookiesession); + outcurl->cookies = Curl_cookie_init(); if(!outcurl->cookies) goto fail; + outcurl->state.cookie_engine = TRUE; } if(data->state.cookielist) { @@ -1020,18 +1012,19 @@ CURL *curl_easy_duphandle(CURL *d) } #endif - if(data->state.url) { - outcurl->state.url = strdup(data->state.url); - if(!outcurl->state.url) + if(Curl_bufref_ptr(&data->state.url)) { + Curl_bufref_set(&outcurl->state.url, + Curl_bufref_dup(&data->state.url), 0, + curl_free); + if(!Curl_bufref_ptr(&outcurl->state.url)) goto fail; - outcurl->state.url_alloc = TRUE; } - - if(data->state.referer) { - outcurl->state.referer = strdup(data->state.referer); - if(!outcurl->state.referer) + if(Curl_bufref_ptr(&data->state.referer)) { + Curl_bufref_set(&outcurl->state.referer, + Curl_bufref_dup(&data->state.referer), 0, + curl_free); + if(!Curl_bufref_ptr(&outcurl->state.referer)) goto fail; - outcurl->state.referer_alloc = TRUE; } /* Reinitialize an SSL engine for the new handle @@ -1072,13 +1065,13 @@ fail: if(outcurl) { #ifndef CURL_DISABLE_COOKIES - free(outcurl->cookies); + curlx_free(outcurl->cookies); #endif curlx_dyn_free(&outcurl->state.headerb); Curl_altsvc_cleanup(&outcurl->asi); Curl_hsts_cleanup(&outcurl->hsts); Curl_freeset(outcurl); - free(outcurl); + curlx_free(outcurl); } return NULL; @@ -1088,9 +1081,9 @@ fail: * curl_easy_reset() is an external interface that allows an app to re- * initialize a session handle to the default values. */ -void curl_easy_reset(CURL *d) +void curl_easy_reset(CURL *curl) { - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return; @@ -1099,14 +1092,10 @@ void curl_easy_reset(CURL *d) /* clear all meta data */ Curl_meta_reset(data); - /* clear any resolve data */ - Curl_async_shutdown(data); - Curl_resolv_unlink(data, &data->state.dns[0]); - Curl_resolv_unlink(data, &data->state.dns[1]); /* zero out UserDefined data: */ Curl_freeset(data); memset(&data->set, 0, sizeof(struct UserDefined)); - (void)Curl_init_userdefined(data); + Curl_init_userdefined(data); /* zero out Progress data: */ memset(&data->progress, 0, sizeof(struct Progress)); @@ -1116,7 +1105,6 @@ void curl_easy_reset(CURL *d) data->progress.hide = TRUE; data->state.current_speed = -1; /* init to negative == impossible */ - data->state.retrycount = 0; /* reset the retry counter */ data->state.recent_conn_id = -1; /* clear remembered connection id */ /* zero out authentication data: */ @@ -1126,7 +1114,7 @@ void curl_easy_reset(CURL *d) #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) Curl_http_auth_cleanup_digest(data); #endif - data->master_mid = UINT_MAX; + data->master_mid = UINT32_MAX; } /* @@ -1142,12 +1130,12 @@ void curl_easy_reset(CURL *d) * NOTE: This is one of few API functions that are allowed to be called from * within a callback. */ -CURLcode curl_easy_pause(CURL *d, int action) +CURLcode curl_easy_pause(CURL *curl, int action) { CURLcode result = CURLE_OK; bool recursive = FALSE; bool changed = FALSE; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; bool recv_paused, recv_paused_new; bool send_paused, send_paused_new; @@ -1163,14 +1151,17 @@ CURLcode curl_easy_pause(CURL *d, int action) send_paused = Curl_xfer_send_is_paused(data); send_paused_new = (action & CURLPAUSE_SEND); - if(send_paused != send_paused_new) { + if((send_paused != send_paused_new) || + (send_paused_new != Curl_creader_is_paused(data))) { changed = TRUE; - result = Curl_1st_err(result, Curl_xfer_pause_send(data, send_paused_new)); + result = Curl_1st_fatal( + result, Curl_xfer_pause_send(data, send_paused_new)); } if(recv_paused != recv_paused_new) { changed = TRUE; - result = Curl_1st_err(result, Curl_xfer_pause_recv(data, recv_paused_new)); + result = Curl_1st_fatal( + result, Curl_xfer_pause_recv(data, recv_paused_new)); } /* If not completely pausing both directions now, run again in any case. */ @@ -1200,7 +1191,6 @@ CURLcode curl_easy_pause(CURL *d, int action) return result; } - static CURLcode easy_connection(struct Curl_easy *data, struct connectdata **connp) { @@ -1230,11 +1220,11 @@ static CURLcode easy_connection(struct Curl_easy *data, * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. * Returns CURLE_OK on success, error code on error. */ -CURLcode curl_easy_recv(CURL *d, void *buffer, size_t buflen, size_t *n) +CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n) { CURLcode result; struct connectdata *c; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1283,7 +1273,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); @@ -1295,9 +1285,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; @@ -1308,11 +1298,12 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, * Sends data over the connected socket. Use after successful * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. */ -CURLcode curl_easy_send(CURL *d, const void *buffer, size_t buflen, size_t *n) +CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, + size_t *n) { size_t written = 0; CURLcode result; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; if(Curl_is_in_callback(data)) @@ -1326,9 +1317,9 @@ CURLcode curl_easy_send(CURL *d, const void *buffer, size_t buflen, size_t *n) /* * Performs connection upkeep for the given session handle. */ -CURLcode curl_easy_upkeep(CURL *d) +CURLcode curl_easy_upkeep(CURL *curl) { - struct Curl_easy *data = d; + struct Curl_easy *data = curl; /* Verify that we got an easy handle we can work with. */ if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1340,18 +1331,18 @@ CURLcode curl_easy_upkeep(CURL *d) return Curl_cpool_upkeep(data); } -CURLcode curl_easy_ssls_import(CURL *d, const char *session_key, +CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key, const unsigned char *shmac, size_t shmac_len, const unsigned char *sdata, size_t sdata_len) { #if defined(USE_SSL) && defined(USE_SSLS_EXPORT) - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; return Curl_ssl_session_import(data, session_key, shmac, shmac_len, sdata, sdata_len); #else - (void)d; + (void)curl; (void)session_key; (void)shmac; (void)shmac_len; @@ -1361,17 +1352,17 @@ CURLcode curl_easy_ssls_import(CURL *d, const char *session_key, #endif } -CURLcode curl_easy_ssls_export(CURL *d, +CURLcode curl_easy_ssls_export(CURL *curl, curl_ssls_export_cb *export_fn, void *userptr) { #if defined(USE_SSL) && defined(USE_SSLS_EXPORT) - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; return Curl_ssl_session_export(data, export_fn, userptr); #else - (void)d; + (void)curl; (void)export_fn; (void)userptr; return CURLE_NOT_BUILT_IN; diff --git a/lib/easy_lock.h b/lib/easy_lock.h index 909753f43a..b8f916ff65 100644 --- a/lib/easy_lock.h +++ b/lib/easy_lock.h @@ -23,17 +23,16 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #define GLOBAL_INIT_IS_THREADSAFE -#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 +#ifdef _WIN32 -#define curl_simple_lock SRWLOCK +#define curl_simple_lock SRWLOCK #define CURL_SIMPLE_LOCK_INIT SRWLOCK_INIT -#define curl_simple_lock_lock(m) AcquireSRWLockExclusive(m) +#define curl_simple_lock_lock(m) AcquireSRWLockExclusive(m) #define curl_simple_lock_unlock(m) ReleaseSRWLockExclusive(m) #elif defined(HAVE_ATOMIC) && defined(HAVE_STDATOMIC_H) @@ -42,27 +41,25 @@ #include #endif -#define curl_simple_lock atomic_int +#define curl_simple_lock atomic_int #define CURL_SIMPLE_LOCK_INIT 0 -/* a clang-thing */ -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif - #ifndef __INTEL_COMPILER /* The Intel compiler tries to look like GCC *and* clang *and* lies in its __has_builtin() function, so override it. */ /* if GCC on i386/x86_64 or if the built-in is present */ -#if ( (defined(__GNUC__) && !defined(__clang__)) && \ - (defined(__i386__) || defined(__x86_64__))) || \ - __has_builtin(__builtin_ia32_pause) +#if (defined(__GNUC__) && !defined(__clang__)) && \ + (defined(__i386__) || defined(__x86_64__)) +#define HAVE_BUILTIN_IA32_PAUSE +#elif defined(__has_builtin) /* Keep this PP check separate from others */ +#if __has_builtin(__builtin_ia32_pause) #define HAVE_BUILTIN_IA32_PAUSE #endif - #endif +#endif /* !__INTEL_COMPILER */ + static CURL_INLINE void curl_simple_lock_lock(curl_simple_lock *lock) { for(;;) { @@ -75,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 @@ -89,18 +84,16 @@ static CURL_INLINE void curl_simple_lock_unlock(curl_simple_lock *lock) atomic_store_explicit(lock, false, memory_order_release); } -#elif defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) +#elif defined(HAVE_THREADS_POSIX) -#include - -#define curl_simple_lock pthread_mutex_t -#define CURL_SIMPLE_LOCK_INIT PTHREAD_MUTEX_INITIALIZER -#define curl_simple_lock_lock(m) pthread_mutex_lock(m) +#define curl_simple_lock pthread_mutex_t +#define CURL_SIMPLE_LOCK_INIT PTHREAD_MUTEX_INITIALIZER +#define curl_simple_lock_lock(m) pthread_mutex_lock(m) #define curl_simple_lock_unlock(m) pthread_mutex_unlock(m) #else -#undef GLOBAL_INIT_IS_THREADSAFE +#undef GLOBAL_INIT_IS_THREADSAFE #endif diff --git a/lib/easygetopt.c b/lib/easygetopt.c index 7ac39bc634..533d7e3670 100644 --- a/lib/easygetopt.c +++ b/lib/easygetopt.c @@ -21,8 +21,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #include "easyoptions.h" #ifndef CURL_DISABLE_GETOPTIONS @@ -62,8 +62,8 @@ const struct curl_easyoption *curl_easy_option_by_id(CURLoption id) } /* Iterates over available options */ -const struct curl_easyoption * -curl_easy_option_next(const struct curl_easyoption *prev) +const struct curl_easyoption *curl_easy_option_next( + const struct curl_easyoption *prev) { if(prev && prev->name) { prev++; @@ -88,8 +88,8 @@ const struct curl_easyoption *curl_easy_option_by_id(CURLoption id) return NULL; } -const struct curl_easyoption * -curl_easy_option_next(const struct curl_easyoption *prev) +const struct curl_easyoption *curl_easy_option_next( + const struct curl_easyoption *prev) { (void)prev; return NULL; diff --git a/lib/easyif.h b/lib/easyif.h index 181ce38f7b..213fd027da 100644 --- a/lib/easyif.h +++ b/lib/easyif.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Prototypes for library-wide functions provided by easy.c */ diff --git a/lib/easyoptions.c b/lib/easyoptions.c index 03d676df0e..4c0b4f0ce4 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -25,352 +25,357 @@ /* This source code is generated by optiontable.pl - DO NOT EDIT BY HAND */ #include "curl_setup.h" + #include "easyoptions.h" /* all easy setopt options listed in alphabetical order */ const struct curl_easyoption Curl_easyopts[] = { - {"ABSTRACT_UNIX_SOCKET", CURLOPT_ABSTRACT_UNIX_SOCKET, CURLOT_STRING, 0}, - {"ACCEPTTIMEOUT_MS", CURLOPT_ACCEPTTIMEOUT_MS, CURLOT_LONG, 0}, - {"ACCEPT_ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, 0}, - {"ADDRESS_SCOPE", CURLOPT_ADDRESS_SCOPE, CURLOT_LONG, 0}, - {"ALTSVC", CURLOPT_ALTSVC, CURLOT_STRING, 0}, - {"ALTSVC_CTRL", CURLOPT_ALTSVC_CTRL, CURLOT_LONG, 0}, - {"APPEND", CURLOPT_APPEND, CURLOT_LONG, 0}, - {"AUTOREFERER", CURLOPT_AUTOREFERER, CURLOT_LONG, 0}, - {"AWS_SIGV4", CURLOPT_AWS_SIGV4, CURLOT_STRING, 0}, - {"BUFFERSIZE", CURLOPT_BUFFERSIZE, CURLOT_LONG, 0}, - {"CAINFO", CURLOPT_CAINFO, CURLOT_STRING, 0}, - {"CAINFO_BLOB", CURLOPT_CAINFO_BLOB, CURLOT_BLOB, 0}, - {"CAPATH", CURLOPT_CAPATH, CURLOT_STRING, 0}, - {"CA_CACHE_TIMEOUT", CURLOPT_CA_CACHE_TIMEOUT, CURLOT_LONG, 0}, - {"CERTINFO", CURLOPT_CERTINFO, CURLOT_LONG, 0}, - {"CHUNK_BGN_FUNCTION", CURLOPT_CHUNK_BGN_FUNCTION, CURLOT_FUNCTION, 0}, - {"CHUNK_DATA", CURLOPT_CHUNK_DATA, CURLOT_CBPTR, 0}, - {"CHUNK_END_FUNCTION", CURLOPT_CHUNK_END_FUNCTION, CURLOT_FUNCTION, 0}, - {"CLOSESOCKETDATA", CURLOPT_CLOSESOCKETDATA, CURLOT_CBPTR, 0}, - {"CLOSESOCKETFUNCTION", CURLOPT_CLOSESOCKETFUNCTION, CURLOT_FUNCTION, 0}, - {"CONNECTTIMEOUT", CURLOPT_CONNECTTIMEOUT, CURLOT_LONG, 0}, - {"CONNECTTIMEOUT_MS", CURLOPT_CONNECTTIMEOUT_MS, CURLOT_LONG, 0}, - {"CONNECT_ONLY", CURLOPT_CONNECT_ONLY, CURLOT_LONG, 0}, - {"CONNECT_TO", CURLOPT_CONNECT_TO, CURLOT_SLIST, 0}, - {"CONV_FROM_NETWORK_FUNCTION", CURLOPT_CONV_FROM_NETWORK_FUNCTION, - CURLOT_FUNCTION, 0}, - {"CONV_FROM_UTF8_FUNCTION", CURLOPT_CONV_FROM_UTF8_FUNCTION, - CURLOT_FUNCTION, 0}, - {"CONV_TO_NETWORK_FUNCTION", CURLOPT_CONV_TO_NETWORK_FUNCTION, - CURLOT_FUNCTION, 0}, - {"COOKIE", CURLOPT_COOKIE, CURLOT_STRING, 0}, - {"COOKIEFILE", CURLOPT_COOKIEFILE, CURLOT_STRING, 0}, - {"COOKIEJAR", CURLOPT_COOKIEJAR, CURLOT_STRING, 0}, - {"COOKIELIST", CURLOPT_COOKIELIST, CURLOT_STRING, 0}, - {"COOKIESESSION", CURLOPT_COOKIESESSION, CURLOT_LONG, 0}, - {"COPYPOSTFIELDS", CURLOPT_COPYPOSTFIELDS, CURLOT_OBJECT, 0}, - {"CRLF", CURLOPT_CRLF, CURLOT_LONG, 0}, - {"CRLFILE", CURLOPT_CRLFILE, CURLOT_STRING, 0}, - {"CURLU", CURLOPT_CURLU, CURLOT_OBJECT, 0}, - {"CUSTOMREQUEST", CURLOPT_CUSTOMREQUEST, CURLOT_STRING, 0}, - {"DEBUGDATA", CURLOPT_DEBUGDATA, CURLOT_CBPTR, 0}, - {"DEBUGFUNCTION", CURLOPT_DEBUGFUNCTION, CURLOT_FUNCTION, 0}, - {"DEFAULT_PROTOCOL", CURLOPT_DEFAULT_PROTOCOL, CURLOT_STRING, 0}, - {"DIRLISTONLY", CURLOPT_DIRLISTONLY, CURLOT_LONG, 0}, - {"DISALLOW_USERNAME_IN_URL", CURLOPT_DISALLOW_USERNAME_IN_URL, - CURLOT_LONG, 0}, - {"DNS_CACHE_TIMEOUT", CURLOPT_DNS_CACHE_TIMEOUT, CURLOT_LONG, 0}, - {"DNS_INTERFACE", CURLOPT_DNS_INTERFACE, CURLOT_STRING, 0}, - {"DNS_LOCAL_IP4", CURLOPT_DNS_LOCAL_IP4, CURLOT_STRING, 0}, - {"DNS_LOCAL_IP6", CURLOPT_DNS_LOCAL_IP6, CURLOT_STRING, 0}, - {"DNS_SERVERS", CURLOPT_DNS_SERVERS, CURLOT_STRING, 0}, - {"DNS_SHUFFLE_ADDRESSES", CURLOPT_DNS_SHUFFLE_ADDRESSES, CURLOT_LONG, 0}, - {"DNS_USE_GLOBAL_CACHE", CURLOPT_DNS_USE_GLOBAL_CACHE, CURLOT_LONG, 0}, - {"DOH_SSL_VERIFYHOST", CURLOPT_DOH_SSL_VERIFYHOST, CURLOT_LONG, 0}, - {"DOH_SSL_VERIFYPEER", CURLOPT_DOH_SSL_VERIFYPEER, CURLOT_LONG, 0}, - {"DOH_SSL_VERIFYSTATUS", CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOT_LONG, 0}, - {"DOH_URL", CURLOPT_DOH_URL, CURLOT_STRING, 0}, - {"ECH", CURLOPT_ECH, CURLOT_STRING, 0}, - {"EGDSOCKET", CURLOPT_EGDSOCKET, CURLOT_STRING, 0}, - {"ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, CURLOT_FLAG_ALIAS}, - {"ERRORBUFFER", CURLOPT_ERRORBUFFER, CURLOT_OBJECT, 0}, - {"EXPECT_100_TIMEOUT_MS", CURLOPT_EXPECT_100_TIMEOUT_MS, CURLOT_LONG, 0}, - {"FAILONERROR", CURLOPT_FAILONERROR, CURLOT_LONG, 0}, - {"FILE", CURLOPT_WRITEDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS}, - {"FILETIME", CURLOPT_FILETIME, CURLOT_LONG, 0}, - {"FNMATCH_DATA", CURLOPT_FNMATCH_DATA, CURLOT_CBPTR, 0}, - {"FNMATCH_FUNCTION", CURLOPT_FNMATCH_FUNCTION, CURLOT_FUNCTION, 0}, - {"FOLLOWLOCATION", CURLOPT_FOLLOWLOCATION, CURLOT_LONG, 0}, - {"FORBID_REUSE", CURLOPT_FORBID_REUSE, CURLOT_LONG, 0}, - {"FRESH_CONNECT", CURLOPT_FRESH_CONNECT, CURLOT_LONG, 0}, - {"FTPAPPEND", CURLOPT_APPEND, CURLOT_LONG, CURLOT_FLAG_ALIAS}, - {"FTPLISTONLY", CURLOPT_DIRLISTONLY, CURLOT_LONG, CURLOT_FLAG_ALIAS}, - {"FTPPORT", CURLOPT_FTPPORT, CURLOT_STRING, 0}, - {"FTPSSLAUTH", CURLOPT_FTPSSLAUTH, CURLOT_VALUES, 0}, - {"FTP_ACCOUNT", CURLOPT_FTP_ACCOUNT, CURLOT_STRING, 0}, - {"FTP_ALTERNATIVE_TO_USER", CURLOPT_FTP_ALTERNATIVE_TO_USER, - CURLOT_STRING, 0}, - {"FTP_CREATE_MISSING_DIRS", CURLOPT_FTP_CREATE_MISSING_DIRS, - CURLOT_LONG, 0}, - {"FTP_FILEMETHOD", CURLOPT_FTP_FILEMETHOD, CURLOT_VALUES, 0}, - {"FTP_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT, - CURLOT_LONG, CURLOT_FLAG_ALIAS}, - {"FTP_SKIP_PASV_IP", CURLOPT_FTP_SKIP_PASV_IP, CURLOT_LONG, 0}, - {"FTP_SSL", CURLOPT_USE_SSL, CURLOT_VALUES, CURLOT_FLAG_ALIAS}, - {"FTP_SSL_CCC", CURLOPT_FTP_SSL_CCC, CURLOT_LONG, 0}, - {"FTP_USE_EPRT", CURLOPT_FTP_USE_EPRT, CURLOT_LONG, 0}, - {"FTP_USE_EPSV", CURLOPT_FTP_USE_EPSV, CURLOT_LONG, 0}, - {"FTP_USE_PRET", CURLOPT_FTP_USE_PRET, CURLOT_LONG, 0}, - {"GSSAPI_DELEGATION", CURLOPT_GSSAPI_DELEGATION, CURLOT_VALUES, 0}, - {"HAPPY_EYEBALLS_TIMEOUT_MS", CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, - CURLOT_LONG, 0}, - {"HAPROXYPROTOCOL", CURLOPT_HAPROXYPROTOCOL, CURLOT_LONG, 0}, - {"HAPROXY_CLIENT_IP", CURLOPT_HAPROXY_CLIENT_IP, CURLOT_STRING, 0}, - {"HEADER", CURLOPT_HEADER, CURLOT_LONG, 0}, - {"HEADERDATA", CURLOPT_HEADERDATA, CURLOT_CBPTR, 0}, - {"HEADERFUNCTION", CURLOPT_HEADERFUNCTION, CURLOT_FUNCTION, 0}, - {"HEADEROPT", CURLOPT_HEADEROPT, CURLOT_VALUES, 0}, - {"HSTS", CURLOPT_HSTS, CURLOT_STRING, 0}, - {"HSTSREADDATA", CURLOPT_HSTSREADDATA, CURLOT_CBPTR, 0}, - {"HSTSREADFUNCTION", CURLOPT_HSTSREADFUNCTION, CURLOT_FUNCTION, 0}, - {"HSTSWRITEDATA", CURLOPT_HSTSWRITEDATA, CURLOT_CBPTR, 0}, - {"HSTSWRITEFUNCTION", CURLOPT_HSTSWRITEFUNCTION, CURLOT_FUNCTION, 0}, - {"HSTS_CTRL", CURLOPT_HSTS_CTRL, CURLOT_LONG, 0}, - {"HTTP09_ALLOWED", CURLOPT_HTTP09_ALLOWED, CURLOT_LONG, 0}, - {"HTTP200ALIASES", CURLOPT_HTTP200ALIASES, CURLOT_SLIST, 0}, - {"HTTPAUTH", CURLOPT_HTTPAUTH, CURLOT_VALUES, 0}, - {"HTTPGET", CURLOPT_HTTPGET, CURLOT_LONG, 0}, - {"HTTPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, 0}, - {"HTTPPOST", CURLOPT_HTTPPOST, CURLOT_OBJECT, 0}, - {"HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL, CURLOT_LONG, 0}, - {"HTTP_CONTENT_DECODING", CURLOPT_HTTP_CONTENT_DECODING, CURLOT_LONG, 0}, - {"HTTP_TRANSFER_DECODING", CURLOPT_HTTP_TRANSFER_DECODING, CURLOT_LONG, 0}, - {"HTTP_VERSION", CURLOPT_HTTP_VERSION, CURLOT_VALUES, 0}, - {"IGNORE_CONTENT_LENGTH", CURLOPT_IGNORE_CONTENT_LENGTH, CURLOT_LONG, 0}, - {"INFILE", CURLOPT_READDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS}, - {"INFILESIZE", CURLOPT_INFILESIZE, CURLOT_LONG, 0}, - {"INFILESIZE_LARGE", CURLOPT_INFILESIZE_LARGE, CURLOT_OFF_T, 0}, - {"INTERFACE", CURLOPT_INTERFACE, CURLOT_STRING, 0}, - {"INTERLEAVEDATA", CURLOPT_INTERLEAVEDATA, CURLOT_CBPTR, 0}, - {"INTERLEAVEFUNCTION", CURLOPT_INTERLEAVEFUNCTION, CURLOT_FUNCTION, 0}, - {"IOCTLDATA", CURLOPT_IOCTLDATA, CURLOT_CBPTR, 0}, - {"IOCTLFUNCTION", CURLOPT_IOCTLFUNCTION, CURLOT_FUNCTION, 0}, - {"IPRESOLVE", CURLOPT_IPRESOLVE, CURLOT_VALUES, 0}, - {"ISSUERCERT", CURLOPT_ISSUERCERT, CURLOT_STRING, 0}, - {"ISSUERCERT_BLOB", CURLOPT_ISSUERCERT_BLOB, CURLOT_BLOB, 0}, - {"KEEP_SENDING_ON_ERROR", CURLOPT_KEEP_SENDING_ON_ERROR, CURLOT_LONG, 0}, - {"KEYPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, 0}, - {"KRB4LEVEL", CURLOPT_KRBLEVEL, CURLOT_STRING, CURLOT_FLAG_ALIAS}, - {"KRBLEVEL", CURLOPT_KRBLEVEL, CURLOT_STRING, 0}, - {"LOCALPORT", CURLOPT_LOCALPORT, CURLOT_LONG, 0}, - {"LOCALPORTRANGE", CURLOPT_LOCALPORTRANGE, CURLOT_LONG, 0}, - {"LOGIN_OPTIONS", CURLOPT_LOGIN_OPTIONS, CURLOT_STRING, 0}, - {"LOW_SPEED_LIMIT", CURLOPT_LOW_SPEED_LIMIT, CURLOT_LONG, 0}, - {"LOW_SPEED_TIME", CURLOPT_LOW_SPEED_TIME, CURLOT_LONG, 0}, - {"MAIL_AUTH", CURLOPT_MAIL_AUTH, CURLOT_STRING, 0}, - {"MAIL_FROM", CURLOPT_MAIL_FROM, CURLOT_STRING, 0}, - {"MAIL_RCPT", CURLOPT_MAIL_RCPT, CURLOT_SLIST, 0}, - {"MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS, - CURLOT_LONG, CURLOT_FLAG_ALIAS}, - {"MAIL_RCPT_ALLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS, CURLOT_LONG, 0}, - {"MAXAGE_CONN", CURLOPT_MAXAGE_CONN, CURLOT_LONG, 0}, - {"MAXCONNECTS", CURLOPT_MAXCONNECTS, CURLOT_LONG, 0}, - {"MAXFILESIZE", CURLOPT_MAXFILESIZE, CURLOT_LONG, 0}, - {"MAXFILESIZE_LARGE", CURLOPT_MAXFILESIZE_LARGE, CURLOT_OFF_T, 0}, - {"MAXLIFETIME_CONN", CURLOPT_MAXLIFETIME_CONN, CURLOT_LONG, 0}, - {"MAXREDIRS", CURLOPT_MAXREDIRS, CURLOT_LONG, 0}, - {"MAX_RECV_SPEED_LARGE", CURLOPT_MAX_RECV_SPEED_LARGE, CURLOT_OFF_T, 0}, - {"MAX_SEND_SPEED_LARGE", CURLOPT_MAX_SEND_SPEED_LARGE, CURLOT_OFF_T, 0}, - {"MIMEPOST", CURLOPT_MIMEPOST, CURLOT_OBJECT, 0}, - {"MIME_OPTIONS", CURLOPT_MIME_OPTIONS, CURLOT_LONG, 0}, - {"NETRC", CURLOPT_NETRC, CURLOT_VALUES, 0}, - {"NETRC_FILE", CURLOPT_NETRC_FILE, CURLOT_STRING, 0}, - {"NEW_DIRECTORY_PERMS", CURLOPT_NEW_DIRECTORY_PERMS, CURLOT_LONG, 0}, - {"NEW_FILE_PERMS", CURLOPT_NEW_FILE_PERMS, CURLOT_LONG, 0}, - {"NOBODY", CURLOPT_NOBODY, CURLOT_LONG, 0}, - {"NOPROGRESS", CURLOPT_NOPROGRESS, CURLOT_LONG, 0}, - {"NOPROXY", CURLOPT_NOPROXY, CURLOT_STRING, 0}, - {"NOSIGNAL", CURLOPT_NOSIGNAL, CURLOT_LONG, 0}, - {"OPENSOCKETDATA", CURLOPT_OPENSOCKETDATA, CURLOT_CBPTR, 0}, - {"OPENSOCKETFUNCTION", CURLOPT_OPENSOCKETFUNCTION, CURLOT_FUNCTION, 0}, - {"PASSWORD", CURLOPT_PASSWORD, CURLOT_STRING, 0}, - {"PATH_AS_IS", CURLOPT_PATH_AS_IS, CURLOT_LONG, 0}, - {"PINNEDPUBLICKEY", CURLOPT_PINNEDPUBLICKEY, CURLOT_STRING, 0}, - {"PIPEWAIT", CURLOPT_PIPEWAIT, CURLOT_LONG, 0}, - {"PORT", CURLOPT_PORT, CURLOT_LONG, 0}, - {"POST", CURLOPT_POST, CURLOT_LONG, 0}, - {"POST301", CURLOPT_POSTREDIR, CURLOT_VALUES, CURLOT_FLAG_ALIAS}, - {"POSTFIELDS", CURLOPT_POSTFIELDS, CURLOT_OBJECT, 0}, - {"POSTFIELDSIZE", CURLOPT_POSTFIELDSIZE, CURLOT_LONG, 0}, - {"POSTFIELDSIZE_LARGE", CURLOPT_POSTFIELDSIZE_LARGE, CURLOT_OFF_T, 0}, - {"POSTQUOTE", CURLOPT_POSTQUOTE, CURLOT_SLIST, 0}, - {"POSTREDIR", CURLOPT_POSTREDIR, CURLOT_VALUES, 0}, - {"PREQUOTE", CURLOPT_PREQUOTE, CURLOT_SLIST, 0}, - {"PREREQDATA", CURLOPT_PREREQDATA, CURLOT_CBPTR, 0}, - {"PREREQFUNCTION", CURLOPT_PREREQFUNCTION, CURLOT_FUNCTION, 0}, - {"PRE_PROXY", CURLOPT_PRE_PROXY, CURLOT_STRING, 0}, - {"PRIVATE", CURLOPT_PRIVATE, CURLOT_OBJECT, 0}, - {"PROGRESSDATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS}, - {"PROGRESSFUNCTION", CURLOPT_PROGRESSFUNCTION, CURLOT_FUNCTION, 0}, - {"PROTOCOLS", CURLOPT_PROTOCOLS, CURLOT_LONG, 0}, - {"PROTOCOLS_STR", CURLOPT_PROTOCOLS_STR, CURLOT_STRING, 0}, - {"PROXY", CURLOPT_PROXY, CURLOT_STRING, 0}, - {"PROXYAUTH", CURLOPT_PROXYAUTH, CURLOT_VALUES, 0}, - {"PROXYHEADER", CURLOPT_PROXYHEADER, CURLOT_SLIST, 0}, - {"PROXYPASSWORD", CURLOPT_PROXYPASSWORD, CURLOT_STRING, 0}, - {"PROXYPORT", CURLOPT_PROXYPORT, CURLOT_LONG, 0}, - {"PROXYTYPE", CURLOPT_PROXYTYPE, CURLOT_VALUES, 0}, - {"PROXYUSERNAME", CURLOPT_PROXYUSERNAME, CURLOT_STRING, 0}, - {"PROXYUSERPWD", CURLOPT_PROXYUSERPWD, CURLOT_STRING, 0}, - {"PROXY_CAINFO", CURLOPT_PROXY_CAINFO, CURLOT_STRING, 0}, - {"PROXY_CAINFO_BLOB", CURLOPT_PROXY_CAINFO_BLOB, CURLOT_BLOB, 0}, - {"PROXY_CAPATH", CURLOPT_PROXY_CAPATH, CURLOT_STRING, 0}, - {"PROXY_CRLFILE", CURLOPT_PROXY_CRLFILE, CURLOT_STRING, 0}, - {"PROXY_ISSUERCERT", CURLOPT_PROXY_ISSUERCERT, CURLOT_STRING, 0}, - {"PROXY_ISSUERCERT_BLOB", CURLOPT_PROXY_ISSUERCERT_BLOB, CURLOT_BLOB, 0}, - {"PROXY_KEYPASSWD", CURLOPT_PROXY_KEYPASSWD, CURLOT_STRING, 0}, - {"PROXY_PINNEDPUBLICKEY", CURLOPT_PROXY_PINNEDPUBLICKEY, CURLOT_STRING, 0}, - {"PROXY_SERVICE_NAME", CURLOPT_PROXY_SERVICE_NAME, CURLOT_STRING, 0}, - {"PROXY_SSLCERT", CURLOPT_PROXY_SSLCERT, CURLOT_STRING, 0}, - {"PROXY_SSLCERTTYPE", CURLOPT_PROXY_SSLCERTTYPE, CURLOT_STRING, 0}, - {"PROXY_SSLCERT_BLOB", CURLOPT_PROXY_SSLCERT_BLOB, CURLOT_BLOB, 0}, - {"PROXY_SSLKEY", CURLOPT_PROXY_SSLKEY, CURLOT_STRING, 0}, - {"PROXY_SSLKEYTYPE", CURLOPT_PROXY_SSLKEYTYPE, CURLOT_STRING, 0}, - {"PROXY_SSLKEY_BLOB", CURLOPT_PROXY_SSLKEY_BLOB, CURLOT_BLOB, 0}, - {"PROXY_SSLVERSION", CURLOPT_PROXY_SSLVERSION, CURLOT_VALUES, 0}, - {"PROXY_SSL_CIPHER_LIST", CURLOPT_PROXY_SSL_CIPHER_LIST, CURLOT_STRING, 0}, - {"PROXY_SSL_OPTIONS", CURLOPT_PROXY_SSL_OPTIONS, CURLOT_LONG, 0}, - {"PROXY_SSL_VERIFYHOST", CURLOPT_PROXY_SSL_VERIFYHOST, CURLOT_LONG, 0}, - {"PROXY_SSL_VERIFYPEER", CURLOPT_PROXY_SSL_VERIFYPEER, CURLOT_LONG, 0}, - {"PROXY_TLS13_CIPHERS", CURLOPT_PROXY_TLS13_CIPHERS, CURLOT_STRING, 0}, - {"PROXY_TLSAUTH_PASSWORD", CURLOPT_PROXY_TLSAUTH_PASSWORD, - CURLOT_STRING, 0}, - {"PROXY_TLSAUTH_TYPE", CURLOPT_PROXY_TLSAUTH_TYPE, CURLOT_STRING, 0}, - {"PROXY_TLSAUTH_USERNAME", CURLOPT_PROXY_TLSAUTH_USERNAME, - CURLOT_STRING, 0}, - {"PROXY_TRANSFER_MODE", CURLOPT_PROXY_TRANSFER_MODE, CURLOT_LONG, 0}, - {"PUT", CURLOPT_PUT, CURLOT_LONG, 0}, - {"QUICK_EXIT", CURLOPT_QUICK_EXIT, CURLOT_LONG, 0}, - {"QUOTE", CURLOPT_QUOTE, CURLOT_SLIST, 0}, - {"RANDOM_FILE", CURLOPT_RANDOM_FILE, CURLOT_STRING, 0}, - {"RANGE", CURLOPT_RANGE, CURLOT_STRING, 0}, - {"READDATA", CURLOPT_READDATA, CURLOT_CBPTR, 0}, - {"READFUNCTION", CURLOPT_READFUNCTION, CURLOT_FUNCTION, 0}, - {"REDIR_PROTOCOLS", CURLOPT_REDIR_PROTOCOLS, CURLOT_LONG, 0}, - {"REDIR_PROTOCOLS_STR", CURLOPT_REDIR_PROTOCOLS_STR, CURLOT_STRING, 0}, - {"REFERER", CURLOPT_REFERER, CURLOT_STRING, 0}, - {"REQUEST_TARGET", CURLOPT_REQUEST_TARGET, CURLOT_STRING, 0}, - {"RESOLVE", CURLOPT_RESOLVE, CURLOT_SLIST, 0}, - {"RESOLVER_START_DATA", CURLOPT_RESOLVER_START_DATA, CURLOT_CBPTR, 0}, - {"RESOLVER_START_FUNCTION", CURLOPT_RESOLVER_START_FUNCTION, - CURLOT_FUNCTION, 0}, - {"RESUME_FROM", CURLOPT_RESUME_FROM, CURLOT_LONG, 0}, - {"RESUME_FROM_LARGE", CURLOPT_RESUME_FROM_LARGE, CURLOT_OFF_T, 0}, - {"RTSPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, CURLOT_FLAG_ALIAS}, - {"RTSP_CLIENT_CSEQ", CURLOPT_RTSP_CLIENT_CSEQ, CURLOT_LONG, 0}, - {"RTSP_REQUEST", CURLOPT_RTSP_REQUEST, CURLOT_VALUES, 0}, - {"RTSP_SERVER_CSEQ", CURLOPT_RTSP_SERVER_CSEQ, CURLOT_LONG, 0}, - {"RTSP_SESSION_ID", CURLOPT_RTSP_SESSION_ID, CURLOT_STRING, 0}, - {"RTSP_STREAM_URI", CURLOPT_RTSP_STREAM_URI, CURLOT_STRING, 0}, - {"RTSP_TRANSPORT", CURLOPT_RTSP_TRANSPORT, CURLOT_STRING, 0}, - {"SASL_AUTHZID", CURLOPT_SASL_AUTHZID, CURLOT_STRING, 0}, - {"SASL_IR", CURLOPT_SASL_IR, CURLOT_LONG, 0}, - {"SEEKDATA", CURLOPT_SEEKDATA, CURLOT_CBPTR, 0}, - {"SEEKFUNCTION", CURLOPT_SEEKFUNCTION, CURLOT_FUNCTION, 0}, - {"SERVER_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT, - CURLOT_LONG, 0}, - {"SERVER_RESPONSE_TIMEOUT_MS", CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, - CURLOT_LONG, 0}, - {"SERVICE_NAME", CURLOPT_SERVICE_NAME, CURLOT_STRING, 0}, - {"SHARE", CURLOPT_SHARE, CURLOT_OBJECT, 0}, - {"SOCKOPTDATA", CURLOPT_SOCKOPTDATA, CURLOT_CBPTR, 0}, - {"SOCKOPTFUNCTION", CURLOPT_SOCKOPTFUNCTION, CURLOT_FUNCTION, 0}, - {"SOCKS5_AUTH", CURLOPT_SOCKS5_AUTH, CURLOT_LONG, 0}, - {"SOCKS5_GSSAPI_NEC", CURLOPT_SOCKS5_GSSAPI_NEC, CURLOT_LONG, 0}, - {"SOCKS5_GSSAPI_SERVICE", CURLOPT_SOCKS5_GSSAPI_SERVICE, CURLOT_STRING, 0}, - {"SSH_AUTH_TYPES", CURLOPT_SSH_AUTH_TYPES, CURLOT_VALUES, 0}, - {"SSH_COMPRESSION", CURLOPT_SSH_COMPRESSION, CURLOT_LONG, 0}, - {"SSH_HOSTKEYDATA", CURLOPT_SSH_HOSTKEYDATA, CURLOT_CBPTR, 0}, - {"SSH_HOSTKEYFUNCTION", CURLOPT_SSH_HOSTKEYFUNCTION, CURLOT_FUNCTION, 0}, - {"SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, - CURLOT_STRING, 0}, - {"SSH_HOST_PUBLIC_KEY_SHA256", CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, - CURLOT_STRING, 0}, - {"SSH_KEYDATA", CURLOPT_SSH_KEYDATA, CURLOT_CBPTR, 0}, - {"SSH_KEYFUNCTION", CURLOPT_SSH_KEYFUNCTION, CURLOT_FUNCTION, 0}, - {"SSH_KNOWNHOSTS", CURLOPT_SSH_KNOWNHOSTS, CURLOT_STRING, 0}, - {"SSH_PRIVATE_KEYFILE", CURLOPT_SSH_PRIVATE_KEYFILE, CURLOT_STRING, 0}, - {"SSH_PUBLIC_KEYFILE", CURLOPT_SSH_PUBLIC_KEYFILE, CURLOT_STRING, 0}, - {"SSLCERT", CURLOPT_SSLCERT, CURLOT_STRING, 0}, - {"SSLCERTPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, CURLOT_FLAG_ALIAS}, - {"SSLCERTTYPE", CURLOPT_SSLCERTTYPE, CURLOT_STRING, 0}, - {"SSLCERT_BLOB", CURLOPT_SSLCERT_BLOB, CURLOT_BLOB, 0}, - {"SSLENGINE", CURLOPT_SSLENGINE, CURLOT_STRING, 0}, - {"SSLENGINE_DEFAULT", CURLOPT_SSLENGINE_DEFAULT, CURLOT_LONG, 0}, - {"SSLKEY", CURLOPT_SSLKEY, CURLOT_STRING, 0}, - {"SSLKEYPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, CURLOT_FLAG_ALIAS}, - {"SSLKEYTYPE", CURLOPT_SSLKEYTYPE, CURLOT_STRING, 0}, - {"SSLKEY_BLOB", CURLOPT_SSLKEY_BLOB, CURLOT_BLOB, 0}, - {"SSLVERSION", CURLOPT_SSLVERSION, CURLOT_VALUES, 0}, - {"SSL_CIPHER_LIST", CURLOPT_SSL_CIPHER_LIST, CURLOT_STRING, 0}, - {"SSL_CTX_DATA", CURLOPT_SSL_CTX_DATA, CURLOT_CBPTR, 0}, - {"SSL_CTX_FUNCTION", CURLOPT_SSL_CTX_FUNCTION, CURLOT_FUNCTION, 0}, - {"SSL_EC_CURVES", CURLOPT_SSL_EC_CURVES, CURLOT_STRING, 0}, - {"SSL_ENABLE_ALPN", CURLOPT_SSL_ENABLE_ALPN, CURLOT_LONG, 0}, - {"SSL_ENABLE_NPN", CURLOPT_SSL_ENABLE_NPN, CURLOT_LONG, 0}, - {"SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CURLOT_LONG, 0}, - {"SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CURLOT_VALUES, 0}, - {"SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CURLOT_LONG, 0}, - {"SSL_SIGNATURE_ALGORITHMS", CURLOPT_SSL_SIGNATURE_ALGORITHMS, - CURLOT_STRING, 0}, - {"SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST, CURLOT_LONG, 0}, - {"SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER, CURLOT_LONG, 0}, - {"SSL_VERIFYSTATUS", CURLOPT_SSL_VERIFYSTATUS, CURLOT_LONG, 0}, - {"STDERR", CURLOPT_STDERR, CURLOT_OBJECT, 0}, - {"STREAM_DEPENDS", CURLOPT_STREAM_DEPENDS, CURLOT_OBJECT, 0}, - {"STREAM_DEPENDS_E", CURLOPT_STREAM_DEPENDS_E, CURLOT_OBJECT, 0}, - {"STREAM_WEIGHT", CURLOPT_STREAM_WEIGHT, CURLOT_LONG, 0}, - {"SUPPRESS_CONNECT_HEADERS", CURLOPT_SUPPRESS_CONNECT_HEADERS, - CURLOT_LONG, 0}, - {"TCP_FASTOPEN", CURLOPT_TCP_FASTOPEN, CURLOT_LONG, 0}, - {"TCP_KEEPALIVE", CURLOPT_TCP_KEEPALIVE, CURLOT_LONG, 0}, - {"TCP_KEEPCNT", CURLOPT_TCP_KEEPCNT, CURLOT_LONG, 0}, - {"TCP_KEEPIDLE", CURLOPT_TCP_KEEPIDLE, CURLOT_LONG, 0}, - {"TCP_KEEPINTVL", CURLOPT_TCP_KEEPINTVL, CURLOT_LONG, 0}, - {"TCP_NODELAY", CURLOPT_TCP_NODELAY, CURLOT_LONG, 0}, - {"TELNETOPTIONS", CURLOPT_TELNETOPTIONS, CURLOT_SLIST, 0}, - {"TFTP_BLKSIZE", CURLOPT_TFTP_BLKSIZE, CURLOT_LONG, 0}, - {"TFTP_NO_OPTIONS", CURLOPT_TFTP_NO_OPTIONS, CURLOT_LONG, 0}, - {"TIMECONDITION", CURLOPT_TIMECONDITION, CURLOT_VALUES, 0}, - {"TIMEOUT", CURLOPT_TIMEOUT, CURLOT_LONG, 0}, - {"TIMEOUT_MS", CURLOPT_TIMEOUT_MS, CURLOT_LONG, 0}, - {"TIMEVALUE", CURLOPT_TIMEVALUE, CURLOT_LONG, 0}, - {"TIMEVALUE_LARGE", CURLOPT_TIMEVALUE_LARGE, CURLOT_OFF_T, 0}, - {"TLS13_CIPHERS", CURLOPT_TLS13_CIPHERS, CURLOT_STRING, 0}, - {"TLSAUTH_PASSWORD", CURLOPT_TLSAUTH_PASSWORD, CURLOT_STRING, 0}, - {"TLSAUTH_TYPE", CURLOPT_TLSAUTH_TYPE, CURLOT_STRING, 0}, - {"TLSAUTH_USERNAME", CURLOPT_TLSAUTH_USERNAME, CURLOT_STRING, 0}, - {"TRAILERDATA", CURLOPT_TRAILERDATA, CURLOT_CBPTR, 0}, - {"TRAILERFUNCTION", CURLOPT_TRAILERFUNCTION, CURLOT_FUNCTION, 0}, - {"TRANSFERTEXT", CURLOPT_TRANSFERTEXT, CURLOT_LONG, 0}, - {"TRANSFER_ENCODING", CURLOPT_TRANSFER_ENCODING, CURLOT_LONG, 0}, - {"UNIX_SOCKET_PATH", CURLOPT_UNIX_SOCKET_PATH, CURLOT_STRING, 0}, - {"UNRESTRICTED_AUTH", CURLOPT_UNRESTRICTED_AUTH, CURLOT_LONG, 0}, - {"UPKEEP_INTERVAL_MS", CURLOPT_UPKEEP_INTERVAL_MS, CURLOT_LONG, 0}, - {"UPLOAD", CURLOPT_UPLOAD, CURLOT_LONG, 0}, - {"UPLOAD_BUFFERSIZE", CURLOPT_UPLOAD_BUFFERSIZE, CURLOT_LONG, 0}, - {"UPLOAD_FLAGS", CURLOPT_UPLOAD_FLAGS, CURLOT_LONG, 0}, - {"URL", CURLOPT_URL, CURLOT_STRING, 0}, - {"USERAGENT", CURLOPT_USERAGENT, CURLOT_STRING, 0}, - {"USERNAME", CURLOPT_USERNAME, CURLOT_STRING, 0}, - {"USERPWD", CURLOPT_USERPWD, CURLOT_STRING, 0}, - {"USE_SSL", CURLOPT_USE_SSL, CURLOT_VALUES, 0}, - {"VERBOSE", CURLOPT_VERBOSE, CURLOT_LONG, 0}, - {"WILDCARDMATCH", CURLOPT_WILDCARDMATCH, CURLOT_LONG, 0}, - {"WRITEDATA", CURLOPT_WRITEDATA, CURLOT_CBPTR, 0}, - {"WRITEFUNCTION", CURLOPT_WRITEFUNCTION, CURLOT_FUNCTION, 0}, - {"WRITEHEADER", CURLOPT_HEADERDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS}, - {"WS_OPTIONS", CURLOPT_WS_OPTIONS, CURLOT_LONG, 0}, - {"XFERINFODATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, 0}, - {"XFERINFOFUNCTION", CURLOPT_XFERINFOFUNCTION, CURLOT_FUNCTION, 0}, - {"XOAUTH2_BEARER", CURLOPT_XOAUTH2_BEARER, CURLOT_STRING, 0}, - {NULL, CURLOPT_LASTENTRY, CURLOT_LONG, 0} /* end of table */ + { "ABSTRACT_UNIX_SOCKET", CURLOPT_ABSTRACT_UNIX_SOCKET, CURLOT_STRING, 0 }, + { "ACCEPTTIMEOUT_MS", CURLOPT_ACCEPTTIMEOUT_MS, CURLOT_LONG, 0 }, + { "ACCEPT_ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, 0 }, + { "ADDRESS_SCOPE", CURLOPT_ADDRESS_SCOPE, CURLOT_LONG, 0 }, + { "ALTSVC", CURLOPT_ALTSVC, CURLOT_STRING, 0 }, + { "ALTSVC_CTRL", CURLOPT_ALTSVC_CTRL, CURLOT_LONG, 0 }, + { "APPEND", CURLOPT_APPEND, CURLOT_LONG, 0 }, + { "AUTOREFERER", CURLOPT_AUTOREFERER, CURLOT_LONG, 0 }, + { "AWS_SIGV4", CURLOPT_AWS_SIGV4, CURLOT_STRING, 0 }, + { "BUFFERSIZE", CURLOPT_BUFFERSIZE, CURLOT_LONG, 0 }, + { "CAINFO", CURLOPT_CAINFO, CURLOT_STRING, 0 }, + { "CAINFO_BLOB", CURLOPT_CAINFO_BLOB, CURLOT_BLOB, 0 }, + { "CAPATH", CURLOPT_CAPATH, CURLOT_STRING, 0 }, + { "CA_CACHE_TIMEOUT", CURLOPT_CA_CACHE_TIMEOUT, CURLOT_LONG, 0 }, + { "CERTINFO", CURLOPT_CERTINFO, CURLOT_LONG, 0 }, + { "CHUNK_BGN_FUNCTION", CURLOPT_CHUNK_BGN_FUNCTION, CURLOT_FUNCTION, 0 }, + { "CHUNK_DATA", CURLOPT_CHUNK_DATA, CURLOT_CBPTR, 0 }, + { "CHUNK_END_FUNCTION", CURLOPT_CHUNK_END_FUNCTION, CURLOT_FUNCTION, 0 }, + { "CLOSESOCKETDATA", CURLOPT_CLOSESOCKETDATA, CURLOT_CBPTR, 0 }, + { "CLOSESOCKETFUNCTION", CURLOPT_CLOSESOCKETFUNCTION, CURLOT_FUNCTION, 0 }, + { "CONNECTTIMEOUT", CURLOPT_CONNECTTIMEOUT, CURLOT_LONG, 0 }, + { "CONNECTTIMEOUT_MS", CURLOPT_CONNECTTIMEOUT_MS, CURLOT_LONG, 0 }, + { "CONNECT_ONLY", CURLOPT_CONNECT_ONLY, CURLOT_LONG, 0 }, + { "CONNECT_TO", CURLOPT_CONNECT_TO, CURLOT_SLIST, 0 }, + { "CONV_FROM_NETWORK_FUNCTION", CURLOPT_CONV_FROM_NETWORK_FUNCTION, + CURLOT_FUNCTION, 0 }, + { "CONV_FROM_UTF8_FUNCTION", CURLOPT_CONV_FROM_UTF8_FUNCTION, + CURLOT_FUNCTION, 0 }, + { "CONV_TO_NETWORK_FUNCTION", CURLOPT_CONV_TO_NETWORK_FUNCTION, + CURLOT_FUNCTION, 0 }, + { "COOKIE", CURLOPT_COOKIE, CURLOT_STRING, 0 }, + { "COOKIEFILE", CURLOPT_COOKIEFILE, CURLOT_STRING, 0 }, + { "COOKIEJAR", CURLOPT_COOKIEJAR, CURLOT_STRING, 0 }, + { "COOKIELIST", CURLOPT_COOKIELIST, CURLOT_STRING, 0 }, + { "COOKIESESSION", CURLOPT_COOKIESESSION, CURLOT_LONG, 0 }, + { "COPYPOSTFIELDS", CURLOPT_COPYPOSTFIELDS, CURLOT_OBJECT, 0 }, + { "CRLF", CURLOPT_CRLF, CURLOT_LONG, 0 }, + { "CRLFILE", CURLOPT_CRLFILE, CURLOT_STRING, 0 }, + { "CURLU", CURLOPT_CURLU, CURLOT_OBJECT, 0 }, + { "CUSTOMREQUEST", CURLOPT_CUSTOMREQUEST, CURLOT_STRING, 0 }, + { "DEBUGDATA", CURLOPT_DEBUGDATA, CURLOT_CBPTR, 0 }, + { "DEBUGFUNCTION", CURLOPT_DEBUGFUNCTION, CURLOT_FUNCTION, 0 }, + { "DEFAULT_PROTOCOL", CURLOPT_DEFAULT_PROTOCOL, CURLOT_STRING, 0 }, + { "DIRLISTONLY", CURLOPT_DIRLISTONLY, CURLOT_LONG, 0 }, + { "DISALLOW_USERNAME_IN_URL", CURLOPT_DISALLOW_USERNAME_IN_URL, + CURLOT_LONG, 0 }, + { "DNS_CACHE_TIMEOUT", CURLOPT_DNS_CACHE_TIMEOUT, CURLOT_LONG, 0 }, + { "DNS_INTERFACE", CURLOPT_DNS_INTERFACE, CURLOT_STRING, 0 }, + { "DNS_LOCAL_IP4", CURLOPT_DNS_LOCAL_IP4, CURLOT_STRING, 0 }, + { "DNS_LOCAL_IP6", CURLOPT_DNS_LOCAL_IP6, CURLOT_STRING, 0 }, + { "DNS_SERVERS", CURLOPT_DNS_SERVERS, CURLOT_STRING, 0 }, + { "DNS_SHUFFLE_ADDRESSES", CURLOPT_DNS_SHUFFLE_ADDRESSES, CURLOT_LONG, 0 }, + { "DNS_USE_GLOBAL_CACHE", CURLOPT_DNS_USE_GLOBAL_CACHE, CURLOT_LONG, 0 }, + { "DOH_SSL_VERIFYHOST", CURLOPT_DOH_SSL_VERIFYHOST, CURLOT_LONG, 0 }, + { "DOH_SSL_VERIFYPEER", CURLOPT_DOH_SSL_VERIFYPEER, CURLOT_LONG, 0 }, + { "DOH_SSL_VERIFYSTATUS", CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOT_LONG, 0 }, + { "DOH_URL", CURLOPT_DOH_URL, CURLOT_STRING, 0 }, + { "ECH", CURLOPT_ECH, CURLOT_STRING, 0 }, + { "EGDSOCKET", CURLOPT_EGDSOCKET, CURLOT_STRING, 0 }, + { "ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, CURLOT_FLAG_ALIAS }, + { "ERRORBUFFER", CURLOPT_ERRORBUFFER, CURLOT_OBJECT, 0 }, + { "EXPECT_100_TIMEOUT_MS", CURLOPT_EXPECT_100_TIMEOUT_MS, CURLOT_LONG, 0 }, + { "FAILONERROR", CURLOPT_FAILONERROR, CURLOT_LONG, 0 }, + { "FILE", CURLOPT_WRITEDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS }, + { "FILETIME", CURLOPT_FILETIME, CURLOT_LONG, 0 }, + { "FNMATCH_DATA", CURLOPT_FNMATCH_DATA, CURLOT_CBPTR, 0 }, + { "FNMATCH_FUNCTION", CURLOPT_FNMATCH_FUNCTION, CURLOT_FUNCTION, 0 }, + { "FOLLOWLOCATION", CURLOPT_FOLLOWLOCATION, CURLOT_LONG, 0 }, + { "FORBID_REUSE", CURLOPT_FORBID_REUSE, CURLOT_LONG, 0 }, + { "FRESH_CONNECT", CURLOPT_FRESH_CONNECT, CURLOT_LONG, 0 }, + { "FTPAPPEND", CURLOPT_APPEND, CURLOT_LONG, CURLOT_FLAG_ALIAS }, + { "FTPLISTONLY", CURLOPT_DIRLISTONLY, CURLOT_LONG, CURLOT_FLAG_ALIAS }, + { "FTPPORT", CURLOPT_FTPPORT, CURLOT_STRING, 0 }, + { "FTPSSLAUTH", CURLOPT_FTPSSLAUTH, CURLOT_VALUES, 0 }, + { "FTP_ACCOUNT", CURLOPT_FTP_ACCOUNT, CURLOT_STRING, 0 }, + { "FTP_ALTERNATIVE_TO_USER", CURLOPT_FTP_ALTERNATIVE_TO_USER, + CURLOT_STRING, 0 }, + { "FTP_CREATE_MISSING_DIRS", CURLOPT_FTP_CREATE_MISSING_DIRS, + CURLOT_LONG, 0 }, + { "FTP_FILEMETHOD", CURLOPT_FTP_FILEMETHOD, CURLOT_VALUES, 0 }, + { "FTP_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT, + CURLOT_LONG, CURLOT_FLAG_ALIAS }, + { "FTP_SKIP_PASV_IP", CURLOPT_FTP_SKIP_PASV_IP, CURLOT_LONG, 0 }, + { "FTP_SSL", CURLOPT_USE_SSL, CURLOT_VALUES, CURLOT_FLAG_ALIAS }, + { "FTP_SSL_CCC", CURLOPT_FTP_SSL_CCC, CURLOT_LONG, 0 }, + { "FTP_USE_EPRT", CURLOPT_FTP_USE_EPRT, CURLOT_LONG, 0 }, + { "FTP_USE_EPSV", CURLOPT_FTP_USE_EPSV, CURLOT_LONG, 0 }, + { "FTP_USE_PRET", CURLOPT_FTP_USE_PRET, CURLOT_LONG, 0 }, + { "GSSAPI_DELEGATION", CURLOPT_GSSAPI_DELEGATION, CURLOT_VALUES, 0 }, + { "HAPPY_EYEBALLS_TIMEOUT_MS", CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, + CURLOT_LONG, 0 }, + { "HAPROXYPROTOCOL", CURLOPT_HAPROXYPROTOCOL, CURLOT_LONG, 0 }, + { "HAPROXY_CLIENT_IP", CURLOPT_HAPROXY_CLIENT_IP, CURLOT_STRING, 0 }, + { "HEADER", CURLOPT_HEADER, CURLOT_LONG, 0 }, + { "HEADERDATA", CURLOPT_HEADERDATA, CURLOT_CBPTR, 0 }, + { "HEADERFUNCTION", CURLOPT_HEADERFUNCTION, CURLOT_FUNCTION, 0 }, + { "HEADEROPT", CURLOPT_HEADEROPT, CURLOT_VALUES, 0 }, + { "HSTS", CURLOPT_HSTS, CURLOT_STRING, 0 }, + { "HSTSREADDATA", CURLOPT_HSTSREADDATA, CURLOT_CBPTR, 0 }, + { "HSTSREADFUNCTION", CURLOPT_HSTSREADFUNCTION, CURLOT_FUNCTION, 0 }, + { "HSTSWRITEDATA", CURLOPT_HSTSWRITEDATA, CURLOT_CBPTR, 0 }, + { "HSTSWRITEFUNCTION", CURLOPT_HSTSWRITEFUNCTION, CURLOT_FUNCTION, 0 }, + { "HSTS_CTRL", CURLOPT_HSTS_CTRL, CURLOT_LONG, 0 }, + { "HTTP09_ALLOWED", CURLOPT_HTTP09_ALLOWED, CURLOT_LONG, 0 }, + { "HTTP200ALIASES", CURLOPT_HTTP200ALIASES, CURLOT_SLIST, 0 }, + { "HTTPAUTH", CURLOPT_HTTPAUTH, CURLOT_VALUES, 0 }, + { "HTTPGET", CURLOPT_HTTPGET, CURLOT_LONG, 0 }, + { "HTTPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, 0 }, + { "HTTPPOST", CURLOPT_HTTPPOST, CURLOT_OBJECT, 0 }, + { "HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL, CURLOT_LONG, 0 }, + { "HTTP_CONTENT_DECODING", CURLOPT_HTTP_CONTENT_DECODING, CURLOT_LONG, 0 }, + { "HTTP_TRANSFER_DECODING", CURLOPT_HTTP_TRANSFER_DECODING, + CURLOT_LONG, 0 }, + { "HTTP_VERSION", CURLOPT_HTTP_VERSION, CURLOT_VALUES, 0 }, + { "IGNORE_CONTENT_LENGTH", CURLOPT_IGNORE_CONTENT_LENGTH, CURLOT_LONG, 0 }, + { "INFILE", CURLOPT_READDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS }, + { "INFILESIZE", CURLOPT_INFILESIZE, CURLOT_LONG, 0 }, + { "INFILESIZE_LARGE", CURLOPT_INFILESIZE_LARGE, CURLOT_OFF_T, 0 }, + { "INTERFACE", CURLOPT_INTERFACE, CURLOT_STRING, 0 }, + { "INTERLEAVEDATA", CURLOPT_INTERLEAVEDATA, CURLOT_CBPTR, 0 }, + { "INTERLEAVEFUNCTION", CURLOPT_INTERLEAVEFUNCTION, CURLOT_FUNCTION, 0 }, + { "IOCTLDATA", CURLOPT_IOCTLDATA, CURLOT_CBPTR, 0 }, + { "IOCTLFUNCTION", CURLOPT_IOCTLFUNCTION, CURLOT_FUNCTION, 0 }, + { "IPRESOLVE", CURLOPT_IPRESOLVE, CURLOT_VALUES, 0 }, + { "ISSUERCERT", CURLOPT_ISSUERCERT, CURLOT_STRING, 0 }, + { "ISSUERCERT_BLOB", CURLOPT_ISSUERCERT_BLOB, CURLOT_BLOB, 0 }, + { "KEEP_SENDING_ON_ERROR", CURLOPT_KEEP_SENDING_ON_ERROR, CURLOT_LONG, 0 }, + { "KEYPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, 0 }, + { "KRB4LEVEL", CURLOPT_KRBLEVEL, CURLOT_STRING, CURLOT_FLAG_ALIAS }, + { "KRBLEVEL", CURLOPT_KRBLEVEL, CURLOT_STRING, 0 }, + { "LOCALPORT", CURLOPT_LOCALPORT, CURLOT_LONG, 0 }, + { "LOCALPORTRANGE", CURLOPT_LOCALPORTRANGE, CURLOT_LONG, 0 }, + { "LOGIN_OPTIONS", CURLOPT_LOGIN_OPTIONS, CURLOT_STRING, 0 }, + { "LOW_SPEED_LIMIT", CURLOPT_LOW_SPEED_LIMIT, CURLOT_LONG, 0 }, + { "LOW_SPEED_TIME", CURLOPT_LOW_SPEED_TIME, CURLOT_LONG, 0 }, + { "MAIL_AUTH", CURLOPT_MAIL_AUTH, CURLOT_STRING, 0 }, + { "MAIL_FROM", CURLOPT_MAIL_FROM, CURLOT_STRING, 0 }, + { "MAIL_RCPT", CURLOPT_MAIL_RCPT, CURLOT_SLIST, 0 }, + { "MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS, + CURLOT_LONG, CURLOT_FLAG_ALIAS }, + { "MAIL_RCPT_ALLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS, CURLOT_LONG, 0 }, + { "MAXAGE_CONN", CURLOPT_MAXAGE_CONN, CURLOT_LONG, 0 }, + { "MAXCONNECTS", CURLOPT_MAXCONNECTS, CURLOT_LONG, 0 }, + { "MAXFILESIZE", CURLOPT_MAXFILESIZE, CURLOT_LONG, 0 }, + { "MAXFILESIZE_LARGE", CURLOPT_MAXFILESIZE_LARGE, CURLOT_OFF_T, 0 }, + { "MAXLIFETIME_CONN", CURLOPT_MAXLIFETIME_CONN, CURLOT_LONG, 0 }, + { "MAXREDIRS", CURLOPT_MAXREDIRS, CURLOT_LONG, 0 }, + { "MAX_RECV_SPEED_LARGE", CURLOPT_MAX_RECV_SPEED_LARGE, CURLOT_OFF_T, 0 }, + { "MAX_SEND_SPEED_LARGE", CURLOPT_MAX_SEND_SPEED_LARGE, CURLOT_OFF_T, 0 }, + { "MIMEPOST", CURLOPT_MIMEPOST, CURLOT_OBJECT, 0 }, + { "MIME_OPTIONS", CURLOPT_MIME_OPTIONS, CURLOT_LONG, 0 }, + { "NETRC", CURLOPT_NETRC, CURLOT_VALUES, 0 }, + { "NETRC_FILE", CURLOPT_NETRC_FILE, CURLOT_STRING, 0 }, + { "NEW_DIRECTORY_PERMS", CURLOPT_NEW_DIRECTORY_PERMS, CURLOT_LONG, 0 }, + { "NEW_FILE_PERMS", CURLOPT_NEW_FILE_PERMS, CURLOT_LONG, 0 }, + { "NOBODY", CURLOPT_NOBODY, CURLOT_LONG, 0 }, + { "NOPROGRESS", CURLOPT_NOPROGRESS, CURLOT_LONG, 0 }, + { "NOPROXY", CURLOPT_NOPROXY, CURLOT_STRING, 0 }, + { "NOSIGNAL", CURLOPT_NOSIGNAL, CURLOT_LONG, 0 }, + { "OPENSOCKETDATA", CURLOPT_OPENSOCKETDATA, CURLOT_CBPTR, 0 }, + { "OPENSOCKETFUNCTION", CURLOPT_OPENSOCKETFUNCTION, CURLOT_FUNCTION, 0 }, + { "PASSWORD", CURLOPT_PASSWORD, CURLOT_STRING, 0 }, + { "PATH_AS_IS", CURLOPT_PATH_AS_IS, CURLOT_LONG, 0 }, + { "PINNEDPUBLICKEY", CURLOPT_PINNEDPUBLICKEY, CURLOT_STRING, 0 }, + { "PIPEWAIT", CURLOPT_PIPEWAIT, CURLOT_LONG, 0 }, + { "PORT", CURLOPT_PORT, CURLOT_LONG, 0 }, + { "POST", CURLOPT_POST, CURLOT_LONG, 0 }, + { "POST301", CURLOPT_POSTREDIR, CURLOT_VALUES, CURLOT_FLAG_ALIAS }, + { "POSTFIELDS", CURLOPT_POSTFIELDS, CURLOT_OBJECT, 0 }, + { "POSTFIELDSIZE", CURLOPT_POSTFIELDSIZE, CURLOT_LONG, 0 }, + { "POSTFIELDSIZE_LARGE", CURLOPT_POSTFIELDSIZE_LARGE, CURLOT_OFF_T, 0 }, + { "POSTQUOTE", CURLOPT_POSTQUOTE, CURLOT_SLIST, 0 }, + { "POSTREDIR", CURLOPT_POSTREDIR, CURLOT_VALUES, 0 }, + { "PREQUOTE", CURLOPT_PREQUOTE, CURLOT_SLIST, 0 }, + { "PREREQDATA", CURLOPT_PREREQDATA, CURLOT_CBPTR, 0 }, + { "PREREQFUNCTION", CURLOPT_PREREQFUNCTION, CURLOT_FUNCTION, 0 }, + { "PRE_PROXY", CURLOPT_PRE_PROXY, CURLOT_STRING, 0 }, + { "PRIVATE", CURLOPT_PRIVATE, CURLOT_OBJECT, 0 }, + { "PROGRESSDATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS }, + { "PROGRESSFUNCTION", CURLOPT_PROGRESSFUNCTION, CURLOT_FUNCTION, 0 }, + { "PROTOCOLS", CURLOPT_PROTOCOLS, CURLOT_LONG, 0 }, + { "PROTOCOLS_STR", CURLOPT_PROTOCOLS_STR, CURLOT_STRING, 0 }, + { "PROXY", CURLOPT_PROXY, CURLOT_STRING, 0 }, + { "PROXYAUTH", CURLOPT_PROXYAUTH, CURLOT_VALUES, 0 }, + { "PROXYHEADER", CURLOPT_PROXYHEADER, CURLOT_SLIST, 0 }, + { "PROXYPASSWORD", CURLOPT_PROXYPASSWORD, CURLOT_STRING, 0 }, + { "PROXYPORT", CURLOPT_PROXYPORT, CURLOT_LONG, 0 }, + { "PROXYTYPE", CURLOPT_PROXYTYPE, CURLOT_VALUES, 0 }, + { "PROXYUSERNAME", CURLOPT_PROXYUSERNAME, CURLOT_STRING, 0 }, + { "PROXYUSERPWD", CURLOPT_PROXYUSERPWD, CURLOT_STRING, 0 }, + { "PROXY_CAINFO", CURLOPT_PROXY_CAINFO, CURLOT_STRING, 0 }, + { "PROXY_CAINFO_BLOB", CURLOPT_PROXY_CAINFO_BLOB, CURLOT_BLOB, 0 }, + { "PROXY_CAPATH", CURLOPT_PROXY_CAPATH, CURLOT_STRING, 0 }, + { "PROXY_CRLFILE", CURLOPT_PROXY_CRLFILE, CURLOT_STRING, 0 }, + { "PROXY_ISSUERCERT", CURLOPT_PROXY_ISSUERCERT, CURLOT_STRING, 0 }, + { "PROXY_ISSUERCERT_BLOB", CURLOPT_PROXY_ISSUERCERT_BLOB, CURLOT_BLOB, 0 }, + { "PROXY_KEYPASSWD", CURLOPT_PROXY_KEYPASSWD, CURLOT_STRING, 0 }, + { "PROXY_PINNEDPUBLICKEY", CURLOPT_PROXY_PINNEDPUBLICKEY, + CURLOT_STRING, 0 }, + { "PROXY_SERVICE_NAME", CURLOPT_PROXY_SERVICE_NAME, CURLOT_STRING, 0 }, + { "PROXY_SSLCERT", CURLOPT_PROXY_SSLCERT, CURLOT_STRING, 0 }, + { "PROXY_SSLCERTTYPE", CURLOPT_PROXY_SSLCERTTYPE, CURLOT_STRING, 0 }, + { "PROXY_SSLCERT_BLOB", CURLOPT_PROXY_SSLCERT_BLOB, CURLOT_BLOB, 0 }, + { "PROXY_SSLKEY", CURLOPT_PROXY_SSLKEY, CURLOT_STRING, 0 }, + { "PROXY_SSLKEYTYPE", CURLOPT_PROXY_SSLKEYTYPE, CURLOT_STRING, 0 }, + { "PROXY_SSLKEY_BLOB", CURLOPT_PROXY_SSLKEY_BLOB, CURLOT_BLOB, 0 }, + { "PROXY_SSLVERSION", CURLOPT_PROXY_SSLVERSION, CURLOT_VALUES, 0 }, + { "PROXY_SSL_CIPHER_LIST", CURLOPT_PROXY_SSL_CIPHER_LIST, + CURLOT_STRING, 0 }, + { "PROXY_SSL_OPTIONS", CURLOPT_PROXY_SSL_OPTIONS, CURLOT_LONG, 0 }, + { "PROXY_SSL_VERIFYHOST", CURLOPT_PROXY_SSL_VERIFYHOST, CURLOT_LONG, 0 }, + { "PROXY_SSL_VERIFYPEER", CURLOPT_PROXY_SSL_VERIFYPEER, CURLOT_LONG, 0 }, + { "PROXY_TLS13_CIPHERS", CURLOPT_PROXY_TLS13_CIPHERS, CURLOT_STRING, 0 }, + { "PROXY_TLSAUTH_PASSWORD", CURLOPT_PROXY_TLSAUTH_PASSWORD, + CURLOT_STRING, 0 }, + { "PROXY_TLSAUTH_TYPE", CURLOPT_PROXY_TLSAUTH_TYPE, CURLOT_STRING, 0 }, + { "PROXY_TLSAUTH_USERNAME", CURLOPT_PROXY_TLSAUTH_USERNAME, + CURLOT_STRING, 0 }, + { "PROXY_TRANSFER_MODE", CURLOPT_PROXY_TRANSFER_MODE, CURLOT_LONG, 0 }, + { "PUT", CURLOPT_PUT, CURLOT_LONG, 0 }, + { "QUICK_EXIT", CURLOPT_QUICK_EXIT, CURLOT_LONG, 0 }, + { "QUOTE", CURLOPT_QUOTE, CURLOT_SLIST, 0 }, + { "RANDOM_FILE", CURLOPT_RANDOM_FILE, CURLOT_STRING, 0 }, + { "RANGE", CURLOPT_RANGE, CURLOT_STRING, 0 }, + { "READDATA", CURLOPT_READDATA, CURLOT_CBPTR, 0 }, + { "READFUNCTION", CURLOPT_READFUNCTION, CURLOT_FUNCTION, 0 }, + { "REDIR_PROTOCOLS", CURLOPT_REDIR_PROTOCOLS, CURLOT_LONG, 0 }, + { "REDIR_PROTOCOLS_STR", CURLOPT_REDIR_PROTOCOLS_STR, CURLOT_STRING, 0 }, + { "REFERER", CURLOPT_REFERER, CURLOT_STRING, 0 }, + { "REQUEST_TARGET", CURLOPT_REQUEST_TARGET, CURLOT_STRING, 0 }, + { "RESOLVE", CURLOPT_RESOLVE, CURLOT_SLIST, 0 }, + { "RESOLVER_START_DATA", CURLOPT_RESOLVER_START_DATA, CURLOT_CBPTR, 0 }, + { "RESOLVER_START_FUNCTION", CURLOPT_RESOLVER_START_FUNCTION, + CURLOT_FUNCTION, 0 }, + { "RESUME_FROM", CURLOPT_RESUME_FROM, CURLOT_LONG, 0 }, + { "RESUME_FROM_LARGE", CURLOPT_RESUME_FROM_LARGE, CURLOT_OFF_T, 0 }, + { "RTSPHEADER", CURLOPT_HTTPHEADER, CURLOT_SLIST, CURLOT_FLAG_ALIAS }, + { "RTSP_CLIENT_CSEQ", CURLOPT_RTSP_CLIENT_CSEQ, CURLOT_LONG, 0 }, + { "RTSP_REQUEST", CURLOPT_RTSP_REQUEST, CURLOT_VALUES, 0 }, + { "RTSP_SERVER_CSEQ", CURLOPT_RTSP_SERVER_CSEQ, CURLOT_LONG, 0 }, + { "RTSP_SESSION_ID", CURLOPT_RTSP_SESSION_ID, CURLOT_STRING, 0 }, + { "RTSP_STREAM_URI", CURLOPT_RTSP_STREAM_URI, CURLOT_STRING, 0 }, + { "RTSP_TRANSPORT", CURLOPT_RTSP_TRANSPORT, CURLOT_STRING, 0 }, + { "SASL_AUTHZID", CURLOPT_SASL_AUTHZID, CURLOT_STRING, 0 }, + { "SASL_IR", CURLOPT_SASL_IR, CURLOT_LONG, 0 }, + { "SEEKDATA", CURLOPT_SEEKDATA, CURLOT_CBPTR, 0 }, + { "SEEKFUNCTION", CURLOPT_SEEKFUNCTION, CURLOT_FUNCTION, 0 }, + { "SERVER_RESPONSE_TIMEOUT", CURLOPT_SERVER_RESPONSE_TIMEOUT, + CURLOT_LONG, 0 }, + { "SERVER_RESPONSE_TIMEOUT_MS", CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, + CURLOT_LONG, 0 }, + { "SERVICE_NAME", CURLOPT_SERVICE_NAME, CURLOT_STRING, 0 }, + { "SHARE", CURLOPT_SHARE, CURLOT_OBJECT, 0 }, + { "SOCKOPTDATA", CURLOPT_SOCKOPTDATA, CURLOT_CBPTR, 0 }, + { "SOCKOPTFUNCTION", CURLOPT_SOCKOPTFUNCTION, CURLOT_FUNCTION, 0 }, + { "SOCKS5_AUTH", CURLOPT_SOCKS5_AUTH, CURLOT_LONG, 0 }, + { "SOCKS5_GSSAPI_NEC", CURLOPT_SOCKS5_GSSAPI_NEC, CURLOT_LONG, 0 }, + { "SOCKS5_GSSAPI_SERVICE", CURLOPT_SOCKS5_GSSAPI_SERVICE, + CURLOT_STRING, 0 }, + { "SSH_AUTH_TYPES", CURLOPT_SSH_AUTH_TYPES, CURLOT_VALUES, 0 }, + { "SSH_COMPRESSION", CURLOPT_SSH_COMPRESSION, CURLOT_LONG, 0 }, + { "SSH_HOSTKEYDATA", CURLOPT_SSH_HOSTKEYDATA, CURLOT_CBPTR, 0 }, + { "SSH_HOSTKEYFUNCTION", CURLOPT_SSH_HOSTKEYFUNCTION, CURLOT_FUNCTION, 0 }, + { "SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, + CURLOT_STRING, 0 }, + { "SSH_HOST_PUBLIC_KEY_SHA256", CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, + CURLOT_STRING, 0 }, + { "SSH_KEYDATA", CURLOPT_SSH_KEYDATA, CURLOT_CBPTR, 0 }, + { "SSH_KEYFUNCTION", CURLOPT_SSH_KEYFUNCTION, CURLOT_FUNCTION, 0 }, + { "SSH_KNOWNHOSTS", CURLOPT_SSH_KNOWNHOSTS, CURLOT_STRING, 0 }, + { "SSH_PRIVATE_KEYFILE", CURLOPT_SSH_PRIVATE_KEYFILE, CURLOT_STRING, 0 }, + { "SSH_PUBLIC_KEYFILE", CURLOPT_SSH_PUBLIC_KEYFILE, CURLOT_STRING, 0 }, + { "SSLCERT", CURLOPT_SSLCERT, CURLOT_STRING, 0 }, + { "SSLCERTPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, CURLOT_FLAG_ALIAS }, + { "SSLCERTTYPE", CURLOPT_SSLCERTTYPE, CURLOT_STRING, 0 }, + { "SSLCERT_BLOB", CURLOPT_SSLCERT_BLOB, CURLOT_BLOB, 0 }, + { "SSLENGINE", CURLOPT_SSLENGINE, CURLOT_STRING, 0 }, + { "SSLENGINE_DEFAULT", CURLOPT_SSLENGINE_DEFAULT, CURLOT_LONG, 0 }, + { "SSLKEY", CURLOPT_SSLKEY, CURLOT_STRING, 0 }, + { "SSLKEYPASSWD", CURLOPT_KEYPASSWD, CURLOT_STRING, CURLOT_FLAG_ALIAS }, + { "SSLKEYTYPE", CURLOPT_SSLKEYTYPE, CURLOT_STRING, 0 }, + { "SSLKEY_BLOB", CURLOPT_SSLKEY_BLOB, CURLOT_BLOB, 0 }, + { "SSLVERSION", CURLOPT_SSLVERSION, CURLOT_VALUES, 0 }, + { "SSL_CIPHER_LIST", CURLOPT_SSL_CIPHER_LIST, CURLOT_STRING, 0 }, + { "SSL_CTX_DATA", CURLOPT_SSL_CTX_DATA, CURLOT_CBPTR, 0 }, + { "SSL_CTX_FUNCTION", CURLOPT_SSL_CTX_FUNCTION, CURLOT_FUNCTION, 0 }, + { "SSL_EC_CURVES", CURLOPT_SSL_EC_CURVES, CURLOT_STRING, 0 }, + { "SSL_ENABLE_ALPN", CURLOPT_SSL_ENABLE_ALPN, CURLOT_LONG, 0 }, + { "SSL_ENABLE_NPN", CURLOPT_SSL_ENABLE_NPN, CURLOT_LONG, 0 }, + { "SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CURLOT_LONG, 0 }, + { "SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CURLOT_VALUES, 0 }, + { "SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CURLOT_LONG, 0 }, + { "SSL_SIGNATURE_ALGORITHMS", CURLOPT_SSL_SIGNATURE_ALGORITHMS, + CURLOT_STRING, 0 }, + { "SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST, CURLOT_LONG, 0 }, + { "SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER, CURLOT_LONG, 0 }, + { "SSL_VERIFYSTATUS", CURLOPT_SSL_VERIFYSTATUS, CURLOT_LONG, 0 }, + { "STDERR", CURLOPT_STDERR, CURLOT_OBJECT, 0 }, + { "STREAM_DEPENDS", CURLOPT_STREAM_DEPENDS, CURLOT_OBJECT, 0 }, + { "STREAM_DEPENDS_E", CURLOPT_STREAM_DEPENDS_E, CURLOT_OBJECT, 0 }, + { "STREAM_WEIGHT", CURLOPT_STREAM_WEIGHT, CURLOT_LONG, 0 }, + { "SUPPRESS_CONNECT_HEADERS", CURLOPT_SUPPRESS_CONNECT_HEADERS, + CURLOT_LONG, 0 }, + { "TCP_FASTOPEN", CURLOPT_TCP_FASTOPEN, CURLOT_LONG, 0 }, + { "TCP_KEEPALIVE", CURLOPT_TCP_KEEPALIVE, CURLOT_LONG, 0 }, + { "TCP_KEEPCNT", CURLOPT_TCP_KEEPCNT, CURLOT_LONG, 0 }, + { "TCP_KEEPIDLE", CURLOPT_TCP_KEEPIDLE, CURLOT_LONG, 0 }, + { "TCP_KEEPINTVL", CURLOPT_TCP_KEEPINTVL, CURLOT_LONG, 0 }, + { "TCP_NODELAY", CURLOPT_TCP_NODELAY, CURLOT_LONG, 0 }, + { "TELNETOPTIONS", CURLOPT_TELNETOPTIONS, CURLOT_SLIST, 0 }, + { "TFTP_BLKSIZE", CURLOPT_TFTP_BLKSIZE, CURLOT_LONG, 0 }, + { "TFTP_NO_OPTIONS", CURLOPT_TFTP_NO_OPTIONS, CURLOT_LONG, 0 }, + { "TIMECONDITION", CURLOPT_TIMECONDITION, CURLOT_VALUES, 0 }, + { "TIMEOUT", CURLOPT_TIMEOUT, CURLOT_LONG, 0 }, + { "TIMEOUT_MS", CURLOPT_TIMEOUT_MS, CURLOT_LONG, 0 }, + { "TIMEVALUE", CURLOPT_TIMEVALUE, CURLOT_LONG, 0 }, + { "TIMEVALUE_LARGE", CURLOPT_TIMEVALUE_LARGE, CURLOT_OFF_T, 0 }, + { "TLS13_CIPHERS", CURLOPT_TLS13_CIPHERS, CURLOT_STRING, 0 }, + { "TLSAUTH_PASSWORD", CURLOPT_TLSAUTH_PASSWORD, CURLOT_STRING, 0 }, + { "TLSAUTH_TYPE", CURLOPT_TLSAUTH_TYPE, CURLOT_STRING, 0 }, + { "TLSAUTH_USERNAME", CURLOPT_TLSAUTH_USERNAME, CURLOT_STRING, 0 }, + { "TRAILERDATA", CURLOPT_TRAILERDATA, CURLOT_CBPTR, 0 }, + { "TRAILERFUNCTION", CURLOPT_TRAILERFUNCTION, CURLOT_FUNCTION, 0 }, + { "TRANSFERTEXT", CURLOPT_TRANSFERTEXT, CURLOT_LONG, 0 }, + { "TRANSFER_ENCODING", CURLOPT_TRANSFER_ENCODING, CURLOT_LONG, 0 }, + { "UNIX_SOCKET_PATH", CURLOPT_UNIX_SOCKET_PATH, CURLOT_STRING, 0 }, + { "UNRESTRICTED_AUTH", CURLOPT_UNRESTRICTED_AUTH, CURLOT_LONG, 0 }, + { "UPKEEP_INTERVAL_MS", CURLOPT_UPKEEP_INTERVAL_MS, CURLOT_LONG, 0 }, + { "UPLOAD", CURLOPT_UPLOAD, CURLOT_LONG, 0 }, + { "UPLOAD_BUFFERSIZE", CURLOPT_UPLOAD_BUFFERSIZE, CURLOT_LONG, 0 }, + { "UPLOAD_FLAGS", CURLOPT_UPLOAD_FLAGS, CURLOT_LONG, 0 }, + { "URL", CURLOPT_URL, CURLOT_STRING, 0 }, + { "USERAGENT", CURLOPT_USERAGENT, CURLOT_STRING, 0 }, + { "USERNAME", CURLOPT_USERNAME, CURLOT_STRING, 0 }, + { "USERPWD", CURLOPT_USERPWD, CURLOT_STRING, 0 }, + { "USE_SSL", CURLOPT_USE_SSL, CURLOT_VALUES, 0 }, + { "VERBOSE", CURLOPT_VERBOSE, CURLOT_LONG, 0 }, + { "WILDCARDMATCH", CURLOPT_WILDCARDMATCH, CURLOT_LONG, 0 }, + { "WRITEDATA", CURLOPT_WRITEDATA, CURLOT_CBPTR, 0 }, + { "WRITEFUNCTION", CURLOPT_WRITEFUNCTION, CURLOT_FUNCTION, 0 }, + { "WRITEHEADER", CURLOPT_HEADERDATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS }, + { "WS_OPTIONS", CURLOPT_WS_OPTIONS, CURLOT_LONG, 0 }, + { "XFERINFODATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, 0 }, + { "XFERINFOFUNCTION", CURLOPT_XFERINFOFUNCTION, CURLOT_FUNCTION, 0 }, + { "XOAUTH2_BEARER", CURLOPT_XOAUTH2_BEARER, CURLOT_STRING, 0 }, + { NULL, CURLOPT_LASTENTRY, CURLOT_LONG, 0 } /* end of table */ }; #ifdef DEBUGBUILD diff --git a/lib/easyoptions.h b/lib/easyoptions.h index 44b6a8280a..d895653ce9 100644 --- a/lib/easyoptions.h +++ b/lib/easyoptions.h @@ -23,15 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* should probably go into the public header */ -#include - /* generated table with all easy options */ extern const struct curl_easyoption Curl_easyopts[]; #ifdef DEBUGBUILD int Curl_easyopts_check(void); #endif -#endif + +#endif /* HEADER_CURL_EASYOPTIONS_H */ diff --git a/lib/escape.c b/lib/escape.c index a292ba3b62..4aff583de1 100644 --- a/lib/escape.c +++ b/lib/escape.c @@ -21,31 +21,21 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* 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. */ #include "curl_setup.h" -#include - struct Curl_easy; #include "urldata.h" -#include "curlx/warnless.h" #include "escape.h" -#include "strdup.h" #include "curlx/strparse.h" - -/* The last 3 #include files should be in this order */ #include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" /* for ABI-compatibility with previous versions */ -char *curl_escape(const char *string, int inlength) +char *curl_escape(const char *string, int length) { - return curl_easy_escape(NULL, string, inlength); + return curl_easy_escape(NULL, string, length); } /* for ABI-compatibility with previous versions */ @@ -57,23 +47,25 @@ char *curl_unescape(const char *string, int length) /* Escapes for URL the given unescaped string of given length. * 'data' is ignored since 7.82.0. */ -char *curl_easy_escape(CURL *data, const char *string, - int inlength) +char *curl_easy_escape(CURL *curl, const char *string, int length) { - size_t length; + size_t len; struct dynbuf d; - (void)data; + (void)curl; - if(!string || (inlength < 0)) + if(!string || (length < 0)) return NULL; - length = (inlength ? (size_t)inlength : strlen(string)); - if(!length) - return strdup(""); + len = (length ? (size_t)length : strlen(string)); + if(!len) + return curlx_strdup(""); - curlx_dyn_init(&d, length * 3 + 1); + if(len > SIZE_MAX / 16) + return NULL; - while(length--) { + curlx_dyn_init(&d, (len * 3) + 1); + + while(len--) { /* treat the characters unsigned */ unsigned char in = (unsigned char)*string++; @@ -84,7 +76,7 @@ char *curl_easy_escape(CURL *data, const char *string, } else { /* encode it */ - unsigned char out[3]={'%'}; + unsigned char out[3] = { '%' }; Curl_hexbyte(&out[1], in); if(curlx_dyn_addn(&d, out, 3)) return NULL; @@ -121,7 +113,7 @@ CURLcode Curl_urldecode(const char *string, size_t length, DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */ alloc = (length ? length : strlen(string)); - ns = malloc(alloc + 1); + ns = curlx_malloc(alloc + 1); if(!ns) return CURLE_OUT_OF_MEMORY; @@ -134,8 +126,8 @@ CURLcode Curl_urldecode(const char *string, size_t length, if(('%' == in) && (alloc > 2) && ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { /* this is two hexadecimal digits following a '%' */ - in = (unsigned char)((Curl_hexval(string[1]) << 4) | - Curl_hexval(string[2])); + in = (unsigned char)((curlx_hexval(string[1]) << 4) | + curlx_hexval(string[2])); string += 3; alloc -= 3; } @@ -146,7 +138,7 @@ CURLcode Curl_urldecode(const char *string, size_t length, if(((ctrl == REJECT_CTRL) && (in < 0x20)) || ((ctrl == REJECT_ZERO) && (in == 0))) { - Curl_safefree(*ostring); + curlx_safefree(*ostring); return CURLE_URL_MALFORMAT; } @@ -168,25 +160,25 @@ CURLcode Curl_urldecode(const char *string, size_t length, * If olen == NULL, no output length is stored. * 'data' is ignored since 7.82.0. */ -char *curl_easy_unescape(CURL *data, const char *string, - int length, int *olen) +char *curl_easy_unescape(CURL *curl, const char *string, int inlength, + int *outlength) { char *str = NULL; - (void)data; - if(string && (length >= 0)) { - size_t inputlen = (size_t)length; + (void)curl; + if(string && (inlength >= 0)) { + size_t inputlen = (size_t)inlength; size_t outputlen; CURLcode res = Curl_urldecode(string, inputlen, &str, &outputlen, REJECT_NADA); if(res) return NULL; - if(olen) { - if(outputlen <= (size_t) INT_MAX) - *olen = curlx_uztosi(outputlen); + if(outlength) { + if(outputlen <= (size_t)INT_MAX) + *outlength = curlx_uztosi(outputlen); else /* too large to return in an int, fail! */ - Curl_safefree(str); + curlx_safefree(str); } } return str; @@ -197,7 +189,7 @@ char *curl_easy_unescape(CURL *data, const char *string, the library's memory system */ void curl_free(void *p) { - free(p); + curlx_free(p); } /* diff --git a/lib/escape.h b/lib/escape.h index a43fc38ed3..9f9a89e402 100644 --- a/lib/escape.h +++ b/lib/escape.h @@ -24,9 +24,7 @@ * ***************************************************************************/ /* Escape and unescape URL encoding in strings. The functions return a new - * allocated string or NULL if an error occurred. */ - -#include "curl_ctype.h" + * allocated string or NULL if an error occurred. */ enum urlreject { REJECT_NADA = 2, diff --git a/lib/fake_addrinfo.c b/lib/fake_addrinfo.c index 20d55ba2d1..5a89202064 100644 --- a/lib/fake_addrinfo.c +++ b/lib/fake_addrinfo.c @@ -21,21 +21,14 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #include "fake_addrinfo.h" #ifdef USE_FAKE_GETADDRINFO -#include -#include #include -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - void r_freeaddrinfo(struct addrinfo *cahead) { struct addrinfo *canext; @@ -43,21 +36,21 @@ void r_freeaddrinfo(struct addrinfo *cahead) for(ca = cahead; ca; ca = canext) { canext = ca->ai_next; - free(ca); + curlx_free(ca); } } struct context { - struct ares_addrinfo *result; + struct ares_addrinfo *addr; }; static void async_addrinfo_cb(void *userp, int status, int timeouts, - struct ares_addrinfo *result) + struct ares_addrinfo *addr) { struct context *ctx = (struct context *)userp; (void)timeouts; if(ARES_SUCCESS == status) { - ctx->result = result; + ctx->addr = addr; } } @@ -74,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) @@ -91,14 +84,14 @@ static struct addrinfo *mk_getaddrinfo(const struct ares_addrinfo *aihead) if((size_t)ai->ai_addrlen < ss_size) continue; - ca = malloc(sizeof(struct addrinfo) + ss_size + namelen); + ca = curlx_malloc(sizeof(struct addrinfo) + ss_size + namelen); if(!ca) { r_freeaddrinfo(cafirst); 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; @@ -178,25 +171,24 @@ int r_getaddrinfo(const char *node, if(env) { rc = ares_set_servers_ports_csv(channel, env); if(rc) { - fprintf(stderr, "ares_set_servers_ports_csv failed: %d", rc); + curl_mfprintf(stderr, "ares_set_servers_ports_csv failed: %d", rc); /* Cleanup */ ares_destroy(channel); - return EAI_MEMORY; /* we can't run */ + return EAI_MEMORY; /* we cannot run */ } } } - ares_getaddrinfo(channel, node, service, &ahints, - async_addrinfo_cb, &ctx); + ares_getaddrinfo(channel, node, service, &ahints, async_addrinfo_cb, &ctx); /* Wait until no more requests are left to be processed */ ares_queue_wait_empty(channel, -1); - if(ctx.result) { + if(ctx.addr) { /* convert the c-ares version */ - *res = mk_getaddrinfo(ctx.result); + *res = mk_getaddrinfo(ctx.addr); /* free the old */ - ares_freeaddrinfo(ctx.result); + ares_freeaddrinfo(ctx.addr); } else rc = EAI_NONAME; /* got nothing */ diff --git a/lib/fake_addrinfo.h b/lib/fake_addrinfo.h index 13b0d71dba..07d5b6da8c 100644 --- a/lib/fake_addrinfo.h +++ b/lib/fake_addrinfo.h @@ -23,15 +23,14 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef USE_ARES #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/lib/file.c b/lib/file.c index 749759653d..fff8feeb92 100644 --- a/lib/file.c +++ b/lib/file.c @@ -21,8 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" +#include "urldata.h" +#include "file.h" #ifndef CURL_DISABLE_FILE @@ -46,10 +47,6 @@ #include #endif -#ifdef HAVE_FCNTL_H -#include -#endif - #ifdef HAVE_SYS_TYPES_H #include #endif @@ -58,24 +55,16 @@ #include #endif -#include "urldata.h" -#include #include "progress.h" #include "sendf.h" +#include "curl_trc.h" #include "escape.h" -#include "file.h" -#include "speedcheck.h" -#include "getinfo.h" #include "multiif.h" #include "transfer.h" #include "url.h" #include "parsedate.h" /* for the week day and month names */ -#include "curlx/warnless.h" +#include "curlx/fopen.h" #include "curl_range.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" #if defined(_WIN32) || defined(MSDOS) #define DOS_FILESYSTEM 1 @@ -93,56 +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); + curlx_safefree(file->freepath); file->path = NULL; if(file->fd != -1) { - close(file->fd); + curlx_close(file->fd); file->fd = -1; } } @@ -153,7 +98,7 @@ static void file_easy_dtor(void *key, size_t klen, void *entry) (void)key; (void)klen; file_cleanup(file); - free(file); + curlx_free(file); } static CURLcode file_setup_connection(struct Curl_easy *data, @@ -162,7 +107,9 @@ static CURLcode file_setup_connection(struct Curl_easy *data, struct FILEPROTO *filep; (void)conn; /* allocate the FILE specific struct */ - filep = calloc(1, sizeof(*filep)); + filep = curlx_calloc(1, sizeof(*filep)); + if(filep) + filep->fd = -1; if(!filep || Curl_meta_set(data, CURL_META_FILE_EASY, filep, file_easy_dtor)) return CURLE_OUT_OF_MEMORY; @@ -170,6 +117,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 @@ -233,20 +193,20 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) if(actual_path[i] == '/') actual_path[i] = '\\'; else if(!actual_path[i]) { /* binary zero */ - Curl_safefree(real_path); + curlx_safefree(real_path); return CURLE_URL_MALFORMAT; } - fd = open(actual_path, O_RDONLY|CURL_O_BINARY); + fd = curlx_open(actual_path, O_RDONLY | CURL_O_BINARY); file->path = actual_path; #else if(memchr(real_path, 0, real_path_len)) { /* binary zeroes indicate foul play */ - Curl_safefree(real_path); + curlx_safefree(real_path); 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. @@ -261,25 +221,25 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done) extern int __unix_path_semantics; if(strchr(real_path + 1, ':')) { /* Amiga absolute path */ - fd = open(real_path + 1, O_RDONLY); + fd = curlx_open(real_path + 1, O_RDONLY); file->path++; } else if(__unix_path_semantics) { /* -lunix fallback */ - fd = open(real_path, O_RDONLY); + fd = curlx_open(real_path, O_RDONLY); } } - #else - fd = open(real_path, O_RDONLY); +#else + fd = curlx_open(real_path, O_RDONLY); file->path = real_path; - #endif #endif - free(file->freepath); +#endif + curlx_free(file->freepath); file->freepath = real_path; /* free this when done */ file->fd = fd; if(!data->state.upload && (fd == -1)) { - failf(data, "Couldn't open file %s", data->state.up.path); + failf(data, "Could not open file %s", data->state.up.path); file_done(data, CURLE_FILE_COULDNT_READ_FILE, FALSE); return CURLE_FILE_COULDNT_READ_FILE; } @@ -288,19 +248,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) @@ -325,8 +272,7 @@ static CURLcode file_upload(struct Curl_easy *data, CURLcode result = CURLE_OK; char *xfer_ulbuf; size_t xfer_ulblen; - curl_off_t bytecount = 0; - struct_stat file_stat; + curlx_struct_stat file_stat; const char *sendbuf; bool eos = FALSE; @@ -341,17 +287,20 @@ static CURLcode file_upload(struct Curl_easy *data, if(!dir[1]) return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */ - mode = O_WRONLY|O_CREAT|CURL_O_BINARY; + mode = O_WRONLY | O_CREAT | CURL_O_BINARY; if(data->state.resume_from) mode |= O_APPEND; else mode |= O_TRUNC; -#if (defined(ANDROID) || defined(__ANDROID__)) && \ +#ifdef _WIN32 + fd = curlx_open(file->path, mode, + data->set.new_file_perms & (_S_IREAD | _S_IWRITE)); +#elif (defined(ANDROID) || defined(__ANDROID__)) && \ (defined(__i386__) || defined(__arm__)) - fd = open(file->path, mode, (mode_t)data->set.new_file_perms); + fd = curlx_open(file->path, mode, (mode_t)data->set.new_file_perms); #else - fd = open(file->path, mode, data->set.new_file_perms); + fd = curlx_open(file->path, mode, data->set.new_file_perms); #endif if(fd < 0) { failf(data, "cannot open %s for writing", file->path); @@ -364,8 +313,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; } @@ -377,8 +326,8 @@ static CURLcode file_upload(struct Curl_easy *data, goto out; while(!result && !eos) { - size_t nread; - ssize_t nwrite; + size_t nread, nwritten; + ssize_t rv; size_t readcount; result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &readcount, &eos); @@ -407,26 +356,20 @@ static CURLcode file_upload(struct Curl_easy *data, sendbuf = xfer_ulbuf; /* write the data to the target */ - nwrite = write(fd, sendbuf, nread); - if((size_t)nwrite != nread) { + rv = write(fd, sendbuf, nread); + if(!curlx_sztouz(rv, &nwritten) || (nwritten != nread)) { result = CURLE_SEND_ERROR; break; } + Curl_pgrs_upload_inc(data, nwritten); - bytecount += nread; - - Curl_pgrsSetUploadCounter(data, bytecount); - - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, curlx_now()); + result = Curl_pgrsCheck(data); } - if(!result && Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; + if(!result) + result = Curl_pgrsUpdate(data); out: - close(fd); + curlx_close(fd); Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf); return result; @@ -449,9 +392,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; @@ -470,7 +411,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 */ @@ -488,11 +429,11 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) const struct tm *tm = &buffer; char header[80]; int headerlen; - static const char accept_ranges[]= { "Accept-ranges: bytes\r\n" }; + static const char accept_ranges[] = { "Accept-ranges: bytes\r\n" }; if(expected_size >= 0) { headerlen = - msnprintf(header, sizeof(header), "Content-Length: %" FMT_OFF_T "\r\n", - expected_size); + curl_msnprintf(header, sizeof(header), + "Content-Length: %" FMT_OFF_T "\r\n", expected_size); result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen); if(result) return result; @@ -504,21 +445,21 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) } filetime = (time_t)statbuf.st_mtime; - result = Curl_gmtime(filetime, &buffer); + result = curlx_gmtime(filetime, &buffer); if(result) return result; /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ headerlen = - msnprintf(header, sizeof(header), - "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); + curl_msnprintf(header, sizeof(header), + "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 = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen); if(!result) /* end of headers */ @@ -576,13 +517,8 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) if(data->state.resume_from) { if(!S_ISDIR(statbuf.st_mode)) { -#if defined(__AMIGA__) || defined(__MINGW32CE__) 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 { @@ -601,11 +537,11 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) size_t bytestoread; if(size_known) { - bytestoread = (expected_size < (curl_off_t)(xfer_blen-1)) ? - curlx_sotouz(expected_size) : (xfer_blen-1); + bytestoread = (expected_size < (curl_off_t)(xfer_blen - 1)) ? + curlx_sotouz(expected_size) : (xfer_blen - 1); } else - bytestoread = xfer_blen-1; + bytestoread = xfer_blen - 1; nread = read(fd, xfer_buf, bytestoread); @@ -622,10 +558,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) if(result) goto out; - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, curlx_now()); + result = Curl_pgrsCheck(data); if(result) goto out; } @@ -659,12 +592,32 @@ static CURLcode file_do(struct Curl_easy *data, bool *done) #endif } - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; + if(!result) + result = Curl_pgrsUpdate(data); out: Curl_multi_xfer_buf_release(data, xfer_buf); return result; } +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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif diff --git a/lib/file.h b/lib/file.h index fea1eea57d..0aa411b19f 100644 --- a/lib/file.h +++ b/lib/file.h @@ -23,9 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #ifndef CURL_DISABLE_FILE -extern const struct Curl_handler Curl_handler_file; +extern const struct Curl_protocol Curl_protocol_file; #endif #endif /* HEADER_CURL_FILE_H */ diff --git a/lib/fileinfo.c b/lib/fileinfo.c index 47cdb102e3..33e5acde01 100644 --- a/lib/fileinfo.c +++ b/lib/fileinfo.c @@ -21,20 +21,15 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifndef CURL_DISABLE_FTP -#include "strdup.h" #include "fileinfo.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" struct fileinfo *Curl_fileinfo_alloc(void) { - return calloc(1, sizeof(struct fileinfo)); + return curlx_calloc(1, sizeof(struct fileinfo)); } void Curl_fileinfo_cleanup(struct fileinfo *finfo) @@ -43,7 +38,7 @@ void Curl_fileinfo_cleanup(struct fileinfo *finfo) return; curlx_dyn_free(&finfo->buf); - free(finfo); + curlx_free(finfo); } -#endif +#endif /* !CURL_DISABLE_FTP */ diff --git a/lib/fileinfo.h b/lib/fileinfo.h index 6746ee2575..737966788c 100644 --- a/lib/fileinfo.h +++ b/lib/fileinfo.h @@ -23,8 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" -#include #include "llist.h" #include "curlx/dynbuf.h" diff --git a/lib/formdata.c b/lib/formdata.c index d8553e3256..80759e792c 100644 --- a/lib/formdata.c +++ b/lib/formdata.c @@ -21,36 +21,28 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - struct Curl_easy; #include "formdata.h" + #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) #include "urldata.h" /* for struct Curl_easy */ #include "mime.h" -#include "vtls/vtls.h" -#include "sendf.h" -#include "strdup.h" -#include "rand.h" -#include "curlx/warnless.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "curlx/strdup.h" +#include "bufref.h" +#include "curlx/fopen.h" -#define HTTPPOST_PTRNAME CURL_HTTPPOST_PTRNAME -#define HTTPPOST_FILENAME CURL_HTTPPOST_FILENAME +#define HTTPPOST_PTRNAME CURL_HTTPPOST_PTRNAME +#define HTTPPOST_FILENAME CURL_HTTPPOST_FILENAME #define HTTPPOST_PTRCONTENTS CURL_HTTPPOST_PTRCONTENTS -#define HTTPPOST_READFILE CURL_HTTPPOST_READFILE -#define HTTPPOST_PTRBUFFER CURL_HTTPPOST_PTRBUFFER -#define HTTPPOST_CALLBACK CURL_HTTPPOST_CALLBACK -#define HTTPPOST_BUFFER CURL_HTTPPOST_BUFFER +#define HTTPPOST_READFILE CURL_HTTPPOST_READFILE +#define HTTPPOST_PTRBUFFER CURL_HTTPPOST_PTRBUFFER +#define HTTPPOST_CALLBACK CURL_HTTPPOST_CALLBACK +#define HTTPPOST_BUFFER CURL_HTTPPOST_BUFFER /*************************************************************************** * @@ -62,31 +54,30 @@ struct Curl_easy; * Returns newly allocated HttpPost on success and NULL if malloc failed. * ***************************************************************************/ -static struct curl_httppost * -AddHttpPost(struct FormInfo *src, - struct curl_httppost *parent_post, - struct curl_httppost **httppost, - struct curl_httppost **last_post) +static struct curl_httppost *AddHttpPost(struct FormInfo *src, + struct curl_httppost *parent_post, + struct curl_httppost **httppost, + struct curl_httppost **last_post) { struct curl_httppost *post; size_t namelength = src->namelength; - if(!namelength && src->name) - namelength = strlen(src->name); + if(!namelength && Curl_bufref_ptr(&src->name)) + namelength = strlen(Curl_bufref_ptr(&src->name)); if((src->bufferlength > LONG_MAX) || (namelength > LONG_MAX)) /* avoid overflow in typecasts below */ return NULL; - post = calloc(1, sizeof(struct curl_httppost)); + post = curlx_calloc(1, sizeof(struct curl_httppost)); if(post) { - post->name = src->name; + post->name = CURL_UNCONST(Curl_bufref_ptr(&src->name)); post->namelength = (long)namelength; - post->contents = src->value; + post->contents = CURL_UNCONST(Curl_bufref_ptr(&src->value)); post->contentlen = src->contentslength; post->buffer = src->buffer; post->bufferlength = (long)src->bufferlength; - post->contenttype = src->contenttype; + post->contenttype = CURL_UNCONST(Curl_bufref_ptr(&src->contenttype)); post->flags = src->flags | CURL_HTTPPOST_LARGE; post->contentheader = src->contentheader; - post->showfilename = src->showfilename; + post->showfilename = CURL_UNCONST(Curl_bufref_ptr(&src->showfilename)); post->userp = src->userp; } else @@ -111,60 +102,63 @@ AddHttpPost(struct FormInfo *src, return post; } -/*************************************************************************** - * - * AddFormInfo() - * - * Adds a FormInfo structure to the list presented by parent_form_info. - * - * Returns newly allocated FormInfo on success and NULL if malloc failed/ - * parent_form_info is NULL. - * - ***************************************************************************/ -static struct FormInfo *AddFormInfo(char *value, - char *contenttype, - struct FormInfo *parent_form_info) +/* Allocate and initialize a new FormInfo structure. */ +static struct FormInfo *NewFormInfo(void) { - struct FormInfo *form_info; - form_info = calloc(1, sizeof(struct FormInfo)); - if(!form_info) - return NULL; - if(value) - form_info->value = value; - if(contenttype) - form_info->contenttype = contenttype; - form_info->flags = HTTPPOST_FILENAME; + struct FormInfo *form_info = curlx_calloc(1, sizeof(struct FormInfo)); - if(parent_form_info) { - /* now, point our 'more' to the original 'more' */ - form_info->more = parent_form_info->more; - - /* then move the original 'more' to point to ourselves */ - parent_form_info->more = form_info; + if(form_info) { + Curl_bufref_init(&form_info->name); + Curl_bufref_init(&form_info->value); + Curl_bufref_init(&form_info->contenttype); + Curl_bufref_init(&form_info->showfilename); } return form_info; } +/* Replace the target field data by a dynamic copy of it. */ +static CURLcode FormInfoCopyField(struct bufref *field, size_t len) +{ + const char *value = Curl_bufref_ptr(field); + CURLcode result = CURLE_OK; + + if(value) { + if(!len) + len = strlen(value); + result = Curl_bufref_memdup0(field, value, len); + } + + return result; +} + +/*************************************************************************** + * + * AddFormInfo() + * + * Adds a FormInfo structure to the list presented by parent. + * + ***************************************************************************/ +static void AddFormInfo(struct FormInfo *form_info, struct FormInfo *parent) +{ + form_info->flags |= HTTPPOST_FILENAME; + + if(parent) { + /* now, point our 'more' to the original 'more' */ + form_info->more = parent->more; + + /* then move the original 'more' to point to ourselves */ + parent->more = form_info; + } +} + static void free_formlist(struct FormInfo *ptr) { for(; ptr != NULL; ptr = ptr->more) { - if(ptr->name_alloc) { - Curl_safefree(ptr->name); - ptr->name_alloc = FALSE; - } - if(ptr->value_alloc) { - Curl_safefree(ptr->value); - ptr->value_alloc = FALSE; - } - if(ptr->contenttype_alloc) { - Curl_safefree(ptr->contenttype); - ptr->contenttype_alloc = FALSE; - } - if(ptr->showfilename_alloc) { - Curl_safefree(ptr->showfilename); - ptr->showfilename_alloc = FALSE; - } + Curl_bufref_free(&ptr->name); + Curl_bufref_free(&ptr->value); + Curl_bufref_free(&ptr->contenttype); + Curl_bufref_free(&ptr->showfilename); } } @@ -174,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 @@ -230,82 +223,62 @@ static CURLFORMcode FormAddCheck(struct FormInfo *first_form, /* go through the list, check for completeness and if everything is * alright add the HttpPost item otherwise set retval accordingly */ - for(form = first_form; - form != NULL; - form = form->more) { - if(((!form->name || !form->value) && !post) || - ( (form->contentslength) && - (form->flags & HTTPPOST_FILENAME) ) || - ( (form->flags & HTTPPOST_FILENAME) && - (form->flags & HTTPPOST_PTRCONTENTS) ) || + for(form = first_form; form != NULL; form = form->more) { + const char *name = Curl_bufref_ptr(&form->name); - ( (!form->buffer) && - (form->flags & HTTPPOST_BUFFER) && - (form->flags & HTTPPOST_PTRBUFFER) ) || + if(((!name || !Curl_bufref_ptr(&form->value)) && !post) || + (form->contentslength && + (form->flags & HTTPPOST_FILENAME)) || + ((form->flags & HTTPPOST_FILENAME) && + (form->flags & HTTPPOST_PTRCONTENTS)) || - ( (form->flags & HTTPPOST_READFILE) && - (form->flags & HTTPPOST_PTRCONTENTS) ) + (!form->buffer && + (form->flags & HTTPPOST_BUFFER) && + (form->flags & HTTPPOST_PTRBUFFER)) || + + ((form->flags & HTTPPOST_READFILE) && + (form->flags & HTTPPOST_PTRCONTENTS)) ) { return CURL_FORMADD_INCOMPLETE; } if(((form->flags & HTTPPOST_FILENAME) || (form->flags & HTTPPOST_BUFFER)) && - !form->contenttype) { - char *f = (form->flags & HTTPPOST_BUFFER) ? - form->showfilename : form->value; - char const *type; - type = Curl_mime_contenttype(f); + !Curl_bufref_ptr(&form->contenttype)) { + const char *f = Curl_bufref_ptr((form->flags & HTTPPOST_BUFFER) ? + &form->showfilename : &form->value); + const char *type = Curl_mime_contenttype(f); if(!type) type = prevtype; if(!type) type = FILE_CONTENTTYPE_DEFAULT; /* our contenttype is missing */ - form->contenttype = strdup(type); - if(!form->contenttype) + if(Curl_bufref_memdup0(&form->contenttype, type, strlen(type))) return CURL_FORMADD_MEMORY; - - form->contenttype_alloc = TRUE; } - if(form->name && form->namelength) { - if(memchr(form->name, 0, form->namelength)) + if(name && form->namelength) { + if(memchr(name, 0, form->namelength)) return CURL_FORMADD_NULL; } - if(!(form->flags & HTTPPOST_PTRNAME) && form->name) { + if(!(form->flags & HTTPPOST_PTRNAME)) { /* Note that there is small risk that form->name is NULL here if the app passed in a bad combo, so we check for that. */ - - /* copy name (without strdup; possibly not null-terminated) */ - char *dupname = Curl_memdup0(form->name, form->namelength ? - form->namelength : strlen(form->name)); - if(!dupname) + if(FormInfoCopyField(&form->name, form->namelength)) return CURL_FORMADD_MEMORY; - - form->name = dupname; - form->name_alloc = TRUE; } if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE | HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER | - HTTPPOST_CALLBACK)) && form->value) { - /* copy value (without strdup; possibly contains null characters) */ - size_t clen = (size_t) form->contentslength; - if(!clen) - clen = strlen(form->value) + 1; - - form->value = Curl_memdup(form->value, clen); - - if(!form->value) + HTTPPOST_CALLBACK))) { + if(FormInfoCopyField(&form->value, (size_t)form->contentslength)) return CURL_FORMADD_MEMORY; - - form->value_alloc = TRUE; } post = AddHttpPost(form, post, httppost, last_post); if(!post) return CURL_FORMADD_MEMORY; - if(form->contenttype) - prevtype = form->contenttype; + if(Curl_bufref_ptr(&form->contenttype)) + prevtype = Curl_bufref_ptr(&form->contenttype); } return CURL_FORMADD_OK; @@ -319,33 +292,33 @@ static void free_chain(struct curl_httppost *c) struct curl_httppost *next = c->next; if(c->more) free_chain(c->more); - free(c); + curlx_free(c); c = next; } } -static -CURLFORMcode FormAdd(struct curl_httppost **httppost, - struct curl_httppost **last_post, - va_list params) +static CURLFORMcode FormAdd(struct curl_httppost **httppost, + struct curl_httppost **last_post, va_list params) { 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; - /* This is a state variable, that if TRUE means that we are parsing an - array that we got passed to us. If FALSE we are parsing the input - va_list arguments. */ - bool array_state = FALSE; +#define form_ptr_arg(t) (forms ? (t)(void *)avalue : va_arg(params, t)) +#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)) +#endif /* * We need to allocate the first struct to fill in. */ - first_form = calloc(1, sizeof(struct FormInfo)); + first_form = NewFormInfo(); if(!first_form) return CURL_FORMADD_MEMORY; @@ -357,7 +330,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, while(retval == CURL_FORMADD_OK) { /* first see if we have more parts of the array param */ - if(array_state && forms) { + if(forms) { /* get the upcoming option from the given array */ option = forms->option; avalue = (char *)CURL_UNCONST(forms->value); @@ -365,7 +338,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, forms++; /* advance this to next entry */ if(CURLFORM_END == option) { /* end of array state */ - array_state = FALSE; + forms = NULL; continue; } } @@ -380,14 +353,12 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, switch(option) { case CURLFORM_ARRAY: - if(array_state) + if(forms) /* we do not support an array from within an array */ retval = CURL_FORMADD_ILLEGAL_ARRAY; else { forms = va_arg(params, struct curl_forms *); - if(forms) - array_state = TRUE; - else + if(!forms) retval = CURL_FORMADD_NULL; } break; @@ -396,17 +367,15 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, * Set the Name property. */ case CURLFORM_PTRNAME: - curr->flags |= HTTPPOST_PTRNAME; /* fall through */ - + curr->flags |= HTTPPOST_PTRNAME; FALLTHROUGH(); case CURLFORM_COPYNAME: - if(curr->name) + if(Curl_bufref_ptr(&curr->name)) retval = CURL_FORMADD_OPTION_TWICE; else { - if(!array_state) - avalue = va_arg(params, char *); + avalue = form_ptr_arg(char *); if(avalue) - curr->name = avalue; /* store for the moment */ + Curl_bufref_set(&curr->name, avalue, 0, NULL); /* No copy yet. */ else retval = CURL_FORMADD_NULL; } @@ -415,8 +384,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, if(curr->namelength) retval = CURL_FORMADD_OPTION_TWICE; else - curr->namelength = - array_state ? (size_t)avalue : (size_t)va_arg(params, long); + curr->namelength = (size_t)form_int_arg(long); break; /* @@ -426,44 +394,36 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, curr->flags |= HTTPPOST_PTRCONTENTS; FALLTHROUGH(); case CURLFORM_COPYCONTENTS: - if(curr->value) + if(Curl_bufref_ptr(&curr->value)) retval = CURL_FORMADD_OPTION_TWICE; else { - if(!array_state) - avalue = va_arg(params, char *); + avalue = form_ptr_arg(char *); if(avalue) - curr->value = avalue; /* store for the moment */ + Curl_bufref_set(&curr->value, avalue, 0, NULL); /* No copy yet. */ else retval = CURL_FORMADD_NULL; } break; case CURLFORM_CONTENTSLENGTH: - curr->contentslength = - array_state ? (size_t)avalue : (size_t)va_arg(params, long); + curr->contentslength = (curl_off_t)(size_t)form_int_arg(long); break; case CURLFORM_CONTENTLEN: curr->flags |= CURL_HTTPPOST_LARGE; - curr->contentslength = - array_state ? (curl_off_t)(size_t)avalue : - va_arg(params, curl_off_t); + curr->contentslength = form_int_arg(curl_off_t); break; /* Get contents from a given filename */ case CURLFORM_FILECONTENT: - if(curr->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_READFILE)) + if(curr->flags & (HTTPPOST_PTRCONTENTS | HTTPPOST_READFILE)) retval = CURL_FORMADD_OPTION_TWICE; else { - if(!array_state) - avalue = va_arg(params, char *); + avalue = form_ptr_arg(char *); if(avalue) { - curr->value = strdup(avalue); - if(!curr->value) + if(Curl_bufref_memdup0(&curr->value, avalue, strlen(avalue))) retval = CURL_FORMADD_MEMORY; - else { + else curr->flags |= HTTPPOST_READFILE; - curr->value_alloc = TRUE; - } } else retval = CURL_FORMADD_NULL; @@ -472,26 +432,20 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, /* We upload a file */ case CURLFORM_FILE: - if(!array_state) - avalue = va_arg(params, char *); - - if(curr->value) { + avalue = form_ptr_arg(char *); + if(Curl_bufref_ptr(&curr->value)) { if(curr->flags & HTTPPOST_FILENAME) { if(avalue) { - char *fname = strdup(avalue); - if(!fname) + form = NewFormInfo(); + if(!form || + Curl_bufref_memdup0(&form->value, avalue, strlen(avalue))) { + curlx_free(form); retval = CURL_FORMADD_MEMORY; + } else { - form = AddFormInfo(fname, NULL, curr); - if(!form) { - free(fname); - retval = CURL_FORMADD_MEMORY; - } - else { - form->value_alloc = TRUE; - curr = form; - form = NULL; - } + AddFormInfo(form, curr); + curr = form; + form = NULL; } } else @@ -502,13 +456,10 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, } else { if(avalue) { - curr->value = strdup(avalue); - if(!curr->value) + if(Curl_bufref_memdup0(&curr->value, avalue, strlen(avalue))) retval = CURL_FORMADD_MEMORY; - else { + else curr->flags |= HTTPPOST_FILENAME; - curr->value_alloc = TRUE; - } } else retval = CURL_FORMADD_NULL; @@ -516,16 +467,15 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, break; case CURLFORM_BUFFERPTR: - curr->flags |= HTTPPOST_PTRBUFFER|HTTPPOST_BUFFER; + curr->flags |= HTTPPOST_PTRBUFFER | HTTPPOST_BUFFER; if(curr->buffer) retval = CURL_FORMADD_OPTION_TWICE; else { - if(!array_state) - avalue = va_arg(params, char *); + avalue = form_ptr_arg(char *); if(avalue) { curr->buffer = avalue; /* store for the moment */ - curr->value = avalue; /* make it non-NULL to be accepted - as fine */ + /* Make value non-NULL to be accepted as fine */ + Curl_bufref_set(&curr->value, avalue, 0, NULL); } else retval = CURL_FORMADD_NULL; @@ -536,8 +486,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, if(curr->bufferlength) retval = CURL_FORMADD_OPTION_TWICE; else - curr->bufferlength = - array_state ? (size_t)avalue : (size_t)va_arg(params, long); + curr->bufferlength = (size_t)form_int_arg(long); break; case CURLFORM_STREAM: @@ -545,14 +494,13 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, if(curr->userp) retval = CURL_FORMADD_OPTION_TWICE; else { - if(!array_state) - avalue = va_arg(params, char *); + avalue = form_ptr_arg(char *); if(avalue) { curr->userp = avalue; - curr->value = avalue; /* this is not strictly true but we derive a - value from this later on and we need this - non-NULL to be accepted as a fine form - part */ + /* The following line is not strictly true but we derive a value + from this later on and we need this non-NULL to be accepted as + a fine form part */ + Curl_bufref_set(&curr->value, avalue, 0, NULL); } else retval = CURL_FORMADD_NULL; @@ -560,25 +508,20 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, break; case CURLFORM_CONTENTTYPE: - if(!array_state) - avalue = va_arg(params, char *); - if(curr->contenttype) { + avalue = form_ptr_arg(char *); + if(Curl_bufref_ptr(&curr->contenttype)) { if(curr->flags & HTTPPOST_FILENAME) { if(avalue) { - char *type = strdup(avalue); - if(!type) + form = NewFormInfo(); + if(!form || Curl_bufref_memdup0(&form->contenttype, avalue, + strlen(avalue))) { + curlx_free(form); retval = CURL_FORMADD_MEMORY; + } else { - form = AddFormInfo(NULL, type, curr); - if(!form) { - free(type); - retval = CURL_FORMADD_MEMORY; - } - else { - form->contenttype_alloc = TRUE; - curr = form; - form = NULL; - } + AddFormInfo(form, curr); + curr = form; + form = NULL; } } else @@ -587,47 +530,33 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, else retval = CURL_FORMADD_OPTION_TWICE; } - else { - if(avalue) { - curr->contenttype = strdup(avalue); - if(!curr->contenttype) - retval = CURL_FORMADD_MEMORY; - else - curr->contenttype_alloc = TRUE; - } - else - retval = CURL_FORMADD_NULL; + else if(avalue) { + if(Curl_bufref_memdup0(&curr->contenttype, avalue, strlen(avalue))) + retval = CURL_FORMADD_MEMORY; } + else + retval = CURL_FORMADD_NULL; break; - case CURLFORM_CONTENTHEADER: - { - /* this "cast increases required alignment of target type" but - we consider it OK anyway */ - struct curl_slist *list = array_state ? - (struct curl_slist *)(void *)avalue : - va_arg(params, struct curl_slist *); + case CURLFORM_CONTENTHEADER: { + /* this "cast increases required alignment of target type" but + we consider it OK anyway */ + struct curl_slist *list = form_ptr_arg(struct curl_slist *); - if(curr->contentheader) - retval = CURL_FORMADD_OPTION_TWICE; - else - curr->contentheader = list; + if(curr->contentheader) + retval = CURL_FORMADD_OPTION_TWICE; + else + curr->contentheader = list; - break; - } + break; + } case CURLFORM_FILENAME: case CURLFORM_BUFFER: - if(!array_state) - avalue = va_arg(params, char *); - if(curr->showfilename) + avalue = form_ptr_arg(char *); + if(Curl_bufref_ptr(&curr->showfilename)) retval = CURL_FORMADD_OPTION_TWICE; - else { - curr->showfilename = strdup(avalue); - if(!curr->showfilename) - retval = CURL_FORMADD_MEMORY; - else - curr->showfilename_alloc = TRUE; - } + else if(Curl_bufref_memdup0(&curr->showfilename, avalue, strlen(avalue))) + retval = CURL_FORMADD_MEMORY; break; default: @@ -649,7 +578,7 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, now by the httppost linked list */ while(first_form) { struct FormInfo *ptr = first_form->more; - free(first_form); + curlx_free(first_form); first_form = ptr; } @@ -666,6 +595,8 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, free_chain(newchain); return retval; +#undef form_ptr_arg +#undef form_int_arg } /* @@ -675,15 +606,14 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost, */ CURLFORMcode curl_formadd(struct curl_httppost **httppost, - struct curl_httppost **last_post, - ...) + struct curl_httppost **last_post, ...) { va_list arg; - CURLFORMcode result; + CURLFORMcode form; va_start(arg, last_post); - result = FormAdd(httppost, last_post, arg); + form = FormAdd(httppost, last_post, arg); va_end(arg); - return result; + return form; } /* @@ -699,6 +629,10 @@ int curl_formget(struct curl_httppost *form, void *arg, CURLcode result; curl_mimepart toppart; + /* Validate callback is provided */ + if(!append) + return (int)CURLE_BAD_FUNCTION_ARGUMENT; + Curl_mime_initpart(&toppart); /* default form is empty */ result = Curl_getformdata(NULL, &toppart, form, NULL); if(!result) @@ -720,7 +654,7 @@ int curl_formget(struct curl_httppost *form, void *arg, } Curl_mime_cleanpart(&toppart); - return (int) result; + return (int)result; } /* @@ -732,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 { @@ -742,19 +676,17 @@ void curl_formfree(struct curl_httppost *form) curl_formfree(form->more); if(!(form->flags & HTTPPOST_PTRNAME)) - free(form->name); /* free the name */ + curlx_free(form->name); /* free the name */ if(!(form->flags & - (HTTPPOST_PTRCONTENTS|HTTPPOST_BUFFER|HTTPPOST_CALLBACK)) - ) - free(form->contents); /* free the contents */ - free(form->contenttype); /* free the content type */ - free(form->showfilename); /* free the faked filename */ - free(form); /* free the struct */ + (HTTPPOST_PTRCONTENTS | HTTPPOST_BUFFER | HTTPPOST_CALLBACK))) + curlx_free(form->contents); /* free the contents */ + curlx_free(form->contenttype); /* free the content type */ + curlx_free(form->showfilename); /* free the faked filename */ + curlx_free(form); /* free the struct */ form = next; } while(form); /* continue */ } - /* Set mime part name, taking care of non null-terminated name string. */ static CURLcode setname(curl_mimepart *part, const char *name, size_t len) { @@ -763,36 +695,22 @@ 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); - free(zname); + curlx_free(zname); return res; } -/* wrap call to fseeko so it matches the calling convention of callback */ -static int fseeko_wrapper(void *stream, curl_off_t offset, int whence) -{ -#if defined(_WIN32) && defined(USE_WIN32_LARGE_FILES) - return _fseeki64(stream, (__int64)offset, whence); -#elif defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO) - return fseeko(stream, (off_t)offset, whence); -#else - if(offset > LONG_MAX) - return -1; - return fseek(stream, (long)offset, whence); -#endif -} - /* * Curl_getformdata() converts a linked list of "meta data" into a mime * structure. The input list is in 'post', while the output is stored in * 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, @@ -868,10 +786,17 @@ CURLcode Curl_getformdata(CURL *data, particular, freopen(stdin) by the caller is not guaranteed to result as expected. This feature has been kept for backward compatibility: use of "-" pseudo filename should be avoided. */ - result = curl_mime_data_cb(part, (curl_off_t) -1, - (curl_read_callback) fread, - fseeko_wrapper, - NULL, (void *) stdin); +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +#endif + result = curl_mime_data_cb(part, (curl_off_t)-1, + (curl_read_callback)fread, + curlx_fseek, + NULL, (void *)stdin); +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic pop +#endif } else result = curl_mime_filedata(part, file->contents); @@ -903,7 +828,7 @@ CURLcode Curl_getformdata(CURL *data, /* Set fake filename. */ if(!result && post->showfilename) if(post->more || (post->flags & (HTTPPOST_FILENAME | HTTPPOST_BUFFER | - HTTPPOST_CALLBACK))) + HTTPPOST_CALLBACK))) result = curl_mime_filename(part, post->showfilename); } } @@ -914,11 +839,9 @@ 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, - ...) + struct curl_httppost **last_post, ...) { (void)httppost; (void)last_post; @@ -940,4 +863,4 @@ void curl_formfree(struct curl_httppost *form) /* Nothing to do. */ } -#endif /* if disabled */ +#endif /* if disabled */ diff --git a/lib/formdata.h b/lib/formdata.h index 74f00bf4fc..33d7d0ae33 100644 --- a/lib/formdata.h +++ b/lib/formdata.h @@ -23,37 +23,33 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifndef CURL_DISABLE_FORM_API +#include "bufref.h" + /* used by FormAdd for temporary storage */ struct FormInfo { - char *name; - size_t namelength; - char *value; - curl_off_t contentslength; - char *contenttype; + struct bufref name; + struct bufref value; + struct bufref contenttype; + struct bufref showfilename; /* The filename to show. If not set, the actual + filename will be used */ char *buffer; /* pointer to existing buffer used for file upload */ - size_t bufferlength; - char *showfilename; /* The filename to show. If not set, the actual - filename will be used */ char *userp; /* pointer for the read callback */ - struct curl_slist *contentheader; struct FormInfo *more; + struct curl_slist *contentheader; + curl_off_t contentslength; + size_t namelength; + size_t bufferlength; unsigned char flags; - BIT(name_alloc); - BIT(value_alloc); - BIT(contenttype_alloc); - BIT(showfilename_alloc); }; CURLcode Curl_getformdata(CURL *data, - curl_mimepart *, + curl_mimepart *finalform, struct curl_httppost *post, curl_read_callback fread_func); #endif /* CURL_DISABLE_FORM_API */ - #endif /* HEADER_CURL_FORMDATA_H */ diff --git a/lib/ftp-int.h b/lib/ftp-int.h new file mode 100644 index 0000000000..68d26f3327 --- /dev/null +++ b/lib/ftp-int.h @@ -0,0 +1,160 @@ +#ifndef HEADER_CURL_FTP_INT_H +#define HEADER_CURL_FTP_INT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "pingpong.h" + +#ifndef CURL_DISABLE_FTP + +/**************************************************************************** + * FTP unique setup + ***************************************************************************/ +enum { + FTP_STOP, /* do nothing state, stops the state machine */ + FTP_WAIT220, /* waiting for the initial 220 response immediately after + a connect */ + FTP_AUTH, + FTP_USER, + FTP_PASS, + FTP_ACCT, + FTP_PBSZ, + FTP_PROT, + FTP_CCC, + FTP_PWD, + FTP_SYST, + FTP_NAMEFMT, + FTP_QUOTE, /* waiting for a response to a command sent in a quote list */ + FTP_RETR_PREQUOTE, + FTP_STOR_PREQUOTE, + FTP_LIST_PREQUOTE, + FTP_POSTQUOTE, + FTP_CWD, /* change directory */ + FTP_MKD, /* if the directory did not exist */ + FTP_MDTM, /* to figure out the datestamp */ + FTP_TYPE, /* to set type when doing a head-like request */ + FTP_LIST_TYPE, /* set type when about to do a directory list */ + FTP_RETR_LIST_TYPE, + FTP_RETR_TYPE, /* set type when about to RETR a file */ + FTP_STOR_TYPE, /* set type when about to STOR a file */ + FTP_SIZE, /* get the remote file's size for head-like request */ + FTP_RETR_SIZE, /* get the remote file's size for RETR */ + FTP_STOR_SIZE, /* get the size for STOR */ + FTP_REST, /* when used to check if the server supports it in head-like */ + FTP_RETR_REST, /* when asking for "resume" in for RETR */ + FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */ + FTP_PRET, /* generic state for PRET RETR, PRET STOR and PRET LIST/NLST */ + FTP_PASV, /* generic state for PASV and EPSV, check count1 */ + FTP_LIST, /* generic state for LIST, NLST or a custom list command */ + FTP_RETR, + FTP_STOR, /* generic state for STOR and APPE */ + FTP_QUIT, + FTP_LAST /* never used */ +}; +typedef unsigned char ftpstate; /* use the enum values */ + +struct ftp_parselist_data; /* defined later in ftplistparser.c */ + +struct ftp_wc { + struct ftp_parselist_data *parser; + + struct { + curl_write_callback write_function; + FILE *file_descriptor; + } backup; +}; + +/* This FTP struct is used in the Curl_easy. All FTP data that is + connection-oriented must be in FTP_conn to properly deal with the fact that + perhaps the Curl_easy is changed between the times the connection is + used. */ +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 to make debuggers + display the full symbol and not the numerical value */ + curl_pp_transfer transfer; + curl_off_t downloadsize; +}; + +/* one struct entry for each path component (of 'rawpath') */ +struct pathcomp { + int start; /* start column */ + int len; /* length in bytes */ +}; + +/* ftp_conn is used for struct connection-oriented data in the connectdata + struct */ +struct ftp_conn { + struct pingpong pp; + char *account; + char *alternative_to_user; + char *entrypath; /* the PWD reply when we logged on */ + const char *file; /* url-decoded filename (or path), points into rawpath */ + char *rawpath; /* URL decoded, allocated, version of the path */ + struct pathcomp *dirs; /* allocated array for path components */ + char *prevpath; /* url-decoded conn->path from the previous transfer */ + char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a + and others (A/I or zero) */ + char *server_os; /* The target server operating system. */ + curl_off_t known_filesize; /* file size is different from -1, if wildcard + LIST parsing was done and wc_statemach set + it */ + int count1; /* general purpose counter for the state machine */ + int count2; /* general purpose counter for the state machine */ + int count3; /* general purpose counter for the state machine */ + unsigned short dirdepth; /* number of entries used in the 'dirs' array, + < FTP_MAX_DIR_DEPTH */ + unsigned short cwdcount; /* number of CWD commands issued, + < FTP_MAX_DIR_DEPTH */ + unsigned char state; /* (ftpstate enum) always use ftp.c:state() to change + state! */ + unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or + IMAP or POP3 or others! (type: curl_usessl)*/ + unsigned char ccc; /* ccc level for this connection */ + BIT(ftp_trying_alternative); + BIT(dont_check); /* Set to TRUE to prevent the final (post-transfer) + file size and 226/250 status check. It should still + 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() */ + BIT(cwddone); /* if it has been determined that the proper CWD combo + already has been done */ + BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent + caching the current directory */ + BIT(wait_data_conn); /* this is set TRUE if data connection is waited */ + BIT(shutdown); /* connection is being shutdown, e.g. QUIT */ +}; + +/* meta key for storing `struct FTP` as easy meta data */ +#define CURL_META_FTP_EASY "meta:proto:ftp:easy" +/* meta key for storing `struct ftp_conn` as connection meta data */ +#define CURL_META_FTP_CONN "meta:proto:ftp:conn" + +#endif /* CURL_DISABLE_FTP */ + +#endif /* HEADER_CURL_FTP_INT_H */ diff --git a/lib/ftp.c b/lib/ftp.c index 29c2f789a3..318ccab719 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -21,8 +21,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" +#include "urldata.h" #ifndef CURL_DISABLE_FTP @@ -40,26 +40,23 @@ #include #endif -#include -#include "urldata.h" #include "sendf.h" +#include "curl_addrinfo.h" +#include "curl_trc.h" #include "if2ip.h" #include "hostip.h" #include "progress.h" #include "transfer.h" #include "escape.h" -#include "http.h" /* for HTTP proxy tunnel stuff */ #include "ftp.h" -#include "fileinfo.h" +#include "ftp-int.h" #include "ftplistparser.h" #include "curl_range.h" -#include "curl_krb5.h" #include "strcase.h" #include "vtls/vtls.h" #include "cfilters.h" #include "cf-socket.h" #include "connect.h" -#include "strerror.h" #include "curlx/inet_ntop.h" #include "curlx/inet_pton.h" #include "select.h" @@ -67,16 +64,11 @@ #include "sockaddr.h" /* required for Curl_sockaddr_storage */ #include "multiif.h" #include "url.h" -#include "speedcheck.h" -#include "curlx/warnless.h" #include "http_proxy.h" -#include "socks.h" -#include "strdup.h" +#include "curlx/strdup.h" +#include "curlx/strerr.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "curl_ctype.h" #ifndef NI_MAXHOST #define NI_MAXHOST 1025 @@ -87,18 +79,15 @@ /* 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])) +#define STATUSCODE(line) \ + (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 */ -static const char * const ftp_state_names[]={ +#ifdef CURLVERBOSE +/* for tracing purposes */ +static const char * const ftp_state_names[] = { "STOP", "WAIT220", "AUTH", @@ -137,63 +126,39 @@ static const char * const ftp_state_names[]={ "STOR", "QUIT" }; -#define FTP_CSTATE(ftpc) ((ftpc)? ftp_state_names[(ftpc)->state] : "???") +#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(struct Curl_easy *data, - struct ftp_conn *ftpc, - ftpstate newstate +static void ftp_state_low(struct Curl_easy *data, + struct ftp_conn *ftpc, + ftpstate newstate #ifdef DEBUGBUILD - , int lineno + , int lineno #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; } - /* Local API functions */ #ifndef DEBUGBUILD -#define ftp_state(x,y,z) _ftp_state(x,y,z) +#define ftp_state(x, y, z) ftp_state_low(x, y, z) #else /* !DEBUGBUILD */ -#define ftp_state(x,y,z) _ftp_state(x,y,z,__LINE__) +#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); @@ -205,108 +170,185 @@ 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); +static CURLcode getftpresponse(struct Curl_easy *data, size_t *nreadp, + int *ftpcodep); -/* - * FTP protocol handler. +static void freedirs(struct ftp_conn *ftpc) +{ + curlx_safefree(ftpc->dirs); + ftpc->dirdepth = 0; + curlx_safefree(ftpc->rawpath); + ftpc->file = NULL; +} + +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. + * */ +static CURLcode ftp_parse_url_path(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) +{ + const char *slashPos = NULL; + const char *fileName = NULL; + CURLcode result = CURLE_OK; + const char *rawPath = NULL; /* URL-decoded "raw" path */ + size_t pathLen = 0; -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 /* flags */ -}; + ftpc->ctl_valid = FALSE; + ftpc->cwdfail = FALSE; + 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; -#ifdef USE_SSL -/* - * FTPS protocol handler. + 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; + + default: /* allow pretty much anything */ + case FTPFILE_MULTICWD: { + /* current position: begin of next path component */ + const char *curPos = rawPath; + + /* number of entries to allocate for the 'dirs' array */ + size_t dirAlloc = numof_slashes(rawPath); + + if(dirAlloc >= FTP_MAX_DIR_DEPTH) + /* suspiciously deep directory hierarchy */ + return CURLE_URL_MALFORMAT; + + if(dirAlloc) { + ftpc->dirs = curlx_calloc(dirAlloc, sizeof(ftpc->dirs[0])); + if(!ftpc->dirs) + return CURLE_OUT_OF_MEMORY; + + /* parse the URL path into separate path components */ + while(dirAlloc--) { + const char *spos = strchr(curPos, '/'); + size_t clen = spos - curPos; + + /* path starts with a slash: add that as a directory */ + if(!clen && (ftpc->dirdepth == 0)) + ++clen; + + /* 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 */ + + if(fileName && *fileName) + ftpc->file = fileName; + else + ftpc->file = NULL; /* instead of point to a zero byte, + we make it a NULL pointer */ + + 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; + } + + ftpc->cwddone = FALSE; /* default to not done */ + + 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 CURLE_OK; +} + +/*********************************************************************** + * + * ftp_need_type() + * + * Returns TRUE if we in the current situation should send TYPE */ - -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 /* flags */ -}; -#endif +static int ftp_need_type(struct ftp_conn *ftpc, + bool ascii_wanted) +{ + return ftpc->transfertype != (ascii_wanted ? 'A' : 'I'); +} static void close_secondarysocket(struct Curl_easy *data, struct ftp_conn *ftpc) @@ -317,32 +359,11 @@ static void close_secondarysocket(struct Curl_easy *data, Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET); } -/* - * NOTE: back in the old days, we added code in the FTP code that made NOBODY - * requests on files respond with headers passed to the client/stdout that - * looked like HTTP ones. - * - * This approach is not elegant, it causes confusion and is error-prone. It is - * subject for removal at the next (or at least a future) soname bump. Until - * then you can test the effects of the removal by undefining the following - * define named CURL_FTP_HTTPSTYLE_HEAD. - */ -#define CURL_FTP_HTTPSTYLE_HEAD 1 - -static void freedirs(struct ftp_conn *ftpc) -{ - Curl_safefree(ftpc->dirs); - ftpc->dirdepth = 0; - Curl_safefree(ftpc->rawpath); - ftpc->file = NULL; - Curl_safefree(ftpc->newhost); -} - #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'. + * ending in '\r\n' and we prefer '\n'. * We might also get a lonely '\r' which we convert into a '\n'. */ struct ftp_cw_lc_ctx { @@ -380,8 +401,8 @@ static CURLcode ftp_cw_lc_write(struct Curl_easy *data, if(result) return result; } - /* either we just wrote the newline or it is part of the next - * chunk of bytes we write. */ + /* either we wrote the newline or it is part of the next chunk of bytes + * we write. */ ctx->newline_pending = FALSE; } @@ -430,6 +451,7 @@ static const struct Curl_cwtype ftp_cw_lc = { }; #endif /* CURL_PREFER_LF_LINEENDS */ + /*********************************************************************** * * ftp_check_ctrl_on_data_wait() @@ -441,24 +463,26 @@ static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data, struct connectdata *conn = data->conn; curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET]; struct pingpong *pp = &ftpc->pp; - ssize_t nread; + size_t nread; int ftpcode; bool response = FALSE; /* First check whether there is a cached response from server */ - if(curlx_dyn_len(&pp->recvbuf) && (*curlx_dyn_ptr(&pp->recvbuf) > '3')) { - /* Data connection could not be established, let's return */ - infof(data, "There is negative response in cache while serv connect"); - (void)Curl_GetFTPResponse(data, &nread, &ftpcode); - return CURLE_FTP_ACCEPT_FAILED; + 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; + } } if(pp->overflow) /* there is pending control data still in the buffer to read */ response = TRUE; else { - int socketstate = Curl_socket_check(ctrl_sock, CURL_SOCKET_BAD, - CURL_SOCKET_BAD, 0); + int socketstate = SOCKET_READABLE(ctrl_sock, 0); /* see if the connection request is already here */ switch(socketstate) { case -1: /* error */ @@ -476,13 +500,14 @@ static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data, 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)); + DEBUGASSERT((pp->overflow + pp->nfinal) <= curlx_dyn_len(&pp->recvbuf)); /* move over the most recently handled response line */ r += pp->nfinal; + len -= pp->nfinal; - if(LASTLINE(r)) { + 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 @@ -495,11 +520,11 @@ static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data, } } - (void)Curl_GetFTPResponse(data, &nread, &ftpcode); + (void)getftpresponse(data, &nread, &ftpcode); infof(data, "FTP code: %03d", ftpcode); - if(ftpcode/100 > 3) + if(ftpcode / 100 > 3) return CURLE_FTP_ACCEPT_FAILED; return CURLE_WEIRD_SERVER_REPLY; @@ -527,14 +552,11 @@ static CURLcode ftp_initiate_transfer(struct Curl_easy *data, if(result || !connected) return result; - if(ftpc->state_saved == FTP_STOR) { + 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); - /* set the SO_SNDBUF for the secondary socket for those who need it */ - Curl_sndbuf_init(data->conn->sock[SECONDARYSOCKET]); - /* FTP upload, shutdown DATA, ignore shutdown errors, as we rely * on the server response on the CONTROL connection. */ Curl_xfer_setup_send(data, SECONDARYSOCKET); @@ -542,7 +564,7 @@ static CURLcode ftp_initiate_transfer(struct Curl_easy *data, } else { /* FTP download, shutdown, do not ignore errors */ - Curl_xfer_setup_recv(data, SECONDARYSOCKET, ftpc->retr_size_saved); + Curl_xfer_setup_recv(data, SECONDARYSOCKET, data->req.size); Curl_xfer_set_shutdown(data, TRUE, FALSE); } @@ -577,28 +599,6 @@ static CURLcode ftp_readresp(struct Curl_easy *data, int code; CURLcode result = Curl_pp_readresp(data, sockindex, pp, &code, size); DEBUGASSERT(ftpcodep); -#ifdef HAVE_GSSAPI - { - struct connectdata *conn = data->conn; - char * const buf = curlx_dyn_ptr(&ftpc->pp.recvbuf); - - /* handle the security-oriented responses 6xx ***/ - switch(code) { - case 631: - code = Curl_sec_read_msg(data, conn, buf, PROT_SAFE); - break; - case 632: - code = Curl_sec_read_msg(data, conn, buf, PROT_PRIVATE); - break; - case 633: - code = Curl_sec_read_msg(data, conn, buf, PROT_CONFIDENTIAL); - break; - default: - /* normal ftp stuff we pass through! */ - break; - } - } -#endif /* store the latest code for later retrieval, except during shutdown */ if(!ftpc->shutdown) @@ -625,21 +625,21 @@ static CURLcode ftp_readresp(struct Curl_easy *data, /* --- parse FTP server responses --- */ /* - * Curl_GetFTPResponse() is a BLOCKING function to read the full response - * from a server after a command. + * getftpresponse() is a BLOCKING function to read the full response from a + * server after a command. * */ - -CURLcode Curl_GetFTPResponse(struct Curl_easy *data, - ssize_t *nreadp, /* return number of bytes read */ - int *ftpcodep) /* return the ftp-code */ +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. + * 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. */ + * line in a response or continue reading. */ struct connectdata *conn = data->conn; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; @@ -650,7 +650,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, int cache_skip = 0; DEBUGASSERT(ftpcodep); - CURL_TRC_FTP(data, "getFTPResponse start"); + CURL_TRC_FTP(data, "getftpresponse start"); *nreadp = 0; *ftpcodep = 0; /* 0 for errors */ @@ -659,7 +659,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, while(!*ftpcodep && !result) { /* check and reset timeout value every lap */ - timediff_t timeout = Curl_pp_state_timeout(data, pp, FALSE); + timediff_t timeout = Curl_pp_state_timeleft_ms(data, pp); timediff_t interval_ms; if(timeout <= 0) { @@ -678,10 +678,10 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, * * 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. + * make sure we do not wait for input while there is unhandled data in + * that cache. 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. * */ @@ -703,9 +703,8 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, return CURLE_RECV_ERROR; } else if(ev == 0) { - if(Curl_pgrsUpdate(data)) - return CURLE_ABORTED_BY_CALLBACK; - continue; /* just continue in our loop for the timeout duration */ + result = Curl_pgrsUpdate(data); + continue; /* continue in our loop for the timeout duration */ } } @@ -733,7 +732,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data, } /* while there is buffer left and loop is requested */ pp->pending_resp = FALSE; - CURL_TRC_FTP(data, "getFTPResponse -> result=%d, nread=%zd, ftpcode=%d", + CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zd, ftpcode=%d", result, *nreadp, *ftpcodep); return result; @@ -785,8 +784,8 @@ static CURLcode ftp_domore_pollset(struct Curl_easy *data, 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 just - * handle ordinary commands. + * 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)); @@ -794,8 +793,8 @@ static CURLcode ftp_domore_pollset(struct Curl_easy *data, /* 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))); + (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]); @@ -814,7 +813,7 @@ static const char *pathpiece(struct ftp_conn *ftpc, int num) { DEBUGASSERT(ftpc->dirs); DEBUGASSERT(ftpc->dirdepth > num); - return &ftpc->rawpath[ ftpc->dirs[num].start ]; + return &ftpc->rawpath[ftpc->dirs[num].start]; } /* This is called after the FTP_QUOTE state is passed. @@ -876,196 +875,227 @@ typedef enum { DONE } ftpport; -static CURLcode ftp_state_use_port(struct Curl_easy *data, - struct ftp_conn *ftpc, - ftpport fcmd) /* start with this */ +/* + * 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) { - CURLcode result = CURLE_FTP_PORT_FAILED; - struct connectdata *conn = data->conn; - curl_socket_t portsock = CURL_SOCKET_BAD; - char myhost[MAX_IPADR_LEN + 1] = ""; - - 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; + const char *ip_end = NULL; + const char *addr = NULL; + size_t addrlen = 0; 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]; +#ifndef USE_IPV6 + (void)conn; + (void)ss; +#endif - /* Step 1, figure out what is requested, - * accepted format : - * (ipv4|ipv6|domain|interface)?(:port(-range)?)? - */ + /* default to nothing */ + *hostp = NULL; + *port_minp = *port_maxp = 0; - if(data->set.str[STRING_FTPPORT] && - (strlen(data->set.str[STRING_FTPPORT]) > 1)) { - char *ip_end = NULL; + if(!string_ftpport || (strlen(string_ftpport) <= 1)) + goto done; #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); - } - - /* parse the port */ + if(*string_ftpport == '[') { + /* [ipv6]:port(-range) */ + const char *ip_start = string_ftpport + 1; + ip_end = strchr(ip_start, ']'); 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; - } + 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); + } + + /* 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; } } - - /* 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(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", - Curl_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 */ } - /* 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; - } + /* correct errors like :1234-1230 or :-4711 */ + if(port_min > port_max) + port_min = port_max = 0; - if(!res) { + if(addrlen) { + const struct Curl_sockaddr_ex *remote_addr = + Curl_conn_get_remote_addr(data, FIRSTSOCKET); + + 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; + + /* 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 */ + +done: + *port_minp = port_min; + *port_maxp = port_max; + return CURLE_OK; +} + +/* + * 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) +{ + 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; + + *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; + + *hostp = hbuf; + *non_localp = FALSE; /* we know it is local now */ + return CURLE_OK; +} + +/* + * 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; + + *resp = NULL; + result = Curl_resolv_blocking( + data, Curl_resolv_dns_queries(data, conn->ip_version), + host, 0, Curl_conn_get_transport(data, conn), dns_entryp); + if(result) failf(data, "failed to resolve the address provided to PORT: %s", host); - goto out; + else { + DEBUGASSERT(*dns_entryp); + *resp = (*dns_entryp)->addr; } + return result; +} - host = NULL; +/* + * 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) +{ + char buffer[STRERROR_LEN]; + int error = 0; + const struct Curl_addrinfo *ai; + CURLcode result = CURLE_FTP_PORT_FAILED; - /* step 2, create a socket for the requested address */ - error = 0; for(ai = res; ai; ai = ai->ai_next) { - if(Curl_socket_open(data, ai, NULL, - Curl_conn_get_transport(data, conn), &portsock)) { + 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; } @@ -1073,16 +1103,39 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, } if(!ai) { failf(data, "socket failure: %s", - Curl_strerror(error, buffer, sizeof(buffer))); - goto out; + curlx_strerror(error, buffer, sizeof(buffer))); + return CURLE_FTP_PORT_FAILED; } - CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket", - FTP_CSTATE(ftpc)); + *aip = ai; + return result; +} - /* step 3, bind to a suitable local address */ +/* + * 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) +{ + 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; memcpy(sa, ai->ai_addr, ai->ai_addrlen); - sslen = ai->ai_addrlen; + *sslen_io = ai->ai_addrlen; for(port = port_min; port <= port_max;) { if(sa->sa_family == AF_INET) @@ -1091,70 +1144,97 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, else sa6->sin6_port = htons(port); #endif - /* Try binding the given address. */ - if(bind(portsock, sa, sslen) ) { - /* It failed. */ + if(bind(portsock, sa, *sslen_io)) { error = SOCKERRNO; - if(possibly_non_local && (error == SOCKEADDRNOTAVAIL)) { + 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 + * the control connection instead and restart the port loop. */ infof(data, "bind(port=%hu) on non-local address failed: %s", port, - Curl_strerror(error, buffer, sizeof(buffer))); + curlx_strerror(error, buffer, sizeof(buffer))); - sslen = sizeof(ss); - if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { + *sslen_io = sizeof(*ss); + if(getsockname(conn->sock[FIRSTSOCKET], sa, sslen_io)) { failf(data, "getsockname() failed: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; + curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_FTP_PORT_FAILED; } port = port_min; - possibly_non_local = FALSE; /* do not try this again */ + non_local = FALSE; /* do not try this again */ continue; } if(error != SOCKEADDRINUSE && error != SOCKEACCES) { failf(data, "bind(port=%hu) failed: %s", port, - Curl_strerror(error, buffer, sizeof(buffer))); - goto out; + curlx_strerror(error, buffer, sizeof(buffer))); + return CURLE_FTP_PORT_FAILED; } } 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) { + failf(data, "bind() failed, ran out of ports"); + return CURLE_FTP_PORT_FAILED; + } port++; } - /* maybe all ports were in use already */ - if(port > port_max) { - failf(data, "bind() failed, we ran out of ports"); - goto out; - } - - /* 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)) { + /* 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", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; + curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_FTP_PORT_FAILED; } - CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), socket bound to port %d", - FTP_CSTATE(ftpc), port); + CURL_TRC_FTP(data, "ftp_port_bind_socket(), socket bound to port %d", + port); + return CURLE_OK; +} - /* step 4, listen on the socket */ +/* + * Start listening on the data socket. + */ +static CURLcode ftp_port_listen(struct Curl_easy *data, curl_socket_t portsock) +{ + char buffer[STRERROR_LEN]; if(listen(portsock, 1)) { failf(data, "socket failure: %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - goto out; + curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_FTP_PORT_FAILED; } - CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), listening on %d", - FTP_CSTATE(ftpc), port); + CURL_TRC_FTP(data, "ftp_port_listen(), listening on port"); + return CURLE_OK; +} - /* step 5, send the proper FTP command */ +/* + * 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; - /* get a plain printable version of the numerical address to work with - below */ + /* 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)); #ifdef USE_IPV6 @@ -1195,21 +1275,20 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, * * 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)); - goto out; + return result; } break; } if(PORT == fcmd) { /* large enough for [IP address],[num],[num] */ char target[sizeof(myhost) + 20]; - char *source = myhost; + const char *source = myhost; char *dest = target; /* translate x.x.x.x to x,x,x,x */ @@ -1222,13 +1301,13 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, source++; } *dest = 0; - msnprintf(dest, 20, ",%d,%d", (int)(port >> 8), (int)(port & 0xff)); + 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; + return result; } break; } @@ -1237,21 +1316,84 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, /* store which command was sent */ ftpc->count1 = fcmd; ftp_state(data, ftpc, FTP_PORT); + return CURLE_OK; +} + +/* + * 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_FTP_PORT_FAILED; + struct connectdata *conn = data->conn; + curl_socket_t portsock = CURL_SOCKET_BAD; + + 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); + + /* 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)); + + /* bind to a suitable local address / port */ + result = ftp_port_bind_socket(data, conn, portsock, ai, &ss, &sslen, + port_min, port_max, non_local); + } + + /* listen */ + if(!result) + result = ftp_port_listen(data, portsock); + + /* send the PORT / EPRT command */ + if(!result) + result = ftp_port_send_command(data, ftpc, conn, &ss, ai, fcmd); + + /* replace any filter on SECONDARY with one listening on this socket */ + if(!result) + result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock); - /* 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 */ + /* cleanup */ + if(dns_entry) - Curl_resolv_unlink(data, &dns_entry); + Curl_dns_entry_unlink(data, &dns_entry); if(result) { ftp_state(data, ftpc, FTP_STOP); } else { - /* successfully setup the list socket filter. Do we need more? */ + /* 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); @@ -1413,14 +1555,14 @@ static CURLcode ftp_state_list(struct Curl_easy *data, Whether the server will support this, is uncertain. The other ftp_filemethods will CWD into dir/dir/ first and - then just do LIST (in that case: nothing to do here) + then do LIST (in that case: nothing to do here) */ const char *lstArg = NULL; int lstArglen = 0; char *cmd; if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) { - /* url-decode before evaluation: e.g. paths starting/ending with %2f */ + /* URL-decode before evaluation: e.g. paths starting/ending with %2f */ const char *rawPath = ftpc->rawpath; const char *slashPos = strrchr(rawPath, '/'); if(slashPos) { @@ -1435,18 +1577,18 @@ static CURLcode ftp_state_list(struct Curl_easy *data, } } - cmd = aprintf("%s%s%.*s", - data->set.str[STRING_CUSTOMREQUEST] ? - data->set.str[STRING_CUSTOMREQUEST] : - (data->state.list_only ? "NLST" : "LIST"), - lstArg ? " " : "", - lstArglen, lstArg ? lstArg : ""); + 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(!cmd) return CURLE_OUT_OF_MEMORY; result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); - free(cmd); + curlx_free(cmd); if(!result) ftp_state(data, ftpc, FTP_LIST); @@ -1488,7 +1630,7 @@ static CURLcode ftp_state_type(struct Curl_easy *data, 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)) { + 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! */ @@ -1498,7 +1640,8 @@ static CURLcode ftp_state_type(struct Curl_easy *data, /* 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); + result = ftp_nb_type(data, ftpc, ftp, (bool)data->state.prefer_ascii, + FTP_TYPE); if(result) return result; } @@ -1532,7 +1675,6 @@ static CURLcode ftp_state_mdtm(struct Curl_easy *data, 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, @@ -1540,21 +1682,21 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, bool sizechecked) { CURLcode result = CURLE_OK; - bool append = data->set.remote_append; + curl_bit append = data->set.remote_append; 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. */ + /* 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 - do not another ftp command. We just skip the source file - offset and then we APPEND the rest on the file instead */ + 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 */ + 3. pass file-size number of bytes in the source file + 4. lower the infilesize counter */ /* => transfer as usual */ int seekerr = CURL_SEEKFUNC_OK; @@ -1585,7 +1727,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, } /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ do { - char scratch[4*1024]; + char scratch[4 * 1024]; size_t readthisamountnow = (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ? sizeof(scratch) : @@ -1633,6 +1775,89 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, return result; } +static CURLcode ftp_state_retr(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + curl_off_t filesize) +{ + CURLcode result = CURLE_OK; + + CURL_TRC_FTP(data, "[%s] ftp_state_retr()", FTP_CSTATE(ftpc)); + if(data->set.max_filesize && (filesize > data->set.max_filesize)) { + failf(data, "Maximum file size exceeded"); + return CURLE_FILESIZE_EXCEEDED; + } + ftp->downloadsize = filesize; + + if(data->state.resume_from) { + /* We always (attempt to) get the size of downloads, so it is done before + 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 + 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. */ + if(data->state.resume_from < 0) { + /* We are supposed to download the last abs(from) bytes */ + if(filesize < -data->state.resume_from) { + failf(data, "Offset (%" FMT_OFF_T + ") was beyond file size (%" FMT_OFF_T ")", + data->state.resume_from, filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + /* convert to size to download */ + ftp->downloadsize = -data->state.resume_from; + /* download from where? */ + data->state.resume_from = filesize - ftp->downloadsize; + } + else { + if(filesize < data->state.resume_from) { + failf(data, "Offset (%" FMT_OFF_T + ") was beyond file size (%" FMT_OFF_T ")", + data->state.resume_from, filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + /* Now store the number of bytes we are expected to download */ + ftp->downloadsize = filesize - data->state.resume_from; + } + } + + if(ftp->downloadsize == 0) { + /* no data to transfer */ + Curl_xfer_setup_nop(data); + infof(data, "File already completely downloaded"); + + /* Set ->transfer so that we will not get any error in ftp_done() + * because we did not transfer the any file */ + ftp->transfer = PPTRANSFER_NONE; + ftp_state(data, ftpc, FTP_STOP); + return CURLE_OK; + } + + /* Set resume file transfer offset */ + infof(data, "Instructs server to resume from offset %" FMT_OFF_T, + data->state.resume_from); + + result = Curl_pp_sendf(data, &ftpc->pp, "REST %" FMT_OFF_T, + data->state.resume_from); + if(!result) + ftp_state(data, ftpc, FTP_RETR_REST); + } + else { + /* no resume */ + result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); + if(!result) + ftp_state(data, ftpc, FTP_RETR); + } + + return result; +} + static CURLcode ftp_state_quote(struct Curl_easy *data, struct ftp_conn *ftpc, struct FTP *ftp, @@ -1678,7 +1903,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data, i++; } if(item) { - char *cmd = item->data; + const char *cmd = item->data; if(cmd[0] == '*') { cmd++; ftpc->count2 = 1; /* the sent command is allowed to fail */ @@ -1720,8 +1945,8 @@ static CURLcode ftp_state_quote(struct Curl_easy *data, 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. + constructive since servers do not report the converted size. + Thus, skip it. */ result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); if(!result) @@ -1783,12 +2008,11 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data, return result; } - -static char *control_address_dup(struct Curl_easy *data, - struct connectdata *conn) +static CURLcode ftp_control_addr_dup(struct Curl_easy *data, char **newhostp) { - struct ip_quadruple ipquad; - bool is_ipv6; + 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 @@ -1796,11 +2020,19 @@ static char *control_address_dup(struct Curl_easy *data, not the ftp host. */ #ifndef CURL_DISABLE_PROXY if(conn->bits.tunnel_proxy || conn->bits.socksproxy) - return strdup(conn->host.name); + *newhostp = curlx_strdup(conn->host.name); + else #endif - if(!Curl_conn_get_ip_info(data, conn, FIRSTSOCKET, &is_ipv6, &ipquad)) - return strdup(ipquad.remote_ip); - return NULL; + 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; } static bool match_pasv_6nums(const char *p, @@ -1829,17 +2061,15 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, CURLcode result; struct Curl_dns_entry *dns = NULL; unsigned short connectport; /* the local port connect() should use! */ - struct pingpong *pp = &ftpc->pp; - char *str = - curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first letter */ - - /* if we come here again, make sure the former name is cleared */ - Curl_safefree(ftpc->newhost); - + 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 */ - char *ptr = strchr(str, '('); + const char *ptr = strchr(str, '('); if(ptr) { char sep; ptr++; @@ -1852,10 +2082,10 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, failf(data, "Illegal port number in EPSV reply"); return CURLE_FTP_WEIRD_PASV_REPLY; } - ftpc->newport = (unsigned short)num; - ftpc->newhost = control_address_dup(data, conn); - if(!ftpc->newhost) - return CURLE_OUT_OF_MEMORY; + newport = (unsigned short)num; + result = ftp_control_addr_dup(data, &newhost); + if(result) + return result; } else ptr = NULL; @@ -1886,7 +2116,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, } if(!*str) { - failf(data, "Couldn't interpret the 227-response"); + failf(data, "Could not interpret the 227-response"); return CURLE_FTP_WEIRD_227_FORMAT; } @@ -1895,17 +2125,18 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, /* 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); - ftpc->newhost = control_address_dup(data, conn); + ip[0], ip[1], ip[2], ip[3], conn->host.name); + result = ftp_control_addr_dup(data, &newhost); + if(result) + return result; } else - ftpc->newhost = aprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + newhost = curl_maprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); - if(!ftpc->newhost) + if(!newhost) return CURLE_OUT_OF_MEMORY; - ftpc->newport = (unsigned short)(((ip[4] << 8) + ip[5]) & 0xffff); + newport = (unsigned short)(((ip[4] << 8) + ip[5]) & 0xffff); } else if(ftpc->count1 == 0) { /* EPSV failed, move on to PASV */ @@ -1919,7 +2150,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, #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've + * 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; @@ -1929,78 +2160,303 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, result = Curl_conn_get_ip_info(data, data->conn, FIRSTSOCKET, &is_ipv6, &ipquad); if(result) - return result; + goto error; - (void)Curl_resolv_blocking(data, host_name, ipquad.remote_port, - is_ipv6 ? CURL_IPRESOLVE_V6 : CURL_IPRESOLVE_V4, - &dns); + (void)Curl_resolv_blocking( + data, is_ipv6 ? CURL_DNSQ_AAAA : CURL_DNSQ_A, + host_name, ipquad.remote_port, Curl_conn_get_transport(data, conn), + &dns); /* we connect to the proxy's port */ connectport = (unsigned short)ipquad.remote_port; if(!dns) { failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport); - return CURLE_COULDNT_RESOLVE_PROXY; + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; } } else #endif { /* normal, direct, ftp connection */ - DEBUGASSERT(ftpc->newhost); + DEBUGASSERT(newhost); /* postponed address resolution in case of tcp fastopen */ - if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) { - free(ftpc->newhost); - ftpc->newhost = control_address_dup(data, conn); - if(!ftpc->newhost) - return CURLE_OUT_OF_MEMORY; + if(conn->bits.tcp_fastopen && !conn->bits.reuse && !newhost[0]) { + curlx_free(newhost); + result = ftp_control_addr_dup(data, &newhost); + if(result) + goto error; } - (void)Curl_resolv_blocking(data, ftpc->newhost, ftpc->newport, - conn->ip_version, &dns); - connectport = ftpc->newport; /* we connect to the remote port */ + (void)Curl_resolv_blocking( + data, Curl_resolv_dns_queries(data, conn->ip_version), + newhost, newport, Curl_conn_get_transport(data, conn), &dns); + connectport = newport; /* we connect to the remote port */ if(!dns) { - failf(data, "cannot resolve new host %s:%hu", - ftpc->newhost, connectport); - return CURLE_FTP_CANT_GET_HOST; + failf(data, "cannot resolve new host %s:%hu", newhost, connectport); + result = CURLE_FTP_CANT_GET_HOST; + goto error; } } + DEBUGASSERT(newhost); + curlx_free(conn->secondaryhostname); + conn->secondary_port = newport; + conn->secondaryhostname = newhost; + newhost = NULL; + result = Curl_conn_setup(data, conn, SECONDARYSOCKET, dns, conn->bits.ftp_use_data_ssl ? CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE); if(result) { - if(ftpc->count1 == 0 && ftpcode == 229) - return ftp_epsv_disable(data, ftpc, conn); - - return result; + if((result != CURLE_OUT_OF_MEMORY) && + (ftpc->count1 == 0) && (ftpcode == 229)) { + result = ftp_epsv_disable(data, ftpc, conn); + } + goto error; } - /* - * When this is used from the multi interface, this might've returned with + * 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.verbose) - /* this just dumps information about this second connection */ - ftp_pasv_verbose(data, dns->addr, ftpc->newhost, connectport); - - free(conn->secondaryhostname); - conn->secondary_port = ftpc->newport; - conn->secondaryhostname = strdup(ftpc->newhost); - if(!conn->secondaryhostname) - return CURLE_OUT_OF_MEMORY; +#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", + conn->secondaryhostname, buf, connectport); + } +#endif conn->bits.do_more = TRUE; ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */ +error: + Curl_dns_entry_unlink(data, &dns); + curlx_free(newhost); return result; } +/* called repeatedly until done from multi.c */ +static CURLcode ftp_statemach(struct Curl_easy *data, + struct ftp_conn *ftpc, + bool *done) +{ + CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE); + + /* 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); + + return result; +} + +/* + * 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 || !ftp) + return CURLE_FAILED_INIT; + + *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 */ + + if(ftpc->wait_data_conn) { + bool serv_conned; + + result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &serv_conned); + if(result) + return result; /* Failed to accept data connection */ + + if(serv_conned) { + /* It looks data connection is established */ + ftpc->wait_data_conn = FALSE; + result = ftp_initiate_transfer(data, ftpc); + + if(result) + return result; + + *completep = 1; /* this state is now complete when the server has + connected back to us */ + } + else { + result = ftp_check_ctrl_on_data_wait(data, ftpc); + if(result) + return result; + } + } + 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 { + /* download */ + ftp->downloadsize = -1; /* unknown as of yet */ + + result = Curl_range(data); + + if(result == CURLE_OK && data->req.maxdownload >= 0) { + /* Do not check for successful transfer */ + ftpc->dont_check = TRUE; + } + + 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. Before that, we also + need to set ASCII transfer mode. */ + + /* 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; + } + + result = ftp_statemach(data, ftpc, &complete); + *completep = (int)complete; + } + 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; +} + +/* 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; + } + } + + 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; + + ftpc->ctl_valid = TRUE; /* seems good */ + + return CURLE_OK; +} + static CURLcode ftp_state_port_resp(struct Curl_easy *data, struct ftp_conn *ftpc, struct FTP *ftp, @@ -2038,24 +2494,32 @@ static CURLcode ftp_state_port_resp(struct Curl_easy *data, return result; } -static int twodigit(const char *p) +/* return TRUE on error, FALSE on success */ +static bool twodigit(const char *p, int *val) { - return (p[0]-'0') * 10 + (p[1]-'0'); + if(!ISDIGIT(p[0]) || !ISDIGIT(p[1])) + return TRUE; + /* curlx_hexval() works fine here since we make sure it is decimal above */ + *val = (curlx_hexval(p[0]) * 10) + curlx_hexval(p[1]); + return FALSE; } -static bool ftp_213_date(const char *p, int *year, int *month, int *day, - int *hour, int *minute, int *second) +/* + * Unittest @1668 + */ +UNITTEST bool ftp_213_date(const char *p, int *year, int *month, int *day, + int *hour, int *minute, int *second); +UNITTEST 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) + int century; + if((strlen(p) < 14) || twodigit(&p[0], ¢ury) || twodigit(&p[2], year) || + twodigit(&p[4], month) || twodigit(&p[6], day) || + twodigit(&p[8], hour) || twodigit(&p[10], minute) || + twodigit(&p[12], second)) 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]); + *year += century * 100; if((*month > 12) || (*day > 31) || (*hour > 23) || (*minute > 59) || (*second > 60)) return FALSE; @@ -2080,7 +2544,7 @@ static CURLcode client_write_header(struct Curl_easy *data, * headers from CONNECT should not automatically be part of the * output. */ CURLcode result; - bool save = data->set.include_header; + 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; @@ -2095,67 +2559,65 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data, 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; - 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]; - 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; - } + 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; + } -#ifdef CURL_FTP_HTTPSTYLE_HEAD - /* 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 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__)) +#if defined(CURL_HAVE_DIAG) && (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__)) + if(data->req.no_body && ftpc->file && + data->set.get_filetime && showtime) { +#if defined(CURL_HAVE_DIAG) && (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; + char headerbuf[128]; + int headerbuflen; + time_t filetime = data->info.filetime; + struct tm buffer; + const struct tm *tm = &buffer; - result = Curl_gmtime(filetime, &buffer); - if(result) - return result; + result = curlx_gmtime(filetime, &buffer); + if(result) + return result; - /* format: "Tue, 15 Nov 1994 12:45:26" */ - headerbuflen = - 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 */ -#endif - } + /* 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"); @@ -2211,11 +2673,11 @@ static CURLcode ftp_state_type_resp(struct Curl_easy *data, { CURLcode result = CURLE_OK; - if(ftpcode/100 != 2) { + 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, "Couldn't set desired mode"); + failf(data, "Could not set desired mode"); return CURLE_FTP_COULDNT_SET_TYPE; } if(ftpcode != 200) @@ -2236,89 +2698,6 @@ static CURLcode ftp_state_type_resp(struct Curl_easy *data, return result; } -static CURLcode ftp_state_retr(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - curl_off_t filesize) -{ - CURLcode result = CURLE_OK; - - CURL_TRC_FTP(data, "[%s] ftp_state_retr()", FTP_CSTATE(ftpc)); - if(data->set.max_filesize && (filesize > data->set.max_filesize)) { - failf(data, "Maximum file size exceeded"); - return CURLE_FILESIZE_EXCEEDED; - } - ftp->downloadsize = filesize; - - if(data->state.resume_from) { - /* We always (attempt to) get the size of downloads, so it is done before - 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. */ - } - 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. */ - if(data->state.resume_from < 0) { - /* We are supposed to download the last abs(from) bytes */ - if(filesize < -data->state.resume_from) { - failf(data, "Offset (%" FMT_OFF_T - ") was beyond file size (%" FMT_OFF_T ")", - data->state.resume_from, filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - /* convert to size to download */ - ftp->downloadsize = -data->state.resume_from; - /* download from where? */ - data->state.resume_from = filesize - ftp->downloadsize; - } - else { - if(filesize < data->state.resume_from) { - failf(data, "Offset (%" FMT_OFF_T - ") was beyond file size (%" FMT_OFF_T ")", - data->state.resume_from, filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - /* Now store the number of bytes we are expected to download */ - ftp->downloadsize = filesize-data->state.resume_from; - } - } - - if(ftp->downloadsize == 0) { - /* no data to transfer */ - Curl_xfer_setup_nop(data); - infof(data, "File already completely downloaded"); - - /* Set ->transfer so that we will not get any error in ftp_done() - * because we did not transfer the any file */ - ftp->transfer = PPTRANSFER_NONE; - ftp_state(data, ftpc, FTP_STOP); - return CURLE_OK; - } - - /* Set resume file transfer offset */ - infof(data, "Instructs server to resume from offset %" FMT_OFF_T, - data->state.resume_from); - - result = Curl_pp_sendf(data, &ftpc->pp, "REST %" FMT_OFF_T, - data->state.resume_from); - if(!result) - ftp_state(data, ftpc, FTP_RETR_REST); - } - else { - /* no resume */ - result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); - if(!result) - ftp_state(data, ftpc, FTP_RETR); - } - - return result; -} - static CURLcode ftp_state_size_resp(struct Curl_easy *data, struct ftp_conn *ftpc, struct FTP *ftp, @@ -2327,7 +2706,7 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data, { CURLcode result = CURLE_OK; curl_off_t filesize = -1; - char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf); + const char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf); size_t len = ftpc->pp.nfinal; /* get the size from the ascii string: */ @@ -2335,8 +2714,8 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data, /* 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); + const char *start = &buf[4]; + const char *fdigit = memchr(start, '\r', len - 4); if(fdigit) { fdigit--; if(*fdigit == '\n') @@ -2359,16 +2738,15 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data, } if(instate == FTP_SIZE) { -#ifdef CURL_FTP_HTTPSTYLE_HEAD if(filesize != -1) { char clbuf[128]; - int clbuflen = msnprintf(clbuf, sizeof(clbuf), - "Content-Length: %" FMT_OFF_T "\r\n", filesize); + 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; } -#endif Curl_pgrsSetDownloadSize(data, filesize); result = ftp_state_rest(data, ftpc, ftp); } @@ -2395,20 +2773,18 @@ static CURLcode ftp_state_rest_resp(struct Curl_easy *data, switch(instate) { case FTP_REST: default: -#ifdef CURL_FTP_HTTPSTYLE_HEAD if(ftpcode == 350) { - char buffer[24]= { "Accept-ranges: bytes\r\n" }; + char buffer[24] = { "Accept-ranges: bytes\r\n" }; result = client_write_header(data, buffer, strlen(buffer)); if(result) return result; } -#endif result = ftp_state_prepare_transfer(data, ftpc, ftp); break; case FTP_RETR_REST: if(ftpcode != 350) { - failf(data, "Couldn't use REST"); + failf(data, "Could not use REST"); result = CURLE_FTP_COULDNT_USE_REST; } else { @@ -2424,19 +2800,16 @@ static CURLcode ftp_state_rest_resp(struct Curl_easy *data, static CURLcode ftp_state_stor_resp(struct Curl_easy *data, struct ftp_conn *ftpc, - int ftpcode, ftpstate instate) + int ftpcode) { CURLcode result = CURLE_OK; if(ftpcode >= 400) { failf(data, "Failed FTP upload: %0d", ftpcode); ftp_state(data, ftpc, FTP_STOP); - /* oops, we never close the sockets! */ return CURLE_UPLOAD_FAILED; } - ftpc->state_saved = instate; - /* PORT means we are now awaiting the server to connect to us. */ if(data->set.ftp_use_port) { bool connected; @@ -2485,8 +2858,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, E: 125 Data connection already open; Transfer starting. */ - curl_off_t size = -1; /* default unknown size */ - + data->req.size = -1; /* default unknown size */ /* * It appears that there are FTP-servers that return size 0 for files when @@ -2508,49 +2880,35 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, * those cases only confuses us. * * Example D above makes this parsing a little tricky */ - const char *bytes; - char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf); - bytes = strstr(buf, " bytes"); - if(bytes) { - long in = (long)(--bytes-buf); - /* this is a hint there is size information in there! ;-) */ - while(--in) { - /* scan for the left parenthesis and break there */ - if('(' == *bytes) - break; - /* skip only digits */ - if(!ISDIGIT(*bytes)) { - bytes = NULL; + 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; } - /* one more estep backwards */ - bytes--; - } - /* if we have nothing but digits: */ - if(bytes) { - ++bytes; - /* get the number! */ - if(curlx_str_number(&bytes, &size, CURL_OFF_T_MAX)) - size = 1; } } } else if(ftp->downloadsize > -1) - size = ftp->downloadsize; + data->req.size = ftp->downloadsize; - if(size > data->req.maxdownload && data->req.maxdownload > 0) - size = data->req.size = data->req.maxdownload; + 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)) - size = -1; /* kludge for servers that understate ASCII mode file size */ + 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, size); - - /* FTP download: */ - ftpc->state_saved = instate; - ftpc->retr_size_saved = size; + infof(data, "Getting file with size: %" FMT_OFF_T, data->req.size); if(data->set.ftp_use_port) { bool connected; @@ -2571,7 +2929,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, } else { if((instate == FTP_LIST) && (ftpcode == 450)) { - /* simply no matching files in the dir listing */ + /* 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 */ } @@ -2624,16 +2982,15 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, { CURLcode result = CURLE_OK; - /* some need password anyway, and others just return 2xx ignored */ + /* 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 ? data->conn->passwd : ""); + 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) { + else if(ftpcode / 100 == 2) { /* 230 User ... logged in. (the user logged in with or without password) */ result = ftp_state_loggedin(data, ftpc); @@ -2659,9 +3016,8 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, 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]); + 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); @@ -2699,8 +3055,8 @@ static CURLcode ftp_pwd_resp(struct Curl_easy *data, CURLcode result; if(ftpcode == 257) { - char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first - letter */ + 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); @@ -2734,10 +3090,18 @@ static CURLcode ftp_pwd_resp(struct Curl_easy *data, break; /* get out of this loop */ } } - else + else { + if(ISCNTRL(*ptr)) { + /* control characters have no business in a path */ + curlx_dyn_free(&out); + return CURLE_WEIRD_SERVER_REPLY; + } result = curlx_dyn_addn(&out, ptr, 1); - if(result) + } + if(result) { + curlx_dyn_free(&out); return result; + } } } if(entry_extracted) { @@ -2757,29 +3121,24 @@ static CURLcode ftp_pwd_resp(struct Curl_easy *data, if(!ftpc->server_os && dir[0] != '/') { result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST"); if(result) { - free(dir); + curlx_dyn_free(&out); return result; } - free(ftpc->entrypath); - ftpc->entrypath = dir; /* remember this */ - infof(data, "Entry path is '%s'", ftpc->entrypath); - /* also save it where getinfo can access it: */ - free(data->state.most_recent_ftp_entrypath); - data->state.most_recent_ftp_entrypath = strdup(ftpc->entrypath); - if(!data->state.most_recent_ftp_entrypath) - return CURLE_OUT_OF_MEMORY; - ftp_state(data, ftpc, FTP_SYST); - return result; } - free(ftpc->entrypath); + 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: */ - free(data->state.most_recent_ftp_entrypath); - data->state.most_recent_ftp_entrypath = strdup(ftpc->entrypath); + 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; + } } else { /* could not get the path */ @@ -2812,25 +3171,6 @@ static CURLcode ftp_wait_resp(struct Curl_easy *data, return CURLE_WEIRD_SERVER_REPLY; } - /* We have received a 220 response fine, now we proceed. */ -#ifdef HAVE_GSSAPI - if(data->set.krb) { - /* If not anonymous login, try a secure login. Note that this - procedure is still BLOCKING. */ - - Curl_sec_request_prot(conn, "private"); - /* We set private first as default, in case the line below fails to - set a valid level */ - Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]); - - if(Curl_sec_login(data, conn)) { - failf(data, "secure login failed"); - return CURLE_WEIRD_SERVER_REPLY; - } - infof(data, "Authentication successful"); - } -#endif - 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 */ @@ -2851,8 +3191,7 @@ static CURLcode ftp_wait_resp(struct Curl_easy *data, (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]); + result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]); if(!result) ftp_state(data, ftpc, FTP_AUTH); } @@ -2952,10 +3291,9 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, break; case FTP_PROT: - if(ftpcode/100 == 2) + if(ftpcode / 100 == 2) /* We have enabled SSL for the data connection! */ - conn->bits.ftp_use_data_ssl = - (data->set.use_ssl != CURLUSESSL_CONTROL); + 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) @@ -2998,10 +3336,10 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, case FTP_SYST: if(ftpcode == 215) { - char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first - letter */ + const char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first + letter */ + const char *start; char *os; - char *start; /* Reply format is like 215 @@ -3010,7 +3348,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, ptr++; for(start = ptr; *ptr && *ptr != ' '; ptr++) ; - os = Curl_memdup0(start, ptr - start); + os = curlx_memdup0(start, ptr - start); if(!os) return CURLE_OUT_OF_MEMORY; @@ -3019,18 +3357,18 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, /* Force OS400 name format 1. */ result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1"); if(result) { - free(os); + curlx_free(os); return result; } /* remember target server OS */ - free(ftpc->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 */ - free(ftpc->server_os); + curlx_free(ftpc->server_os); ftpc->server_os = os; } else { @@ -3067,7 +3405,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, break; case FTP_CWD: - if(ftpcode/100 != 2) { + if(ftpcode / 100 != 2) { /* failure to CWD there */ if(data->set.ftp_create_missing_dirs && ftpc->cwdcount && !ftpc->count2) { @@ -3109,8 +3447,8 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, break; case FTP_MKD: - if((ftpcode/100 != 2) && !ftpc->count3--) { - /* failure to MKD the dir */ + if((ftpcode / 100 != 2) && !ftpc->count3--) { + /* failure to MKD the directory */ failf(data, "Failed to MKD dir: %03d", ftpcode); result = CURLE_REMOTE_ACCESS_DENIED; } @@ -3169,7 +3507,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, break; case FTP_STOR: - result = ftp_state_stor_resp(data, ftpc, ftpcode, ftpc->state); + result = ftp_state_stor_resp(data, ftpc, ftpcode); break; case FTP_QUIT: @@ -3182,22 +3520,6 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, return result; } - -/* called repeatedly until done from multi.c */ -static CURLcode ftp_statemach(struct Curl_easy *data, - struct ftp_conn *ftpc, - bool *done) -{ - CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE); - - /* 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); - - return result; -} - /* called repeatedly until done from multi.c */ static CURLcode ftp_multi_statemach(struct Curl_easy *data, bool *done) @@ -3243,9 +3565,6 @@ static CURLcode ftp_connect(struct Curl_easy *data, if(!ftpc) return CURLE_FAILED_INIT; pp = &ftpc->pp; - /* We always support persistent connections on ftp */ - connkeep(conn, "FTP default"); - PINGPONG_SETUP(pp, ftp_pp_statemachine, ftp_endofresp); if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { @@ -3256,7 +3575,7 @@ static CURLcode ftp_connect(struct Curl_easy *data, conn->bits.ftp_use_control_ssl = TRUE; } - Curl_pp_init(pp); /* once per transfer */ + Curl_pp_init(pp, Curl_pgrs_now(data)); /* once per transfer */ /* When we connect, we start in the state where we await the 220 response */ @@ -3267,6 +3586,61 @@ static CURLcode ftp_connect(struct Curl_easy *data, 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; + + item = quote; + while(item) { + if(item->data) { + size_t nread; + const char *cmd = item->data; + bool acceptfail = FALSE; + CURLcode result; + int ftpcode = 0; + + /* 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. */ + + if(cmd[0] == '*') { + cmd++; + acceptfail = TRUE; + } + + 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; + + if(!acceptfail && (ftpcode >= 400)) { + failf(data, "QUOT string not accepted: %s", cmd); + return CURLE_QUOTE_ERROR; + } + } + + item = item->next; + } + + return CURLE_OK; +} + /*********************************************************************** * * ftp_done() @@ -3283,7 +3657,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, 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; - ssize_t nread; + size_t nread; int ftpcode; CURLcode result = CURLE_OK; @@ -3338,7 +3712,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, * the error path) */ ftpc->ctl_valid = FALSE; /* mark control connection as bad */ connclose(conn, "FTP: out of memory!"); /* mark for connection closure */ - free(ftpc->prevpath); + curlx_free(ftpc->prevpath); ftpc->prevpath = NULL; /* no path remembering */ } else { /* remember working directory for connection reuse */ @@ -3349,30 +3723,26 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, else { size_t pathLen = strlen(ftpc->rawpath); - free(ftpc->prevpath); + 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 */ + /* file is URL-decoded */ pathLen -= ftpc->file ? strlen(ftpc->file) : 0; - ftpc->prevpath = Curl_memdup0(rawPath, pathLen); + ftpc->prevpath = curlx_memdup0(rawPath, pathLen); } else ftpc->prevpath = NULL; /* no path */ } } if(ftpc->prevpath) - infof(data, "Remembering we are in dir \"%s\"", ftpc->prevpath); + infof(data, "Remembering we are in directory \"%s\"", ftpc->prevpath); } /* shut down the socket to inform the server we are done */ -#ifdef UNDER_CE - shutdown(conn->sock[SECONDARYSOCKET], 2); /* SD_BOTH */ -#endif - if(Curl_conn_is_setup(conn, SECONDARYSOCKET)) { if(!result && ftpc->dont_check && data->req.maxdownload > 0) { /* partial download completed */ @@ -3391,21 +3761,15 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, 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 + * 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. */ - timediff_t old_time = pp->response_time; + pp->response = *Curl_pgrs_now(data); /* timeout relative now */ + result = getftpresponse(data, &nread, &ftpcode); - pp->response_time = 60*1000; /* give it only a minute for now */ - pp->response = curlx_now(); /* timeout relative now */ - - result = Curl_GetFTPResponse(data, &nread, &ftpcode); - - pp->response_time = old_time; /* set this back to previous value */ - - if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) { + 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 */ @@ -3415,7 +3779,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, return result; if(ftpc->dont_check && data->req.maxdownload > 0) { - /* we have just sent ABOR and there is no reliable way to check if it was + /* 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"); @@ -3485,75 +3849,6 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, 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; - - item = quote; - while(item) { - if(item->data) { - ssize_t nread; - char *cmd = item->data; - bool acceptfail = FALSE; - CURLcode result; - int ftpcode = 0; - - /* 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. */ - - if(cmd[0] == '*') { - cmd++; - acceptfail = TRUE; - } - - result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd); - if(!result) { - pp->response = curlx_now(); /* timeout relative now */ - result = Curl_GetFTPResponse(data, &nread, &ftpcode); - } - if(result) - return result; - - if(!acceptfail && (ftpcode >= 400)) { - failf(data, "QUOT string not accepted: %s", cmd); - return CURLE_QUOTE_ERROR; - } - } - - item = item->next; - } - - 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'); -} - /*********************************************************************** * * ftp_nb_type() @@ -3585,195 +3880,6 @@ static CURLcode ftp_nb_type(struct Curl_easy *data, 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) -{ - 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 || !ftp) - return CURLE_FAILED_INIT; - /* 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 || (!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); - } - *completep = (int)complete; - 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 */ - - if(ftpc->wait_data_conn) { - bool serv_conned; - - result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &serv_conned); - if(result) - return result; /* Failed to accept data connection */ - - if(serv_conned) { - /* It looks data connection is established */ - ftpc->wait_data_conn = FALSE; - result = ftp_initiate_transfer(data, ftpc); - - if(result) - return result; - - *completep = 1; /* this state is now complete when the server has - connected back to us */ - } - else { - result = ftp_check_ctrl_on_data_wait(data, ftpc); - if(result) - return result; - } - } - 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 { - /* download */ - ftp->downloadsize = -1; /* unknown as of yet */ - - result = Curl_range(data); - - if(result == CURLE_OK && data->req.maxdownload >= 0) { - /* Do not check for successful transfer */ - ftpc->dont_check = TRUE; - } - - 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. */ - - /* 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; - } - - result = ftp_statemach(data, ftpc, &complete); - *completep = (int)complete; - } - 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() @@ -3781,13 +3887,12 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) * 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) +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; @@ -3829,7 +3934,7 @@ static void wc_data_dtor(void *ptr) struct ftp_wc *ftpwc = ptr; if(ftpwc && ftpwc->parser) Curl_ftp_parselist_data_free(&ftpwc->parser); - free(ftpwc); + curlx_free(ftpwc); } static CURLcode init_wc_data(struct Curl_easy *data, @@ -3849,14 +3954,14 @@ static CURLcode init_wc_data(struct Curl_easy *data, wildcard->state = CURLWC_CLEAN; return ftp_parse_url_path(data, ftpc, ftp); } - wildcard->pattern = strdup(last_slash); + 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 = strdup(path); + wildcard->pattern = curlx_strdup(path); if(!wildcard->pattern) return CURLE_OUT_OF_MEMORY; path[0] = '\0'; @@ -3871,7 +3976,7 @@ static CURLcode init_wc_data(struct Curl_easy *data, resources for wildcard transfer */ /* allocate ftp protocol specific wildcard data */ - ftpwc = calloc(1, sizeof(struct ftp_wc)); + ftpwc = curlx_calloc(1, sizeof(struct ftp_wc)); if(!ftpwc) { result = CURLE_OUT_OF_MEMORY; goto fail; @@ -3897,7 +4002,7 @@ static CURLcode init_wc_data(struct Curl_easy *data, goto fail; } - wildcard->path = strdup(ftp->path); + wildcard->path = curlx_strdup(ftp->path); if(!wildcard->path) { result = CURLE_OUT_OF_MEMORY; goto fail; @@ -3918,9 +4023,9 @@ static CURLcode init_wc_data(struct Curl_easy *data, fail: if(ftpwc) { Curl_ftp_parselist_data_free(&ftpwc->parser); - free(ftpwc); + curlx_free(ftpwc); } - Curl_safefree(wildcard->pattern); + curlx_safefree(wildcard->pattern); wildcard->dtor = ZERO_NULL; wildcard->ftpwc = NULL; return result; @@ -3971,12 +4076,12 @@ static CURLcode wc_statemach(struct Curl_easy *data, struct Curl_llist_node *head = Curl_llist_head(&wildcard->filelist); struct curl_fileinfo *finfo = Curl_node_elem(head); - char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename); + 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 */ - free(ftp->pathalloc); + curlx_free(ftp->pathalloc); ftp->pathalloc = ftp->path = tmp_path; infof(data, "Wildcard - START of \"%s\"", finfo->filename); @@ -3989,8 +4094,7 @@ static CURLcode wc_statemach(struct Curl_easy *data, Curl_set_in_callback(data, FALSE); switch(userresponse) { case CURL_CHUNK_BGN_FUNC_SKIP: - infof(data, "Wildcard - \"%s\" skipped by user", - finfo->filename); + infof(data, "Wildcard - \"%s\" skipped by user", finfo->filename); wildcard->state = CURLWC_SKIP; continue; case CURL_CHUNK_BGN_FUNC_FAIL: @@ -4058,6 +4162,52 @@ static CURLcode wc_statemach(struct Curl_easy *data, /* UNREACHABLE */ } +/*********************************************************************** + * + * ftp_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. + * + * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the + * ftp_done() function without finding any major problem. + */ +static CURLcode ftp_regular_transfer(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + bool *dophase_done) +{ + CURLcode result = CURLE_OK; + bool connected = FALSE; + data->req.size = -1; /* make sure this is unknown at this point */ + + Curl_pgrsReset(data); + + ftpc->ctl_valid = TRUE; /* starts good */ + + result = ftp_perform(data, ftpc, ftp, + &connected, /* have we connected after PASV/PORT */ + dophase_done); /* all commands in the DO-phase done? */ + + if(!result) { + + if(!*dophase_done) + /* the DO phase has not completed yet */ + return CURLE_OK; + + result = ftp_dophase_done(data, ftpc, ftp, connected); + + if(result) + return result; + } + else + freedirs(ftpc); + + return result; +} + /*********************************************************************** * * ftp_do() @@ -4172,7 +4322,7 @@ static CURLcode ftp_disconnect(struct Curl_easy *data, 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. + will try to send the QUIT command, otherwise it will return. */ ftpc->shutdown = TRUE; if(dead_connection || Curl_pp_needs_flush(data, &ftpc->pp)) @@ -4183,190 +4333,6 @@ static CURLcode ftp_disconnect(struct Curl_easy *data, 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; -} - -/*********************************************************************** - * - * ftp_parse_url_path() - * - * Parse the URL path into separate path components. - * - */ -static -CURLcode ftp_parse_url_path(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp) -{ - const char *slashPos = NULL; - const char *fileName = NULL; - CURLcode result = CURLE_OK; - const char *rawPath = NULL; /* url-decoded "raw" path */ - size_t pathLen = 0; - - ftpc->ctl_valid = FALSE; - ftpc->cwdfail = FALSE; - - 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; - - 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 = 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 dir */ - fileName = slashPos + 1; /* rest is filename */ - } - else - fileName = rawPath; /* filename only (or empty) */ - break; - - default: /* allow pretty much anything */ - case FTPFILE_MULTICWD: { - /* current position: begin of next path component */ - const char *curPos = rawPath; - - /* number of entries to allocate for the 'dirs' array */ - size_t dirAlloc = numof_slashes(rawPath); - - if(dirAlloc >= 1000) - /* suspiciously deep dir hierarchy */ - return CURLE_URL_MALFORMAT; - - if(dirAlloc) { - ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0])); - if(!ftpc->dirs) - return CURLE_OUT_OF_MEMORY; - - /* parse the URL path into separate path components */ - while(dirAlloc--) { - const char *spos = strchr(curPos, '/'); - size_t clen = spos - curPos; - - /* path starts with a slash: add that as a directory */ - if(!clen && (ftpc->dirdepth == 0)) - ++clen; - - /* 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 */ - - if(fileName && *fileName) - ftpc->file = fileName; - else - ftpc->file = NULL; /* instead of point to a zero byte, - we make it a NULL pointer */ - - 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; - } - - ftpc->cwddone = FALSE; /* default to not done */ - - 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 CURLE_OK; -} - -/* 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; - } - } - - 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; - - ftpc->ctl_valid = TRUE; /* seems good */ - - return CURLE_OK; -} - /* called from multi.c while DOing */ static CURLcode ftp_doing(struct Curl_easy *data, bool *dophase_done) @@ -4389,63 +4355,13 @@ static CURLcode ftp_doing(struct Curl_easy *data, return result; } -/*********************************************************************** - * - * ftp_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. - * - * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the - * ftp_done() function without finding any major problem. - */ -static -CURLcode ftp_regular_transfer(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - bool connected = FALSE; - data->req.size = -1; /* make sure this is unknown at this point */ - - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - ftpc->ctl_valid = TRUE; /* starts good */ - - result = ftp_perform(data, ftpc, ftp, - &connected, /* have we connected after PASV/PORT */ - dophase_done); /* all commands in the DO-phase done? */ - - if(!result) { - - if(!*dophase_done) - /* the DO phase has not completed yet */ - return CURLE_OK; - - result = ftp_dophase_done(data, ftpc, ftp, connected); - - if(result) - return result; - } - else - freedirs(ftpc); - - return result; -} - static void ftp_easy_dtor(void *key, size_t klen, void *entry) { struct FTP *ftp = entry; (void)key; (void)klen; - Curl_safefree(ftp->pathalloc); - free(ftp); + curlx_safefree(ftp->pathalloc); + curlx_free(ftp); } static void ftp_conn_dtor(void *key, size_t klen, void *entry) @@ -4454,64 +4370,25 @@ static void ftp_conn_dtor(void *key, size_t klen, void *entry) (void)key; (void)klen; freedirs(ftpc); - Curl_safefree(ftpc->account); - Curl_safefree(ftpc->alternative_to_user); - Curl_safefree(ftpc->entrypath); - Curl_safefree(ftpc->prevpath); - Curl_safefree(ftpc->server_os); + curlx_safefree(ftpc->account); + curlx_safefree(ftpc->alternative_to_user); + curlx_safefree(ftpc->entrypath); + curlx_safefree(ftpc->prevpath); + curlx_safefree(ftpc->server_os); Curl_pp_disconnect(&ftpc->pp); - free(ftpc); + curlx_free(ftpc); } -static CURLcode ftp_setup_connection(struct Curl_easy *data, - struct connectdata *conn) +static void type_url_check(struct Curl_easy *data, struct FTP *ftp) { - char *type; - struct FTP *ftp; - CURLcode result = CURLE_OK; - struct ftp_conn *ftpc; - - ftp = calloc(1, sizeof(*ftp)); - if(!ftp || - Curl_meta_set(data, CURL_META_FTP_EASY, ftp, ftp_easy_dtor)) - return CURLE_OUT_OF_MEMORY; - - ftpc = calloc(1, sizeof(*ftpc)); - if(!ftpc || - Curl_conn_meta_set(conn, CURL_META_FTP_CONN, ftpc, ftp_conn_dtor)) - return CURLE_OUT_OF_MEMORY; - - /* clone connection related data that is FTP specific */ - if(data->set.str[STRING_FTP_ACCOUNT]) { - ftpc->account = strdup(data->set.str[STRING_FTP_ACCOUNT]); - if(!ftpc->account) { - Curl_conn_meta_remove(conn, CURL_META_FTP_CONN); - return CURLE_OUT_OF_MEMORY; - } - } - if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]) { - ftpc->alternative_to_user = - strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); - if(!ftpc->alternative_to_user) { - Curl_safefree(ftpc->account); - Curl_conn_meta_remove(conn, CURL_META_FTP_CONN); - return CURLE_OUT_OF_MEMORY; - } - } - - ftp->path = &data->state.up.path[1]; /* do not include the initial slash */ - + size_t len = strlen(ftp->path); /* FTP URLs support an extension like ";type=" that * we will try to get now! */ - type = strstr(ftp->path, ";type="); + if((len >= 7) && !memcmp(&ftp->path[len - 7], ";type=", 6)) { + char *type = &ftp->path[len - 7]; + char command = Curl_raw_toupper(type[6]); - if(!type) - type = strstr(conn->host.rawalloc, ";type="); - - if(type) { - char command; - *type = 0; /* it was in the middle of the hostname */ - command = Curl_raw_toupper(type[6]); + *type = 0; /* cut it off */ switch(command) { case 'A': /* ASCII mode */ @@ -4529,6 +4406,46 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, break; } } +} + +static CURLcode ftp_setup_connection(struct Curl_easy *data, + struct connectdata *conn) +{ + struct FTP *ftp; + CURLcode result = CURLE_OK; + struct ftp_conn *ftpc; + + ftp = curlx_calloc(1, sizeof(*ftp)); + if(!ftp || + Curl_meta_set(data, CURL_META_FTP_EASY, ftp, ftp_easy_dtor)) + return CURLE_OUT_OF_MEMORY; + + ftpc = curlx_calloc(1, sizeof(*ftpc)); + if(!ftpc || + Curl_conn_meta_set(conn, CURL_META_FTP_CONN, ftpc, ftp_conn_dtor)) + return CURLE_OUT_OF_MEMORY; + + /* clone connection related data that is FTP specific */ + if(data->set.str[STRING_FTP_ACCOUNT]) { + ftpc->account = curlx_strdup(data->set.str[STRING_FTP_ACCOUNT]); + if(!ftpc->account) { + Curl_conn_meta_remove(conn, CURL_META_FTP_CONN); + return CURLE_OUT_OF_MEMORY; + } + } + if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]) { + ftpc->alternative_to_user = + curlx_strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); + if(!ftpc->alternative_to_user) { + curlx_safefree(ftpc->account); + Curl_conn_meta_remove(conn, CURL_META_FTP_CONN); + return CURLE_OUT_OF_MEMORY; + } + } + + ftp->path = &data->state.up.path[1]; /* do not include the initial slash */ + + type_url_check(data, ftp); /* get some initial data into the ftp struct */ ftp->transfer = PPTRANSFER_BODY; @@ -4556,4 +4473,27 @@ bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn) return TRUE; } +/* + * FTP protocol. + */ +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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* CURL_DISABLE_FTP */ diff --git a/lib/ftp.h b/lib/ftp.h index aba1db7f2d..257f595826 100644 --- a/lib/ftp.h +++ b/lib/ftp.h @@ -23,82 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#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 - -CURLcode Curl_GetFTPResponse(struct Curl_easy *data, ssize_t *nread, - int *ftpcode); +extern const struct Curl_protocol Curl_protocol_ftp; bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn); -#endif /* CURL_DISABLE_FTP */ - -/**************************************************************************** - * FTP unique setup - ***************************************************************************/ -enum { - FTP_STOP, /* do nothing state, stops the state machine */ - FTP_WAIT220, /* waiting for the initial 220 response immediately after - a connect */ - FTP_AUTH, - FTP_USER, - FTP_PASS, - FTP_ACCT, - FTP_PBSZ, - FTP_PROT, - FTP_CCC, - FTP_PWD, - FTP_SYST, - FTP_NAMEFMT, - FTP_QUOTE, /* waiting for a response to a command sent in a quote list */ - FTP_RETR_PREQUOTE, - FTP_STOR_PREQUOTE, - FTP_LIST_PREQUOTE, - FTP_POSTQUOTE, - FTP_CWD, /* change dir */ - FTP_MKD, /* if the dir did not exist */ - FTP_MDTM, /* to figure out the datestamp */ - FTP_TYPE, /* to set type when doing a head-like request */ - FTP_LIST_TYPE, /* set type when about to do a dir list */ - FTP_RETR_LIST_TYPE, - FTP_RETR_TYPE, /* set type when about to RETR a file */ - FTP_STOR_TYPE, /* set type when about to STOR a file */ - FTP_SIZE, /* get the remote file's size for head-like request */ - FTP_RETR_SIZE, /* get the remote file's size for RETR */ - FTP_STOR_SIZE, /* get the size for STOR */ - FTP_REST, /* when used to check if the server supports it in head-like */ - FTP_RETR_REST, /* when asking for "resume" in for RETR */ - FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */ - FTP_PRET, /* generic state for PRET RETR, PRET STOR and PRET LIST/NLST */ - FTP_PASV, /* generic state for PASV and EPSV, check count1 */ - FTP_LIST, /* generic state for LIST, NLST or a custom list command */ - FTP_RETR, - FTP_STOR, /* generic state for STOR and APPE */ - FTP_QUIT, - FTP_LAST /* never used */ -}; -typedef unsigned char ftpstate; /* use the enum values */ - -struct ftp_parselist_data; /* defined later in ftplistparser.c */ - -struct ftp_wc { - struct ftp_parselist_data *parser; - - struct { - curl_write_callback write_function; - FILE *file_descriptor; - } backup; -}; - typedef enum { FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */ FTPFILE_NOCWD = 2, /* use SIZE / RETR / STOR on the full path */ @@ -106,78 +37,7 @@ typedef enum { file */ } curl_ftpfile; -/* This FTP struct is used in the Curl_easy. All FTP data that is - connection-oriented must be in FTP_conn to properly deal with the fact that - perhaps the Curl_easy is changed between the times the connection is - used. */ -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 */ - curl_pp_transfer transfer; - curl_off_t downloadsize; -}; - -/* one struct entry for each path component (of 'rawpath') */ -struct pathcomp { - int start; /* start column */ - int len; /* length in bytes */ -}; - -/* ftp_conn is used for struct connection-oriented data in the connectdata - struct */ -struct ftp_conn { - struct pingpong pp; - char *account; - char *alternative_to_user; - char *entrypath; /* the PWD reply when we logged on */ - const char *file; /* url-decoded filename (or path), points into rawpath */ - char *rawpath; /* URL decoded, allocated, version of the path */ - struct pathcomp *dirs; /* allocated array for path components */ - char *newhost; /* the (allocated) IP addr or hostname to connect the data - connection to */ - char *prevpath; /* url-decoded conn->path from the previous transfer */ - char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a - and others (A/I or zero) */ - curl_off_t retr_size_saved; /* Size of retrieved file saved */ - char *server_os; /* The target server operating system. */ - curl_off_t known_filesize; /* file size is different from -1, if wildcard - LIST parsing was done and wc_statemach set - it */ - int dirdepth; /* number of entries used in the 'dirs' array */ - int cwdcount; /* number of CWD commands issued */ - int count1; /* general purpose counter for the state machine */ - int count2; /* general purpose counter for the state machine */ - int count3; /* general purpose counter for the state machine */ - unsigned short newport; /* the port of 'newhost' to connect the data - connection to */ - ftpstate state; /* always use ftp.c:state() to change state! */ - ftpstate state_saved; /* transfer type saved to be reloaded after data - connection is established */ - unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or - IMAP or POP3 or others! (type: curl_usessl)*/ - unsigned char ccc; /* ccc level for this connection */ - BIT(ftp_trying_alternative); - BIT(dont_check); /* Set to TRUE to prevent the final (post-transfer) - file size and 226/250 status check. It should still - read the line, just 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() */ - BIT(cwddone); /* if it has been determined that the proper CWD combo - already has been done */ - BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent - caching the current directory */ - BIT(wait_data_conn); /* this is set TRUE if data connection is waited */ - BIT(shutdown); /* connection is being shutdown, e.g. QUIT */ -}; - -/* meta key for storing `struct FTP` as easy meta data */ -#define CURL_META_FTP_EASY "meta:proto:ftp:easy" -/* meta key for storing `struct ftp_conn` as connection meta data */ -#define CURL_META_FTP_CONN "meta:proto:ftp:conn" +#endif /* CURL_DISABLE_FTP */ #define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */ diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c index 360f7ae4fc..b5d9338c1b 100644 --- a/lib/ftplistparser.c +++ b/lib/ftplistparser.c @@ -21,6 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" + +#ifndef CURL_DISABLE_FTP /** * Now implemented: @@ -37,26 +40,16 @@ * 01-29-97 11:32PM prog */ -#include "curl_setup.h" - -#ifndef CURL_DISABLE_FTP - -#include - #include "urldata.h" #include "fileinfo.h" #include "llist.h" #include "ftp.h" +#include "ftp-int.h" #include "ftplistparser.h" #include "curl_fnmatch.h" #include "multiif.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - typedef enum { PL_UNIX_TOTALSIZE = 0, PL_UNIX_FILETYPE, @@ -186,12 +179,10 @@ static void fileinfo_dtor(void *user, void *element) Curl_fileinfo_cleanup(element); } -CURLcode Curl_wildcard_init(struct WildcardData *wc) +void Curl_wildcard_init(struct WildcardData *wc) { Curl_llist_init(&wc->filelist, fileinfo_dtor); wc->state = CURLWC_INIT; - - return CURLE_OK; } void Curl_wildcard_dtor(struct WildcardData **wcp) @@ -208,37 +199,34 @@ void Curl_wildcard_dtor(struct WildcardData **wcp) DEBUGASSERT(wc->ftpwc == NULL); Curl_llist_destroy(&wc->filelist, NULL); - free(wc->path); + curlx_free(wc->path); wc->path = NULL; - free(wc->pattern); + curlx_free(wc->pattern); wc->pattern = NULL; wc->state = CURLWC_INIT; - free(wc); + curlx_free(wc); *wcp = NULL; } struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void) { - return calloc(1, sizeof(struct ftp_parselist_data)); + return curlx_calloc(1, sizeof(struct ftp_parselist_data)); } - void Curl_ftp_parselist_data_free(struct ftp_parselist_data **parserp) { struct ftp_parselist_data *parser = *parserp; if(parser) Curl_fileinfo_cleanup(parser->file_data); - free(parser); + curlx_free(parser); *parserp = NULL; } - CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data) { return pl_data->error; } - #define FTP_LP_MALFORMATED_PERM 0x01000000 static unsigned int ftp_pl_get_permission(const char *str) @@ -291,7 +279,7 @@ static unsigned int ftp_pl_get_permission(const char *str) if(str[7] == 'w') permissions |= 1 << 1; else if(str[7] != '-') - permissions |= FTP_LP_MALFORMATED_PERM; + permissions |= FTP_LP_MALFORMATED_PERM; if(str[8] == 'x') permissions |= 1; else if(str[8] == 't') { @@ -337,8 +325,7 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data, /* filter pattern-corresponding filenames */ Curl_set_in_callback(data, TRUE); - if(compare(data->set.fnmatch_data, wc->pattern, - finfo->filename) == 0) { + if(compare(data->set.fnmatch_data, wc->pattern, finfo->filename) == 0) { /* discard symlink which is containing multiple " -> " */ if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target && (strstr(finfo->strings.target, " -> "))) { @@ -437,7 +424,6 @@ static CURLcode parse_unix_totalsize(struct ftp_parselist_data *parser, } else return CURLE_FTP_BAD_FILE_LIST; - } break; } @@ -494,7 +480,7 @@ static CURLcode parse_unix_hlinks(struct ftp_parselist_data *parser, } break; case PL_UNIX_HLINKS_NUMBER: - parser->item_length ++; + parser->item_length++; if(c == ' ') { const char *p = &mem[parser->item_offset]; curl_off_t hlinks; @@ -630,7 +616,7 @@ static CURLcode parse_unix_time(struct ftp_parselist_data *parser, case PL_UNIX_TIME_PREPART1: if(c != ' ') { if(ISALNUM(c) && len) { - parser->item_offset = len -1; + parser->item_offset = len - 1; parser->item_length = 1; parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1; } @@ -675,7 +661,7 @@ static CURLcode parse_unix_time(struct ftp_parselist_data *parser, case PL_UNIX_TIME_PART3: parser->item_length++; if(c == ' ') { - mem[parser->item_offset + parser->item_length -1] = 0; + mem[parser->item_offset + parser->item_length - 1] = 0; parser->offsets.time = parser->item_offset; if(finfo->filetype == CURLFILETYPE_SYMLINK) { parser->state.UNIX.main = PL_UNIX_SYMLINK; @@ -931,7 +917,7 @@ static CURLcode parse_winnt(struct Curl_easy *data, case PL_WINNT_TIME_TIME: if(c == ' ') { parser->offsets.time = parser->item_offset; - mem[parser->item_offset + parser->item_length -1] = 0; + mem[parser->item_offset + parser->item_length - 1] = 0; parser->state.NT.main = PL_WINNT_DIRORSIZE; parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE; parser->item_length = 0; @@ -951,7 +937,7 @@ static CURLcode parse_winnt(struct Curl_easy *data, } break; case PL_WINNT_DIRORSIZE_CONTENT: - parser->item_length ++; + parser->item_length++; if(c == ' ') { mem[parser->item_offset + parser->item_length - 1] = 0; if(strcmp("", mem + parser->item_offset) == 0) { @@ -979,7 +965,7 @@ static CURLcode parse_winnt(struct Curl_easy *data, switch(parser->state.NT.sub.filename) { case PL_WINNT_FILENAME_PRESPACE: if(c != ' ' && len) { - parser->item_offset = len -1; + parser->item_offset = len - 1; parser->item_length = 1; parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT; } @@ -1027,7 +1013,7 @@ static CURLcode parse_winnt(struct Curl_easy *data, size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb, void *connptr) { - size_t bufflen = size*nmemb; + size_t bufflen = size * nmemb; struct Curl_easy *data = (struct Curl_easy *)connptr; struct ftp_wc *ftpwc = data->wildcard->ftpwc; struct ftp_parselist_data *parser = ftpwc->parser; @@ -1102,4 +1088,4 @@ fail: return retsize; } -#endif /* CURL_DISABLE_FTP */ +#endif /* !CURL_DISABLE_FTP */ diff --git a/lib/ftplistparser.h b/lib/ftplistparser.h index 5ba1f6a97d..5d7aa492ba 100644 --- a/lib/ftplistparser.h +++ b/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 { @@ -65,13 +65,13 @@ struct WildcardData { unsigned char state; /* wildcard_states */ }; -CURLcode Curl_wildcard_init(struct WildcardData *wc); +void Curl_wildcard_init(struct WildcardData *wc); void Curl_wildcard_dtor(struct WildcardData **wcp); struct Curl_easy; -#else -/* FTP is disabled */ +#else /* CURL_DISABLE_FTP */ #define Curl_wildcard_dtor(x) -#endif /* CURL_DISABLE_FTP */ +#endif /* !CURL_DISABLE_FTP */ + #endif /* HEADER_CURL_FTPLISTPARSER_H */ diff --git a/lib/functypes.h b/lib/functypes.h index b4dccc0ce4..887c2612ef 100644 --- a/lib/functypes.h +++ b/lib/functypes.h @@ -23,9 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "curl_setup.h" - /* defaults: ssize_t recv(int, void *, size_t, int); @@ -38,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 * @@ -60,15 +57,14 @@ #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 #endif - #ifndef RECV_TYPE_ARG1 #define RECV_TYPE_ARG1 int #endif @@ -89,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/lib/getenv.c b/lib/getenv.c index 3bfcf707a4..a957e6e801 100644 --- a/lib/getenv.c +++ b/lib/getenv.c @@ -21,17 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include -#include "curl_memory.h" - -#include "memdebug.h" - -static char *GetEnv(const char *variable) +char *curl_getenv(const char *variable) { -#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE) || \ +#if defined(CURL_WINDOWS_UWP) || \ defined(__ORBIS__) || defined(__PROSPERO__) /* PlayStation 4 and 5 */ (void)variable; return NULL; @@ -45,9 +39,9 @@ static char *GetEnv(const char *variable) const DWORD max = 32768; /* max env var size from MSCRT source */ for(;;) { - tmp = realloc(buf, rc); + tmp = curlx_realloc(buf, rc); if(!tmp) { - free(buf); + curlx_free(buf); return NULL; } @@ -58,7 +52,7 @@ static char *GetEnv(const char *variable) Since getenv does not make that distinction we ignore it as well. */ rc = GetEnvironmentVariableA(variable, buf, bufsize); if(!rc || rc == bufsize || rc > max) { - free(buf); + curlx_free(buf); return NULL; } @@ -70,11 +64,6 @@ static char *GetEnv(const char *variable) } #else char *env = getenv(variable); - return (env && env[0]) ? strdup(env) : NULL; + return (env && env[0]) ? curlx_strdup(env) : NULL; #endif } - -char *curl_getenv(const char *v) -{ - return GetEnv(v); -} diff --git a/lib/getinfo.c b/lib/getinfo.c index 7ff78d2d6d..fab63e669a 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -21,23 +21,16 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "urldata.h" #include "getinfo.h" #include "cfilters.h" #include "vtls/vtls.h" #include "connect.h" /* Curl_getconnectinfo() */ -#include "progress.h" +#include "bufref.h" #include "curlx/strparse.h" -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - /* * Initialize statistical and informational data. * @@ -45,7 +38,7 @@ * beginning of a perform session. It must reset the session-info variables, * in particular all variables in struct PureInfo. */ -CURLcode Curl_initinfo(struct Curl_easy *data) +void Curl_initinfo(struct Curl_easy *data) { struct Progress *pro = &data->progress; struct PureInfo *info = &data->info; @@ -74,32 +67,32 @@ CURLcode Curl_initinfo(struct Curl_easy *data) info->httpauthpicked = 0; info->numconnects = 0; - free(info->contenttype); + curlx_free(info->contenttype); info->contenttype = NULL; - free(info->wouldredirect); + curlx_free(info->wouldredirect); info->wouldredirect = NULL; memset(&info->primary, 0, sizeof(info->primary)); - info->primary.remote_port = -1; - info->primary.local_port = -1; info->retry_after = 0; info->conn_scheme = 0; info->conn_protocol = 0; + info->used_proxy = 0; #ifdef USE_SSL Curl_ssl_free_certinfo(data); #endif - return CURLE_OK; } static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info, const char **param_charp) { switch(info) { - case CURLINFO_EFFECTIVE_URL: - *param_charp = data->state.url ? data->state.url : ""; + case CURLINFO_EFFECTIVE_URL: { + const char *s = Curl_bufref_ptr(&data->state.url); + *param_charp = s ? s : ""; + } break; case CURLINFO_EFFECTIVE_METHOD: { const char *m = data->set.str[STRING_CUSTOMREQUEST]; @@ -135,12 +128,12 @@ 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. This pointer was copied from the connectdata structure by FTP. - The actual string may be free()ed by subsequent libcurl calls so + The actual string may be freed by subsequent libcurl calls so it must be copied to a safer area before the next libcurl call. Callers must never free it themselves. */ *param_charp = data->state.most_recent_ftp_entrypath; @@ -152,7 +145,7 @@ static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info, break; case CURLINFO_REFERER: /* Return the referrer header for this request, or NULL if unset */ - *param_charp = data->state.referer; + *param_charp = Curl_bufref_ptr(&data->state.referer); break; case CURLINFO_PRIMARY_IP: /* Return the ip address of the most recent (primary) connection */ @@ -205,31 +198,31 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, } lptr; #ifdef DEBUGBUILD - const char *timestr = getenv("CURL_TIME"); - if(timestr) { - curl_off_t val; - curlx_str_number(×tr, &val, TIME_T_MAX); - switch(info) { - case CURLINFO_LOCAL_PORT: - *param_longp = (long)val; - return CURLE_OK; - default: - break; - } - } + const char *envstr; + /* use another variable for this to allow different values */ - timestr = getenv("CURL_DEBUG_SIZE"); - if(timestr) { - curl_off_t val; - curlx_str_number(×tr, &val, LONG_MAX); - switch(info) { - case CURLINFO_HEADER_SIZE: - case CURLINFO_REQUEST_SIZE: + switch(info) { + case CURLINFO_LOCAL_PORT: + envstr = getenv("CURL_TIME"); + if(envstr) { + curl_off_t val; + curlx_str_number(&envstr, &val, TIME_T_MAX); *param_longp = (long)val; return CURLE_OK; - default: - break; } + break; + case CURLINFO_HEADER_SIZE: + case CURLINFO_REQUEST_SIZE: + envstr = getenv("CURL_DEBUG_SIZE"); + if(envstr) { + curl_off_t val; + curlx_str_number(&envstr, &val, LONG_MAX); + *param_longp = (long)val; + return CURLE_OK; + } + break; + default: + break; } #endif @@ -289,7 +282,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); @@ -305,11 +303,17 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, break; case CURLINFO_PRIMARY_PORT: /* Return the (remote) port of the most recent (primary) connection */ - *param_longp = data->info.primary.remote_port; + if(CUR_IP_QUAD_HAS_PORTS(&data->info.primary)) + *param_longp = data->info.primary.remote_port; + else + *param_longp = -1; break; case CURLINFO_LOCAL_PORT: /* Return the local port of the most recent (primary) connection */ - *param_longp = data->info.primary.local_port; + if(CUR_IP_QUAD_HAS_PORTS(&data->info.primary)) + *param_longp = data->info.primary.local_port; + else + *param_longp = -1; break; case CURLINFO_PROXY_ERROR: *param_longp = (long)data->info.pxcode; @@ -376,40 +380,44 @@ 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) { #ifdef DEBUGBUILD - const char *timestr = getenv("CURL_TIME"); - if(timestr) { - curl_off_t val; - curlx_str_number(×tr, &val, CURL_OFF_T_MAX); - - switch(info) { - case CURLINFO_TOTAL_TIME_T: - case CURLINFO_NAMELOOKUP_TIME_T: - case CURLINFO_CONNECT_TIME_T: - case CURLINFO_APPCONNECT_TIME_T: - case CURLINFO_PRETRANSFER_TIME_T: - case CURLINFO_POSTTRANSFER_TIME_T: - case CURLINFO_QUEUE_TIME_T: - case CURLINFO_STARTTRANSFER_TIME_T: - case CURLINFO_REDIRECT_TIME_T: - case CURLINFO_SPEED_DOWNLOAD_T: - case CURLINFO_SPEED_UPLOAD_T: + const char *envstr; + switch(info) { + case CURLINFO_TOTAL_TIME_T: + case CURLINFO_NAMELOOKUP_TIME_T: + case CURLINFO_CONNECT_TIME_T: + case CURLINFO_APPCONNECT_TIME_T: + case CURLINFO_PRETRANSFER_TIME_T: + case CURLINFO_POSTTRANSFER_TIME_T: + case CURLINFO_QUEUE_TIME_T: + case CURLINFO_STARTTRANSFER_TIME_T: + case CURLINFO_REDIRECT_TIME_T: + case CURLINFO_SPEED_DOWNLOAD_T: + case CURLINFO_SPEED_UPLOAD_T: + envstr = getenv("CURL_TIME"); + if(envstr) { + curl_off_t val; + curlx_str_number(&envstr, &val, CURL_OFF_T_MAX); *param_offt = (curl_off_t)val; return CURLE_OK; - default: - break; } + break; + default: + break; } #endif switch(info) { case CURLINFO_FILETIME_T: *param_offt = (curl_off_t)data->info.filetime; break; + case CURLINFO_SIZE_DELIVERED: + *param_offt = data->progress.deliver; + break; case CURLINFO_SIZE_UPLOAD_T: *param_offt = data->progress.ul.cur_size; break; @@ -481,26 +489,28 @@ static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info, double *param_doublep) { #ifdef DEBUGBUILD - const char *timestr = getenv("CURL_TIME"); - if(timestr) { - curl_off_t val; - curlx_str_number(×tr, &val, CURL_OFF_T_MAX); + const char *envstr; - switch(info) { - case CURLINFO_TOTAL_TIME: - case CURLINFO_NAMELOOKUP_TIME: - case CURLINFO_CONNECT_TIME: - case CURLINFO_APPCONNECT_TIME: - case CURLINFO_PRETRANSFER_TIME: - case CURLINFO_STARTTRANSFER_TIME: - case CURLINFO_REDIRECT_TIME: - case CURLINFO_SPEED_DOWNLOAD: - case CURLINFO_SPEED_UPLOAD: + switch(info) { + case CURLINFO_TOTAL_TIME: + case CURLINFO_NAMELOOKUP_TIME: + case CURLINFO_CONNECT_TIME: + case CURLINFO_APPCONNECT_TIME: + case CURLINFO_PRETRANSFER_TIME: + case CURLINFO_STARTTRANSFER_TIME: + case CURLINFO_REDIRECT_TIME: + case CURLINFO_SPEED_DOWNLOAD: + case CURLINFO_SPEED_UPLOAD: + envstr = getenv("CURL_TIME"); + if(envstr) { + curl_off_t val; + curlx_str_number(&envstr, &val, CURL_OFF_T_MAX); *param_doublep = (double)val; return CURLE_OK; - default: - break; } + break; + default: + break; } #endif switch(info) { @@ -575,20 +585,21 @@ static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info, *param_slistp = ptr.to_slist; break; case CURLINFO_TLS_SESSION: - case CURLINFO_TLS_SSL_PTR: - { - struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **) - param_slistp; - struct curl_tlssessioninfo *tsi = &data->tsi; + case CURLINFO_TLS_SSL_PTR: { + int query = (info == CURLINFO_TLS_SSL_PTR) ? + CF_QUERY_SSL_INFO : CF_QUERY_SSL_CTX_INFO; + struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **) + param_slistp; + struct curl_tlssessioninfo *tsi = &data->tsi; - /* we are exposing a pointer to internal memory with unknown - * lifetime here. */ - *tsip = tsi; - if(!Curl_conn_get_ssl_info(data, data->conn, FIRSTSOCKET, tsi)) { - tsi->backend = Curl_ssl_backend(); - tsi->internals = NULL; - } + /* we are exposing a pointer to internal memory with unknown + * lifetime here. */ + *tsip = tsi; + if(!Curl_conn_get_ssl_info(data, data->conn, FIRSTSOCKET, query, tsi)) { + tsi->backend = Curl_ssl_backend(); + tsi->internals = NULL; } + } break; default: return CURLE_UNKNOWN_OPTION; @@ -621,7 +632,7 @@ CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...) struct curl_slist **param_slistp = NULL; curl_socket_t *param_socketp = NULL; int type; - CURLcode result = CURLE_UNKNOWN_OPTION; + CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -661,6 +672,7 @@ CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...) result = getinfo_socket(data, info, param_socketp); break; default: + result = CURLE_UNKNOWN_OPTION; break; } diff --git a/lib/getinfo.h b/lib/getinfo.h index 56bb440b43..f5efe4185c 100644 --- a/lib/getinfo.h +++ b/lib/getinfo.h @@ -24,6 +24,6 @@ * ***************************************************************************/ CURLcode Curl_getinfo(struct Curl_easy *data, CURLINFO info, ...); -CURLcode Curl_initinfo(struct Curl_easy *data); +void Curl_initinfo(struct Curl_easy *data); #endif /* HEADER_CURL_GETINFO_H */ diff --git a/lib/gopher.c b/lib/gopher.c index 93db85e9d0..f087121d07 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -21,97 +21,22 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" +#include "urldata.h" +#include "gopher.h" #ifndef CURL_DISABLE_GOPHER -#include "urldata.h" -#include #include "transfer.h" #include "sendf.h" +#include "curl_trc.h" #include "cfilters.h" #include "connect.h" -#include "progress.h" -#include "gopher.h" #include "select.h" -#include "strdup.h" -#include "vtls/vtls.h" #include "url.h" #include "escape.h" -#include "curlx/warnless.h" -#include "curl_printf.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.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; @@ -138,13 +63,12 @@ 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; - char *sel = NULL; - char *sel_org = NULL; + 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; timediff_t timeout_ms; - ssize_t k; - size_t amount, len; int what; *done = TRUE; /* unconditionally */ @@ -153,57 +77,55 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) DEBUGASSERT(path); if(query) - gopherpath = aprintf("%s?%s", path, query); + gopherpath = curl_maprintf("%s?%s", path, query); else - gopherpath = strdup(path); + gopherpath = curlx_strdup(path); if(!gopherpath) return CURLE_OUT_OF_MEMORY; /* Create selector. Degenerate cases: / and /1 => convert to "" */ if(strlen(gopherpath) <= 2) { - sel = (char *)CURL_UNCONST(""); - len = strlen(sel); - free(gopherpath); + buf = ""; + buf_len = 0; + curlx_free(gopherpath); } else { - char *newp; + const char *newp; /* Otherwise, drop / and the first character (i.e., item type) ... */ newp = gopherpath; newp += 2; /* ... and finally unescape */ - result = Curl_urldecode(newp, 0, &sel, &len, REJECT_ZERO); - free(gopherpath); + result = Curl_urldecode(newp, 0, &buf_alloc, &buf_len, REJECT_ZERO); + curlx_free(gopherpath); if(result) return result; - sel_org = sel; + buf = buf_alloc; } - k = curlx_uztosz(len); + for(; buf_len;) { - for(;;) { - /* Break out of the loop if the selector is empty because OpenSSL and/or - LibreSSL fail with errno 0 if this is the case. */ - if(strlen(sel) < 1) - break; - - result = Curl_xfer_send(data, sel, k, FALSE, &amount); + result = Curl_xfer_send(data, buf, buf_len, FALSE, &nwritten); if(!result) { /* Which may not have written it all! */ - result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount); + result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, nwritten); if(result) break; - k -= amount; - sel += amount; - if(k < 1) + if(nwritten > buf_len) { + DEBUGASSERT(0); + break; + } + buf_len -= nwritten; + buf += nwritten; + if(!buf_len) break; /* but it did write it all */ } else break; - timeout_ms = Curl_timeleft(data, NULL, FALSE); + timeout_ms = Curl_timeleft_ms(data); if(timeout_ms < 0) { result = CURLE_OPERATION_TIMEDOUT; break; @@ -228,10 +150,10 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) } } - free(sel_org); + curlx_free(buf_alloc); if(!result) - result = Curl_xfer_send(data, "\r\n", 2, FALSE, &amount); + result = Curl_xfer_send(data, "\r\n", 2, FALSE, &nwritten); if(result) { failf(data, "Failed sending Gopher request"); return result; @@ -243,4 +165,53 @@ 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. + */ + +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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#ifdef USE_SSL +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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; +#endif + #endif /* CURL_DISABLE_GOPHER */ diff --git a/lib/gopher.h b/lib/gopher.h index 9e3365b71c..1c126be16a 100644 --- a/lib/gopher.h +++ b/lib/gopher.h @@ -23,12 +23,9 @@ * 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 +extern const struct Curl_protocol Curl_protocol_gopher; +extern const struct Curl_protocol Curl_protocol_gophers; #endif #endif /* HEADER_CURL_GOPHER_H */ diff --git a/lib/hash.c b/lib/hash.c index 89f47ed71e..3eae1d26a7 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -21,17 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "hash.h" -#include "llist.h" -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" /* random patterns for API verification */ #ifdef DEBUGBUILD @@ -39,10 +31,8 @@ #define ITERINIT 0x5FEDCBA9 #endif - #if 0 /* useful function for debugging hashes and their contents */ -void Curl_hash_print(struct Curl_hash *h, - void (*func)(void *)) +void Curl_hash_print(struct Curl_hash *h, void (*func)(void *)) { struct Curl_hash_iterator iter; struct Curl_hash_element *he; @@ -51,16 +41,16 @@ void Curl_hash_print(struct Curl_hash *h, if(!h) return; - fprintf(stderr, "=Hash dump=\n"); + curl_mfprintf(stderr, "=Hash dump=\n"); Curl_hash_start_iterate(h, &iter); he = Curl_hash_next_element(&iter); while(he) { if(iter.slot_index != last_index) { - fprintf(stderr, "index %d:", (int)iter.slot_index); + curl_mfprintf(stderr, "index %d:", (int)iter.slot_index); if(last_index != UINT_MAX) { - fprintf(stderr, "\n"); + curl_mfprintf(stderr, "\n"); } last_index = iter.slot_index; } @@ -68,13 +58,13 @@ void Curl_hash_print(struct Curl_hash *h, if(func) func(he->ptr); else - fprintf(stderr, " [key=%.*s, he=%p, ptr=%p]", - (int)he->key_len, (char *)he->key, - (void *)he, (void *)he->ptr); + curl_mfprintf(stderr, " [key=%.*s, he=%p, ptr=%p]", + (int)he->key_len, (char *)he->key, + (void *)he, (void *)he->ptr); he = Curl_hash_next_element(&iter); } - fprintf(stderr, "\n"); + curl_mfprintf(stderr, "\n"); } #endif @@ -84,12 +74,11 @@ void Curl_hash_print(struct Curl_hash *h, * @unittest: 1602 * @unittest: 1603 */ -void -Curl_hash_init(struct Curl_hash *h, - size_t slots, - hash_function hfunc, - comp_function comparator, - Curl_hash_dtor dtor) +void Curl_hash_init(struct Curl_hash *h, + size_t slots, + hash_function hfunc, + comp_function comparator, + Curl_hash_dtor dtor) { DEBUGASSERT(h); DEBUGASSERT(slots); @@ -108,14 +97,15 @@ Curl_hash_init(struct Curl_hash *h, #endif } -static struct Curl_hash_element * -hash_elem_create(const void *key, size_t key_len, const void *p, - Curl_hash_elem_dtor dtor) +static struct Curl_hash_element *hash_elem_create(const void *key, + size_t key_len, + const void *p, + Curl_hash_elem_dtor dtor) { struct Curl_hash_element *he; /* allocate the struct plus memory after it to store the key */ - he = malloc(sizeof(struct Curl_hash_element) + key_len); + he = curlx_malloc(sizeof(struct Curl_hash_element) + key_len); if(he) { he->next = NULL; /* copy the key */ @@ -145,7 +135,7 @@ static void hash_elem_destroy(struct Curl_hash *h, struct Curl_hash_element *he) { hash_elem_clear_ptr(h, he); - free(he); + curlx_free(he); } static void hash_elem_unlink(struct Curl_hash *h, @@ -165,8 +155,8 @@ 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_ADDR(x,y,z) &CURL_HASH_SLOT(x,y,z) +#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, Curl_hash_elem_dtor dtor) @@ -177,7 +167,7 @@ void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p, DEBUGASSERT(h->slots); DEBUGASSERT(h->init == HASHINIT); if(!h->table) { - h->table = calloc(h->slots, sizeof(struct Curl_hash_element *)); + h->table = curlx_calloc(h->slots, sizeof(struct Curl_hash_element *)); if(!h->table) return NULL; /* OOM */ } @@ -187,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; } @@ -209,8 +199,7 @@ void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p, * @unittest: 1602 * @unittest: 1603 */ -void * -Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p) +void *Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p) { return Curl_hash_add2(h, key, key_len, p, NULL); } @@ -246,8 +235,7 @@ int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len) * * @unittest: 1603 */ -void * -Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len) +void *Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len) { DEBUGASSERT(h); DEBUGASSERT(h->init == HASHINIT); @@ -272,13 +260,12 @@ Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len) * @unittest: 1602 * @unittest: 1603 */ -void -Curl_hash_destroy(struct Curl_hash *h) +void Curl_hash_destroy(struct Curl_hash *h) { DEBUGASSERT(h->init == HASHINIT); if(h->table) { Curl_hash_clean(h); - Curl_safefree(h->table); + curlx_safefree(h->table); } DEBUGASSERT(h->size == 0); h->slots = 0; @@ -312,9 +299,8 @@ size_t Curl_hash_count(struct Curl_hash *h) } /* Cleans all entries that pass the comp function criteria. */ -void -Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user, - int (*comp)(void *, void *)) +void Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user, + int (*comp)(void *, void *)) { size_t i; @@ -339,7 +325,7 @@ Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user, size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num) { - const char *key_str = (const char *) key; + const char *key_str = (const char *)key; const char *end = key_str + key_length; size_t h = 5381; @@ -373,8 +359,8 @@ void Curl_hash_start_iterate(struct Curl_hash *hash, #endif } -struct Curl_hash_element * -Curl_hash_next_element(struct Curl_hash_iterator *iter) +struct Curl_hash_element *Curl_hash_next_element( + struct Curl_hash_iterator *iter) { struct Curl_hash *h; DEBUGASSERT(iter->init == ITERINIT); diff --git a/lib/hash.h b/lib/hash.h index fcd92db664..1d7d3de8ce 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -23,25 +23,20 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - -#include "llist.h" - /* Hash function prototype */ -typedef size_t (*hash_function) (void *key, - size_t key_length, - size_t slots_num); +typedef size_t (*hash_function)(void *key, + size_t key_length, + size_t slots_num); /* Comparator function prototype. Compares two keys. */ -typedef size_t (*comp_function) (void *key1, - size_t key1_len, - void *key2, - size_t key2_len); +typedef size_t (*comp_function)(void *key1, + size_t key1_len, + void *key2, + size_t key2_len); typedef void (*Curl_hash_dtor)(void *); @@ -49,10 +44,10 @@ typedef void (*Curl_hash_elem_dtor)(void *key, size_t key_len, void *p); struct Curl_hash_element { struct Curl_hash_element *next; - void *ptr; + void *ptr; Curl_hash_elem_dtor dtor; size_t key_len; - char key[1]; /* allocated memory following the struct */ + char key[1]; /* allocated memory following the struct */ }; struct Curl_hash { @@ -63,7 +58,7 @@ struct Curl_hash { /* Comparator function to compare keys */ comp_function comp_func; /* General element construct, unless element itself carries one */ - Curl_hash_dtor dtor; + Curl_hash_dtor dtor; size_t slots; size_t size; #ifdef DEBUGBUILD @@ -90,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); @@ -102,10 +97,9 @@ size_t curlx_str_key_compare(void *k1, size_t key1_len, void *k2, size_t key2_len); void Curl_hash_start_iterate(struct Curl_hash *hash, struct Curl_hash_iterator *iter); -struct Curl_hash_element * -Curl_hash_next_element(struct Curl_hash_iterator *iter); +struct Curl_hash_element *Curl_hash_next_element( + struct Curl_hash_iterator *iter); -void Curl_hash_print(struct Curl_hash *h, - void (*func)(void *)); +void Curl_hash_print(struct Curl_hash *h, void (*func)(void *)); #endif /* HEADER_CURL_HASH_H */ diff --git a/lib/headers.c b/lib/headers.c index 426d0e57b7..195e12b371 100644 --- a/lib/headers.c +++ b/lib/headers.c @@ -21,19 +21,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #include "urldata.h" -#include "strdup.h" #include "sendf.h" +#include "curl_trc.h" #include "headers.h" -#include "curlx/strparse.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HEADERS_API) @@ -52,30 +45,30 @@ static void copy_header_external(struct Curl_header_store *hs, h->index = index; /* this will randomly OR a reserved bit for the sole purpose of making it impossible for applications to do == comparisons, as that would otherwise - be very tempting and then lead to the reserved bits not being reserved + be tempting and then lead to the reserved bits not being reserved anymore. */ h->origin = (unsigned int)(hs->type | (1 << 27)); h->anchor = e; } /* public API */ -CURLHcode curl_easy_header(CURL *easy, +CURLHcode curl_easy_header(CURL *curl, const char *name, size_t nameindex, - unsigned int type, + unsigned int origin, int request, struct curl_header **hout) { struct Curl_llist_node *e; struct Curl_llist_node *e_pick = NULL; - struct Curl_easy *data = easy; + struct Curl_easy *data = curl; size_t match = 0; size_t amount = 0; struct Curl_header_store *hs = NULL; struct Curl_header_store *pick = NULL; if(!name || !hout || !data || - (type > (CURLH_HEADER|CURLH_TRAILER|CURLH_CONNECT|CURLH_1XX| - CURLH_PSEUDO)) || !type || (request < -1)) + (origin > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX | + CURLH_PSEUDO)) || !origin || (request < -1)) return CURLHE_BAD_ARGUMENT; if(!Curl_llist_count(&data->state.httphdrs)) return CURLHE_NOHEADERS; /* no headers available */ @@ -88,7 +81,7 @@ CURLHcode curl_easy_header(CURL *easy, for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { hs = Curl_node_elem(e); if(curl_strequal(hs->name, name) && - (hs->type & type) && + (hs->type & origin) && (hs->request == request)) { amount++; pick = hs; @@ -107,7 +100,7 @@ CURLHcode curl_easy_header(CURL *easy, for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { hs = Curl_node_elem(e); if(curl_strequal(hs->name, name) && - (hs->type & type) && + (hs->type & origin) && (hs->request == request) && (match++ == nameindex)) { e_pick = e; @@ -125,12 +118,12 @@ CURLHcode curl_easy_header(CURL *easy, } /* public API */ -struct curl_header *curl_easy_nextheader(CURL *easy, - unsigned int type, +struct curl_header *curl_easy_nextheader(CURL *curl, + unsigned int origin, int request, struct curl_header *prev) { - struct Curl_easy *data = easy; + struct Curl_easy *data = curl; struct Curl_llist_node *pick; struct Curl_llist_node *e; struct Curl_header_store *hs; @@ -156,7 +149,7 @@ struct curl_header *curl_easy_nextheader(CURL *easy, /* make sure it is the next header of the desired type */ do { hs = Curl_node_elem(pick); - if((hs->type & type) && (hs->request == request)) + if((hs->type & origin) && (hs->request == request)) break; pick = Curl_node_next(pick); } while(pick); @@ -174,7 +167,7 @@ struct curl_header *curl_easy_nextheader(CURL *easy, struct Curl_header_store *check = Curl_node_elem(e); if(curl_strequal(hs->name, check->name) && (check->request == request) && - (check->type & type)) + (check->type & origin)) amount++; if(e == pick) index = amount - 1; @@ -220,97 +213,42 @@ static CURLcode namevalue(char *header, size_t hlen, unsigned int type, return CURLE_OK; } -static CURLcode unfold_value(struct Curl_easy *data, const char *value, - size_t vlen) /* length of the incoming header */ -{ - struct Curl_header_store *hs; - struct Curl_header_store *newhs; - size_t olen; /* length of the old value */ - size_t oalloc; /* length of the old name + value + separator */ - size_t offset; - DEBUGASSERT(data->state.prevhead); - hs = data->state.prevhead; - olen = strlen(hs->value); - offset = hs->value - hs->buffer; - oalloc = olen + offset + 1; - - /* skip all trailing space letters */ - while(vlen && ISBLANK(value[vlen - 1])) - vlen--; - - /* save only one leading space */ - while((vlen > 1) && ISBLANK(value[0]) && ISBLANK(value[1])) { - vlen--; - value++; - } - - /* since this header block might move in the realloc below, it needs to - first be unlinked from the list and then re-added again after the - realloc */ - Curl_node_remove(&hs->node); - - /* new size = struct + new value length + old name+value length */ - newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1); - if(!newhs) - return CURLE_OUT_OF_MEMORY; - /* ->name and ->value point into ->buffer (to keep the header allocation - in a single memory block), which now potentially have moved. Adjust - them. */ - newhs->name = newhs->buffer; - newhs->value = &newhs->buffer[offset]; - - /* put the data at the end of the previous data, not the newline */ - memcpy(&newhs->value[olen], value, vlen); - newhs->value[olen + vlen] = 0; /* null-terminate at newline */ - - /* insert this node into the list of headers */ - Curl_llist_append(&data->state.httphdrs, newhs, &newhs->node); - data->state.prevhead = newhs; - return CURLE_OK; -} - - /* * Curl_headers_push() gets passed a full HTTP header to store. It gets called - * immediately before the header callback. The header is CRLF terminated. + * immediately before the header callback. The header is CRLF, CR or LF + * terminated. */ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, + size_t hlen, /* length of header */ unsigned char type) { char *value = NULL; char *name = NULL; - char *end; - size_t hlen; /* length of the incoming header */ struct Curl_header_store *hs; CURLcode result = CURLE_OUT_OF_MEMORY; + const size_t ilen = hlen; if((header[0] == '\r') || (header[0] == '\n')) /* ignore the body separator */ return CURLE_OK; - end = strchr(header, '\r'); - if(!end) { - end = strchr(header, '\n'); - if(!end) - /* neither CR nor LF as terminator is not a valid header */ - return CURLE_WEIRD_SERVER_REPLY; - } - hlen = end - header; + /* trim off newline characters */ + if(hlen && (header[hlen - 1] == '\n')) + hlen--; + if(hlen && (header[hlen - 1] == '\r')) + hlen--; + if(hlen == ilen) + /* neither CR nor LF as terminator is not a valid header */ + return CURLE_WEIRD_SERVER_REPLY; - if((header[0] == ' ') || (header[0] == '\t')) { - if(data->state.prevhead) - /* line folding, append value to the previous header's value */ - return unfold_value(data, header, hlen); - else { - /* cannot unfold without a previous header. Instead of erroring, just - pass the leading blanks. */ - while(hlen && ISBLANK(*header)) { - header++; - hlen--; - } - if(!hlen) - return CURLE_WEIRD_SERVER_REPLY; + if(ISBLANK(header[0])) { + /* pass leading blanks */ + while(hlen && ISBLANK(*header)) { + header++; + hlen--; } + if(!hlen) + return CURLE_WEIRD_SERVER_REPLY; } if(Curl_llist_count(&data->state.httphdrs) >= MAX_HTTP_RESP_HEADER_COUNT) { failf(data, "Too many response headers, %d is max", @@ -318,7 +256,7 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, return CURLE_TOO_LARGE; } - hs = calloc(1, sizeof(*hs) + hlen); + hs = curlx_calloc(1, sizeof(*hs) + hlen); if(!hs) return CURLE_OUT_OF_MEMORY; memcpy(hs->buffer, header, hlen); @@ -337,7 +275,7 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, } else { failf(data, "Invalid response header"); - free(hs); + curlx_free(hs); } return result; } @@ -365,7 +303,7 @@ static CURLcode hds_cw_collect_write(struct Curl_easy *data, (type & CLIENTWRITE_1XX ? CURLH_1XX : (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER : CURLH_HEADER))); - CURLcode result = Curl_headers_push(data, buf, htype); + CURLcode result = Curl_headers_push(data, buf, blen, htype); CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d", htype, blen, result); if(result) @@ -388,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; @@ -418,7 +356,7 @@ CURLcode Curl_headers_cleanup(struct Curl_easy *data) for(e = Curl_llist_head(&data->state.httphdrs); e; e = n) { struct Curl_header_store *hs = Curl_node_elem(e); n = Curl_node_next(e); - free(hs); + curlx_free(hs); } headers_reset(data); return CURLE_OK; diff --git a/lib/headers.h b/lib/headers.h index e11fe9804e..283179acdc 100644 --- a/lib/headers.h +++ b/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 */ @@ -38,7 +38,7 @@ struct Curl_header_store { /* * Initialize header collecting for a transfer. - * Will add a client writer that catches CLIENTWRITE_HEADER writes. + * Add a client writer that catches CLIENTWRITE_HEADER writes. */ CURLcode Curl_headers_init(struct Curl_easy *data); @@ -46,7 +46,7 @@ CURLcode Curl_headers_init(struct Curl_easy *data); * Curl_headers_push() gets passed a full header to store. */ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header, - unsigned char type); + size_t hlen, unsigned char type); /* * Curl_headers_cleanup(). Free all stored headers and associated memory. @@ -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) 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/lib/hmac.c b/lib/hmac.c index 5e7dd0df0c..a4d6ebdd8a 100644 --- a/lib/hmac.c +++ b/lib/hmac.c @@ -23,21 +23,13 @@ * RFC2104 Keyed-Hashing for Message Authentication * ***************************************************************************/ - #include "curl_setup.h" -#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ - !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ + !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ defined(USE_SSL) -#include - #include "curl_hmac.h" -#include "curl_memory.h" -#include "curlx/warnless.h" - -/* The last #include file should be: */ -#include "memdebug.h" /* * Generic HMAC algorithm. @@ -50,10 +42,9 @@ static const unsigned char hmac_ipad = 0x36; static const unsigned char hmac_opad = 0x5C; -struct HMAC_context * -Curl_HMAC_init(const struct HMAC_params *hashparams, - const unsigned char *key, - unsigned int keylen) +struct HMAC_context *Curl_HMAC_init(const struct HMAC_params *hashparams, + const unsigned char *key, + unsigned int keylen) { size_t i; struct HMAC_context *ctxt; @@ -61,22 +52,22 @@ Curl_HMAC_init(const struct HMAC_params *hashparams, unsigned char b; /* Create HMAC context. */ - i = sizeof(*ctxt) + 2 * hashparams->ctxtsize + hashparams->resultlen; - ctxt = malloc(i); + i = sizeof(*ctxt) + (2 * hashparams->ctxtsize) + hashparams->resultlen; + ctxt = curlx_malloc(i); if(!ctxt) return ctxt; ctxt->hash = hashparams; - ctxt->hashctxt1 = (void *) (ctxt + 1); - ctxt->hashctxt2 = (void *) ((char *) ctxt->hashctxt1 + hashparams->ctxtsize); + ctxt->hashctxt1 = (void *)(ctxt + 1); + ctxt->hashctxt2 = (void *)((char *)ctxt->hashctxt1 + hashparams->ctxtsize); /* If the key is too long, replace it by its hash digest. */ if(keylen > hashparams->maxkeylen) { if(hashparams->hinit(ctxt->hashctxt1)) - return NULL; + goto fail; hashparams->hupdate(ctxt->hashctxt1, key, keylen); - hkey = (unsigned char *) ctxt->hashctxt2 + hashparams->ctxtsize; + hkey = (unsigned char *)ctxt->hashctxt2 + hashparams->ctxtsize; hashparams->hfinal(hkey, ctxt->hashctxt1); key = hkey; keylen = hashparams->resultlen; @@ -85,7 +76,7 @@ Curl_HMAC_init(const struct HMAC_params *hashparams, /* Prime the two hash contexts with the modified key. */ if(hashparams->hinit(ctxt->hashctxt1) || hashparams->hinit(ctxt->hashctxt2)) - return NULL; + goto fail; for(i = 0; i < keylen; i++) { b = (unsigned char)(*key ^ hmac_ipad); @@ -101,18 +92,20 @@ Curl_HMAC_init(const struct HMAC_params *hashparams, /* Done, return pointer to HMAC context. */ return ctxt; + +fail: + curlx_free(ctxt); + return NULL; } -int Curl_HMAC_update(struct HMAC_context *ctxt, - const unsigned char *ptr, - unsigned int len) +void Curl_HMAC_update(struct HMAC_context *ctxt, + const unsigned char *data, + unsigned int len) { /* Update first hash calculation. */ - ctxt->hash->hupdate(ctxt->hashctxt1, ptr, len); - return 0; + ctxt->hash->hupdate(ctxt->hashctxt1, data, len); } - int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *output) { const struct HMAC_params *hashparams = ctxt->hash; @@ -121,12 +114,12 @@ int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *output) storage. */ if(!output) - output = (unsigned char *) ctxt->hashctxt2 + ctxt->hash->ctxtsize; + output = (unsigned char *)ctxt->hashctxt2 + ctxt->hash->ctxtsize; hashparams->hfinal(output, ctxt->hashctxt1); hashparams->hupdate(ctxt->hashctxt2, output, hashparams->resultlen); hashparams->hfinal(output, ctxt->hashctxt2); - free(ctxt); + curlx_free(ctxt); return 0; } @@ -149,17 +142,24 @@ 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, size_t datalen, unsigned char *output) { - struct HMAC_context *ctxt = - Curl_HMAC_init(hashparams, key, curlx_uztoui(keylen)); + struct HMAC_context *ctxt; + if(keylen > UINT_MAX) /* unlikely to ever happen */ + return CURLE_BAD_FUNCTION_ARGUMENT; + ctxt = Curl_HMAC_init(hashparams, key, curlx_uztoui(keylen)); if(!ctxt) return CURLE_OUT_OF_MEMORY; /* Update the digest with the given challenge */ - Curl_HMAC_update(ctxt, buf, curlx_uztoui(buflen)); + do { + unsigned int ilen = (unsigned int)CURLMIN(datalen, UINT_MAX); + Curl_HMAC_update(ctxt, data, ilen); + datalen -= ilen; + data += ilen; + } while(datalen); /* Finalise the digest */ Curl_HMAC_final(ctxt, output); diff --git a/lib/hostip.c b/lib/hostip.c index 2d122fb999..7dac8790e0 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_NETINET_IN_H @@ -41,34 +40,27 @@ #include #endif -#include -#ifndef UNDER_CE +#include /* for sigjmp_buf, sigsetjmp() */ #include -#endif #include "urldata.h" -#include "sendf.h" -#include "connect.h" +#include "curl_addrinfo.h" +#include "curl_trc.h" +#include "dnscache.h" #include "hostip.h" -#include "hash.h" -#include "rand.h" -#include "share.h" +#include "httpsrr.h" #include "url.h" -#include "curlx/inet_ntop.h" -#include "curlx/inet_pton.h" #include "multiif.h" +#include "progress.h" #include "doh.h" -#include "curlx/warnless.h" #include "select.h" #include "strcase.h" #include "easy_lock.h" +#include "curlx/inet_ntop.h" +#include "curlx/inet_pton.h" +#include "curlx/strcopy.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - #if defined(CURLRES_SYNCH) && \ defined(HAVE_ALARM) && \ defined(SIGALRM) && \ @@ -94,10 +86,10 @@ * take that into account. Hosts that are not IPv6-enabled have CURLRES_IPV4 * defined. * - * CURLRES_ARES - is defined if libcurl is built to use c-ares for + * USE_RESOLV_ARES - is defined if libcurl is built to use c-ares for * asynchronous name resolves. This can be Windows or *nix. * - * CURLRES_THREADED - is defined if libcurl is built to run under (native) + * USE_RESOLV_THREADED - is defined if libcurl is built to run under (native) * Windows, and then the name resolve will be done in a new thread, and the * supported API will be the same as for ares-builds. * @@ -119,13 +111,47 @@ * CURLRES_* defines based on the config*.h and curl_setup.h defines. */ -static void dnscache_entry_free(struct Curl_dns_entry *dns); +uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version) +{ + (void)data; + switch(ip_version) { + case CURL_IPRESOLVE_V6: + return CURL_DNSQ_AAAA; + case CURL_IPRESOLVE_V4: + return CURL_DNSQ_A; + default: + if(Curl_ipv6works(data)) + return (CURL_DNSQ_A | CURL_DNSQ_AAAA); + else + return CURL_DNSQ_A; + } +} -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void show_resolve_info(struct Curl_easy *data, - struct Curl_dns_entry *dns); -#else -#define show_resolve_info(x,y) Curl_nop_stmt +#ifdef CURLVERBOSE +const char *Curl_resolv_query_str(uint8_t dns_queries) +{ + switch(dns_queries) { + case (CURL_DNSQ_A | CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS): + return "A+AAAA+HTTPS"; + case (CURL_DNSQ_A | CURL_DNSQ_AAAA): + return "A+AAAA"; + case (CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS): + return "AAAA+HTTPS"; + case (CURL_DNSQ_AAAA): + return "AAAA"; + case (CURL_DNSQ_A | CURL_DNSQ_HTTPS): + return "A+HTTPS"; + case (CURL_DNSQ_A): + return "A"; + case (CURL_DNSQ_HTTPS): + return "HTTPS"; + case 0: + return "-"; + default: + DEBUGASSERT(0); + return "???"; + } +} #endif /* @@ -161,151 +187,6 @@ void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf, } } -/* - * Create a hostcache id string for the provided host + port, to be used by - * the DNS caching. Without alloc. Return length of the id string. - */ -static size_t -create_dnscache_id(const char *name, - size_t nlen, /* 0 or actual name length */ - int port, char *ptr, size_t buflen) -{ - size_t len = nlen ? nlen : strlen(name); - DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN); - if(len > (buflen - 7)) - len = buflen - 7; - /* store and lower case the name */ - Curl_strntolower(ptr, name, len); - return msnprintf(&ptr[len], 7, ":%u", port) + len; -} - -struct dnscache_prune_data { - struct curltime now; - timediff_t oldest_ms; /* oldest time in cache not pruned. */ - timediff_t max_age_ms; -}; - -/* - * This function is set as a callback to be called for every entry in the DNS - * cache when we want to prune old unused entries. - * - * Returning non-zero means remove the entry, return 0 to keep it in the - * cache. - */ -static int -dnscache_entry_is_stale(void *datap, void *hc) -{ - struct dnscache_prune_data *prune = - (struct dnscache_prune_data *) datap; - struct Curl_dns_entry *dns = (struct Curl_dns_entry *) hc; - - if(dns->timestamp.tv_sec || dns->timestamp.tv_usec) { - /* get age in milliseconds */ - timediff_t age = curlx_timediff(prune->now, dns->timestamp); - if(!dns->addr) - age *= 2; /* negative entries age twice as fast */ - if(age >= prune->max_age_ms) - return TRUE; - if(age > prune->oldest_ms) - prune->oldest_ms = age; - } - return FALSE; -} - -/* - * Prune the DNS cache. This assumes that a lock has already been taken. - * Returns the 'age' of the oldest still kept entry - in milliseconds. - */ -static timediff_t -dnscache_prune(struct Curl_hash *hostcache, timediff_t cache_timeout_ms, - struct curltime now) -{ - struct dnscache_prune_data user; - - user.max_age_ms = cache_timeout_ms; - user.now = now; - user.oldest_ms = 0; - - Curl_hash_clean_with_criterium(hostcache, - (void *) &user, - dnscache_entry_is_stale); - - return user.oldest_ms; -} - -static struct Curl_dnscache *dnscache_get(struct Curl_easy *data) -{ - if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) - return &data->share->dnscache; - if(data->multi) - return &data->multi->dnscache; - return NULL; -} - -static void dnscache_lock(struct Curl_easy *data, - struct Curl_dnscache *dnscache) -{ - if(data->share && dnscache == &data->share->dnscache) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); -} - -static void dnscache_unlock(struct Curl_easy *data, - struct Curl_dnscache *dnscache) -{ - if(data->share && dnscache == &data->share->dnscache) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); -} - -/* - * Library-wide function for pruning the DNS cache. This function takes and - * returns the appropriate locks. - */ -void Curl_dnscache_prune(struct Curl_easy *data) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - struct curltime now; - /* the timeout may be set -1 (forever) */ - timediff_t timeout_ms = data->set.dns_cache_timeout_ms; - - if(!dnscache || (timeout_ms == -1)) - /* NULL hostcache means we cannot do it */ - return; - - dnscache_lock(data, dnscache); - - now = curlx_now(); - - do { - /* Remove outdated and unused entries from the hostcache */ - timediff_t oldest_ms = dnscache_prune(&dnscache->entries, timeout_ms, now); - - if(Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE) { - if(oldest_ms < INT_MAX) - /* prune the ones over half this age */ - timeout_ms = (int)oldest_ms / 2; - else - timeout_ms = INT_MAX/2; - } - else - break; - - /* if the cache size is still too big, use the oldest age as new prune - limit */ - } while(timeout_ms); - - dnscache_unlock(data, dnscache); -} - -void Curl_dnscache_clear(struct Curl_easy *data) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - if(dnscache) { - dnscache_lock(data, dnscache); - Curl_hash_clean(&dnscache->entries); - dnscache_unlock(data, dnscache); - } -} - #ifdef USE_ALARM_TIMEOUT /* Beware this is a global and unique instance. This is used to store the return address that we can jump back to from inside a signal handler. This @@ -314,304 +195,9 @@ static sigjmp_buf curl_jmpenv; static curl_simple_lock curl_jmpenv_lock; #endif -/* lookup address, returns entry if found and not stale */ -static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data, - struct Curl_dnscache *dnscache, - const char *hostname, - int port, - int ip_version) -{ - struct Curl_dns_entry *dns = NULL; - char entry_id[MAX_HOSTCACHE_LEN]; - size_t entry_len; - - if(!dnscache) - return NULL; - - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(hostname, 0, port, - entry_id, sizeof(entry_id)); - - /* See if it is already in our dns cache */ - dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); - - /* No entry found in cache, check if we might have a wildcard entry */ - if(!dns && data->state.wildcard_resolve) { - entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id)); - - /* See if it is already in our dns cache */ - dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); - } - - if(dns && (data->set.dns_cache_timeout_ms != -1)) { - /* See whether the returned entry is stale. Done before we release lock */ - struct dnscache_prune_data user; - - user.now = curlx_now(); - user.max_age_ms = data->set.dns_cache_timeout_ms; - user.oldest_ms = 0; - - if(dnscache_entry_is_stale(&user, dns)) { - infof(data, "Hostname in DNS cache was stale, zapped"); - dns = NULL; /* the memory deallocation is being handled by the hash */ - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); - } - } - - /* See if the returned entry matches the required resolve mode */ - if(dns && ip_version != CURL_IPRESOLVE_WHATEVER) { - int pf = PF_INET; - bool found = FALSE; - struct Curl_addrinfo *addr = dns->addr; - -#ifdef PF_INET6 - if(ip_version == CURL_IPRESOLVE_V6) - pf = PF_INET6; -#endif - - while(addr) { - if(addr->ai_family == pf) { - found = TRUE; - break; - } - addr = addr->ai_next; - } - - if(!found) { - infof(data, "Hostname in DNS cache does not have needed family, zapped"); - dns = NULL; /* the memory deallocation is being handled by the hash */ - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); - } - } - return dns; -} - -/* - * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache. - * - * Curl_resolv() checks initially and multi_runsingle() checks each time - * it discovers the handle in the state WAITRESOLVE whether the hostname - * has already been resolved and the address has already been stored in - * the DNS cache. This short circuits waiting for a lot of pending - * lookups for the same hostname requested by different handles. - * - * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. - * - * The returned data *MUST* be "released" with Curl_resolv_unlink() after - * use, or we will leak memory! - */ -struct Curl_dns_entry * -Curl_dnscache_get(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns = NULL; - - dnscache_lock(data, dnscache); - - dns = fetch_addr(data, dnscache, hostname, port, ip_version); - if(dns) - dns->refcount++; /* we use it! */ - - dnscache_unlock(data, dnscache); - - return dns; -} - -#ifndef CURL_DISABLE_SHUFFLE_DNS -/* - * Return # of addresses in a Curl_addrinfo struct - */ -static int num_addresses(const struct Curl_addrinfo *addr) -{ - int i = 0; - while(addr) { - addr = addr->ai_next; - i++; - } - return i; -} - -UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, - struct Curl_addrinfo **addr); -/* - * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo' - * struct by re-linking its linked list. - * - * The addr argument should be the address of a pointer to the head node of a - * `Curl_addrinfo` list and it will be modified to point to the new head after - * shuffling. - * - * Not declared static only to make it easy to use in a unit test! - * - * @unittest: 1608 - */ -UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, - struct Curl_addrinfo **addr) -{ - CURLcode result = CURLE_OK; - const int num_addrs = num_addresses(*addr); - - if(num_addrs > 1) { - struct Curl_addrinfo **nodes; - infof(data, "Shuffling %i addresses", num_addrs); - - nodes = malloc(num_addrs*sizeof(*nodes)); - if(nodes) { - int i; - unsigned int *rnd; - const size_t rnd_size = num_addrs * sizeof(*rnd); - - /* build a plain array of Curl_addrinfo pointers */ - nodes[0] = *addr; - for(i = 1; i < num_addrs; i++) { - nodes[i] = nodes[i-1]->ai_next; - } - - rnd = malloc(rnd_size); - if(rnd) { - /* Fisher-Yates shuffle */ - if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) { - struct Curl_addrinfo *swap_tmp; - for(i = num_addrs - 1; i > 0; i--) { - swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)]; - nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i]; - nodes[i] = swap_tmp; - } - - /* relink list in the new order */ - for(i = 1; i < num_addrs; i++) { - nodes[i-1]->ai_next = nodes[i]; - } - - nodes[num_addrs-1]->ai_next = NULL; - *addr = nodes[0]; - } - free(rnd); - } - else - result = CURLE_OUT_OF_MEMORY; - free(nodes); - } - else - result = CURLE_OUT_OF_MEMORY; - } - return result; -} -#endif - -struct Curl_dns_entry * -Curl_dnscache_mk_entry(struct Curl_easy *data, - struct Curl_addrinfo *addr, - const char *hostname, - size_t hostlen, /* length or zero */ - int port, - bool permanent) -{ - struct Curl_dns_entry *dns; - -#ifndef CURL_DISABLE_SHUFFLE_DNS - /* shuffle addresses if requested */ - if(data->set.dns_shuffle_addresses) { - CURLcode result = Curl_shuffle_addr(data, &addr); - if(result) { - Curl_freeaddrinfo(addr); - return NULL; - } - } -#else - (void)data; -#endif - if(!hostlen) - hostlen = strlen(hostname); - - /* Create a new cache entry */ - dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen); - if(!dns) { - Curl_freeaddrinfo(addr); - return NULL; - } - - dns->refcount = 1; /* the cache has the first reference */ - dns->addr = addr; /* 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 */ - } - else { - dns->timestamp = curlx_now(); - } - dns->hostport = port; - if(hostlen) - memcpy(dns->hostname, hostname, hostlen); - - return dns; -} - -static struct Curl_dns_entry * -dnscache_add_addr(struct Curl_easy *data, - struct Curl_dnscache *dnscache, - struct Curl_addrinfo *addr, - const char *hostname, - size_t hlen, /* length or zero */ - int port, - bool permanent) -{ - char entry_id[MAX_HOSTCACHE_LEN]; - size_t entry_len; - struct Curl_dns_entry *dns; - struct Curl_dns_entry *dns2; - - dns = Curl_dnscache_mk_entry(data, addr, hostname, hlen, port, permanent); - if(!dns) - return NULL; - - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(hostname, hlen, port, - entry_id, sizeof(entry_id)); - - /* Store the resolved data in our DNS cache. */ - dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1, - (void *)dns); - if(!dns2) { - dnscache_entry_free(dns); - return NULL; - } - - dns = dns2; - dns->refcount++; /* mark entry as in-use */ - return dns; -} - -CURLcode Curl_dnscache_add(struct Curl_easy *data, - struct Curl_dns_entry *entry) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - char id[MAX_HOSTCACHE_LEN]; - size_t idlen; - - if(!dnscache) - return CURLE_FAILED_INIT; - /* Create an entry id, based upon the hostname and port */ - idlen = create_dnscache_id(entry->hostname, 0, entry->hostport, - id, sizeof(id)); - - /* Store the resolved data in our DNS cache and up ref count */ - dnscache_lock(data, dnscache); - if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) { - dnscache_unlock(data, dnscache); - return CURLE_OUT_OF_MEMORY; - } - entry->refcount++; - dnscache_unlock(data, dnscache); - return CURLE_OK; -} - #ifdef USE_IPV6 /* return a static IPv6 ::1 for the name */ -static struct Curl_addrinfo *get_localhost6(int port, const char *name) +static struct Curl_addrinfo *get_localhost6(uint16_t port, const char *name) { struct Curl_addrinfo *ca; const size_t ss_size = sizeof(struct sockaddr_in6); @@ -619,16 +205,13 @@ static struct Curl_addrinfo *get_localhost6(int port, const char *name) struct sockaddr_in6 sa6; unsigned char ipv6[16]; unsigned short port16 = (unsigned short)(port & 0xffff); - ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1); + ca = curlx_calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1); if(!ca) return NULL; + memset(&sa6, 0, sizeof(sa6)); sa6.sin6_family = AF_INET6; sa6.sin6_port = htons(port16); - sa6.sin6_flowinfo = 0; -#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID - sa6.sin6_scope_id = 0; -#endif (void)curlx_inet_pton(AF_INET6, "::1", ipv6); memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6)); @@ -642,15 +225,15 @@ static struct Curl_addrinfo *get_localhost6(int port, const char *name) ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); memcpy(ca->ai_addr, &sa6, ss_size); ca->ai_canonname = (char *)ca->ai_addr + ss_size; - strcpy(ca->ai_canonname, name); + curlx_strcopy(ca->ai_canonname, hostlen + 1, name, hostlen); return ca; } #else -#define get_localhost6(x,y) NULL +#define get_localhost6(x, y) NULL #endif /* return a static IPv4 127.0.0.1 for the given name */ -static struct Curl_addrinfo *get_localhost(int port, const char *name) +static struct Curl_addrinfo *get_localhost(uint16_t port, const char *name) { struct Curl_addrinfo *ca; struct Curl_addrinfo *ca6; @@ -668,7 +251,7 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name) return NULL; memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4)); - ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1); + ca = curlx_calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1); if(!ca) return NULL; ca->ai_flags = 0; @@ -679,7 +262,7 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name) ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo)); memcpy(ca->ai_addr, &sa, ss_size); ca->ai_canonname = (char *)ca->ai_addr + ss_size; - strcpy(ca->ai_canonname, name); + curlx_strcopy(ca->ai_canonname, hostlen + 1, name, hostlen); ca6 = get_localhost6(port, name); if(!ca6) @@ -689,36 +272,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 = 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 */ @@ -741,7 +321,6 @@ bool Curl_host_is_ipnum(const char *hostname) return FALSE; } - /* return TRUE if 'part' is a case insensitive tail of 'full' */ static bool tailmatch(const char *full, size_t flen, const char *part, size_t plen) @@ -751,256 +330,422 @@ static bool tailmatch(const char *full, size_t flen, return curl_strnequal(part, &full[flen - plen], plen); } -static struct Curl_addrinfo * -convert_ipaddr_direct(const char *hostname, int port, bool *is_ipaddr) +static bool can_resolve_dns_queries(struct Curl_easy *data, + uint8_t dns_queries) { - struct in_addr in; - *is_ipaddr = FALSE; - /* First check if this is an IPv4 address string */ - if(curlx_inet_pton(AF_INET, hostname, &in) > 0) { - /* This is a dotted IP address 123.123.123.123-style */ - *is_ipaddr = TRUE; -#ifdef USE_RESOLVE_ON_IPS - (void)port; - return NULL; -#else - return Curl_ip2addr(AF_INET, &in, hostname, port); -#endif - } -#ifdef USE_IPV6 - else { - struct in6_addr in6; - /* check if this is an IPv6 address string */ - if(curlx_inet_pton(AF_INET6, hostname, &in6) > 0) { - /* This is an IPv6 address literal */ - *is_ipaddr = TRUE; -#ifdef USE_RESOLVE_ON_IPS - return NULL; -#else - return Curl_ip2addr(AF_INET6, &in6, hostname, port); -#endif + (void)data; + if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && !Curl_ipv6works(data)) + return FALSE; + return TRUE; +} + +CURLcode Curl_resolv_announce_start(struct Curl_easy *data, + void *resolver) +{ + if(data->set.resolver_start) { + int rc; + + CURL_TRC_DNS(data, "announcing resolve to application"); + Curl_set_in_callback(data, TRUE); + rc = data->set.resolver_start(resolver, NULL, + data->set.resolver_start_client); + Curl_set_in_callback(data, FALSE); + if(rc) { + CURL_TRC_DNS(data, "application aborted resolve"); + return CURLE_ABORTED_BY_CALLBACK; } } -#endif /* USE_IPV6 */ + return CURLE_OK; +} + +#ifdef USE_CURL_ASYNC +static struct Curl_resolv_async *hostip_async_new(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + timediff_t timeout_ms) +{ + struct Curl_resolv_async *async; + size_t hostlen = strlen(hostname); + + if(!data->multi) { + DEBUGASSERT(0); + return NULL; + } + + /* struct size already includes the NUL for hostname */ + async = curlx_calloc(1, sizeof(*async) + hostlen); + if(!async) + return NULL; + + /* Give every async resolve operation a "unique" id. This may + * wrap around after a long time, making collisions highly unlikely. + * As we keep the async structs at the easy handle, chances of + * easy `mid plus resolv->id` colliding should be astronomical. + * `resolv_id == 0` is never used. */ + if(data->multi->last_resolv_id == UINT32_MAX) + data->multi->last_resolv_id = 1; /* wrap around */ + else + data->multi->last_resolv_id++; + async->id = data->multi->last_resolv_id; + async->dns_queries = dns_queries; + async->port = port; + async->transport = transport; + async->start = *Curl_pgrs_now(data); + async->timeout_ms = timeout_ms; + if(hostlen) { + memcpy(async->hostname, hostname, hostlen); + async->is_ipaddr = Curl_is_ipaddr(async->hostname); + if(async->is_ipaddr) + async->is_ipv4addr = Curl_is_ipv4addr(async->hostname); + } + + return async; +} + +static CURLcode hostip_resolv_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns) +{ + CURLcode result; + + /* If async resolving is ongoing, this must be set */ + if(!async) + return CURLE_FAILED_INIT; + +#ifndef CURL_DISABLE_DOH + if(data->conn->bits.doh) + result = Curl_doh_take_result(data, async, pdns); + else +#endif + result = Curl_async_take_result(data, async, pdns); + + if(result == CURLE_AGAIN) { + CURL_TRC_DNS(data, "result incomplete, queries=%s, responses=%s, " + "ongoing=%d", Curl_resolv_query_str(async->dns_queries), + Curl_resolv_query_str(async->dns_responses), + async->queries_ongoing); + result = CURLE_OK; + } + else if(result) { + CURL_TRC_DNS(data, "result error %d", result); + Curl_resolver_error(data, NULL); + } + else { + CURL_TRC_DNS(data, "result complete"); + DEBUGASSERT(*pdns); + } + + return result; +} + +const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, + uint32_t resolv_id, + int ai_family, + unsigned int index) +{ +#ifdef CURLRES_ASYNCH + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + if(async) { + if((ai_family == AF_INET) && !(async->dns_queries & CURL_DNSQ_A)) + return NULL; +#ifdef USE_IPV6 + if((ai_family == AF_INET6) && !(async->dns_queries & CURL_DNSQ_AAAA)) + return NULL; +#endif + return Curl_async_get_ai(data, async, ai_family, index); + } +#else + (void)data; + (void)resolv_id; + (void)ai_family; + (void)index; +#endif return NULL; } -static bool can_resolve_ip_version(struct Curl_easy *data, int ip_version) +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo * +Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id) { -#ifdef CURLRES_IPV6 - if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data)) - return FALSE; -#elif defined(CURLRES_IPV4) - (void)data; - if(ip_version == CURL_IPRESOLVE_V6) - return FALSE; +#ifdef CURLRES_ASYNCH + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + if(async) + return Curl_async_get_https(data, async); #else -#error either CURLRES_IPV6 or CURLRES_IPV4 need to be defined + (void)data; + (void)resolv_id; +#endif + return NULL; +} + +bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id) +{ +#ifdef CURLRES_ASYNCH + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + if(async) + return Curl_async_knows_https(data, async); +#else + (void)data; + (void)resolv_id; #endif return TRUE; } -static CURLcode store_negative_resolve(struct Curl_easy *data, - const char *host, - int port) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns; - DEBUGASSERT(dnscache); - if(!dnscache) - return CURLE_FAILED_INIT; +#endif /* USE_HTTPSRR */ - /* put this new host in the cache */ - dns = dnscache_add_addr(data, dnscache, NULL, host, 0, port, FALSE); - if(dns) { - /* release the returned reference; the cache itself will keep the - * entry alive: */ - dns->refcount--; - infof(data, "Store negative name resolve for %s:%d", host, port); - return CURLE_OK; +#endif /* USE_CURL_ASYNC */ + +static CURLcode hostip_resolv_start(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + timediff_t timeout_ms, + bool allowDOH, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns) +{ +#ifdef USE_CURL_ASYNC + struct Curl_resolv_async *async = NULL; +#endif + struct Curl_addrinfo *addr = NULL; + size_t hostname_len; + CURLcode result = CURLE_OK; + + (void)timeout_ms; /* not in all ifdefs */ + *presolv_id = 0; + *pdns = NULL; + + /* Check for "known" things to resolve ourselves. */ +#ifndef USE_RESOLVE_ON_IPS + if(Curl_is_ipaddr(hostname)) { + /* test655 verifies that the announce is done, even though there + * is no real resolving. So, keep doing this. */ + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + /* shortcut literal IP addresses, if we are not told to resolve them. */ + result = Curl_str2addr(hostname, port, &addr); + goto out; } - return CURLE_OUT_OF_MEMORY; +#endif + + hostname_len = strlen(hostname); + if(curl_strequal(hostname, "localhost") || + curl_strequal(hostname, "localhost.") || + tailmatch(hostname, hostname_len, STRCONST(".localhost")) || + tailmatch(hostname, hostname_len, STRCONST(".localhost."))) { + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + addr = get_localhost(port, hostname); + if(!addr) + result = CURLE_OUT_OF_MEMORY; + goto out; + } + +#ifndef CURL_DISABLE_DOH + if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) { + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + if(!async) { + async = hostip_async_new(data, dns_queries, hostname, port, + transport, timeout_ms); + if(!async) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + result = Curl_doh(data, async); + goto out; + } +#else + (void)allowDOH; +#endif + + /* Can we provide the requested IP specifics in resolving? */ + if(!can_resolve_dns_queries(data, dns_queries)) { + result = CURLE_COULDNT_RESOLVE_HOST; + goto out; + } + +#ifdef CURLRES_ASYNCH + (void)addr; + if(!async) { + async = hostip_async_new(data, dns_queries, hostname, port, + transport, timeout_ms); + if(!async) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + result = Curl_async_getaddrinfo(data, async); + if(result == CURLE_AGAIN) { + /* the answer might be there already. Check. */ + CURLcode r2 = hostip_resolv_take_result(data, async, pdns); + if(r2) + result = r2; + else if(*pdns) + result = CURLE_OK; + } +#else + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + addr = Curl_sync_getaddrinfo(data, dns_queries, hostname, port, transport); + if(!addr) + result = CURLE_COULDNT_RESOLVE_HOST; +#endif + +out: + if(!result) { + if(addr) { + /* we got a response, create a dns entry, add to cache, return */ + DEBUGASSERT(!*pdns); + *pdns = Curl_dnscache_mk_entry(data, dns_queries, &addr, hostname, port); + if(!*pdns) + result = CURLE_OUT_OF_MEMORY; + } + else if(!*pdns) + result = CURLE_AGAIN; + } + else if(*pdns) + Curl_dns_entry_unlink(data, pdns); + else if(addr) + Curl_freeaddrinfo(addr); + +#ifdef USE_CURL_ASYNC + if(async) { + if(result == CURLE_AGAIN) { /* still need it, link, return id. */ + *presolv_id = async->id; + async->next = data->state.async; + data->state.async = async; + } + else { + Curl_async_destroy(data, async); + } + } +#endif + return result; } -/* - * Curl_resolv() is the main name resolve function within libcurl. It resolves - * a name and returns a pointer to the entry in the 'entry' argument (if one - * is provided). This function might return immediately if we are using asynch - * resolves. See the return codes. - * - * The cache entry we return will get its 'inuse' counter increased when this - * function is used. You MUST call Curl_resolv_unlink() later (when you are - * done using this struct) to decrease the reference counter again. - * - * Return codes: - * CURLE_OK = success, *entry set to non-NULL - * CURLE_AGAIN = resolving in progress, *entry == NULL - * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL - * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL - */ -CURLcode Curl_resolv(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - bool allowDOH, - struct Curl_dns_entry **entry) +static CURLcode hostip_resolv(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + timediff_t timeout_ms, + bool allowDOH, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns) { - struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns = NULL; - struct Curl_addrinfo *addr = NULL; - int respwait = 0; - bool is_ipaddr; size_t hostname_len; + CURLcode result = CURLE_COULDNT_RESOLVE_HOST; + bool cache_dns = FALSE; + + (void)timeout_ms; /* not used in all ifdefs */ + *presolv_id = 0; + *pdns = NULL; #ifndef CURL_DISABLE_DOH data->conn->bits.doh = FALSE; /* default is not */ #else (void)allowDOH; #endif - if(!dnscache) - goto error; /* We should intentionally error and not resolve .onion TLDs */ hostname_len = strlen(hostname); + DEBUGASSERT(hostname_len); if(hostname_len >= 7 && (curl_strequal(&hostname[hostname_len - 6], ".onion") || curl_strequal(&hostname[hostname_len - 7], ".onion."))) { failf(data, "Not resolving .onion address (RFC 7686)"); - goto error; + goto out; } +#ifdef DEBUGBUILD + CURL_TRC_DNS(data, "hostip_resolv(%s:%u, queries=%s)", + hostname, port, Curl_resolv_query_str(dns_queries)); + if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && + getenv("CURL_DBG_RESOLV_FAIL_IPV6")) { + infof(data, "DEBUG fail ipv6 resolve"); + result = Curl_resolver_error(data, NULL); + goto out; + } +#endif /* Let's check our DNS cache first */ - dnscache_lock(data, dnscache); - dns = fetch_addr(data, dnscache, hostname, port, ip_version); - if(dns) - dns->refcount++; /* we pass out the reference. */ - dnscache_unlock(data, dnscache); - if(dns) { + result = Curl_dnscache_get(data, dns_queries, hostname, port, pdns); + if(*pdns) { infof(data, "Hostname %s was found in DNS cache", hostname); - goto out; + result = CURLE_OK; } - - /* No luck, we need to resolve hostname. Notify user callback. */ - if(data->set.resolver_start) { - void *resolver = NULL; - int st; -#ifdef CURLRES_ASYNCH - if(Curl_async_get_impl(data, &resolver)) - goto error; -#endif - Curl_set_in_callback(data, TRUE); - st = data->set.resolver_start(resolver, NULL, - data->set.resolver_start_client); - Curl_set_in_callback(data, FALSE); - if(st) - goto error; + else if(result) { + infof(data, "Negative DNS entry"); + result = Curl_resolver_error(data, NULL); } - - /* shortcut literal IP addresses, if we are not told to resolve them. */ - addr = convert_ipaddr_direct(hostname, port, &is_ipaddr); - if(addr) - goto out; - -#ifndef USE_RESOLVE_ON_IPS - /* allowed to convert, hostname is IP address, then NULL means error */ - if(is_ipaddr) - goto error; -#endif - - /* Really need a resolver for hostname. */ - if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data)) - goto error; - - if(!is_ipaddr && - (curl_strequal(hostname, "localhost") || - curl_strequal(hostname, "localhost.") || - tailmatch(hostname, hostname_len, STRCONST(".localhost")) || - tailmatch(hostname, hostname_len, STRCONST(".localhost.")))) { - addr = get_localhost(port, hostname); - } -#ifndef CURL_DISABLE_DOH - else if(!is_ipaddr && allowDOH && data->set.doh) { - addr = Curl_doh(data, hostname, port, ip_version, &respwait); - } -#endif else { - /* Can we provide the requested IP specifics in resolving? */ - if(!can_resolve_ip_version(data, ip_version)) - goto error; - -#ifdef CURLRES_ASYNCH - addr = Curl_async_getaddrinfo(data, hostname, port, ip_version, &respwait); -#else - respwait = 0; /* no async waiting here */ - addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version); -#endif + /* No luck, we need to start resolving. */ + cache_dns = TRUE; + result = hostip_resolv_start(data, dns_queries, hostname, port, + transport, timeout_ms, allowDOH, + presolv_id, pdns); } out: - /* We either have found a `dns` or looked up the `addr` - * or `respwait` is set for an async operation. - * Everything else is a failure to resolve. */ - if(dns) { - if(!dns->addr) { - infof(data, "Negative DNS entry"); - dns->refcount--; - return CURLE_COULDNT_RESOLVE_HOST; + if(result && (result != CURLE_AGAIN)) { + Curl_dns_entry_unlink(data, pdns); + if((result == CURLE_COULDNT_RESOLVE_HOST) || + (result == CURLE_COULDNT_RESOLVE_PROXY)) { + if(cache_dns) + Curl_dnscache_add_negative(data, dns_queries, hostname, port); + failf(data, "Could not resolve: %s:%u", hostname, port); } - *entry = dns; - return CURLE_OK; - } - 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); - if(!dns) - goto error; - if(Curl_dnscache_add(data, dns)) - goto error; - show_resolve_info(data, dns); - *entry = dns; - return CURLE_OK; - } - else if(respwait) { - if(!Curl_resolv_check(data, &dns)) { - *entry = dns; - return dns ? CURLE_OK : CURLE_AGAIN; + else { + failf(data, "Error %d resolving %s:%u", result, hostname, port); } } -error: - if(dns) - Curl_resolv_unlink(data, &dns); - *entry = NULL; - Curl_async_shutdown(data); - store_negative_resolve(data, hostname, port); - return CURLE_COULDNT_RESOLVE_HOST; + else if(cache_dns && *pdns) { + result = Curl_dnscache_add(data, *pdns); + if(result) + Curl_dns_entry_unlink(data, pdns); + } + + return result; } CURLcode Curl_resolv_blocking(struct Curl_easy *data, + uint8_t dns_queries, const char *hostname, - int port, - int ip_version, - struct Curl_dns_entry **dnsentry) + uint16_t port, + uint8_t transport, + struct Curl_dns_entry **pdns) { CURLcode result; - - *dnsentry = NULL; - result = Curl_resolv(data, hostname, port, ip_version, FALSE, dnsentry); + uint32_t resolv_id; + DEBUGASSERT(hostname && *hostname); + *pdns = NULL; + /* We cannot do a blocking resolve using DoH currently */ + result = hostip_resolv(data, dns_queries, + hostname, port, transport, 0, FALSE, + &resolv_id, pdns); switch(result) { case CURLE_OK: - DEBUGASSERT(*dnsentry); - return CURLE_OK; + DEBUGASSERT(*pdns); + break; +#ifdef USE_CURL_ASYNC case CURLE_AGAIN: - DEBUGASSERT(!*dnsentry); - result = Curl_async_await(data, dnsentry); - if(result || !*dnsentry) { - /* close the connection, since we cannot return failure here without - cleaning up this connection properly. */ - connclose(data->conn, "async resolve failed"); - } - return result; + DEBUGASSERT(!*pdns); + result = Curl_async_await(data, resolv_id, pdns); + Curl_resolv_destroy(data, resolv_id); + break; +#endif default: - return result; + break; } + return result; } #ifdef USE_ALARM_TIMEOUT @@ -1009,45 +754,26 @@ CURLcode Curl_resolv_blocking(struct Curl_easy *data, * execution. This effectively causes the remainder of the application to run * within a signal handler which is nonportable and could lead to problems. */ -CURL_NORETURN static -void alarmfunc(int sig) +CURL_NORETURN static void alarmfunc(int sig) { (void)sig; siglongjmp(curl_jmpenv, 1); } #endif /* USE_ALARM_TIMEOUT */ -/* - * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a - * timeout. This function might return immediately if we are using asynch - * resolves. See the return codes. - * - * The cache entry we return will get its 'inuse' counter increased when this - * function is used. You MUST call Curl_resolv_unlink() later (when you are - * done using this struct) to decrease the reference counter again. - * - * If built with a synchronous resolver and use of signals is not - * disabled by the application, then a nonzero timeout will cause a - * timeout after the specified number of milliseconds. Otherwise, timeout - * is ignored. - * - * Return codes: - * CURLE_OK = success, *entry set to non-NULL - * CURLE_AGAIN = resolving in progress, *entry == NULL - * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL - * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL - */ - -CURLcode Curl_resolv_timeout(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - struct Curl_dns_entry **entry, - timediff_t timeoutms) -{ #ifdef USE_ALARM_TIMEOUT + +static CURLcode resolv_alarm_timeout(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + timediff_t timeout_ms, + uint32_t *presolv_id, + struct Curl_dns_entry **entry) +{ #ifdef HAVE_SIGACTION - struct sigaction keep_sigact; /* store the old struct here */ + struct sigaction keep_sigact; /* store the old struct here */ volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */ struct sigaction sigact; #else @@ -1057,37 +783,23 @@ CURLcode Curl_resolv_timeout(struct Curl_easy *data, #endif /* HAVE_SIGACTION */ volatile long timeout; volatile unsigned int prev_alarm = 0; -#endif /* USE_ALARM_TIMEOUT */ CURLcode result; - *entry = NULL; - - if(timeoutms < 0) - /* got an already expired timeout */ - return CURLE_OPERATION_TIMEDOUT; - -#ifdef USE_ALARM_TIMEOUT - if(data->set.no_signal) - /* Ignore the timeout when signals are disabled */ - timeout = 0; - else - timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms; - - if(!timeout + DEBUGASSERT(hostname && *hostname); + DEBUGASSERT(timeout_ms > 0); + DEBUGASSERT(!data->set.no_signal); #ifndef CURL_DISABLE_DOH - || data->set.doh + DEBUGASSERT(!data->set.doh); #endif - ) - /* USE_ALARM_TIMEOUT defined, but no timeout actually requested or resolve - done using DoH */ - return Curl_resolv(data, hostname, port, ip_version, TRUE, entry); + *entry = NULL; + timeout = (timeout_ms > LONG_MAX) ? LONG_MAX : (long)timeout_ms; if(timeout < 1000) { /* The alarm() function only provides integer second resolution, so if we want to wait less than one second we must bail out already now. */ failf(data, - "remaining timeout of %ld too small to resolve via SIGALRM method", - timeout); + "remaining timeout of %ld too small to resolve via SIGALRM method", + timeout); return CURLE_OPERATION_TIMEDOUT; } /* This allows us to time-out from the name resolver, as the timeout @@ -1129,30 +841,15 @@ CURLcode Curl_resolv_timeout(struct Curl_easy *data, /* alarm() makes a signal get sent when the timeout fires off, and that will abort system calls */ - prev_alarm = alarm(curlx_sltoui(timeout/1000L)); + prev_alarm = alarm(curlx_sltoui(timeout / 1000L)); } -#ifdef DEBUGBUILD - Curl_resolve_test_delay(); -#endif - -#else /* !USE_ALARM_TIMEOUT */ -#ifndef CURLRES_ASYNCH - if(timeoutms) - infof(data, "timeout on name lookup is not supported"); -#else - (void)timeoutms; -#endif -#endif /* USE_ALARM_TIMEOUT */ - /* Perform the actual name resolution. This might be interrupted by an - * alarm if it takes too long. - */ - result = Curl_resolv(data, hostname, port, ip_version, TRUE, entry); + * alarm if it takes too long. */ + result = hostip_resolv(data, dns_queries, hostname, port, transport, + timeout_ms, FALSE, presolv_id, entry); -#ifdef USE_ALARM_TIMEOUT clean_up: - if(!prev_alarm) /* deactivate a possibly active alarm before uninstalling the handler */ alarm(0); @@ -1176,14 +873,14 @@ clean_up: the time we spent until now! */ if(prev_alarm) { /* there was an alarm() set before us, now put it back */ - timediff_t elapsed_secs = curlx_timediff(curlx_now(), - data->conn->created) / 1000; + timediff_t elapsed_secs = curlx_ptimediff_ms(Curl_pgrs_now(data), + &data->conn->created) / 1000; /* the alarm period is counted in even number of seconds */ unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs); if(!alarm_set || - ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) { + ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000))) { /* if the alarm time-left reached zero or turned "negative" (counted with unsigned values), we should fire off a SIGALRM here, but we will not, and zero would be to switch it off so we never set it to @@ -1195,429 +892,185 @@ clean_up: else alarm((unsigned int)alarm_set); } -#endif /* USE_ALARM_TIMEOUT */ 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); - free(dns->hinfo); - } -#endif - free(dns); -} +#endif /* USE_ALARM_TIMEOUT */ /* - * Curl_resolv_unlink() releases a reference to the given cached DNS entry. - * When the reference count reaches 0, the entry is destroyed. It is important - * that only one unlink is made for each Curl_resolv() call. + * Curl_resolv() is the main name resolve function within libcurl. It resolves + * a name and returns a pointer to the entry in the 'entry' argument. This + * function might return immediately if we are using asynch resolves. See the + * return codes. * - * May be called with 'data' == NULL for global cache. + * The cache entry we return will get its 'inuse' counter increased when this + * function is used. You MUST call Curl_dns_entry_unlink() later (when you are + * done using this struct) to decrease the reference counter again. + * + * If built with a synchronous resolver and use of signals is not + * disabled by the application, then a nonzero timeout will cause a + * timeout after the specified number of milliseconds. Otherwise, timeout + * is ignored. + * + * Return codes: + * CURLE_OK = success, *pdns set to non-NULL + * CURLE_AGAIN = resolving in progress, *pdns == NULL + * CURLE_COULDNT_RESOLVE_HOST = error, *pdns == NULL + * CURLE_OPERATION_TIMEDOUT = timeout expired, *pdns == NULL */ -void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns) +CURLcode Curl_resolv(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + timediff_t timeout_ms, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns) { - if(*pdns) { - struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns = *pdns; - *pdns = NULL; - dnscache_lock(data, dnscache); - dns->refcount--; - if(dns->refcount == 0) - dnscache_entry_free(dns); - dnscache_unlock(data, dnscache); + DEBUGASSERT(hostname && *hostname); + *presolv_id = 0; + *pdns = NULL; + + if(timeout_ms < 0) + /* got an already expired timeout */ + return CURLE_OPERATION_TIMEDOUT; + else if(!timeout_ms) + timeout_ms = CURL_TIMEOUT_RESOLVE_MS; + +#ifdef USE_ALARM_TIMEOUT + if(timeout_ms && data->set.no_signal) { + /* Cannot use ALARM when signals are disabled */ + timeout_ms = 0; } -} - -static void dnscache_entry_dtor(void *entry) -{ - struct Curl_dns_entry *dns = (struct Curl_dns_entry *) entry; - DEBUGASSERT(dns && (dns->refcount > 0)); - dns->refcount--; - if(dns->refcount == 0) - dnscache_entry_free(dns); -} - -/* - * Curl_dnscache_init() inits a new DNS cache. - */ -void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size) -{ - Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare, - dnscache_entry_dtor); -} - -void Curl_dnscache_destroy(struct Curl_dnscache *dns) -{ - Curl_hash_destroy(&dns->entries); -} - -CURLcode Curl_loadhostpairs(struct Curl_easy *data) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - struct curl_slist *hostp; - - if(!dnscache) - return CURLE_FAILED_INIT; - - /* Default is no wildcard found */ - data->state.wildcard_resolve = FALSE; - - for(hostp = data->state.resolve; hostp; hostp = hostp->next) { - char entry_id[MAX_HOSTCACHE_LEN]; - const char *host = hostp->data; - struct Curl_str source; - if(!host) - continue; - if(*host == '-') { - curl_off_t num = 0; - size_t entry_len; - host++; - if(!curlx_str_single(&host, '[')) { - if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') || - curlx_str_single(&host, ']') || - curlx_str_single(&host, ':')) - continue; - } - else { - if(curlx_str_until(&host, &source, 4096, ':') || - curlx_str_single(&host, ':')) { - continue; - } - } - - if(!curlx_str_number(&host, &num, 0xffff)) { - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(curlx_str(&source), - curlx_strlen(&source), (int)num, - entry_id, sizeof(entry_id)); - dnscache_lock(data, dnscache); - /* delete entry, ignore if it did not exist */ - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); - dnscache_unlock(data, dnscache); - } - } - else { - struct Curl_dns_entry *dns; - 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; - - if(*host == '+') { - host++; - permanent = FALSE; - } - if(!curlx_str_single(&host, '[')) { - if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') || - curlx_str_single(&host, ']')) - continue; - } - else { - if(curlx_str_until(&host, &source, 4096, ':')) - continue; - } - if(curlx_str_single(&host, ':') || - curlx_str_number(&host, &port, 0xffff) || - curlx_str_single(&host, ':')) - goto err; - -#ifndef CURL_DISABLE_VERBOSE_STRINGS - addresses = host; -#endif - - /* start the address section */ - while(*host) { - struct Curl_str target; - struct Curl_addrinfo *ai; - - if(!curlx_str_single(&host, '[')) { - if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') || - curlx_str_single(&host, ']')) - goto err; - } - else { - if(curlx_str_until(&host, &target, 4096, ',')) { - if(curlx_str_single(&host, ',')) - goto err; - /* survive nothing but just a comma */ - continue; - } - } -#ifndef USE_IPV6 - if(memchr(target.str, ':', target.len)) { - infof(data, "Ignoring resolve address '%s', missing IPv6 support.", - address); - if(curlx_str_single(&host, ',')) - goto err; - continue; - } -#endif - - if(curlx_strlen(&target) >= sizeof(address)) - goto err; - - memcpy(address, curlx_str(&target), curlx_strlen(&target)); - address[curlx_strlen(&target)] = '\0'; - - ai = Curl_str2addr(address, (int)port); - if(!ai) { - infof(data, "Resolve address '%s' found illegal", address); - goto err; - } - - if(tail) { - tail->ai_next = ai; - tail = tail->ai_next; - } - else { - head = tail = ai; - } - if(curlx_str_single(&host, ',')) - break; - } - - if(!head) - goto err; - - error = FALSE; -err: - if(error) { - failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'", - hostp->data); - Curl_freeaddrinfo(head); - return CURLE_SETOPT_OPTION_SYNTAX; - } - - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source), - (int)port, - entry_id, sizeof(entry_id)); - - dnscache_lock(data, dnscache); - - /* See if it is already in our dns cache */ - dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); - - if(dns) { - infof(data, "RESOLVE %.*s:%" CURL_FORMAT_CURL_OFF_T - " - old addresses discarded", (int)curlx_strlen(&source), - curlx_str(&source), port); - /* delete old entry, there are two reasons for this - 1. old entry may have different addresses. - 2. even if entry with correct addresses is already in the cache, - but if it is close to expire, then by the time next http - request is made, it can get expired and pruned because old - entry is not necessarily marked as permanent. - 3. when adding a non-permanent entry, we want it to remove and - replace an existing permanent entry. - 4. when adding a non-permanent entry, we want it to get a "fresh" - timeout that starts _now_. */ - - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); - } - - /* put this new host in the cache */ - 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--; - } - - 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, "*")) { - infof(data, "RESOLVE *:%" CURL_FORMAT_CURL_OFF_T " using wildcard", - port); - data->state.wildcard_resolve = TRUE; - } - } + if(timeout_ms && !Curl_doh_wanted(data)) { + return resolv_alarm_timeout(data, dns_queries, hostname, port, transport, + timeout_ms, presolv_id, pdns); } - data->state.resolve = NULL; /* dealt with now */ +#endif /* !USE_ALARM_TIMEOUT */ - return CURLE_OK; +#ifndef CURLRES_ASYNCH + if(timeout_ms) + infof(data, "timeout on name lookup is not supported"); +#endif + + return hostip_resolv(data, dns_queries, hostname, port, transport, + timeout_ms, TRUE, presolv_id, pdns); } -#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) + +struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data, + uint32_t resolv_id) { + struct Curl_resolv_async *async = data->state.async; + for(; async; async = async->next) { + if(async->id == resolv_id) + return async; + } + return NULL; +} + +CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); CURLcode result; /* If async resolving is ongoing, this must be set */ - if(!data->state.async.hostname) + if(!async) return CURLE_FAILED_INIT; /* check if we have the name resolved by now (from someone else) */ - *dns = Curl_dnscache_get(data, data->state.async.hostname, - data->state.async.port, - data->state.async.ip_version); - if(*dns) { + result = Curl_dnscache_get(data, async->dns_queries, + async->hostname, async->port, pdns); + if(*pdns) { /* Tell a possibly async resolver we no longer need the results. */ - infof(data, "Hostname '%s' was found in DNS cache", - data->state.async.hostname); - Curl_async_shutdown(data); - data->state.async.dns = *dns; - data->state.async.done = TRUE; + infof(data, "Hostname '%s' was found in DNS cache", async->hostname); + Curl_async_shutdown(data, async); return CURLE_OK; } - -#ifndef CURL_DISABLE_DOH - if(data->conn->bits.doh) { - result = Curl_doh_is_resolved(data, dns); - if(result) - Curl_resolver_error(data, NULL); + else if(result) { + Curl_async_shutdown(data, async); + return Curl_resolver_error(data, NULL); + } + + result = hostip_resolv_take_result(data, async, pdns); + + if(*pdns) { + /* Add to cache */ + result = Curl_dnscache_add(data, *pdns); + if(result) + Curl_dns_entry_unlink(data, pdns); + } + else if((result == CURLE_COULDNT_RESOLVE_HOST) || + (result == CURLE_COULDNT_RESOLVE_PROXY)) { + Curl_dnscache_add_negative(data, async->dns_queries, + async->hostname, async->port); + failf(data, "Could not resolve: %s:%u", async->hostname, async->port); + } + else if(result) { + failf(data, "Error %d resolving %s:%u", + result, async->hostname, async->port); } - else -#endif - result = Curl_async_is_resolved(data, dns); - if(*dns) - show_resolve_info(data, *dns); - if(result) - store_negative_resolve(data, data->state.async.hostname, - data->state.async.port); return result; } -#endif CURLcode Curl_resolv_pollset(struct Curl_easy *data, struct easy_pollset *ps) { -#ifdef CURLRES_ASYNCH -#ifndef CURL_DISABLE_DOH - if(data->conn->bits.doh) - /* nothing to wait for during DoH resolve, those handles have their own - sockets */ - return CURLE_OK; -#endif - return Curl_async_pollset(data, ps); -#else - (void)data; + struct Curl_resolv_async *async = data->state.async; + CURLcode result = CURLE_OK; + (void)ps; - return CURLE_OK; + for(; async && !result; async = async->next) { +#ifndef CURL_DISABLE_DOH + if(async->doh) /* DoH has nothing for the pollset */ + continue; #endif -} - -/* Call this function after Curl_connect() has returned async=TRUE and - then a successful name resolve has been received. - - Note: this function disconnects and frees the conn data in case of - resolve failure */ -CURLcode Curl_once_resolved(struct Curl_easy *data, - struct Curl_dns_entry *dns, - bool *protocol_done) -{ - CURLcode result; - struct connectdata *conn = data->conn; - -#ifdef USE_CURL_ASYNC - if(data->state.async.dns) { - DEBUGASSERT(data->state.async.dns == dns); - data->state.async.dns = NULL; - } -#endif - - result = Curl_setup_conn(data, dns, protocol_done); - - if(result) { - Curl_detach_connection(data); - Curl_conn_terminate(data, conn, TRUE); + result = Curl_async_pollset(data, async, ps); } return result; } +void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id) +{ + struct Curl_resolv_async **panchor = &data->state.async; + + for(; *panchor; panchor = &(*panchor)->next) { + struct Curl_resolv_async *async = *panchor; + if(async->id == resolv_id) { + *panchor = async->next; + Curl_async_destroy(data, async); + break; + } + } +} + +void Curl_resolv_shutdown_all(struct Curl_easy *data) +{ + struct Curl_resolv_async *async = data->state.async; + for(; async; async = async->next) { + Curl_async_shutdown(data, async); + } +} + +void Curl_resolv_destroy_all(struct Curl_easy *data) +{ + while(data->state.async) { + struct Curl_resolv_async *async = data->state.async; + data->state.async = async->next; + Curl_async_destroy(data, async); + } +} + +#endif /* USE_CURL_ASYNC */ + + /* * Curl_resolver_error() calls failf() with the appropriate message after a * resolve error */ - -#ifdef USE_CURL_ASYNC CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail) { struct connectdata *conn = data->conn; @@ -1638,19 +1091,30 @@ CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail) detail ? " (" : "", detail ? detail : "", detail ? ")" : ""); return result; } -#endif /* USE_CURL_ASYNC */ -#ifdef DEBUGBUILD -#include "curlx/wait.h" - -void Curl_resolve_test_delay(void) +#ifdef USE_UNIX_SOCKETS +CURLcode Curl_resolv_unix(struct Curl_easy *data, + const char *unix_path, + bool abstract_path, + struct Curl_dns_entry **pdns) { - const char *p = getenv("CURL_DNS_DELAY_MS"); - if(p) { - curl_off_t l; - if(!curlx_str_number(&p, &l, TIME_T_MAX) && l) { - curlx_wait_ms((timediff_t)l); + struct Curl_addrinfo *addr; + CURLcode result; + + DEBUGASSERT(unix_path); + *pdns = NULL; + + result = Curl_unix2addr(unix_path, abstract_path, &addr); + if(result) { + if(result == CURLE_TOO_LARGE) { + /* Long paths are not supported for now */ + failf(data, "Unix socket path too long: '%s'", unix_path); + result = CURLE_COULDNT_RESOLVE_HOST; } + return result; } + + *pdns = Curl_dnscache_mk_entry(data, 0, &addr, NULL, 0); + return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY; } -#endif +#endif /* USE_UNIX_SOCKETS */ diff --git a/lib/hostip.h b/lib/hostip.h index 2f78be82c2..fc91a0ca41 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -23,19 +23,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #include "hash.h" -#include "curl_addrinfo.h" -#include "curlx/timeval.h" /* for timediff_t */ -#include "asyn.h" -#include "httpsrr.h" - -#include - -#ifdef USE_HTTPSRR -# include -#endif +#include "curlx/timeval.h" /* for curltime, timediff_t */ /* Allocate enough memory to hold the full name information structs and * everything. OSF1 is known to require at least 8872 bytes. The buffer @@ -44,14 +35,31 @@ */ #define CURL_HOSTENT_SIZE 9000 -#define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this - many seconds for a name resolve */ +#define CURL_TIMEOUT_RESOLVE_MS (300 * 1000) struct addrinfo; struct hostent; struct Curl_easy; struct connectdata; struct easy_pollset; +struct Curl_https_rrinfo; +struct Curl_multi; +struct Curl_dns_entry; + +/* DNS query types */ +#define CURL_DNSQ_A (1U << 0) +#define CURL_DNSQ_AAAA (1U << 1) +#define CURL_DNSQ_HTTPS (1U << 2) + +#define CURL_DNSQ_ALL (CURL_DNSQ_A | CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS) +#define CURL_DNSQ_IP(x) (uint8_t)((x)&(CURL_DNSQ_A | CURL_DNSQ_AAAA)) + +#ifdef CURLVERBOSE +const char *Curl_resolv_query_str(uint8_t dns_queries); +#endif + +/* Return CURL_DNSQ_* bits for the transfer and ip_version. */ +uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version); enum alpnid { ALPN_none = 0, @@ -60,147 +68,101 @@ enum alpnid { ALPN_h3 = CURLALTSVC_H3 }; -struct Curl_dns_entry { - struct Curl_addrinfo *addr; -#ifdef USE_HTTPSRR - struct Curl_https_rrinfo *hinfo; -#endif - /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */ - struct curltime timestamp; - /* reference counter, entry is freed on reaching 0 */ - size_t refcount; - /* hostname port number that resolved to addr. */ - int hostport; - /* hostname that resolved to addr. may be NULL (Unix domain sockets). */ - char hostname[1]; -}; - -struct Curl_dnscache { - struct Curl_hash entries; -}; - bool Curl_host_is_ipnum(const char *hostname); -/* - * Curl_resolv() returns an entry with the info for the specified host - * and port. - * - * The returned data *MUST* be "released" with Curl_resolv_unlink() after - * use, or we will leak memory! - */ -CURLcode Curl_resolv(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - bool allowDOH, - struct Curl_dns_entry **dnsentry); - -CURLcode Curl_resolv_blocking(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - struct Curl_dns_entry **dnsentry); - -CURLcode Curl_resolv_timeout(struct Curl_easy *data, - const char *hostname, int port, - int ip_version, - struct Curl_dns_entry **dnsentry, - 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 - -/* unlink a dns entry, potentially shared with a cache */ -void Curl_resolv_unlink(struct Curl_easy *data, - struct Curl_dns_entry **pdns); - -/* init a new dns cache */ -void Curl_dnscache_init(struct Curl_dnscache *dns, size_t hashsize); - -void Curl_dnscache_destroy(struct Curl_dnscache *dns); - -/* prune old entries from the DNS cache */ -void Curl_dnscache_prune(struct Curl_easy *data); - -/* clear the DNS cache */ -void Curl_dnscache_clear(struct Curl_easy *data); - -/* IPv4 threadsafe resolve function used for synch and asynch builds */ -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); +/* IPv4 thread-safe resolve function used for synch and asynch builds */ +struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, uint16_t port); /* * 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 + * given in the 'ai' 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); -/* - * Make a `Curl_dns_entry`. - * Creates a dnscache entry *without* adding it to a dnscache. This allows - * further modifications of the entry *before* then adding it to a cache. - * - * 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`. - * - * Returns entry or NULL on OOM. +/* Start DNS resolving for the given parameters. Returns + * - CURLE_OK: `*pdns` is the resolved DNS entry (needs to be unlinked). + * `*presolv_id` is 0. + * - CURLE_AGAIN: resolve is asynchronous and not finished yet. + * `presolv_id` is the identifier for querying results later. + * - other: the operation failed, `*pdns` is NULL, `*presolv_id` is 0. */ -struct Curl_dns_entry * -Curl_dnscache_mk_entry(struct Curl_easy *data, - struct Curl_addrinfo *addr, - const char *hostname, - size_t hostlen, /* length or zero */ - int port, - bool permanent); +CURLcode Curl_resolv(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + timediff_t timeout_ms, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns); -/* - * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache. - * - * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. - * - * The returned data *MUST* be "released" with Curl_resolv_unlink() after - * use, or we will leak memory! - */ -struct Curl_dns_entry * -Curl_dnscache_get(struct Curl_easy *data, - const char *hostname, - int port, int ip_version); +CURLcode Curl_resolv_blocking(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + struct Curl_dns_entry **pdns); -/* - * Curl_dnscache_addr() adds `entry` to the cache, increasing its - * reference count on success. - */ -CURLcode Curl_dnscache_add(struct Curl_easy *data, - struct Curl_dns_entry *entry); - -/* - * Populate the cache with specified entries from CURLOPT_RESOLVE. - */ -CURLcode Curl_loadhostpairs(struct Curl_easy *data); +/* Announce start of a resolve operation to application callback, + * passing the resolver implementation (maybe NULL). */ +CURLcode Curl_resolv_announce_start(struct Curl_easy *data, + void *resolver); #ifdef USE_CURL_ASYNC -CURLcode Curl_resolv_check(struct Curl_easy *data, - struct Curl_dns_entry **dns); -#else -#define Curl_resolv_check(x,y) CURLE_NOT_BUILT_IN -#endif + CURLcode Curl_resolv_pollset(struct Curl_easy *data, struct easy_pollset *ps); +/* Get the `async` struct for the given `resolv_id`, if it exists. */ +struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data, + uint32_t resolv_id); + +/* Shut down all resolves of the given easy handle. */ +void Curl_resolv_shutdown_all(struct Curl_easy *data); + +/* Destroy all resolve resources of the given easy handle. */ +void Curl_resolv_destroy_all(struct Curl_easy *data); + +CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns); + +void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id); + +const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, + uint32_t resolv_id, + int ai_family, + unsigned int index); +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_resolv_get_https(struct Curl_easy *data, + uint32_t resolv_id); +bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id); +#endif /* USE_HTTPSRR */ + +#else /* !USE_CURL_ASYNC */ +#define Curl_resolv_shutdown_all(x) Curl_nop_stmt +#define Curl_resolv_destroy_all(x) Curl_nop_stmt +#define Curl_resolv_take_result(x, y, z) CURLE_NOT_BUILT_IN +#define Curl_resolv_get_ai(x, y, z, a) NULL +#define Curl_resolv_get_https(x, y) NULL +#define Curl_resolv_knows_https(x, y) TRUE +#define Curl_resolv_pollset(x, y) CURLE_OK +#define Curl_resolv_destroy(x, y) Curl_nop_stmt +#endif /* USE_CURL_ASYNC */ + CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail); #ifdef CURLRES_SYNCH @@ -210,14 +172,17 @@ CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail); * support and platform. */ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, + uint8_t dns_queries, const char *hostname, - int port, - int ip_version); - + uint16_t port, + uint8_t transport); #endif -#ifdef DEBUGBUILD -void Curl_resolve_test_delay(void); +#ifdef USE_UNIX_SOCKETS +CURLcode Curl_resolv_unix(struct Curl_easy *data, + const char *unix_path, + bool abstract_path, + struct Curl_dns_entry **pdns); #endif #endif /* HEADER_CURL_HOSTIP_H */ diff --git a/lib/hostip4.c b/lib/hostip4.c index 2c356f3464..fb35e3992c 100644 --- a/lib/hostip4.c +++ b/lib/hostip4.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" /*********************************************************************** @@ -44,15 +43,10 @@ #endif #include "urldata.h" -#include "sendf.h" +#include "curl_addrinfo.h" +#include "curl_trc.h" #include "hostip.h" -#include "hash.h" -#include "share.h" #include "url.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" #ifdef CURLRES_SYNCH @@ -74,16 +68,15 @@ * */ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, + uint8_t dns_queries, const char *hostname, - int port, - int ip_version) + uint16_t port, + uint8_t transport) { struct Curl_addrinfo *ai = NULL; - (void)ip_version; -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif + (void)dns_queries; + (void)transport; ai = Curl_ipv4_resolve_r(hostname, port); if(!ai) @@ -94,17 +87,18 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, #endif /* CURLRES_SYNCH */ #endif /* CURLRES_IPV4 */ -#if defined(CURLRES_IPV4) && !defined(CURLRES_ARES) && !defined(CURLRES_AMIGA) +#if defined(CURLRES_IPV4) && !defined(USE_RESOLV_ARES) && \ + !defined(CURLRES_AMIGA) /* - * Curl_ipv4_resolve_r() - ipv4 threadsafe resolver function. + * Curl_ipv4_resolve_r() - ipv4 thread-safe resolver function. * * This is used for both synchronous and asynchronous resolver builds, - * implying that only threadsafe code and function calls may be used. + * implying that only thread-safe code and function calls may be used. * */ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, - int port) + uint16_t port) { #if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) && \ defined(HAVE_GETHOSTBYNAME_R_3) @@ -125,7 +119,7 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; if(port) { - msnprintf(sbuf, sizeof(sbuf), "%d", port); + curl_msnprintf(sbuf, sizeof(sbuf), "%d", port); sbufptr = sbuf; } @@ -139,7 +133,7 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, */ int h_errnop; - buf = calloc(1, CURL_HOSTENT_SIZE); + buf = curlx_calloc(1, CURL_HOSTENT_SIZE); if(!buf) return NULL; /* major failure */ /* @@ -157,7 +151,7 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, &h_errnop); /* If the buffer is too small, it returns NULL and sets errno to - * ERANGE. The errno is thread safe if this is compiled with + * ERANGE. The errno is thread-safe if this is compiled with * -D_REENTRANT as then the 'errno' variable is a macro defined to get * used properly for threads. */ @@ -170,11 +164,11 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, /* Linux */ (void)gethostbyname_r(hostname, - (struct hostent *)buf, - (char *)buf + sizeof(struct hostent), - CURL_HOSTENT_SIZE - sizeof(struct hostent), - &h, /* DIFFERENCE */ - &h_errnop); + (struct hostent *)buf, + (char *)buf + sizeof(struct hostent), + CURL_HOSTENT_SIZE - sizeof(struct hostent), + &h, /* DIFFERENCE */ + &h_errnop); /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a * sudden this function returns EAGAIN if the given buffer size is too * small. Previous versions are known to return ERANGE for the same @@ -253,8 +247,8 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, * Since we do not know how big buffer this particular lookup required, * we cannot realloc down the huge alloc without doing closer analysis of * the returned data. Thus, we always use CURL_HOSTENT_SIZE for every - * name lookup. Fixing this would require an extra malloc() and then - * calling Curl_addrinfo_copy() that subsequent realloc()s down the new + * name lookup. Fixing this would require an extra allocation and then + * calling Curl_addrinfo_copy() that subsequent reallocation down the new * memory area to the actually used amount. */ } @@ -262,12 +256,12 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, #endif /* HAVE_...BYNAME_R_5 || HAVE_...BYNAME_R_6 || HAVE_...BYNAME_R_3 */ { h = NULL; /* set return code to NULL */ - free(buf); + curlx_free(buf); } #else /* (HAVE_GETADDRINFO && HAVE_GETADDRINFO_THREADSAFE) || HAVE_GETHOSTBYNAME_R */ /* - * Here is code for platforms that do not have a thread safe + * Here is code for platforms that do not have a thread-safe * getaddrinfo() nor gethostbyname_r() function or for which * gethostbyname() is the preferred one. */ @@ -280,10 +274,10 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, ai = Curl_he2ai(h, port); if(buf) /* used a *_r() function */ - free(buf); + curlx_free(buf); } #endif return ai; } -#endif /* CURLRES_IPV4 && !CURLRES_ARES && !CURLRES_AMIGA */ +#endif /* CURLRES_IPV4 && !USE_RESOLV_ARES && !CURLRES_AMIGA */ diff --git a/lib/hostip6.c b/lib/hostip6.c index 4c05f0247e..7412f428a4 100644 --- a/lib/hostip6.c +++ b/lib/hostip6.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" /*********************************************************************** @@ -45,36 +44,15 @@ #include "urldata.h" #include "cfilters.h" -#include "sendf.h" +#include "curl_addrinfo.h" +#include "curl_trc.h" #include "hostip.h" -#include "hash.h" -#include "share.h" #include "url.h" #include "curlx/inet_pton.h" #include "connect.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" #ifdef CURLRES_SYNCH -#ifdef DEBUG_ADDRINFO -static void dump_addrinfo(const struct Curl_addrinfo *ai) -{ - printf("dump_addrinfo:\n"); - for(; ai; ai = ai->ai_next) { - char buf[INET6_ADDRSTRLEN]; - printf(" fam %2d, CNAME %s, ", - ai->ai_family, ai->ai_canonname ? ai->ai_canonname : ""); - Curl_printable_address(ai, buf, sizeof(buf)); - printf("%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). @@ -85,9 +63,10 @@ static void dump_addrinfo(const struct Curl_addrinfo *ai) * Curl_freeaddrinfo(), nothing else. */ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, + uint8_t dns_queries, const char *hostname, - int port, - int ip_version) + uint16_t port, + uint8_t transport) { struct addrinfo hints; struct Curl_addrinfo *res; @@ -99,14 +78,12 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, #endif int pf = PF_INET; - if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) - /* The stack seems to be IPv6-enabled */ + if(dns_queries & CURL_DNSQ_AAAA) pf = PF_UNSPEC; memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; - hints.ai_socktype = - (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? + hints.ai_socktype = (transport == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM; #ifndef USE_RESOLVE_ON_IPS @@ -122,7 +99,7 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, #endif if(port) { - msnprintf(sbuf, sizeof(sbuf), "%d", port); + curl_msnprintf(sbuf, sizeof(sbuf), "%d", port); sbufptr = sbuf; } @@ -136,8 +113,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/lib/hsts.c b/lib/hsts.c index 9525158bcc..400b4423da 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -28,28 +28,20 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS) -#include #include "urldata.h" #include "llist.h" #include "hsts.h" +#include "curl_fopen.h" #include "curl_get_line.h" -#include "sendf.h" #include "parsedate.h" -#include "fopen.h" -#include "rename.h" -#include "share.h" -#include "strdup.h" +#include "curl_share.h" #include "curlx/strparse.h" +#include "curlx/strcopy.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define MAX_HSTS_LINE 4095 +#define MAX_HSTS_LINE 4095 #define MAX_HSTS_HOSTLEN 2048 -#define MAX_HSTS_DATELEN 256 -#define UNLIMITED "unlimited" +#define MAX_HSTS_DATELEN 17 +#define UNLIMITED "unlimited" #if defined(DEBUGBUILD) || defined(UNITTESTS) /* to play well with debug builds, we can *set* a fixed time this will @@ -73,18 +65,14 @@ static time_t hsts_debugtime(void *unused) struct hsts *Curl_hsts_init(void) { - struct hsts *h = calloc(1, sizeof(struct hsts)); + struct hsts *h = curlx_calloc(1, sizeof(struct hsts)); if(h) { Curl_llist_init(&h->list, NULL); } return h; } -static void hsts_free(struct stsentry *e) -{ - free(CURL_UNCONST(e->host)); - free(e); -} +#define hsts_free(x) curlx_free(x) void Curl_hsts_cleanup(struct hsts **hp) { @@ -97,12 +85,26 @@ void Curl_hsts_cleanup(struct hsts **hp) n = Curl_node_next(e); hsts_free(sts); } - free(h->filename); - free(h); + curlx_free(h->filename); + curlx_free(h); *hp = NULL; } } +/* append the new entry to the list after possibly removing an old entry + first */ +static void hsts_append(struct hsts *h, struct stsentry *sts) +{ + if(Curl_llist_count(&h->list) == MAX_HSTS_ENTRIES) { + /* It's full. Remove the first entry in the list */ + struct Curl_llist_node *e = Curl_llist_head(&h->list); + struct stsentry *oldsts = Curl_node_elem(e); + Curl_node_remove(e); + hsts_free(oldsts); + } + Curl_llist_append(&h->list, sts, &sts->node); +} + static CURLcode hsts_create(struct hsts *h, const char *hostname, size_t hlen, @@ -116,21 +118,14 @@ static CURLcode hsts_create(struct hsts *h, /* strip off any trailing dot */ --hlen; if(hlen) { - char *duphost; - struct stsentry *sts = calloc(1, sizeof(struct stsentry)); + struct stsentry *sts = curlx_calloc(1, sizeof(struct stsentry) + hlen); if(!sts) return CURLE_OUT_OF_MEMORY; - - duphost = Curl_memdup0(hostname, hlen); - if(!duphost) { - free(sts); - return CURLE_OUT_OF_MEMORY; - } - - sts->host = duphost; + /* the null terminator is already there */ + memcpy(sts->host, hostname, hlen); sts->expires = expires; sts->includeSubDomains = subdomains; - Curl_llist_append(&h->list, sts, &sts->node); + hsts_append(h, sts); } return CURLE_OK; } @@ -225,7 +220,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; } @@ -253,7 +248,7 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, if((hlen > MAX_HSTS_HOSTLEN) || !hlen) return NULL; - if(hostname[hlen-1] == '.') + if(hostname[hlen - 1] == '.') /* remove the trailing dot */ --hlen; @@ -270,7 +265,7 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, ntail = strlen(sts->host); if((subdomain && sts->includeSubDomains) && (ntail < hlen)) { size_t offs = hlen - ntail; - if((hostname[offs-1] == '.') && + if((hostname[offs - 1] == '.') && curl_strnequal(&hostname[offs], sts->host, ntail) && (ntail > blen)) { /* save the tail match with the longest tail */ @@ -278,7 +273,7 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, blen = ntail; } } - /* avoid curl_strequal because the host name is not null-terminated */ + /* avoid curl_strequal because the hostname is not null-terminated */ if((hlen == ntail) && curl_strnequal(hostname, sts->host, hlen)) return sts; } @@ -299,24 +294,23 @@ static CURLcode hsts_push(struct Curl_easy *data, struct tm stamp; CURLcode result; - e.name = (char *)CURL_UNCONST(sts->host); + e.name = (char *)sts->host; e.namelen = strlen(sts->host); e.includeSubDomains = sts->includeSubDomains; if(sts->expires != TIME_T_MAX) { - result = Curl_gmtime((time_t)sts->expires, &stamp); + result = curlx_gmtime((time_t)sts->expires, &stamp); if(result) return result; - msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d", - stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, - stamp.tm_hour, stamp.tm_min, stamp.tm_sec); + curl_msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d", + stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, + stamp.tm_hour, stamp.tm_min, stamp.tm_sec); } else - strcpy(e.expire, UNLIMITED); + curlx_strcopy(e.expire, sizeof(e.expire), STRCONST(UNLIMITED)); - sc = data->set.hsts_write(data, &e, i, - data->set.hsts_write_userp); + sc = data->set.hsts_write(data, &e, i, data->set.hsts_write_userp); *stop = (sc != CURLSTS_OK); return sc == CURLSTS_FAIL ? CURLE_BAD_FUNCTION_ARGUMENT : CURLE_OK; } @@ -328,21 +322,20 @@ static CURLcode hsts_out(struct stsentry *sts, FILE *fp) { struct tm stamp; if(sts->expires != TIME_T_MAX) { - CURLcode result = Curl_gmtime((time_t)sts->expires, &stamp); + CURLcode result = curlx_gmtime((time_t)sts->expires, &stamp); if(result) return result; - fprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n", - sts->includeSubDomains ? ".": "", sts->host, - stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, - stamp.tm_hour, stamp.tm_min, stamp.tm_sec); + curl_mfprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n", + sts->includeSubDomains ? "." : "", sts->host, + stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, + stamp.tm_hour, stamp.tm_min, stamp.tm_sec); } else - fprintf(fp, "%s%s \"%s\"\n", - sts->includeSubDomains ? ".": "", sts->host, UNLIMITED); + curl_mfprintf(fp, "%s%s \"%s\"\n", + sts->includeSubDomains ? "." : "", sts->host, UNLIMITED); return CURLE_OK; } - /* * Curl_https_save() writes the HSTS cache to file and callback. */ @@ -379,14 +372,14 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h, if(result) break; } - fclose(out); - if(!result && tempstore && Curl_rename(tempstore, file)) + curlx_fclose(out); + if(!result && tempstore && curlx_rename(tempstore, file)) result = CURLE_WRITE_ERROR; if(result && tempstore) unlink(tempstore); } - free(tempstore); + curlx_free(tempstore); skipsave: if(data->set.hsts_write) { /* if there is a write callback */ @@ -406,6 +399,61 @@ skipsave: return result; } +/* only returns SERIOUS errors */ +static CURLcode hsts_add_host_expire(struct hsts *h, + const char *host, size_t hostlen, + const char *expire, size_t explen, + bool subdomain) /* default */ +{ + CURLcode result = CURLE_OK; + struct stsentry *e; + char dbuf[MAX_HSTS_DATELEN + 1]; + time_t expires = 0; + time_t now = time(NULL); + + /* The date parser works on a null-terminated string. */ + if(explen > MAX_HSTS_DATELEN) + return CURLE_BAD_FUNCTION_ARGUMENT; + memcpy(dbuf, expire, explen); + dbuf[explen] = 0; + + if(!strcmp(dbuf, UNLIMITED)) + expires = TIME_T_MAX; + else + Curl_getdate_capped(dbuf, &expires); + + if(expires <= now) + /* this entry already expired */ + return CURLE_OK; + + if(host[0] == '.') { + host++; + hostlen--; + subdomain = TRUE; + } + if(hostlen && (host[hostlen - 1] == '.')) + /* strip off any trailing dot */ + hostlen--; + + if(hostlen) { + /* only add it if not already present */ + e = Curl_hsts(h, host, hostlen, subdomain); + if(!e) + result = hsts_create(h, host, hostlen, subdomain, expires); + /* 'host' is not necessarily null terminated */ + else if((hostlen == strlen(e->host) && + curl_strnequal(host, e->host, hostlen))) { + /* the same hostname, use the largest expire time and keep the strictest + subdomain policy */ + if(expires > e->expires) + e->expires = expires; + if(subdomain) + e->includeSubDomains = TRUE; + } + } + return result; +} + /* only returns SERIOUS errors */ static CURLcode hsts_add(struct hsts *h, const char *line) { @@ -422,39 +470,9 @@ static CURLcode hsts_add(struct hsts *h, const char *line) curlx_str_newline(&line)) ; else { - CURLcode result = CURLE_OK; - bool subdomain = FALSE; - struct stsentry *e; - char dbuf[MAX_HSTS_DATELEN + 1]; - time_t expires = 0; - const char *hp = curlx_str(&host); - - /* The date parser works on a null-terminated string. The maximum length - is upheld by curlx_str_quotedword(). */ - memcpy(dbuf, curlx_str(&date), curlx_strlen(&date)); - dbuf[curlx_strlen(&date)] = 0; - - if(!strcmp(dbuf, UNLIMITED)) - expires = TIME_T_MAX; - else - Curl_getdate_capped(dbuf, &expires); - - if(hp[0] == '.') { - curlx_str_nudge(&host, 1); - subdomain = TRUE; - } - /* only add it if not already present */ - e = Curl_hsts(h, curlx_str(&host), curlx_strlen(&host), subdomain); - if(!e) - result = hsts_create(h, curlx_str(&host), curlx_strlen(&host), - subdomain, expires); - else if(curlx_str_casecompare(&host, e->host)) { - /* the same hostname, use the largest expire time */ - if(expires > e->expires) - e->expires = expires; - } - if(result) - return result; + return hsts_add_host_expire(h, curlx_str(&host), curlx_strlen(&host), + curlx_str(&date), curlx_strlen(&date), + FALSE); } return CURLE_OK; @@ -474,26 +492,26 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h) char buffer[MAX_HSTS_HOSTLEN + 1]; struct curl_hstsentry e; e.name = buffer; - e.namelen = sizeof(buffer)-1; + e.namelen = sizeof(buffer) - 1; e.includeSubDomains = FALSE; /* default */ e.expire[0] = 0; - e.name[0] = 0; /* just to make it clean */ + e.expire[MAX_HSTS_DATELEN] = 0; + e.name[0] = 0; /* to make it clean */ + e.name[MAX_HSTS_HOSTLEN] = 0; sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp); if(sc == CURLSTS_OK) { - time_t expires = 0; CURLcode result; - DEBUGASSERT(e.name[0]); - if(!e.name[0]) - /* bail out if no name was stored */ + const char *date = e.expire; + if(!e.name[0] || e.expire[MAX_HSTS_DATELEN] || + e.name[MAX_HSTS_HOSTLEN]) + /* bail out if no name was stored or if a null terminator is gone */ return CURLE_BAD_FUNCTION_ARGUMENT; - if(e.expire[0]) - Curl_getdate_capped(e.expire, &expires); - else - expires = TIME_T_MAX; /* the end of time */ - result = hsts_create(h, e.name, strlen(e.name), - /* bitfield to bool conversion: */ - e.includeSubDomains ? TRUE : FALSE, - expires); + if(!date[0]) + date = UNLIMITED; + result = hsts_add_host_expire(h, e.name, strlen(e.name), + date, strlen(date), + /* bitfield to bool conversion: */ + e.includeSubDomains ? TRUE : FALSE); if(result) return result; } @@ -519,30 +537,36 @@ static CURLcode hsts_load(struct hsts *h, const char *file) /* we need a private copy of the filename so that the hsts cache file name survives an easy handle reset */ - free(h->filename); - h->filename = strdup(file); + curlx_free(h->filename); + h->filename = curlx_strdup(file); if(!h->filename) return CURLE_OUT_OF_MEMORY; - fp = fopen(file, FOPEN_READTEXT); + fp = curlx_fopen(file, FOPEN_READTEXT); if(fp) { - struct dynbuf buf; - curlx_dyn_init(&buf, MAX_HSTS_LINE); - while(Curl_get_line(&buf, fp)) { - const char *lineptr = curlx_dyn_ptr(&buf); - curlx_str_passblanks(&lineptr); + curlx_struct_stat stat; + if((curlx_fstat(fileno(fp), &stat) == -1) || !S_ISDIR(stat.st_mode)) { + struct dynbuf buf; + bool eof = FALSE; + curlx_dyn_init(&buf, MAX_HSTS_LINE); + do { + result = Curl_get_line(&buf, fp, &eof); + if(!result) { + const char *lineptr = curlx_dyn_ptr(&buf); + curlx_str_passblanks(&lineptr); - /* - * Skip empty or commented lines, since we know the line will have a - * trailing newline from Curl_get_line we can treat length 1 as empty. - */ - if((*lineptr == '#') || strlen(lineptr) <= 1) - continue; + /* Skip empty or commented lines, since we know the line will have + a trailing newline from Curl_get_line we can treat length 1 as + empty. */ + if((*lineptr == '#') || strlen(lineptr) <= 1) + continue; - hsts_add(h, lineptr); + hsts_add(h, lineptr); + } + } while(!result && !eof); + curlx_dyn_free(&buf); /* free the line buffer */ } - curlx_dyn_free(&buf); /* free the line buffer */ - fclose(fp); + curlx_fclose(fp); } return result; } @@ -568,18 +592,22 @@ CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h) return CURLE_OK; } -void Curl_hsts_loadfiles(struct Curl_easy *data) +CURLcode Curl_hsts_loadfiles(struct Curl_easy *data) { + CURLcode result = CURLE_OK; struct curl_slist *l = data->state.hstslist; if(l) { Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE); while(l) { - (void)Curl_hsts_loadfile(data, data->hsts, l->data); + result = Curl_hsts_loadfile(data, data->hsts, l->data); + if(result) + break; l = l->next; } Curl_share_unlock(data, CURL_LOCK_DATA_HSTS); } + return result; } #if defined(DEBUGBUILD) || defined(UNITTESTS) diff --git a/lib/hsts.h b/lib/hsts.h index 8ec9637cb0..0e6585f116 100644 --- a/lib/hsts.h +++ b/lib/hsts.h @@ -26,18 +26,19 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS) -#include #include "llist.h" +#define MAX_HSTS_ENTRIES 10000 + #if defined(DEBUGBUILD) || defined(UNITTESTS) extern time_t deltatime; #endif struct stsentry { struct Curl_llist_node node; - const char *host; curl_off_t expires; /* the timestamp of this entry's expiry */ BIT(includeSubDomains); + char host[1]; }; /* The HSTS cache. Needs to be able to tailmatch hostnames. */ @@ -50,7 +51,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, @@ -59,11 +60,11 @@ CURLcode Curl_hsts_loadfile(struct Curl_easy *data, struct hsts *h, const char *file); CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h); -void Curl_hsts_loadfiles(struct Curl_easy *data); +CURLcode Curl_hsts_loadfiles(struct Curl_easy *data); #else #define Curl_hsts_cleanup(x) -#define Curl_hsts_loadcb(x,y) CURLE_OK -#define Curl_hsts_save(x,y,z) -#define Curl_hsts_loadfiles(x) +#define Curl_hsts_loadcb(x, y) CURLE_OK +#define Curl_hsts_save(x, y, z) +#define Curl_hsts_loadfiles(x) CURLE_OK #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */ #endif /* HEADER_CURL_HSTS_H */ diff --git a/lib/http.c b/lib/http.c index e01de6f477..0f826f0596 100644 --- a/lib/http.c +++ b/lib/http.c @@ -21,8 +21,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" +#include "urldata.h" #ifndef CURL_DISABLE_HTTP @@ -47,17 +47,15 @@ #include #endif -#include "urldata.h" -#include #include "transfer.h" #include "sendf.h" +#include "curl_trc.h" #include "formdata.h" #include "mime.h" #include "progress.h" #include "curlx/base64.h" #include "cookie.h" #include "vauth/vauth.h" -#include "vtls/vtls.h" #include "vquic/vquic.h" #include "http_digest.h" #include "http_ntlm.h" @@ -65,7 +63,7 @@ #include "http_aws_sigv4.h" #include "url.h" #include "urlapi-int.h" -#include "share.h" +#include "curl_share.h" #include "hostip.h" #include "dynhds.h" #include "http.h" @@ -76,108 +74,17 @@ #include "strcase.h" #include "content_encoding.h" #include "http_proxy.h" -#include "curlx/warnless.h" #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" #include "ws.h" -#include "curl_ctype.h" +#include "bufref.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.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); - - -/* - * 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 */ - Curl_http_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - Curl_http_do_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* 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 -}; - -#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 */ - Curl_http_connect, /* connect_it */ - NULL, /* connecting */ - ZERO_NULL, /* doing */ - NULL, /* proto_pollset */ - Curl_http_do_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* 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 -}; - -#endif - void Curl_http_neg_init(struct Curl_easy *data, struct http_negotiation *neg) { memset(neg, 0, sizeof(*neg)); @@ -221,7 +128,6 @@ CURLcode Curl_http_setup_conn(struct Curl_easy *data, { /* allocate the HTTP-specific struct for the Curl_easy, only to survive during this request */ - connkeep(conn, "HTTP default"); if(data->state.http_neg.wanted == CURL_HTTP_V3x) { /* only HTTP/3, needs to work */ CURLcode result = Curl_conn_may_http3(data, conn, conn->transport_wanted); @@ -258,33 +164,80 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, return NULL; } -#else -/* disabled */ -#define Curl_checkProxyheaders(x,y,z,a) NULL #endif +/* If the header has a value, this function returns TRUE and the value is in + 'outp' with blanks trimmed off. +*/ +static bool header_has_value(const char **headerp, struct Curl_str *outp) +{ + bool value = !curlx_str_cspn(headerp, outp, ";:") && + (!curlx_str_single(headerp, ':') || !curlx_str_single(headerp, ';')); + + if(value) { + curlx_str_untilnl(headerp, outp, MAX_HTTP_RESP_HEADER_SIZE); + curlx_str_trimblanks(outp); + } + return value; +} + +static bool http_header_is_empty(const char *header) +{ + struct Curl_str out; + + if(header_has_value(&header, &out)) { + return curlx_strlen(&out) == 0; + } + return TRUE; /* invalid header format, treat as empty */ +} + /* * Strip off leading and trailing whitespace from the value in the given HTTP - * header line and return a strdup()ed copy. Returns NULL in case of - * allocation failure or bad input. Returns an empty string if the header - * value consists entirely of whitespace. + * header line and return a strdup-ed copy in 'valp' - returns an empty + * string if the header value consists entirely of whitespace. * - * If the header is provided as "name;", ending with a semicolon, it must - * return a blank string. + * If the header is provided as "name;", ending with a semicolon, it returns a + * blank string. + */ +static CURLcode copy_custom_value(const char *header, char **valp) +{ + struct Curl_str out; + + /* find the end of the header name */ + if(header_has_value(&header, &out)) { + *valp = curlx_memdup0(curlx_str(&out), curlx_strlen(&out)); + if(*valp) + return CURLE_OK; + return CURLE_OUT_OF_MEMORY; + } + /* bad input */ + *valp = NULL; + return CURLE_BAD_FUNCTION_ARGUMENT; +} + +/* + * Strip off leading and trailing whitespace from the value in the given HTTP + * header line and return a strdup-ed copy in 'valp' - returns an empty + * string if the header value consists entirely of whitespace. + * + * This function MUST be used after the header has already been confirmed to + * lead with "word:". + * + * @unittest: 1626 */ char *Curl_copy_header_value(const char *header) { struct Curl_str out; /* find the end of the header name */ - if(!curlx_str_cspn(&header, &out, ";:") && - (!curlx_str_single(&header, ':') || !curlx_str_single(&header, ';'))) { + if(!curlx_str_until(&header, &out, MAX_HTTP_RESP_HEADER_SIZE, ':') && + !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 */ + /* bad input, should never happen */ + DEBUGASSERT(0); return NULL; } @@ -311,7 +264,7 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) connection */ if(proxy) { #ifndef CURL_DISABLE_PROXY - userp = &data->state.aptr.proxyuserpwd; + userp = &data->req.proxyuserpwd; user = data->state.aptr.proxyuser; pwd = data->state.aptr.proxypasswd; #else @@ -319,16 +272,17 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) #endif } else { - userp = &data->state.aptr.userpwd; + userp = &data->req.userpwd; user = data->state.aptr.user; pwd = data->state.aptr.passwd; } - out = aprintf("%s:%s", user ? user : "", pwd ? pwd : ""); + out = curl_maprintf("%s:%s", user ? user : "", pwd ? pwd : ""); if(!out) return CURLE_OUT_OF_MEMORY; - result = curlx_base64_encode(out, strlen(out), &authorization, &size); + result = curlx_base64_encode((uint8_t *)out, strlen(out), + &authorization, &size); if(result) goto fail; @@ -337,18 +291,18 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) goto fail; } - free(*userp); - *userp = aprintf("%sAuthorization: Basic %s\r\n", - proxy ? "Proxy-" : "", - authorization); - free(authorization); + curlx_free(*userp); + *userp = curl_maprintf("%sAuthorization: Basic %s\r\n", + proxy ? "Proxy-" : "", + authorization); + curlx_free(authorization); if(!*userp) { result = CURLE_OUT_OF_MEMORY; goto fail; } fail: - free(out); + curlx_free(out); return result; } @@ -366,10 +320,10 @@ static CURLcode http_output_bearer(struct Curl_easy *data) char **userp; CURLcode result = CURLE_OK; - userp = &data->state.aptr.userpwd; - free(*userp); - *userp = aprintf("Authorization: Bearer %s\r\n", - data->set.str[STRING_BEARER]); + userp = &data->req.userpwd; + curlx_free(*userp); + *userp = curl_maprintf("Authorization: Bearer %s\r\n", + data->set.str[STRING_BEARER]); if(!*userp) { result = CURLE_OUT_OF_MEMORY; @@ -384,7 +338,7 @@ fail: #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 @@ -447,10 +401,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 @@ -465,11 +419,11 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, return CURLE_OK; if(abort_upload) { - /* We'd like to abort the upload - but should we? */ + /* We would like to abort the upload - but should we? */ #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. @@ -482,7 +436,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. @@ -507,24 +461,95 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, /* We decided to abort the ongoing transfer */ streamclose(conn, "Mid-auth HTTP and much data left to send"); data->req.size = 0; /* do not download any more than 0 bytes */ + data->req.http_bodyless = TRUE; } 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. 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; @@ -562,7 +587,6 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) data->state.authproblem = TRUE; else data->info.proxyauthpicked = data->state.authproxy.picked; - } #endif @@ -574,13 +598,14 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) /* In case this is GSS auth, the newurl field is already allocated so we must make sure to free it before allocating a new one. As figured out in bug #2284386 */ - free(data->req.newurl); - data->req.newurl = strdup(data->state.url); /* clone URL */ + curlx_free(data->req.newurl); + /* clone URL */ + data->req.newurl = Curl_bufref_dup(&data->state.url); if(!data->req.newurl) return CURLE_OUT_OF_MEMORY; } else if((data->req.httpcode < 300) && - (!data->state.authhost.done) && + !data->state.authhost.done && data->req.authneg) { /* no (known) authentication available, authentication is not "done" yet and @@ -588,7 +613,8 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) we did not try HEAD or GET */ if((data->state.httpreq != HTTPREQ_GET) && (data->state.httpreq != HTTPREQ_HEAD)) { - data->req.newurl = strdup(data->state.url); /* clone URL */ + /* clone URL */ + data->req.newurl = Curl_bufref_dup(&data->state.url); if(!data->req.newurl) return CURLE_OUT_OF_MEMORY; data->state.authhost.done = TRUE; @@ -608,13 +634,12 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) * Output the correct authentication header depending on the auth type * and whether or not it is to a proxy. */ -static CURLcode -output_auth_headers(struct Curl_easy *data, - struct connectdata *conn, - struct auth *authstatus, - const char *request, - const char *path, - bool proxy) +static CURLcode output_auth_headers(struct Curl_easy *data, + struct connectdata *conn, + struct auth *authstatus, + const char *request, + const char *path, + bool proxy) { const char *auth = NULL; CURLcode result = CURLE_OK; @@ -669,11 +694,12 @@ 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) @@ -688,8 +714,9 @@ 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) @@ -704,6 +731,10 @@ output_auth_headers(struct Curl_easy *data, if(auth) { #ifndef CURL_DISABLE_PROXY + if(proxy) + data->info.proxyauthpicked = authstatus->picked; + else + data->info.httpauthpicked = authstatus->picked; infof(data, "%s auth using %s with user '%s'", proxy ? "Proxy" : "Server", auth, proxy ? (data->state.aptr.proxyuser ? @@ -718,8 +749,13 @@ output_auth_headers(struct Curl_easy *data, #endif authstatus->multipass = !authstatus->done; } - else + else { authstatus->multipass = FALSE; + if(proxy) + data->info.proxyauthpicked = 0; + else + data->info.httpauthpicked = 0; + } return result; } @@ -738,14 +774,14 @@ output_auth_headers(struct Curl_easy *data, * * @returns CURLcode */ -CURLcode -Curl_http_output_auth(struct Curl_easy *data, - struct connectdata *conn, - const char *request, - Curl_HttpReq httpreq, - const char *path, - bool proxytunnel) /* TRUE if this is the request setting - up the proxy tunnel */ +CURLcode Curl_http_output_auth(struct Curl_easy *data, + struct connectdata *conn, + const char *request, + Curl_HttpReq httpreq, + const char *path, + bool proxytunnel) /* TRUE if this is + the request setting up + the proxy tunnel */ { CURLcode result = CURLE_OK; struct auth *authhost; @@ -788,7 +824,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; @@ -828,13 +864,12 @@ Curl_http_output_auth(struct Curl_easy *data, #else /* when disabled */ -CURLcode -Curl_http_output_auth(struct Curl_easy *data, - struct connectdata *conn, - const char *request, - Curl_HttpReq httpreq, - const char *path, - bool proxytunnel) +CURLcode Curl_http_output_auth(struct Curl_easy *data, + struct connectdata *conn, + const char *request, + Curl_HttpReq httpreq, + const char *path, + bool proxytunnel) { (void)data; (void)conn; @@ -863,7 +898,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; @@ -875,8 +910,8 @@ static CURLcode auth_spnego(struct Curl_easy *data, curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state : &conn->http_negotiate_state; if(!result) { - free(data->req.newurl); - data->req.newurl = strdup(data->state.url); + curlx_free(data->req.newurl); + data->req.newurl = Curl_bufref_dup(&data->state.url); if(!data->req.newurl) return CURLE_OUT_OF_MEMORY; data->state.authproblem = FALSE; @@ -896,7 +931,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()) { @@ -909,6 +944,8 @@ static CURLcode auth_ntlm(struct Curl_easy *data, if(!result) data->state.authproblem = FALSE; else { + if(result == CURLE_OUT_OF_MEMORY) + return result; infof(data, "NTLM authentication problem, ignoring."); data->state.authproblem = TRUE; } @@ -923,10 +960,12 @@ 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) + if(authp->avail & CURLAUTH_DIGEST) { + *availp |= CURLAUTH_DIGEST; infof(data, "Ignoring duplicate digest auth header."); + } else if(Curl_auth_is_digest_supported()) { CURLcode result; @@ -939,6 +978,8 @@ static CURLcode auth_digest(struct Curl_easy *data, * Digest */ result = Curl_input_digest(data, proxy, auth); if(result) { + if(result == CURLE_OUT_OF_MEMORY) + return result; infof(data, "Digest authentication problem, ignoring."); data->state.authproblem = TRUE; } @@ -950,14 +991,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; @@ -969,13 +1009,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; @@ -1003,7 +1043,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); @@ -1077,82 +1117,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; } @@ -1163,6 +1134,11 @@ static void http_switch_to_get(struct Curl_easy *data, int code) Curl_creader_set_rewind(data, FALSE); } +#define HTTPREQ_IS_POST(data) \ + ((data)->state.httpreq == HTTPREQ_POST || \ + (data)->state.httpreq == HTTPREQ_POST_FORM || \ + (data)->state.httpreq == HTTPREQ_POST_MIME) + CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, followtype type) { @@ -1195,18 +1171,15 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, /* We are asked to automatically set the previous URL as the referer when we get the next URL. We pick the ->url field, which may or may not be 100% correct */ - - if(data->state.referer_alloc) { - Curl_safefree(data->state.referer); - data->state.referer_alloc = FALSE; - } + Curl_bufref_free(&data->state.referer); /* Make a copy of the URL without credentials and fragment */ u = curl_url(); if(!u) return CURLE_OUT_OF_MEMORY; - uc = curl_url_set(u, CURLUPART_URL, data->state.url, 0); + uc = curl_url_set(u, CURLUPART_URL, + Curl_bufref_ptr(&data->state.url), 0); if(!uc) uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0); if(!uc) @@ -1221,8 +1194,7 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, if(uc || !referer) return CURLE_OUT_OF_MEMORY; - data->state.referer = referer; - data->state.referer_alloc = TRUE; /* yes, free this later */ + Curl_bufref_set(&data->state.referer, referer, 0, curl_free); } } } @@ -1242,15 +1214,15 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, CURLU_ALLOW_SPACE | (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); if(uc) { - if(type != FOLLOW_FAKE) { + if((uc == CURLUE_OUT_OF_MEMORY) || (type != FOLLOW_FAKE)) { failf(data, "The redirect target URL could not be parsed: %s", curl_url_strerror(uc)); return Curl_uc_to_curlcode(uc); } /* the URL could not be parsed for some reason, but since this is FAKE - mode, just duplicate the field as-is */ - follow_url = strdup(newurl); + mode, duplicate the field as-is */ + follow_url = curlx_strdup(newurl); if(!follow_url) return CURLE_OUT_OF_MEMORY; } @@ -1262,22 +1234,26 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, /* Clear auth if this redirects to a different port number or protocol, unless permitted */ if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) { - char *portnum; - int port; + uint16_t port; bool clear = FALSE; if(data->set.use_port && data->state.allow_port) /* a custom port is used */ - port = (int)data->set.use_port; + port = data->set.use_port; else { + curl_off_t value; + char *portnum; + const char *p; uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum, CURLU_DEFAULT_PORT); if(uc) { - free(follow_url); + curlx_free(follow_url); return Curl_uc_to_curlcode(uc); } - port = atoi(portnum); - free(portnum); + p = portnum; + curlx_str_number(&p, &value, 0xffff); + port = (uint16_t)value; + curlx_free(portnum); } if(port != data->info.conn_remote_port) { infof(data, "Clear auth, redirects to port from %u to %u", @@ -1286,28 +1262,40 @@ 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) { - free(follow_url); + 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); clear = TRUE; } - free(scheme); + curlx_free(scheme); } if(clear) { - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); + CURLcode result = Curl_reset_userpwd(data); + if(result) { + curlx_free(follow_url); + return result; + } + curlx_safefree(data->state.aptr.user); + curlx_safefree(data->state.aptr.passwd); } } } DEBUGASSERT(follow_url); + { + CURLcode result = Curl_reset_proxypwd(data); + if(result) { + curlx_free(follow_url); + return result; + } + } if(type == FOLLOW_FAKE) { /* we are only figuring out the new URL if we would have followed locations @@ -1324,13 +1312,9 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, if(disallowport) data->state.allow_port = FALSE; - if(data->state.url_alloc) - Curl_safefree(data->state.url); - - data->state.url = follow_url; - data->state.url_alloc = TRUE; + Curl_bufref_set(&data->state.url, follow_url, 0, curl_free); rewind_result = Curl_req_soft_reset(&data->req, data); - infof(data, "Issue another request to this URL: '%s'", data->state.url); + infof(data, "Issue another request to this URL: '%s'", follow_url); if((data->set.http_follow_mode == CURLFOLLOW_FIRSTONLY) && data->set.str[STRING_CUSTOMREQUEST] && !data->state.http_ignorecustom) { @@ -1353,7 +1337,7 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, /* 300 - Multiple Choices */ /* 306 - Not used */ /* 307 - Temporary Redirect */ - default: /* for all above (and the unknown ones) */ + default: /* for all above (and the unknown ones) */ /* Some codes are explicitly mentioned since I have checked RFC2616 and * they seem to be OK to POST to. */ @@ -1375,10 +1359,7 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and * can be overridden with CURLOPT_POSTREDIR. */ - if((data->state.httpreq == HTTPREQ_POST - || data->state.httpreq == HTTPREQ_POST_FORM - || data->state.httpreq == HTTPREQ_POST_MIME) - && !(data->set.keep_post & CURL_REDIR_POST_301)) { + if(HTTPREQ_IS_POST(data) && !data->set.post301) { http_switch_to_get(data, 301); switch_to_get = TRUE; } @@ -1400,10 +1381,7 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and * can be overridden with CURLOPT_POSTREDIR. */ - if((data->state.httpreq == HTTPREQ_POST - || data->state.httpreq == HTTPREQ_POST_FORM - || data->state.httpreq == HTTPREQ_POST_MIME) - && !(data->set.keep_post & CURL_REDIR_POST_302)) { + if(HTTPREQ_IS_POST(data) && !data->set.post302) { http_switch_to_get(data, 302); switch_to_get = TRUE; } @@ -1413,13 +1391,8 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, /* 'See Other' location is not the resource but a substitute for the * resource. In this case we switch the method to GET/HEAD, unless the * method is POST and the user specified to keep it as POST. - * https://github.com/curl/curl/issues/5237#issuecomment-614641049 */ - if(data->state.httpreq != HTTPREQ_GET && - ((data->state.httpreq != HTTPREQ_POST && - data->state.httpreq != HTTPREQ_POST_FORM && - data->state.httpreq != HTTPREQ_POST_MIME) || - !(data->set.keep_post & CURL_REDIR_POST_303))) { + if(!HTTPREQ_IS_POST(data) || !data->set.post303) { http_switch_to_get(data, 303); switch_to_get = TRUE; } @@ -1454,15 +1427,16 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, /* * Curl_compareheader() * - * Returns TRUE if 'headerline' contains the 'header' with given 'content'. - * Pass headers WITH the colon. + * Returns TRUE if 'headerline' contains the 'header' with given 'content' + * (within a comma-separated list of tokens). Pass 'header' WITH the colon. + * + * @unittest: 1625 */ -bool -Curl_compareheader(const char *headerline, /* line to check */ - const char *header, /* header keyword _with_ colon */ - const size_t hlen, /* len of the keyword in bytes */ - const char *content, /* content string to find */ - const size_t clen) /* len of the content in bytes */ +bool Curl_compareheader(const char *headerline, /* line to check */ + const char *header, /* header keyword _with_ colon */ + const size_t hlen, /* len of the keyword in bytes */ + const char *content, /* content string to find */ + const size_t clen) /* len of the content in bytes */ { /* RFC2616, section 4.2 says: "Each header field consists of a name followed * by a colon (":") and the field value. Field names are case-insensitive. @@ -1490,39 +1464,222 @@ Curl_compareheader(const char *headerline, /* line to check */ 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)) + for(len = curlx_strlen(&val); len >= clen;) { + struct Curl_str next; + const char *o = p; + /* after a match there must be a comma, space, newline or null byte */ + if(curl_strnequal(p, content, clen) && + ((p[clen] == ',') || ISBLANK(p[clen]) || ISNEWLINE(p[clen]) || + !p[clen])) return TRUE; /* match! */ + /* advance to the next comma */ + if(curlx_str_until(&p, &next, MAX_HTTP_RESP_HEADER_SIZE, ',') || + curlx_str_single(&p, ',')) + break; /* no comma, get out */ + + /* if there are more dummy commas, move over them as well */ + do + curlx_str_passblanks(&p); + while(!curlx_str_single(&p, ',')); + len -= (p - o); } } return FALSE; /* no match */ } -/* - * Curl_http_connect() performs HTTP stuff to do at connect-time, called from - * the generic Curl_connect(). - */ -CURLcode Curl_http_connect(struct Curl_easy *data, bool *done) +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 connectdata *conn = data->conn; + 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); + } +} - /* We default to persistent connections. We set this already in this connect - function to make the reuse checks properly be able to check this bit. */ - connkeep(conn, "HTTP default"); +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; - return Curl_conn_connect(data, FIRSTSOCKET, FALSE, done); + 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); +} + +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 interface and then we are always _sending_ a request and thus we wait for the single socket to become writable only */ -CURLcode Curl_http_do_pollset(struct Curl_easy *data, - struct easy_pollset *ps) +CURLcode Curl_http_doing_pollset(struct Curl_easy *data, + struct easy_pollset *ps) { /* write mode */ return Curl_pollset_add_out(data, ps, data->conn->sock[FIRSTSOCKET]); } +CURLcode Curl_http_perform_pollset(struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + + if(CURL_REQ_WANT_RECV(data)) { + result = Curl_pollset_add_in(data, ps, conn->sock[FIRSTSOCKET]); + } + + /* on a "Expect: 100-continue" timed wait, do not poll for outgoing */ + if(!result && Curl_req_want_send(data) && !http_exp100_is_waiting(data)) { + result = Curl_pollset_add_out(data, ps, conn->sock[FIRSTSOCKET]); + } + 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. @@ -1538,6 +1695,10 @@ CURLcode Curl_http_done(struct Curl_easy *data, data->state.authhost.multipass = FALSE; data->state.authproxy.multipass = FALSE; + if(curlx_dyn_len(&data->state.headerb)) { + (void)http_write_header(data, curlx_dyn_ptr(&data->state.headerb), + curlx_dyn_len(&data->state.headerb)); + } curlx_dyn_reset(&data->state.headerb); if(status) @@ -1550,7 +1711,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"); @@ -1595,14 +1756,14 @@ static unsigned char http_request_version(struct Curl_easy *data) static const char *get_http_string(int httpversion) { switch(httpversion) { - case 30: - return "3"; - case 20: - return "2"; - case 11: - return "1.1"; - default: - return "1.0"; + case 30: + return "3"; + case 20: + return "2"; + case 11: + return "1.1"; + default: + return "1.0"; } } @@ -1672,7 +1833,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, curlx_str_untilnl(&p, &val, MAX_HTTP_RESP_HEADER_SIZE); curlx_str_trimblanks(&val); if(!curlx_strlen(&val)) - /* no content, don't send this */ + /* no content, do not send this */ continue; } else @@ -1702,7 +1863,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, curlx_str_casecompare(&name, "Content-Length")) ; else if(curlx_str_casecompare(&name, "Connection")) - /* Normal Connection: header generation takes care of this */ + /* Connection headers are handled specially */ ; else if((httpversion >= 20) && curlx_str_casecompare(&name, "Transfer-Encoding")) @@ -1743,7 +1904,7 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, /* no condition was asked for */ return CURLE_OK; - result = Curl_gmtime(data->set.timevalue, &keeptime); + result = curlx_gmtime(data->set.timevalue, &keeptime); if(result) { failf(data, "Invalid TIMEVALUE"); return result; @@ -1782,16 +1943,16 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, */ /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ - msnprintf(datestr, sizeof(datestr), - "%s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", - condp, - 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); + curl_msnprintf(datestr, sizeof(datestr), + "%s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", + condp, + 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 = curlx_dyn_add(req, datestr); return result; @@ -1812,9 +1973,12 @@ void Curl_http_method(struct Curl_easy *data, { Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq; const char *request; - if(data->conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) +#ifndef CURL_DISABLE_WEBSOCKETS + if(data->conn->scheme->protocol & (CURLPROTO_WS | CURLPROTO_WSS)) httpreq = HTTPREQ_GET; - else if((data->conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) && + else +#endif + if((data->conn->scheme->protocol & (PROTO_FAMILY_HTTP | CURLPROTO_FTP)) && data->state.upload) httpreq = HTTPREQ_PUT; @@ -1853,18 +2017,17 @@ void Curl_http_method(struct Curl_easy *data, static CURLcode http_useragent(struct Curl_easy *data) { - /* The User-Agent string might have been allocated in url.c already, because + /* The User-Agent string might have been allocated already, because it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ if(Curl_checkheaders(data, STRCONST("User-Agent"))) { - free(data->state.aptr.uagent); + curlx_free(data->state.aptr.uagent); data->state.aptr.uagent = NULL; } return CURLE_OK; } - static CURLcode http_set_aptr_host(struct Curl_easy *data) { struct connectdata *conn = data->conn; @@ -1873,16 +2036,19 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) if(!data->state.this_is_a_follow) { /* Free to avoid leaking memory on multiple requests */ - free(data->state.first_host); + curlx_free(data->state.first_host); - data->state.first_host = strdup(conn->host.name); + data->state.first_host = curlx_strdup(conn->host.name); if(!data->state.first_host) 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); + curlx_safefree(aptr->host); +#ifndef CURL_DISABLE_COOKIES + curlx_safefree(data->req.cookiehost); +#endif ptr = Curl_checkheaders(data, STRCONST("Host")); if(ptr && (!data->state.this_is_a_follow || @@ -1893,19 +2059,20 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) custom Host: header if this is NOT a redirect, as setting Host: in the redirected request is being out on thin ice. Except if the hostname is the same as the first one! */ - char *cookiehost = Curl_copy_header_value(ptr); - if(!cookiehost) - return CURLE_OUT_OF_MEMORY; + char *cookiehost; + CURLcode result = copy_custom_value(ptr, &cookiehost); + if(result) + return result; if(!*cookiehost) /* ignore empty data */ - free(cookiehost); + curlx_free(cookiehost); else { /* If the host begins with '[', we start searching for the port after the bracket has been closed */ 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) @@ -1917,35 +2084,30 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) if(colon) *colon = 0; /* The host must not include an embedded port number */ } - free(aptr->cookiehost); - aptr->cookiehost = cookiehost; + data->req.cookiehost = cookiehost; } #endif if(!curl_strequal("Host:", ptr)) { - aptr->host = aprintf("Host:%s\r\n", &ptr[5]); + aptr->host = curl_maprintf("Host:%s\r\n", &ptr[5]); if(!aptr->host) return CURLE_OUT_OF_MEMORY; } } else { - /* When building Host: headers, we must put the hostname within - [brackets] if the hostname is a plain IPv6-address. RFC2732-style. */ - const char *host = conn->host.name; + /* Use the hostname as present in the URL if it was IPv6. */ + char *host = (data->state.up.hostname[0] == '[') ? + data->state.up.hostname : conn->host.name; - if(((conn->given->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS)) && + if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) && (conn->remote_port == PORT_HTTPS)) || - ((conn->given->protocol&(CURLPROTO_HTTP|CURLPROTO_WS)) && - (conn->remote_port == PORT_HTTP)) ) + ((conn->given->protocol & (CURLPROTO_HTTP | CURLPROTO_WS)) && + (conn->remote_port == PORT_HTTP))) /* if(HTTPS on port 443) OR (HTTP on port 80) then do not include the port number in the host string */ - aptr->host = aprintf("Host: %s%s%s\r\n", conn->bits.ipv6_ip ? "[" : "", - host, conn->bits.ipv6_ip ? "]" : ""); + aptr->host = curl_maprintf("Host: %s\r\n", host); else - aptr->host = aprintf("Host: %s%s%s:%d\r\n", - conn->bits.ipv6_ip ? "[" : "", - host, conn->bits.ipv6_ip ? "]" : "", - conn->remote_port); + aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->remote_port); if(!aptr->host) /* without Host: we cannot make a nice request */ @@ -1963,6 +2125,9 @@ static CURLcode http_target(struct Curl_easy *data, CURLcode result = CURLE_OK; const char *path = data->state.up.path; const char *query = data->state.up.query; +#ifndef CURL_DISABLE_PROXY + struct connectdata *conn = data->conn; +#endif if(data->set.str[STRING_TARGET]) { path = data->set.str[STRING_TARGET]; @@ -1970,10 +2135,10 @@ static CURLcode http_target(struct Curl_easy *data, } #ifndef CURL_DISABLE_PROXY - if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) { + if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { /* Using a proxy but does not tunnel through it */ - /* The path sent to the proxy is in fact the entire URL. But if the remote + /* The path sent to the proxy is in fact the entire URL, but if the remote host is a IDN-name, we must make sure that the request we produce only uses the encoded hostname! */ @@ -1984,8 +2149,8 @@ static CURLcode http_target(struct Curl_easy *data, if(!h) return CURLE_OUT_OF_MEMORY; - if(data->conn->host.dispname != data->conn->host.name) { - uc = curl_url_set(h, CURLUPART_HOST, data->conn->host.name, 0); + if(conn->host.dispname != conn->host.name) { + uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0); if(uc) { curl_url_cleanup(h); return CURLE_OUT_OF_MEMORY; @@ -2022,31 +2187,30 @@ static CURLcode http_target(struct Curl_easy *data, /* target or URL */ result = curlx_dyn_add(r, data->set.str[STRING_TARGET] ? data->set.str[STRING_TARGET] : url); - free(url); + curlx_free(url); if(result) return result; - if(curl_strequal("ftp", data->state.up.scheme)) { - if(data->set.proxy_transfer_mode) { - /* when doing ftp, append ;type= if not present */ - char *type = strstr(path, ";type="); - if(type && type[6] && type[7] == 0) { - switch(Curl_raw_toupper(type[6])) { - case 'A': - case 'D': - case 'I': - break; - default: - type = NULL; - } - } - if(!type) { - result = curlx_dyn_addf(r, ";type=%c", - data->state.prefer_ascii ? 'a' : 'i'); - if(result) - return result; + if(curl_strequal("ftp", data->state.up.scheme) && + data->set.proxy_transfer_mode) { + /* when doing ftp, append ;type= if not present */ + size_t len = strlen(path); + bool type_present = FALSE; + if((len >= 7) && !memcmp(&path[len - 7], ";type=", 6)) { + switch(Curl_raw_toupper(path[len - 1])) { + case 'A': + case 'D': + case 'I': + type_present = TRUE; + break; } } + if(!type_present) { + result = curlx_dyn_addf(r, ";type=%c", + data->state.prefer_ascii ? 'a' : 'i'); + if(result) + return result; + } } } @@ -2071,7 +2235,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 @@ -2079,14 +2243,14 @@ static CURLcode set_post_reader(struct Curl_easy *data, Curl_HttpReq httpreq) /* Convert the form structure into a mime structure, then keep the conversion */ if(!data->state.formp) { - data->state.formp = calloc(1, sizeof(curl_mimepart)); + data->state.formp = curlx_calloc(1, sizeof(curl_mimepart)); if(!data->state.formp) return CURLE_OUT_OF_MEMORY; Curl_mime_cleanpart(data->state.formp); result = Curl_getformdata(data, data->state.formp, data->set.httppost, data->state.fread_func); if(result) { - Curl_safefree(data->state.formp); + curlx_safefree(data->state.formp); return result; } data->state.mimepost = data->state.formp; @@ -2171,9 +2335,11 @@ static CURLcode set_reader(struct Curl_easy *data, Curl_HttpReq httpreq) result = Curl_creader_set_null(data); } else if(data->set.postfields) { - if(postsize > 0) - result = Curl_creader_set_buf(data, data->set.postfields, - (size_t)postsize); + size_t plen = curlx_sotouz_range(postsize, 0, SIZE_MAX); + if(plen == SIZE_MAX) + return CURLE_OUT_OF_MEMORY; + else if(plen) + result = Curl_creader_set_buf(data, data->set.postfields, plen); else result = Curl_creader_set_null(data); } @@ -2289,11 +2455,11 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, *announced_exp100 = FALSE; /* Avoid Expect: 100-continue if Upgrade: is used */ - if(data->req.upgr101 != UPGR101_INIT) + if(data->req.upgr101 != UPGR101_NONE) 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")); @@ -2351,7 +2517,7 @@ static CURLcode http_add_content_hds(struct Curl_easy *data, (data->req.authneg || !Curl_checkheaders(data, STRCONST("Content-Length")))) { /* we allow replacing this header if not during auth negotiation, - although it is not very wise to actually set your own */ + although it is not wise to actually set your own */ result = curlx_dyn_addf(r, "Content-Length: %" FMT_OFF_T "\r\n", req_clen); } @@ -2412,10 +2578,12 @@ static CURLcode http_cookies(struct Curl_easy *data, int count = 0; if(data->cookies && data->state.cookie_engine) { - const char *host = data->state.aptr.cookiehost ? - data->state.aptr.cookiehost : data->conn->host.name; + bool okay; + const char *host = data->req.cookiehost ? + data->req.cookiehost : data->conn->host.name; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - if(!Curl_cookie_getlist(data, data->conn, host, &list)) { + result = Curl_cookie_getlist(data, data->conn, &okay, host, &list); + if(!result && okay) { struct Curl_llist_node *n; size_t clen = 8; /* hold the size of the generated Cookie: header */ @@ -2465,7 +2633,7 @@ static CURLcode http_cookies(struct Curl_easy *data, return result; } #else -#define http_cookies(a,b) CURLE_OK +#define http_cookies(a, b) CURLE_OK #endif static CURLcode http_range(struct Curl_easy *data, @@ -2480,24 +2648,25 @@ static CURLcode http_range(struct Curl_easy *data, if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) && !Curl_checkheaders(data, STRCONST("Range"))) { /* if a line like this was already allocated, free the previous one */ - free(data->state.aptr.rangeline); - data->state.aptr.rangeline = aprintf("Range: bytes=%s\r\n", - data->state.range); + curlx_free(data->state.aptr.rangeline); + data->state.aptr.rangeline = curl_maprintf("Range: bytes=%s\r\n", + data->state.range); + if(!data->state.aptr.rangeline) + return CURLE_OUT_OF_MEMORY; } else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) && !Curl_checkheaders(data, STRCONST("Content-Range"))) { curl_off_t req_clen = Curl_creader_total_length(data); /* if a line like this was already allocated, free the previous one */ - free(data->state.aptr.rangeline); + curlx_free(data->state.aptr.rangeline); if(data->set.set_resume_from < 0) { /* Upload resume was asked for, but we do not know the size of the remote part so we tell the server (and act accordingly) that we upload the whole file (again) */ data->state.aptr.rangeline = - aprintf("Content-Range: bytes 0-%" FMT_OFF_T "/%" FMT_OFF_T "\r\n", - req_clen - 1, req_clen); - + 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 */ @@ -2508,15 +2677,16 @@ static CURLcode http_range(struct Curl_easy *data, data->state.infilesize : (data->state.resume_from + req_clen); data->state.aptr.rangeline = - aprintf("Content-Range: bytes %s%" FMT_OFF_T "/%" FMT_OFF_T "\r\n", - data->state.range, total_len-1, total_len); + curl_maprintf("Content-Range: bytes %s%" FMT_OFF_T "/" + "%" FMT_OFF_T "\r\n", + data->state.range, total_len - 1, total_len); } else { - /* Range was selected and then we just pass the incoming range and - append total size */ + /* Range was selected and then we pass the incoming range and append + total size */ data->state.aptr.rangeline = - aprintf("Content-Range: bytes %s/%" FMT_OFF_T "\r\n", - data->state.range, req_clen); + curl_maprintf("Content-Range: bytes %s/%" FMT_OFF_T "\r\n", + data->state.range, req_clen); } if(!data->state.aptr.rangeline) return CURLE_OUT_OF_MEMORY; @@ -2534,7 +2704,7 @@ static CURLcode http_firstwrite(struct Curl_easy *data) if(conn->bits.close) { /* Abort after the headers if "follow Location" is set and we are set to close anyway. */ - k->keepon &= ~KEEP_RECV; + CURL_REQ_CLEAR_RECV(data); k->done = TRUE; return CURLE_OK; } @@ -2553,7 +2723,7 @@ static CURLcode http_firstwrite(struct Curl_easy *data) infof(data, "The entire document is already downloaded"); streamclose(conn, "already downloaded"); /* Abort download */ - k->keepon &= ~KEEP_RECV; + CURL_REQ_CLEAR_RECV(data); k->done = TRUE; return CURLE_OK; } @@ -2622,6 +2792,7 @@ static CURLcode http_check_new_conn(struct Curl_easy *data) info_version = "HTTP/2"; /* There is no ALPN here, but the connection is now definitely h2 */ conn->httpversion_seen = 20; + Curl_conn_set_multiplex(conn); } else info_version = "HTTP/1.x"; @@ -2635,17 +2806,29 @@ static CURLcode http_check_new_conn(struct Curl_easy *data) static CURLcode http_add_connection_hd(struct Curl_easy *data, struct dynbuf *req) { - char *custom = Curl_checkheaders(data, STRCONST("Connection")); - char *custom_val = custom ? Curl_copy_header_value(custom) : NULL; - const char *sep = (custom_val && *custom_val) ? ", " : "Connection: "; + struct curl_slist *head; + const char *sep = "Connection: "; CURLcode result = CURLE_OK; size_t rlen = curlx_dyn_len(req); + bool skip; - if(custom && !custom_val) - return CURLE_OUT_OF_MEMORY; + /* Add the 1st custom "Connection: " header, if there is one */ + for(head = data->set.headers; head; head = head->next) { + if(curl_strnequal(head->data, "Connection", 10) && + Curl_headersep(head->data[10]) && + !http_header_is_empty(head->data)) { + char *value; + result = copy_custom_value(head->data, &value); + if(result) + return result; + result = curlx_dyn_addf(req, "%s%s", sep, value); + sep = ", "; + curlx_free(value); + break; /* leave, having added 1st one */ + } + } - if(custom_val && *custom_val) - result = curlx_dyn_addf(req, "Connection: %s", custom_val); + /* add our internal Connection: header values, if we have any */ if(!result && data->state.http_hd_te) { result = curlx_dyn_addf(req, "%s%s", sep, "TE"); sep = ", "; @@ -2659,9 +2842,26 @@ static CURLcode http_add_connection_hd(struct Curl_easy *data, } if(!result && (rlen < curlx_dyn_len(req))) result = curlx_dyn_addn(req, STRCONST("\r\n")); + if(result) + return result; - free(custom_val); - return result; + /* Add all user-defined Connection: headers after the first */ + skip = TRUE; + for(head = data->set.headers; head; head = head->next) { + if(curl_strnequal(head->data, "Connection", 10) && + Curl_headersep(head->data[10]) && + !http_header_is_empty(head->data)) { + if(skip) { + skip = FALSE; + continue; + } + result = curlx_dyn_addf(req, "%s\r\n", head->data); + if(result) + return result; + } + } + + return CURLE_OK; } /* Header identifier in order we send them by default */ @@ -2702,6 +2902,11 @@ static CURLcode http_add_hd(struct Curl_easy *data, Curl_HttpReq httpreq) { CURLcode result = CURLE_OK; +#if !defined(CURL_DISABLE_ALTSVC) || \ + !defined(CURL_DISABLE_PROXY) || \ + !defined(CURL_DISABLE_WEBSOCKETS) + struct connectdata *conn = data->conn; +#endif switch(id) { case H1_HD_REQUEST: /* add the main request stuff */ @@ -2721,14 +2926,14 @@ static CURLcode http_add_hd(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY case H1_HD_PROXY_AUTH: - if(data->state.aptr.proxyuserpwd) - result = curlx_dyn_add(req, data->state.aptr.proxyuserpwd); + if(data->req.proxyuserpwd) + result = curlx_dyn_add(req, data->req.proxyuserpwd); break; #endif case H1_HD_USER_AUTH: - if(data->state.aptr.userpwd) - result = curlx_dyn_add(req, data->state.aptr.userpwd); + if(data->req.userpwd) + result = curlx_dyn_add(req, data->req.userpwd); break; case H1_HD_RANGE: @@ -2759,7 +2964,7 @@ static CURLcode http_add_hd(struct Curl_easy *data, break; case H1_HD_ACCEPT_ENCODING: - Curl_safefree(data->state.aptr.accept_encoding); + curlx_safefree(data->state.aptr.accept_encoding); if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && data->set.str[STRING_ENCODING]) result = curlx_dyn_addf(req, "Accept-Encoding: %s\r\n", @@ -2767,15 +2972,17 @@ static CURLcode http_add_hd(struct Curl_easy *data, break; case H1_HD_REFERER: - Curl_safefree(data->state.aptr.ref); - if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) - result = curlx_dyn_addf(req, "Referer: %s\r\n", data->state.referer); + curlx_safefree(data->state.aptr.ref); + if(Curl_bufref_ptr(&data->state.referer) && + !Curl_checkheaders(data, STRCONST("Referer"))) + result = curlx_dyn_addf(req, "Referer: %s\r\n", + Curl_bufref_ptr(&data->state.referer)); break; #ifndef CURL_DISABLE_PROXY case H1_HD_PROXY_CONNECTION: - if(data->conn->bits.httpproxy && - !data->conn->bits.tunnel_proxy && + if(conn->bits.httpproxy && + !conn->bits.tunnel_proxy && !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && !Curl_checkProxyheaders(data, data->conn, STRCONST("Proxy-Connection"))) result = curlx_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n"); @@ -2788,11 +2995,10 @@ static CURLcode http_add_hd(struct Curl_easy *data, #ifndef CURL_DISABLE_ALTSVC case H1_HD_ALT_USED: - if(data->conn->bits.altused && - !Curl_checkheaders(data, STRCONST("Alt-Used"))) - result = curlx_dyn_addf(req, "Alt-Used: %s:%d\r\n", - data->conn->conn_to_host.name, - data->conn->conn_to_port); + if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) + result = curlx_dyn_addf(req, "Alt-Used: %s:%u\r\n", + conn->conn_to_host.name, + conn->conn_to_port); break; #endif @@ -2805,7 +3011,7 @@ static CURLcode http_add_hd(struct Curl_easy *data, result = Curl_http2_request_upgrade(req, data); } #ifndef CURL_DISABLE_WEBSOCKETS - if(!result && data->conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) + if(!result && conn->scheme->protocol & (CURLPROTO_WS | CURLPROTO_WSS)) result = Curl_ws_request(data, req); #endif break; @@ -2861,6 +3067,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) /* make sure the header buffer is reset - if there are leftovers from a previous transfer */ curlx_dyn_reset(&data->state.headerb); + data->state.maybe_folded = FALSE; if(!data->conn->bits.reuse) { result = http_check_new_conn(data); @@ -2888,13 +3095,15 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) /* setup the authentication headers, how that method and host are known */ char *pq = NULL; if(data->state.up.query) { - pq = aprintf("%s?%s", data->state.up.path, data->state.up.query); - if(!pq) - return CURLE_OUT_OF_MEMORY; + pq = curl_maprintf("%s?%s", data->state.up.path, data->state.up.query); + if(!pq) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } } result = Curl_http_output_auth(data, data->conn, method, httpreq, (pq ? pq : data->state.up.path), FALSE); - free(pq); + curlx_free(pq); } if(result) goto out; @@ -2932,15 +3141,9 @@ 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 - * from reused connections */ - Curl_safefree(data->state.aptr.userpwd); -#ifndef CURL_DISABLE_PROXY - Curl_safefree(data->state.aptr.proxyuserpwd); -#endif curlx_dyn_free(&req); return result; } @@ -2951,7 +3154,6 @@ typedef enum { STATUS_BAD /* not a status line */ } statusline; - /* Check a string for a prefix. Check no more than 'len' bytes */ static bool checkprefixmax(const char *prefix, const char *buffer, size_t len) { @@ -2964,9 +3166,8 @@ static bool checkprefixmax(const char *prefix, const char *buffer, size_t len) * * Returns TRUE if member of the list matches prefix of string */ -static statusline -checkhttpprefix(struct Curl_easy *data, - const char *s, size_t len) +static statusline checkhttpprefix(struct Curl_easy *data, + const char *s, size_t len) { struct curl_slist *head = data->set.http200aliases; statusline rc = STATUS_BAD; @@ -2987,26 +3188,25 @@ checkhttpprefix(struct Curl_easy *data, } #ifndef CURL_DISABLE_RTSP -static statusline -checkrtspprefix(struct Curl_easy *data, - const char *s, size_t len) +static statusline checkrtspprefix(struct Curl_easy *data, + const char *s, size_t len) { - statusline result = STATUS_BAD; + statusline status = STATUS_BAD; statusline onmatch = len >= 5 ? STATUS_DONE : STATUS_UNKNOWN; (void)data; if(checkprefixmax("RTSP/", s, len)) - result = onmatch; + status = onmatch; - return result; + return status; } #endif /* CURL_DISABLE_RTSP */ -static statusline -checkprotoprefix(struct Curl_easy *data, struct connectdata *conn, - const char *s, size_t len) +static statusline checkprotoprefix(struct Curl_easy *data, + struct connectdata *conn, + 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; @@ -3017,17 +3217,17 @@ checkprotoprefix(struct Curl_easy *data, struct connectdata *conn, /* 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) */ #define HD_IS_AND_SAYS(hd, hdlen, n, v) \ (HD_IS(hd, hdlen, n) && \ - ((hdlen) > ((sizeof(n)-1) + (sizeof(v)-1))) && \ + ((hdlen) > ((sizeof(n) - 1) + (sizeof(v) - 1))) && \ Curl_compareheader(hd, STRCONST(n), STRCONST(v))) /* @@ -3040,7 +3240,7 @@ static CURLcode http_header_a(struct Curl_easy *data, const char *v; struct connectdata *conn = data->conn; v = (data->asi && - (Curl_conn_is_ssl(data->conn, FIRSTSOCKET) || + (Curl_xfer_is_secure(data) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_ALTSVC_HTTP") @@ -3074,42 +3274,59 @@ static CURLcode http_header_c(struct Curl_easy *data, struct SingleRequest *k = &data->req; const char *v; - /* Check for Content-Length: header lines to get size */ + /* Check for Content-Length: header lines to get size. Browsers insist we + should accept multiple Content-Length headers and that a comma separated + list also is fine and then we should accept them all as long as they are + the same value. Different values trigger error. + */ v = (!k->http_bodyless && !data->set.ignorecl) ? HD_VAL(hd, hdlen, "Content-Length:") : NULL; if(v) { - curl_off_t contentlength; - int offt = curlx_str_numblanks(&v, &contentlength); + do { + curl_off_t contentlength; + int offt = curlx_str_numblanks(&v, &contentlength); - if(offt == STRE_OK) { - k->size = contentlength; - k->maxdownload = k->size; - } - else if(offt == STRE_OVERFLOW) { - /* out of range */ - if(data->set.max_filesize) { - failf(data, "Maximum file size exceeded"); - return CURLE_FILESIZE_EXCEEDED; + if(offt == STRE_OVERFLOW) { + /* out of range */ + if(data->set.max_filesize) { + failf(data, "Maximum file size exceeded"); + return CURLE_FILESIZE_EXCEEDED; + } + streamclose(conn, "overflow content-length"); + infof(data, "Overflow Content-Length: value"); + return CURLE_OK; } - streamclose(conn, "overflow content-length"); - infof(data, "Overflow Content-Length: value"); - } - else { - /* negative or just rubbish - bad HTTP */ - failf(data, "Invalid Content-Length: value"); - return CURLE_WEIRD_SERVER_REPLY; - } - return CURLE_OK; + else { + if((offt == STRE_OK) && + ((k->size == -1) || /* not set to something before */ + (k->size == contentlength))) { /* or the same value */ + + k->size = contentlength; + curlx_str_passblanks(&v); + + /* on a comma, loop and get the next instead */ + if(!curlx_str_single(&v, ',')) + continue; + + if(!curlx_str_newline(&v)) { + k->maxdownload = k->size; + return CURLE_OK; + } + } + /* negative, different value or rubbish - bad HTTP */ + failf(data, "Invalid Content-Length: value"); + return CURLE_WEIRD_SERVER_REPLY; + } + } while(1); } v = (!k->http_bodyless && data->set.str[STRING_ENCODING]) ? 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); } @@ -3121,21 +3338,22 @@ static CURLcode http_header_c(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; if(!*contenttype) /* ignore empty data */ - free(contenttype); + curlx_free(contenttype); else { - free(data->info.contenttype); + curlx_free(data->info.contenttype); data->info.contenttype = contenttype; } return CURLE_OK; } - if(HD_IS_AND_SAYS(hd, hdlen, "Connection:", "close")) { + if((k->httpversion < 20) && + HD_IS_AND_SAYS(hd, hdlen, "Connection:", "close")) { /* * [RFC 2616, section 8.1.2.1] * "Connection: close" is HTTP/1.1 language and means that * the connection will close when this request has been * served. */ - streamclose(conn, "Connection: close used"); + connclose(conn, "Connection: close used"); return CURLE_OK; } if((k->httpversion == 10) && @@ -3200,23 +3418,31 @@ static CURLcode http_header_l(struct Curl_easy *data, data->info.filetime = k->timeofdoc; return CURLE_OK; } - if((k->httpcode >= 300 && k->httpcode < 400) && - HD_IS(hd, hdlen, "Location:") && - !data->req.location) { + if(HD_IS(hd, hdlen, "Location:")) { /* this is the URL that the server advises us to use instead */ char *location = Curl_copy_header_value(hd); if(!location) return CURLE_OUT_OF_MEMORY; - if(!*location) - /* ignore empty data */ - free(location); + if(!*location || + (data->req.location && !strcmp(data->req.location, location))) { + /* ignore empty header, or exact repeat of a previous one */ + curlx_free(location); + return CURLE_OK; + } else { + /* has value and is not an exact repeat */ + if(data->req.location) { + failf(data, "Multiple Location headers"); + curlx_free(location); + return CURLE_WEIRD_SERVER_REPLY; + } data->req.location = location; - if(data->set.http_follow_mode) { + if((k->httpcode >= 300 && k->httpcode < 400) && + data->set.http_follow_mode) { CURLcode result; DEBUGASSERT(!data->req.newurl); - data->req.newurl = strdup(data->req.location); /* clone */ + data->req.newurl = curlx_strdup(data->req.location); /* clone */ if(!data->req.newurl) return CURLE_OUT_OF_MEMORY; @@ -3271,11 +3497,11 @@ static CURLcode http_header_p(struct Curl_easy *data, #endif if((407 == k->httpcode) && HD_IS(hd, hdlen, "Proxy-authenticate:")) { char *auth = Curl_copy_header_value(hd); - CURLcode result; - if(!auth) - return CURLE_OUT_OF_MEMORY; - result = Curl_http_input_auth(data, TRUE, auth); - free(auth); + CURLcode result = auth ? CURLE_OK : CURLE_OUT_OF_MEMORY; + if(!result) { + result = Curl_http_input_auth(data, TRUE, auth); + curlx_free(auth); + } return result; } #ifdef USE_SPNEGO @@ -3293,7 +3519,7 @@ static CURLcode http_header_p(struct Curl_easy *data, negdata->havenoauthpersist = TRUE; infof(data, "Negotiate: noauthpersist -> %d, header part: %s", negdata->noauthpersist, persistentauth); - free(persistentauth); + curlx_free(persistentauth); } } #endif @@ -3354,20 +3580,21 @@ static CURLcode http_header_s(struct Curl_easy *data, if(v) { /* If there is a custom-set Host: name, use it here, or else use * real peer hostname. */ - const char *host = data->state.aptr.cookiehost ? - data->state.aptr.cookiehost : conn->host.name; + const char *host = data->req.cookiehost ? + data->req.cookiehost : conn->host.name; const bool secure_context = Curl_secure_context(conn, host); + CURLcode result; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host, - data->state.up.path, secure_context); + result = Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host, + data->state.up.path, secure_context); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - return CURLE_OK; + return result; } #endif #ifndef CURL_DISABLE_HSTS /* If enabled, the header is incoming and this is over HTTPS */ v = (data->hsts && - (Curl_conn_is_ssl(conn, FIRSTSOCKET) || + (Curl_xfer_is_secure(data) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_HSTS_HTTP") @@ -3379,8 +3606,11 @@ static CURLcode http_header_s(struct Curl_easy *data, if(v) { CURLcode check = Curl_hsts_parse(data->hsts, conn->host.name, v); - if(check) + if(check) { + if(check == CURLE_OUT_OF_MEMORY) + return check; infof(data, "Illegal STS header skipped"); + } #ifdef DEBUGBUILD else infof(data, "Parsed STS header fine (%zu entries)", @@ -3450,9 +3680,11 @@ static CURLcode http_header_w(struct Curl_easy *data, if((401 == k->httpcode) && HD_IS(hd, hdlen, "WWW-Authenticate:")) { char *auth = Curl_copy_header_value(hd); if(!auth) - return CURLE_OUT_OF_MEMORY; - result = Curl_http_input_auth(data, FALSE, auth); - free(auth); + result = CURLE_OUT_OF_MEMORY; + else { + result = Curl_http_input_auth(data, FALSE, auth); + curlx_free(auth); + } } return result; } @@ -3502,7 +3734,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; @@ -3528,26 +3760,26 @@ static CURLcode http_statusline(struct Curl_easy *data, #endif /* no major version switch mid-connection */ if(k->httpversion_sent && - (k->httpversion/10 != k->httpversion_sent/10)) { + (k->httpversion / 10 != k->httpversion_sent / 10)) { failf(data, "Version mismatch (from HTTP/%u to HTTP/%u)", - k->httpversion_sent/10, k->httpversion/10); + k->httpversion_sent / 10, k->httpversion / 10); return CURLE_WEIRD_SERVER_REPLY; } break; default: failf(data, "Unsupported HTTP version (%u.%d) in response", - k->httpversion/10, k->httpversion%10); + k->httpversion / 10, k->httpversion % 10); return CURLE_UNSUPPORTED_PROTOCOL; } 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 @@ -3559,8 +3791,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. */ } @@ -3571,10 +3803,6 @@ static CURLcode http_statusline(struct Curl_easy *data, infof(data, "HTTP 1.0, assume close after body"); connclose(conn, "HTTP/1.0 close after body"); } - else if(k->httpversion == 20 || - (k->upgr101 == UPGR101_H2 && k->httpcode == 101)) { - DEBUGF(infof(data, "HTTP/2 found, allow multiplexing")); - } k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200; switch(k->httpcode) { @@ -3582,7 +3810,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(); @@ -3632,7 +3860,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"); @@ -3682,157 +3910,112 @@ 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, - size_t *pconsumed) +/* + * Handle a 101 Switching Protocols response. Performs the actual protocol + * upgrade to HTTP/2 or WebSocket based on what was requested. + */ +static CURLcode http_on_101_upgrade(struct Curl_easy *data, + const char *buf, size_t blen, + size_t *pconsumed, + bool *conn_changed) { struct connectdata *conn = data->conn; - CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; - (void)buf; /* not used without HTTP2 enabled */ - *pconsumed = 0; - - if(k->upgr101 == UPGR101_RECEIVED) { - /* supposedly upgraded to http2 now */ - if(data->req.httpversion != 20) - infof(data, "Lying server, not serving HTTP/2"); - } - - if(k->httpcode < 200 && last_hd) { - /* Intermediate responses might trigger processing of more - * responses, write the last header to the client before - * proceeding. */ - result = http_write_header(data, last_hd, last_hd_len); - last_hd = NULL; /* handled it */ - if(result) - goto out; - } - - if(k->httpcode < 100) { - failf(data, "Unsupported response code in HTTP response"); - result = CURLE_UNSUPPORTED_PROTOCOL; - goto out; - } - else if(k->httpcode < 200) { - /* "A user agent MAY ignore unexpected 1xx status responses." - * By default, we expect to get more responses after this one. */ - k->header = TRUE; - k->headerline = 0; /* restart the header line counter */ - - switch(k->httpcode) { - case 100: - /* - * We have made an HTTP PUT or POST and this is 1.1-lingo - * that tells us that the server is OK with this and ready - * to receive the data. - */ - http_exp100_got100(data); - break; - case 101: - /* Switching Protocols only allowed from HTTP/1.1 */ - if(k->httpversion_sent != 11) { - /* invalid for other HTTP versions */ - failf(data, "unexpected 101 response code"); - result = CURLE_WEIRD_SERVER_REPLY; - goto out; - } - if(k->upgr101 == UPGR101_H2) { - /* Switching to HTTP/2, where we will get more responses */ - infof(data, "Received 101, Switching to HTTP/2"); - k->upgr101 = UPGR101_RECEIVED; - data->conn->bits.asks_multiplex = FALSE; - /* We expect more response from HTTP/2 later */ - k->header = TRUE; - k->headerline = 0; /* restart the header line counter */ - k->httpversion_sent = 20; /* It's an HTTP/2 request now */ - /* Any remaining `buf` bytes are already HTTP/2 and passed to - * be processed. */ - result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); - if(result) - goto out; - *pconsumed += blen; - } -#ifndef CURL_DISABLE_WEBSOCKETS - else if(k->upgr101 == UPGR101_WS) { - /* verify the response. Any passed `buf` bytes are already in - * WebSockets format and taken in by the protocol handler. */ - result = Curl_ws_accept(data, buf, blen); - if(result) - goto out; - *pconsumed += blen; /* ws accept handled the data */ - } +#if !defined(USE_NGHTTP2) && defined(CURL_DISABLE_WEBSOCKETS) + (void)buf; + (void)blen; + (void)pconsumed; +#else + CURLcode result; + int upgr101_requested = k->upgr101; #endif - else { - /* We silently accept this as the final response. What are we - * switching to if we did not ask for an Upgrade? Maybe the - * application provided an `Upgrade: xxx` header? */ - k->header = FALSE; - } - break; - default: - /* The server may send us other 1xx responses, like informative - * 103. This have no influence on request processing and we expect - * to receive a final response eventually. */ - break; - } - goto out; + + if(k->httpversion_sent != 11) { + /* invalid for other HTTP versions */ + failf(data, "server sent 101 response while not talking HTTP/1.1"); + return CURLE_WEIRD_SERVER_REPLY; } - /* k->httpcode >= 200, final response */ + /* Whatever the success, upgrade was selected. */ + k->upgr101 = UPGR101_RECEIVED; + conn->bits.upgrade_in_progress = FALSE; + *conn_changed = TRUE; + + /* To be fully compliant, we would check the "Upgrade:" response header to + * mention the protocol we requested. */ +#ifdef USE_NGHTTP2 + if(upgr101_requested == UPGR101_H2) { + /* Switch to HTTP/2, where we will get more responses. blen bytes in buf + * are already h2 protocol bytes */ + infof(data, "Received 101, Switching to HTTP/2"); + result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); + if(!result) + *pconsumed += blen; + return result; + } +#endif +#ifndef CURL_DISABLE_WEBSOCKETS + if(upgr101_requested == UPGR101_WS) { + /* Switch to WebSocket, where we now stream ws frames. blen bytes in buf + * are already ws protocol bytes */ + infof(data, "Received 101, Switching to WebSocket"); + result = Curl_ws_accept(data, buf, blen); + if(!result) + *pconsumed += blen; /* ws accept handled the data */ + return result; + } +#endif + /* We silently accept this as the final response. What are we switching to + * if we did not ask for an Upgrade? Maybe the application provided an + * `Upgrade: xxx` header? */ k->header = FALSE; + return CURLE_OK; +} - if(k->upgr101 == UPGR101_H2) { - /* A requested upgrade was denied, poke the multi handle to possibly - allow a pending pipewait to continue */ - data->conn->bits.asks_multiplex = FALSE; - Curl_multi_connchanged(data->multi); +/* + * Handle 1xx intermediate HTTP responses. Sets up state for more + * headers and processes 100-continue and 101 upgrade responses. + */ +static CURLcode http_on_1xx_response(struct Curl_easy *data, + const char *buf, size_t blen, + size_t *pconsumed, + bool *conn_changed) +{ + struct SingleRequest *k = &data->req; + + /* "A user agent MAY ignore unexpected 1xx status responses." + * By default, we expect to get more responses after this one. */ + k->header = TRUE; + k->headerline = 0; /* restart the header line counter */ + + switch(k->httpcode) { + case 100: + /* We have made an HTTP PUT or POST and this is 1.1-lingo that tells us + * that the server is OK with this and ready to receive the data. */ + http_exp100_got100(data); + break; + case 101: + return http_on_101_upgrade(data, buf, blen, pconsumed, conn_changed); + default: + /* The server may send us other 1xx responses, like informative 103. This + * has no influence on request processing and we expect to receive a + * final response eventually. */ + break; } + return CURLE_OK; +} - if((k->size == -1) && !k->chunk && !conn->bits.close && - (k->httpversion == 11) && - !(conn->handler->protocol & CURLPROTO_RTSP) && - data->state.httpreq != HTTPREQ_HEAD) { - /* On HTTP 1.1, when connection is not to get closed, but no - Content-Length nor Transfer-Encoding chunked have been - received, according to RFC2616 section 4.4 point 5, we - assume that the server will close the connection to - signal the end of the document. */ - infof(data, "no chunk, no close, no size. Assume close to " - "signal end"); - streamclose(conn, "HTTP: No end-of-message indicator"); - } - - /* At this point we have some idea about the fate of the connection. - If we are closing the connection it may result auth failure. */ +#if defined(USE_NTLM) || defined(USE_SPNEGO) +/* + * Check if NTLM or SPNEGO authentication negotiation failed due to + * connection closure (typically on HTTP/1.0 servers). + */ +static void http_check_auth_closure(struct Curl_easy *data, + struct connectdata *conn) +{ + /* At this point we have some idea about the fate of the connection. If we + are closing the connection it may result auth failure. */ #ifdef USE_NTLM if(conn->bits.close && (((data->req.httpcode == 401) && @@ -3861,11 +4044,156 @@ static CURLcode http_on_response(struct Curl_easy *data, conn->proxy_negotiate_state = GSS_AUTHSUCC; } #endif +} +#else +#define http_check_auth_closure(x, y) /* empty */ +#endif + +/* + * Handle an error response (>= 300) received while still sending the + * request body. Deals with 417 Expectation Failed retries, keep-sending + * on error, and aborting the send. + */ +static CURLcode http_handle_send_error(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + struct SingleRequest *k = &data->req; + CURLcode result = CURLE_OK; + + 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 because if something + * else has already deemed the connection to get closed then + * something else should have considered the big picture and + * we avoid this check. + */ + + switch(data->state.httpreq) { + case HTTPREQ_PUT: + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + /* We got an error response. If this happened before the + * whole request body has been sent we stop sending and + * mark the connection for closure after we have read the + * entire response. */ + if(!Curl_req_done_sending(data)) { + if((k->httpcode == 417) && http_exp100_is_selected(data)) { + /* 417 Expectation Failed - try again without the + Expect header */ + if(!k->writebytecount && http_exp100_is_waiting(data)) { + infof(data, "Got HTTP failure 417 while waiting for a 100"); + } + else { + infof(data, "Got HTTP failure 417 while sending data"); + streamclose(conn, "Stop sending data before everything sent"); + result = http_perhapsrewind(data, conn); + if(result) + return result; + } + data->state.disableexpect = TRUE; + Curl_req_abort_sending(data); + DEBUGASSERT(!data->req.newurl); + data->req.newurl = Curl_bufref_dup(&data->state.url); + if(!data->req.newurl) + return CURLE_OUT_OF_MEMORY; + } + else if(data->set.http_keep_sending_on_error) { + infof(data, "HTTP error before end of send, keep sending"); + http_exp100_send_anyway(data); + } + else { + infof(data, "HTTP error before end of send, stop sending"); + streamclose(conn, "Stop sending data before everything sent"); + result = Curl_req_abort_sending(data); + if(result) + return result; + } + } + break; + + default: /* default label present to avoid compiler warnings */ + break; + } + } + + if(Curl_creader_will_rewind(data) && !Curl_req_done_sending(data)) { + /* We rewind before next send, continue sending now */ + infof(data, "Keep sending data to get tossed away"); + CURL_REQ_SET_SEND(data); + } + 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, + size_t *pconsumed) +{ + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + bool conn_changed = FALSE; + + (void)buf; /* not used without HTTP2 enabled */ + *pconsumed = 0; + + if(k->upgr101 == UPGR101_RECEIVED) { + /* supposedly upgraded to http2 now */ + if(data->req.httpversion != 20) + infof(data, "Lying server, not serving HTTP/2"); + } + + if(k->httpcode < 200 && last_hd) { + /* Intermediate responses might trigger processing of more responses, + * write the last header to the client before proceeding. */ + result = http_write_header(data, last_hd, last_hd_len); + last_hd = NULL; /* handled it */ + if(result) + goto out; + } + + if(k->httpcode < 100) { + failf(data, "Unsupported response code in HTTP response"); + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; + } + else if(k->httpcode < 200) { + result = http_on_1xx_response(data, buf, blen, pconsumed, &conn_changed); + goto out; + } + + /* k->httpcode >= 200, final response */ + k->header = FALSE; + if(conn->bits.upgrade_in_progress) { + /* Asked for protocol upgrade, but it was not selected */ + conn->bits.upgrade_in_progress = FALSE; + conn_changed = TRUE; + } + + if((k->size == -1) && !k->chunk && !conn->bits.close && + (k->httpversion == 11) && + !(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 received, + according to RFC2616 section 4.4 point 5, we assume that the server + will close the connection to signal the end of the document. */ + infof(data, "no chunk, no close, no size. Assume close to signal end"); + streamclose(conn, "HTTP: No end-of-message indicator"); + } + + http_check_auth_closure(data, conn); #ifndef CURL_DISABLE_WEBSOCKETS - /* All >=200 HTTP status codes are errors when wanting WebSockets */ + /* All >=200 HTTP status codes are errors when wanting ws */ if(data->req.upgr101 == UPGR101_WS) { - failf(data, "Refused WebSockets upgrade: %d", k->httpcode); + failf(data, "Refused WebSocket upgrade: %d", k->httpcode); result = CURLE_HTTP_RETURNED_ERROR; goto out; } @@ -3879,81 +4207,17 @@ static CURLcode http_on_response(struct Curl_easy *data, goto out; } - /* Curl_http_auth_act() checks what authentication methods - * that are available and decides which one (if any) to - * use. It will set 'newurl' if an auth method was picked. */ + /* Curl_http_auth_act() checks what authentication methods that are + * available and decides which one (if any) to use. It will set 'newurl' if + * an auth method was picked. */ result = Curl_http_auth_act(data); if(result) goto out; if(k->httpcode >= 300) { - 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 - * else has already deemed the connection to get closed then - * something else should've considered the big picture and we - * avoid this check. - * - */ - - switch(data->state.httpreq) { - case HTTPREQ_PUT: - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - /* We got an error response. If this happened before the whole - * request body has been sent we stop sending and mark the - * connection for closure after we have read the entire response. - */ - if(!Curl_req_done_sending(data)) { - if((k->httpcode == 417) && http_exp100_is_selected(data)) { - /* 417 Expectation Failed - try again without the Expect - header */ - if(!k->writebytecount && http_exp100_is_waiting(data)) { - infof(data, "Got HTTP failure 417 while waiting for a 100"); - } - else { - infof(data, "Got HTTP failure 417 while sending data"); - streamclose(conn, - "Stop sending data before everything sent"); - result = http_perhapsrewind(data, conn); - if(result) - goto out; - } - data->state.disableexpect = TRUE; - DEBUGASSERT(!data->req.newurl); - data->req.newurl = strdup(data->state.url); - Curl_req_abort_sending(data); - } - else if(data->set.http_keep_sending_on_error) { - infof(data, "HTTP error before end of send, keep sending"); - http_exp100_send_anyway(data); - } - else { - infof(data, "HTTP error before end of send, stop sending"); - streamclose(conn, "Stop sending data before everything sent"); - result = Curl_req_abort_sending(data); - if(result) - goto out; - } - } - break; - - default: /* default label present to avoid compiler warnings */ - break; - } - } - - if(Curl_creader_will_rewind(data) && !Curl_req_done_sending(data)) { - /* We rewind before next send, continue sending now */ - infof(data, "Keep sending data to get tossed away"); - k->keepon |= KEEP_SEND; - } - + result = http_handle_send_error(data); + if(result) + goto out; } /* If we requested a "no body", this is a good time to get @@ -3962,11 +4226,10 @@ static CURLcode http_on_response(struct Curl_easy *data, if(data->req.no_body) k->download_done = TRUE; - /* If max download size is *zero* (nothing) we already have - nothing and can safely return ok now! But for HTTP/2, we would - like to call http2_handle_stream_close to properly close a - stream. In order to do this, we keep reading until we - close the stream. */ + /* If max download size is *zero* (nothing) we already have nothing and can + safely return ok now! For HTTP/2, we would like to call + http2_handle_stream_close to properly close a stream. In order to do + this, we keep reading until we close the stream. */ if((k->maxdownload == 0) && (k->httpversion_sent < 20)) k->download_done = TRUE; @@ -3974,17 +4237,18 @@ static CURLcode http_on_response(struct Curl_easy *data, result = http_firstwrite(data); if(!result) - /* This is the last response that we get for the current request. - * Check on the body size and determine if the response is complete. - */ + /* This is the last response that we get for the current request. Check on + * the body size and determine if the response is complete. */ result = http_size(data); out: - if(last_hd) { + if(last_hd) /* if not written yet, write it now */ - result = Curl_1st_err( - result, http_write_header(data, last_hd, last_hd_len)); - } + result = Curl_1st_fatal(result, + http_write_header(data, last_hd, last_hd_len)); + if(conn_changed) + /* poke the multi handle to allow pending pipewait to retry */ + Curl_multi_connchanged(data->multi); return result; } @@ -3996,6 +4260,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; int writetype; + DEBUGASSERT(!hd[hdlen]); /* null-terminated */ *pconsumed = 0; if((0x0a == *hd) || (0x0d == *hd)) { @@ -4032,7 +4297,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 * @@ -4053,10 +4318,10 @@ 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 don't so let's not insist */ + but the browsers do not so let's not insist */ fine_statusline = TRUE; } } @@ -4073,7 +4338,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)) @@ -4099,7 +4364,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; @@ -4144,7 +4409,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, */ Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen); - if(k->httpcode/100 == 1) + if(k->httpcode / 100 == 1) writetype |= CLIENTWRITE_1XX; result = Curl_client_write(data, writetype, hd, hdlen); if(result) @@ -4157,6 +4422,26 @@ static CURLcode http_rw_hd(struct Curl_easy *data, return CURLE_OK; } +/* remove trailing CRLF then all trailing whitespace */ +void Curl_http_to_fold(struct dynbuf *bf) +{ + size_t len = curlx_dyn_len(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 */ + len--; + curlx_dyn_setlen(bf, len); +} + +static void unfold_header(struct Curl_easy *data) +{ + Curl_http_to_fold(&data->state.headerb); + data->state.leading_unfold = TRUE; +} + /* * Read any HTTP header lines from the server and pass them to the client app. */ @@ -4167,13 +4452,52 @@ 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 + before passing it on */ + if(data->state.maybe_folded && blen) { + if(ISBLANK(buf[0])) { + /* folded, remove the trailing newlines and append the next header */ + unfold_header(data); + } + else { + /* the header data we hold is a complete header, pass it on */ + size_t ignore_this; + result = http_rw_hd(data, curlx_dyn_ptr(&data->state.headerb), + curlx_dyn_len(&data->state.headerb), + NULL, 0, &ignore_this); + curlx_dyn_reset(&data->state.headerb); + if(result) + return result; + } + data->state.maybe_folded = FALSE; + } + /* header line within buffer loop */ *pconsumed = 0; while(blen && k->header) { size_t consumed; + size_t hlen; + const char *hd; + size_t unfold_len = 0; + + if(data->state.leading_unfold) { + /* immediately after an unfold, keep only a single whitespace */ + while(blen && ISBLANK(buf[0])) { + buf++; + blen--; + unfold_len++; + } + if(blen) { + /* insert a single space */ + result = curlx_dyn_addn(&data->state.headerb, " ", 1); + if(result) + return result; + data->state.leading_unfold = FALSE; /* done now */ + } + } end_ptr = memchr(buf, '\n', blen); if(!end_ptr) { @@ -4182,7 +4506,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data, result = curlx_dyn_addn(&data->state.headerb, buf, blen); if(result) return result; - *pconsumed += blen; + *pconsumed += blen + unfold_len; if(!k->headerline) { /* check if this looks like a protocol header */ @@ -4211,24 +4535,26 @@ static CURLcode http_parse_headers(struct Curl_easy *data, goto out; /* read more and try again */ } - /* decrease the size of the remaining (supposed) header line */ + /* the size of the remaining header line */ consumed = (end_ptr - buf) + 1; + result = curlx_dyn_addn(&data->state.headerb, buf, consumed); if(result) return result; blen -= consumed; buf += consumed; - *pconsumed += consumed; + *pconsumed += consumed + unfold_len; /**** * We now have a FULL header line in 'headerb'. *****/ + hlen = curlx_dyn_len(&data->state.headerb); + hd = curlx_dyn_ptr(&data->state.headerb); + if(!k->headerline) { - /* the first read header */ - statusline st = checkprotoprefix(data, conn, - curlx_dyn_ptr(&data->state.headerb), - curlx_dyn_len(&data->state.headerb)); + /* the first read "header", the status line */ + statusline st = checkprotoprefix(data, conn, hd, hlen); if(st == STATUS_BAD) { streamclose(conn, "bad HTTP: No end-of-message indicator"); /* this is not the beginning of a protocol first header line. @@ -4246,10 +4572,25 @@ static CURLcode http_parse_headers(struct Curl_easy *data, goto out; } } + else { + if(hlen && !ISNEWLINE(hd[0])) { + /* this is NOT the header separator */ - result = http_rw_hd(data, curlx_dyn_ptr(&data->state.headerb), - curlx_dyn_len(&data->state.headerb), - buf, blen, &consumed); + /* if we have bytes for the next header, check for folding */ + if(blen && ISBLANK(buf[0])) { + /* remove the trailing CRLF and append the next header */ + unfold_header(data); + continue; + } + else if(!blen) { + /* this might be a folded header so deal with it in next invoke */ + data->state.maybe_folded = TRUE; + break; + } + } + } + + result = http_rw_hd(data, hd, hlen, buf, blen, &consumed); /* We are done with this line. We reset because response * processing might switch to HTTP/2 and that might call us * directly again. */ @@ -4280,17 +4621,18 @@ CURLcode Curl_http_write_resp_hd(struct Curl_easy *data, CURLcode result; size_t consumed; char tmp = 0; + DEBUGASSERT(!hd[hdlen]); /* null-terminated */ result = http_rw_hd(data, hd, hdlen, &tmp, 0, &consumed); if(!result && is_eos) { - result = Curl_client_write(data, (CLIENTWRITE_BODY|CLIENTWRITE_EOS), + result = Curl_client_write(data, (CLIENTWRITE_BODY | CLIENTWRITE_EOS), &tmp, 0); } return result; } /* - * HTTP protocol `write_resp` implementation. Will parse headers + * HTTP protocol `write_resp` implementation. Parse headers * when not done yet and otherwise return without consuming data. */ CURLcode Curl_http_write_resp_hds(struct Curl_easy *data, @@ -4385,7 +4727,7 @@ CURLcode Curl_http_req_make(struct httpreq **preq, DEBUGASSERT(method && m_len); - req = calloc(1, sizeof(*req) + m_len); + req = curlx_calloc(1, sizeof(*req) + m_len); if(!req) goto out; #if defined(__GNUC__) && __GNUC__ >= 13 @@ -4400,17 +4742,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; } @@ -4427,12 +4769,12 @@ out: static CURLcode req_assign_url_authority(struct httpreq *req, CURLU *url) { - char *user, *pass, *host, *port; + char *host, *port; struct dynbuf buf; CURLUcode uc; CURLcode result = CURLE_URL_MALFORMAT; - user = pass = host = port = NULL; + host = port = NULL; curlx_dyn_init(&buf, DYN_HTTP_REQUEST); uc = curl_url_get(url, CURLUPART_HOST, &host, 0); @@ -4447,28 +4789,7 @@ static CURLcode req_assign_url_authority(struct httpreq *req, CURLU *url) uc = curl_url_get(url, CURLUPART_PORT, &port, CURLU_NO_DEFAULT_PORT); if(uc && uc != CURLUE_NO_PORT) goto out; - uc = curl_url_get(url, CURLUPART_USER, &user, 0); - if(uc && uc != CURLUE_NO_USER) - goto out; - if(user) { - uc = curl_url_get(url, CURLUPART_PASSWORD, &pass, 0); - if(uc && uc != CURLUE_NO_PASSWORD) - goto out; - } - if(user) { - result = curlx_dyn_add(&buf, user); - if(result) - goto out; - if(pass) { - result = curlx_dyn_addf(&buf, ":%s", pass); - if(result) - goto out; - } - result = curlx_dyn_add(&buf, "@"); - if(result) - goto out; - } result = curlx_dyn_add(&buf, host); if(result) goto out; @@ -4477,17 +4798,12 @@ static CURLcode req_assign_url_authority(struct httpreq *req, CURLU *url) if(result) goto out; } - req->authority = strdup(curlx_dyn_ptr(&buf)); - if(!req->authority) - goto out; - result = CURLE_OK; - + req->authority = curlx_dyn_ptr(&buf); out: - free(user); - free(pass); - free(host); - free(port); - curlx_dyn_free(&buf); + curlx_free(host); + curlx_free(port); + if(result) + curlx_dyn_free(&buf); return result; } @@ -4501,41 +4817,32 @@ static CURLcode req_assign_url_path(struct httpreq *req, CURLU *url) path = query = NULL; curlx_dyn_init(&buf, DYN_HTTP_REQUEST); - uc = curl_url_get(url, CURLUPART_PATH, &path, CURLU_PATH_AS_IS); + uc = curl_url_get(url, CURLUPART_PATH, &path, 0); if(uc) goto out; uc = curl_url_get(url, CURLUPART_QUERY, &query, 0); if(uc && uc != CURLUE_NO_QUERY) goto out; - if(!path && !query) { - req->path = NULL; - } - else if(path && !query) { + if(!query) { req->path = path; path = NULL; } else { - if(path) { - result = curlx_dyn_add(&buf, path); - if(result) - goto out; - } - if(query) { + result = curlx_dyn_add(&buf, path); + if(!result) result = curlx_dyn_addf(&buf, "?%s", query); - if(result) - goto out; - } - req->path = strdup(curlx_dyn_ptr(&buf)); - if(!req->path) + if(result) goto out; + req->path = curlx_dyn_ptr(&buf); } result = CURLE_OK; out: - free(path); - free(query); - curlx_dyn_free(&buf); + curlx_free(path); + curlx_free(query); + if(result) + curlx_dyn_free(&buf); return result; } @@ -4549,7 +4856,7 @@ CURLcode Curl_http_req_make2(struct httpreq **preq, DEBUGASSERT(method && m_len); - req = calloc(1, sizeof(*req) + m_len); + req = curlx_calloc(1, sizeof(*req) + m_len); if(!req) goto out; memcpy(req->method, method, m_len); @@ -4558,7 +4865,7 @@ CURLcode Curl_http_req_make2(struct httpreq **preq, if(uc && uc != CURLUE_NO_SCHEME) goto out; if(!req->scheme && scheme_default) { - req->scheme = strdup(scheme_default); + req->scheme = curlx_strdup(scheme_default); if(!req->scheme) goto out; } @@ -4584,12 +4891,12 @@ out: void Curl_http_req_free(struct httpreq *req) { if(req) { - free(req->scheme); - free(req->authority); - free(req->path); + curlx_free(req->scheme); + curlx_free(req->authority); + curlx_free(req->path); Curl_dynhds_free(&req->headers); Curl_dynhds_free(&req->trailers); - free(req); + curlx_free(req); } } @@ -4672,8 +4979,7 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme); } else { - scheme = Curl_conn_is_ssl(data->conn, FIRSTSOCKET) ? - "https" : "http"; + scheme = Curl_xfer_is_secure(data) ? "https" : "http"; } } @@ -4727,13 +5033,13 @@ CURLcode Curl_http_resp_make(struct http_resp **presp, struct http_resp *resp; CURLcode result = CURLE_OUT_OF_MEMORY; - resp = calloc(1, sizeof(*resp)); + resp = curlx_calloc(1, sizeof(*resp)); if(!resp) goto out; resp->status = status; if(description) { - resp->description = strdup(description); + resp->description = curlx_strdup(description); if(!resp->description) goto out; } @@ -4751,160 +5057,36 @@ out: void Curl_http_resp_free(struct http_resp *resp) { if(resp) { - free(resp->description); + curlx_free(resp->description); Curl_dynhds_free(&resp->headers); Curl_dynhds_free(&resp->trailers); if(resp->prev) Curl_http_resp_free(resp->prev); - free(resp); + curlx_free(resp); } } -struct cr_exp100_ctx { - struct Curl_creader super; - struct curltime start; /* time started waiting */ - enum expect100 state; +/* + * HTTP handler interface. + */ +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_is_dead */ + 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; - data->req.keepon |= KEEP_SEND; - data->req.keepon &= ~KEEP_SEND_TIMED; - 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 = curlx_now(); - Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); - data->req.keepon &= ~KEEP_SEND; - data->req.keepon |= KEEP_SEND_TIMED; - *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_timediff(curlx_now(), ctx->start); - if(ms < data->set.expect_100_timeout) { - DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired")); - data->req.keepon &= ~KEEP_SEND; - data->req.keepon |= KEEP_SEND_TIMED; - *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; - data->req.keepon &= ~KEEP_SEND_TIMED; - 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); -} - -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 */ diff --git a/lib/http.h b/lib/http.h index 67ef17f5b9..6e33c00e92 100644 --- a/lib/http.h +++ b/lib/http.h @@ -27,7 +27,6 @@ #include "bufq.h" #include "dynhds.h" -#include "ws.h" typedef enum { HTTPREQ_GET, @@ -38,35 +37,15 @@ typedef enum { HTTPREQ_HEAD } Curl_HttpReq; - -/* When redirecting transfers. */ -typedef enum { - FOLLOW_NONE, /* not used within the function, just a placeholder to - allow initing to this */ - FOLLOW_FAKE, /* only records stuff, not actually following */ - FOLLOW_RETRY, /* set if this is a request retry as opposed to a real - redirect following */ - FOLLOW_REDIR /* a full true redirect */ -} followtype; - #define CURL_HTTP_V1x (1 << 0) #define CURL_HTTP_V2x (1 << 1) #define CURL_HTTP_V3x (1 << 2) /* bitmask of CURL_HTTP_V* values */ typedef unsigned char http_majors; - #ifndef CURL_DISABLE_HTTP -#ifdef USE_HTTP3 -#include -#endif - -extern const struct Curl_handler Curl_handler_http; - -#ifdef USE_SSL -extern const struct Curl_handler Curl_handler_https; -#endif +extern const struct Curl_protocol Curl_protocol_http; struct dynhds; @@ -74,6 +53,7 @@ struct http_negotiation { unsigned char rcvd_min; /* minimum version seen in responses, 09, 10, 11 */ http_majors wanted; /* wanted major versions when talking to server */ http_majors allowed; /* allowed major versions when talking to server */ + http_majors preferred; /* preferred major version when talking to server */ BIT(h2_upgrade); /* Do HTTP Upgrade from 1.1 to 2 */ BIT(h2_prior_knowledge); /* Directly do HTTP/2 without ALPN/SSL */ BIT(accept_09); /* Accept an HTTP/0.9 response */ @@ -106,17 +86,21 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect, CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect, struct dynhds *hds); +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_connect(struct Curl_easy *data, bool *done); -CURLcode Curl_http_do_pollset(struct Curl_easy *data, - struct easy_pollset *ps); +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, + struct easy_pollset *ps); CURLcode Curl_http_write_resp(struct Curl_easy *data, const char *buf, size_t blen, bool is_eos); @@ -138,7 +122,7 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, selected to use no auth at all. Ie, we actively select no auth, as opposed to not having one selected. The other CURLAUTH_* defines are present in the public curl/curl.h header. */ -#define CURLAUTH_PICKNONE (1<<30) /* do not use auth */ +#define CURLAUTH_PICKNONE (1 << 30) /* do not use auth */ /* MAX_INITIAL_POST_SIZE indicates the number of bytes that will make the POST data get included in the initial data chunk sent to the server. If the @@ -153,7 +137,7 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, It must not be greater than 64K to work on VMS. */ #ifndef MAX_INITIAL_POST_SIZE -#define MAX_INITIAL_POST_SIZE (64*1024) +#define MAX_INITIAL_POST_SIZE (64 * 1024) #endif /* EXPECT_100_THRESHOLD is the request body size limit for when libcurl will @@ -162,13 +146,13 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, * */ #ifndef EXPECT_100_THRESHOLD -#define EXPECT_100_THRESHOLD (1024*1024) +#define EXPECT_100_THRESHOLD (1024 * 1024) #endif /* MAX_HTTP_RESP_HEADER_SIZE is the maximum size of all response headers combined that libcurl allows for a single HTTP response, any HTTP version. This count includes CONNECT response headers. */ -#define MAX_HTTP_RESP_HEADER_SIZE (300*1024) +#define MAX_HTTP_RESP_HEADER_SIZE (300 * 1024) /* MAX_HTTP_RESP_HEADER_COUNT is the maximum number of response headers that libcurl allows for a single HTTP response, including CONNECT and @@ -201,19 +185,18 @@ CURLcode Curl_http_write_resp_hds(struct Curl_easy *data, * * @returns CURLcode */ -CURLcode -Curl_http_output_auth(struct Curl_easy *data, - struct connectdata *conn, - const char *request, - Curl_HttpReq httpreq, - const char *path, - bool proxytunnel); /* TRUE if this is the request setting - up the proxy tunnel */ +CURLcode Curl_http_output_auth(struct Curl_easy *data, + struct connectdata *conn, + const char *request, + Curl_HttpReq httpreq, + const char *path, + bool proxytunnel); /* TRUE if this is + the request setting up + the proxy tunnel */ /* Decode HTTP status code string. */ CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len); - /** * All about a core HTTP request, excluding body and trailers */ @@ -241,11 +224,11 @@ CURLcode Curl_http_req_make2(struct httpreq **preq, void Curl_http_req_free(struct httpreq *req); -#define HTTP_PSEUDO_METHOD ":method" -#define HTTP_PSEUDO_SCHEME ":scheme" +#define HTTP_PSEUDO_METHOD ":method" +#define HTTP_PSEUDO_SCHEME ":scheme" #define HTTP_PSEUDO_AUTHORITY ":authority" -#define HTTP_PSEUDO_PATH ":path" -#define HTTP_PSEUDO_STATUS ":status" +#define HTTP_PSEUDO_PATH ":path" +#define HTTP_PSEUDO_STATUS ":status" /** * Create the list of HTTP/2 headers which represent the request, diff --git a/lib/http1.c b/lib/http1.c index 537e6db2ff..60ad32ce89 100644 --- a/lib/http1.c +++ b/lib/http1.c @@ -21,24 +21,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifndef CURL_DISABLE_HTTP #include "urldata.h" -#include #include "http.h" #include "http1.h" #include "urlapi-int.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define H1_MAX_URL_LEN (8*1024) +#define H1_MAX_URL_LEN (8 * 1024) void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len) { @@ -81,63 +74,66 @@ static CURLcode trim_line(struct h1_req_parser *parser, int options) return CURLE_OK; } -static ssize_t detect_line(struct h1_req_parser *parser, - const char *buf, const size_t buflen, - CURLcode *err) +static CURLcode detect_line(struct h1_req_parser *parser, + const uint8_t *buf, const size_t buflen, + size_t *pnread) { - const char *line_end; + const char *line_end; DEBUGASSERT(!parser->line); + *pnread = 0; line_end = memchr(buf, '\n', buflen); - if(!line_end) { - *err = CURLE_AGAIN; - return -1; - } - parser->line = buf; - parser->line_len = line_end - buf + 1; - *err = CURLE_OK; - return (ssize_t)parser->line_len; + if(!line_end) + return CURLE_AGAIN; + parser->line = (const char *)buf; + parser->line_len = line_end - parser->line + 1; + *pnread = parser->line_len; + return CURLE_OK; } -static ssize_t next_line(struct h1_req_parser *parser, - const char *buf, const size_t buflen, int options, - CURLcode *err) +static CURLcode next_line(struct h1_req_parser *parser, + const uint8_t *buf, const size_t buflen, int options, + size_t *pnread) { - ssize_t nread = 0; + CURLcode result; + *pnread = 0; if(parser->line) { parser->line = NULL; parser->line_len = 0; curlx_dyn_reset(&parser->scratch); } - nread = detect_line(parser, buf, buflen, err); - if(nread >= 0) { + result = detect_line(parser, buf, buflen, pnread); + if(!result) { if(curlx_dyn_len(&parser->scratch)) { /* append detected line to scratch to have the complete line */ - *err = curlx_dyn_addn(&parser->scratch, parser->line, parser->line_len); - if(*err) - return -1; + result = curlx_dyn_addn(&parser->scratch, parser->line, + parser->line_len); + if(result) + return result; parser->line = curlx_dyn_ptr(&parser->scratch); parser->line_len = curlx_dyn_len(&parser->scratch); } - *err = trim_line(parser, options); - if(*err) - return -1; + result = trim_line(parser, options); + if(result) + return result; } - else if(*err == CURLE_AGAIN) { + else if(result == CURLE_AGAIN) { /* no line end in `buf`, add it to our scratch */ - *err = curlx_dyn_addn(&parser->scratch, (const unsigned char *)buf, - buflen); - nread = (*err) ? -1 : (ssize_t)buflen; + result = curlx_dyn_addn(&parser->scratch, (const unsigned char *)buf, + buflen); + *pnread = buflen; } - return nread; + return result; } static CURLcode start_req(struct h1_req_parser *parser, - const char *scheme_default, int options) + const char *scheme_default, + const char *custom_method, + int options) { - const char *p, *m, *target, *hv, *scheme, *authority, *path; + const char *p, *m, *target, *hv, *scheme, *authority, *path; size_t m_len, target_len, hv_len, scheme_len, authority_len, path_len; size_t i; CURLU *url = NULL; @@ -145,9 +141,15 @@ static CURLcode start_req(struct h1_req_parser *parser, DEBUGASSERT(!parser->req); /* line must match: "METHOD TARGET HTTP_VERSION" */ - p = memchr(parser->line, ' ', parser->line_len); - if(!p || p == parser->line) - goto out; + if(custom_method && custom_method[0] && + !strncmp(custom_method, parser->line, strlen(custom_method))) { + p = parser->line + strlen(custom_method); + } + else { + p = memchr(parser->line, ' ', parser->line_len); + if(!p || p == parser->line) + goto out; + } m = parser->line; m_len = p - parser->line; @@ -223,8 +225,8 @@ static CURLcode start_req(struct h1_req_parser *parser, result = CURLE_OUT_OF_MEMORY; goto out; } - url_options = (CURLU_NON_SUPPORT_SCHEME| - CURLU_PATH_AS_IS| + url_options = (CURLU_NON_SUPPORT_SCHEME | + CURLU_PATH_AS_IS | CURLU_NO_DEFAULT_PORT); if(!(options & H1_PARSE_OPT_STRICT)) url_options |= CURLU_ALLOW_SPACE; @@ -257,28 +259,33 @@ out: return result; } -ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser, - const char *buf, size_t buflen, - const char *scheme_default, int options, - CURLcode *err) +CURLcode Curl_h1_req_parse_read(struct h1_req_parser *parser, + const uint8_t *buf, size_t buflen, + const char *scheme_default, + const char *custom_method, + int options, size_t *pnread) { - ssize_t nread = 0, n; + CURLcode result = CURLE_OK; + size_t nread; + + *pnread = 0; + + DEBUGASSERT(buf); + if(!buf) + return CURLE_BAD_FUNCTION_ARGUMENT; - *err = CURLE_OK; while(!parser->done) { - n = next_line(parser, buf, buflen, options, err); - if(n < 0) { - if(*err != CURLE_AGAIN) { - nread = -1; - } - *err = CURLE_OK; + result = next_line(parser, buf, buflen, options, &nread); + if(result) { + if(result == CURLE_AGAIN) + result = CURLE_OK; goto out; } /* Consume this line */ - nread += (size_t)n; - buf += (size_t)n; - buflen -= (size_t)n; + *pnread += nread; + buf += nread; + buflen -= nread; if(!parser->line) { /* consumed bytes, but line not complete */ @@ -286,17 +293,14 @@ ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser, goto out; } else if(!parser->req) { - *err = start_req(parser, scheme_default, options); - if(*err) { - nread = -1; + result = start_req(parser, scheme_default, custom_method, options); + if(result) goto out; - } } else if(parser->line_len == 0) { /* last, empty line, we are finished */ if(!parser->req) { - *err = CURLE_URL_MALFORMAT; - nread = -1; + result = CURLE_URL_MALFORMAT; goto out; } parser->done = TRUE; @@ -304,17 +308,15 @@ ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser, /* last chance adjustments */ } else { - *err = Curl_dynhds_h1_add_line(&parser->req->headers, - parser->line, parser->line_len); - if(*err) { - nread = -1; + result = Curl_dynhds_h1_add_line(&parser->req->headers, + parser->line, parser->line_len); + if(result) goto out; - } } } out: - return nread; + return result; } CURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor, diff --git a/lib/http1.h b/lib/http1.h index b38b32f591..c2894613de 100644 --- a/lib/http1.h +++ b/lib/http1.h @@ -23,14 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifndef CURL_DISABLE_HTTP #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 @@ -48,10 +47,11 @@ struct h1_req_parser { void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len); void Curl_h1_req_parse_free(struct h1_req_parser *parser); -ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser, - const char *buf, size_t buflen, - const char *scheme_default, int options, - CURLcode *err); +CURLcode Curl_h1_req_parse_read(struct h1_req_parser *parser, + const uint8_t *buf, size_t buflen, + const char *scheme_default, + const char *custom_method, + int options, size_t *pnread); CURLcode Curl_h1_req_dprint(const struct httpreq *req, struct dynbuf *dbuf); diff --git a/lib/http2.c b/lib/http2.c index e2cdc9181f..54044bcb9d 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -21,12 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGHTTP2) -#include #include + #include "urldata.h" #include "bufq.h" #include "uint-hash.h" @@ -34,32 +33,24 @@ #include "http2.h" #include "http.h" #include "sendf.h" +#include "curl_trc.h" #include "select.h" #include "curlx/base64.h" #include "multiif.h" +#include "progress.h" #include "url.h" #include "urlapi-int.h" #include "cfilters.h" #include "connect.h" -#include "rand.h" -#include "strdup.h" -#include "curlx/strparse.h" #include "transfer.h" +#include "bufref.h" #include "curlx/dynbuf.h" #include "headers.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" #if (NGHTTP2_VERSION_NUM < 0x010c00) #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 @@ -72,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. */ @@ -95,36 +86,9 @@ * is blocked from sending us any data. See #10988 for an issue with this. */ #define HTTP2_HUGE_WINDOW_SIZE (100 * H2_STREAM_WINDOW_SIZE_MAX) -#define H2_SETTINGS_IV_LEN 3 +#define H2_SETTINGS_IV_LEN 3 #define H2_BINSETTINGS_LEN 80 -static size_t populate_settings(nghttp2_settings_entry *iv, - struct Curl_easy *data) -{ - 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_STREAM_WINDOW_SIZE_INITIAL; - - iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; - iv[2].value = data->multi->push_cb != NULL; - - return 3; -} - -static ssize_t populate_binsettings(uint8_t *binsettings, - struct Curl_easy *data) -{ - nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; - size_t ivlen; - - ivlen = populate_settings(iv, data); - /* this returns number of bytes it wrote or a negative number on error. */ - return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, - iv, ivlen); -} - struct cf_h2_ctx { nghttp2_session *h2; /* The easy handle used in the current filter call, cleared at return */ @@ -137,13 +101,11 @@ struct cf_h2_ctx { struct uint_hash streams; /* hash of `data->mid` to `h2_stream_ctx` */ size_t drain_total; /* sum of all stream's UrlState drain */ + uint32_t initial_win_size; /* current initial window size (settings) */ uint32_t max_concurrent_streams; uint32_t goaway_error; /* goaway error code from server */ int32_t remote_max_sid; /* max id processed by server */ int32_t local_max_sid; /* max id processed by us */ -#ifdef DEBUGBUILD - int32_t stream_win_max; /* max h2 stream window size */ -#endif BIT(initialized); BIT(via_h1_upgrade); BIT(conn_closed); @@ -155,60 +117,7 @@ struct cf_h2_ctx { /* How to access `call_data` from a cf_h2 filter */ #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); - -static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade) -{ - Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES); - Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0); - Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0); - curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); - Curl_uint_hash_init(&ctx->streams, 63, h2_stream_hash_free); - ctx->remote_max_sid = 2147483647; - ctx->via_h1_upgrade = via_h1_upgrade; -#ifdef DEBUGBUILD - { - const char *p = getenv("CURL_H2_STREAM_WIN_MAX"); - - ctx->stream_win_max = H2_STREAM_WINDOW_SIZE_MAX; - if(p) { - curl_off_t l; - if(!curlx_str_number(&p, &l, INT_MAX)) - ctx->stream_win_max = (int32_t)l; - } - } -#endif - ctx->initialized = TRUE; -} - -static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) -{ - if(ctx && ctx->initialized) { - Curl_bufq_free(&ctx->inbufq); - Curl_bufq_free(&ctx->outbufq); - Curl_bufcp_free(&ctx->stream_bufcp); - curlx_dyn_free(&ctx->scratch); - Curl_uint_hash_destroy(&ctx->streams); - memset(ctx, 0, sizeof(*ctx)); - } - free(ctx); -} - -static void cf_h2_ctx_close(struct cf_h2_ctx *ctx) -{ - if(ctx->h2) { - nghttp2_session_del(ctx->h2); - } -} - -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); +#define CF_CTX_CALL_DATA(cf) ((struct cf_h2_ctx *)(cf)->ctx)->call_data /** * All about the H2 internals of a stream @@ -232,6 +141,7 @@ struct h2_stream_ctx { 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 @@ -239,16 +149,128 @@ struct h2_stream_ctx { BIT(write_paused); /* stream write is paused */ }; -#define H2_STREAM_CTX(ctx,data) \ - ((struct h2_stream_ctx *)( \ - data? Curl_uint_hash_get(&(ctx)->streams, (data)->mid) : NULL)) +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]); + curlx_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) +{ + Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES); + Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0); + Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0); + curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); + Curl_uint32_hash_init(&ctx->streams, 63, h2_stream_hash_free); + ctx->remote_max_sid = 2147483647; + ctx->via_h1_upgrade = via_h1_upgrade; + ctx->initialized = TRUE; +} + +static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) +{ + if(ctx && ctx->initialized) { + Curl_bufq_free(&ctx->inbufq); + Curl_bufq_free(&ctx->outbufq); + Curl_bufcp_free(&ctx->stream_bufcp); + curlx_dyn_free(&ctx->scratch); + Curl_uint32_hash_destroy(&ctx->streams); + memset(ctx, 0, sizeof(*ctx)); + } + curlx_free(ctx); +} + +static void cf_h2_ctx_close(struct cf_h2_ctx *ctx) +{ + if(ctx->h2) { + nghttp2_session_del(ctx->h2); + ctx->h2 = NULL; + } +} + +static uint32_t cf_h2_initial_win_size(struct Curl_easy *data) +{ +#if NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE + /* 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. */ + 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; +} + +static size_t populate_settings(nghttp2_settings_entry *iv, + struct Curl_easy *data, + struct cf_h2_ctx *ctx) +{ + 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 = cf_h2_initial_win_size(data); + if(ctx) + ctx->initial_win_size = iv[1].value; + iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; + iv[2].value = data->multi->push_cb != NULL; + + return 3; +} + +static ssize_t populate_binsettings(uint8_t *binsettings, + struct Curl_easy *data) +{ + nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN]; + size_t ivlen; + + ivlen = populate_settings(iv, data, NULL); + /* this returns number of bytes it wrote or a negative number on error. */ + return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, + iv, ivlen); +} + +static CURLcode cf_h2_update_settings(struct cf_h2_ctx *ctx, + uint32_t initial_win_size) +{ + nghttp2_settings_entry entry; + entry.settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + entry.value = initial_win_size; + if(nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE, &entry, 1)) + return CURLE_SEND_ERROR; + ctx->initial_win_size = initial_win_size; + return CURLE_OK; +} + +#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) { struct h2_stream_ctx *stream; (void)ctx; - stream = calloc(1, sizeof(*stream)); + stream = curlx_calloc(1, sizeof(*stream)); if(!stream) return NULL; @@ -267,52 +289,21 @@ 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++) - 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); - 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) { + curl_off_t avail = Curl_rlimit_avail(&data->progress.dl.rlimit, + Curl_pgrs_now(data)); + (void)cf; - if(data->set.max_recv_speed && data->set.max_recv_speed < INT32_MAX) { - /* The transfer should only receive `max_recv_speed` bytes per second. - * We restrict the stream's local window size, so that the server cannot - * send us "too much" at a time. - * This gets less precise the higher the latency. */ - return (int32_t)data->set.max_recv_speed; + if(avail < CURL_OFF_T_MAX) { /* limit in place */ + if(avail <= 0) + return 0; + else if(avail < INT32_MAX) + return (int32_t)avail; } -#ifdef DEBUGBUILD - else { - struct cf_h2_ctx *ctx = cf->ctx; - CURL_TRC_CF(data, cf, "stream_win_max=%d", ctx->stream_win_max); - return ctx->stream_win_max; - } -#else return H2_STREAM_WINDOW_SIZE_MAX; -#endif } static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf, @@ -376,7 +367,6 @@ static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf, } #endif /* !NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE */ - static CURLcode http2_data_setup(struct Curl_cfilter *cf, struct Curl_easy *data, struct h2_stream_ctx **pstream) @@ -386,6 +376,10 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf, (void)cf; DEBUGASSERT(data); + + if(!data) + return CURLE_BAD_FUNCTION_ARGUMENT; + stream = H2_STREAM_CTX(ctx, data); if(stream) { *pstream = stream; @@ -396,7 +390,7 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf, if(!stream) return CURLE_OUT_OF_MEMORY; - if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) { + if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) { h2_stream_ctx_free(stream); return CURLE_OUT_OF_MEMORY; } @@ -405,6 +399,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; @@ -436,7 +453,7 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) } } - Curl_uint_hash_remove(&ctx->streams, data->mid); + Curl_uint32_hash_remove(&ctx->streams, data->mid); } static int h2_client_new(struct Curl_cfilter *cf, @@ -444,8 +461,8 @@ static int h2_client_new(struct Curl_cfilter *cf, { struct cf_h2_ctx *ctx = cf->ctx; nghttp2_option *o; - nghttp2_mem mem = {NULL, Curl_nghttp2_malloc, Curl_nghttp2_free, - Curl_nghttp2_calloc, Curl_nghttp2_realloc}; + nghttp2_mem mem = { NULL, Curl_nghttp2_malloc, Curl_nghttp2_free, + Curl_nghttp2_calloc, Curl_nghttp2_realloc }; int rc = nghttp2_option_new(&o); if(rc) @@ -463,152 +480,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, "Couldn't 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, "Couldn't 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 binlen; /* length of the binsettings data */ - - binlen = populate_binsettings(binsettings, data); - if(binlen <= 0) { - 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, (size_t)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); - 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. */ @@ -623,24 +494,21 @@ static int should_close_session(struct cf_h2_ctx *ctx) * This function returns 0 if it succeeds, or -1 and error code will * be assigned to *err. */ -static int h2_process_pending_input(struct Curl_cfilter *cf, - struct Curl_easy *data, - CURLcode *err) +static CURLcode h2_process_pending_input(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_h2_ctx *ctx = cf->ctx; const unsigned char *buf; - size_t blen; + size_t blen, nread; ssize_t rv; while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) { - rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen); - if(rv < 0) { + if(!curlx_sztouz(rv, &nread)) { failf(data, "nghttp2 recv error %zd: %s", rv, nghttp2_strerror((int)rv)); - *err = CURLE_HTTP2; - return -1; + return CURLE_HTTP2; } - Curl_bufq_skip(&ctx->inbufq, (size_t)rv); + Curl_bufq_skip(&ctx->inbufq, nread); if(Curl_bufq_is_empty(&ctx->inbufq)) { break; } @@ -658,12 +526,12 @@ static int h2_process_pending_input(struct Curl_cfilter *cf, connclose(cf->conn, "http/2: No new requests allowed"); } - return 0; + return CURLE_OK; } /* - * 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. @@ -690,7 +558,8 @@ static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data, if(!result) { CURL_TRC_CF(data, cf, "%zu bytes stray data read before trying " "h2 connection", nread); - if(h2_process_pending_input(cf, data, &result) < 0) + result = h2_process_pending_input(cf, data); + if(result) /* immediate error, considered dead */ alive = FALSE; else { @@ -716,7 +585,7 @@ static CURLcode http2_send_ping(struct Curl_cfilter *cf, if(rc) { failf(data, "nghttp2_submit_ping() failed: %s(%d)", nghttp2_strerror(rc), rc); - return CURLE_HTTP2; + return CURLE_HTTP2; } rc = nghttp2_session_send(ctx->h2); @@ -734,31 +603,7 @@ static CURLcode http2_send_ping(struct Curl_cfilter *cf, void Curl_http2_ver(char *p, size_t len) { nghttp2_info *h2 = nghttp2_version(0); - (void)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; + (void)curl_msnprintf(p, len, "nghttp2/%s", h2->version_str); } /* @@ -799,11 +644,10 @@ static ssize_t send_callback(nghttp2_session *h2, ctx->nw_out_blocked = 1; return NGHTTP2_ERR_WOULDBLOCK; } - return (nwritten > SSIZE_MAX) ? + return (nwritten > SSIZE_MAX) ? NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nwritten; } - /* We pass a pointer to this struct in the push callback, but the contents of the struct are hidden from the user. */ struct curl_pushheaders { @@ -831,28 +675,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; @@ -874,7 +718,7 @@ static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf, return second; } -static int set_transfer_url(struct Curl_easy *data, +static int set_transfer_url(struct Curl_easy *data, bool via_ssl_conn, struct curl_pushheaders *hp) { const char *v; @@ -888,6 +732,14 @@ static int set_transfer_url(struct Curl_easy *data, v = curl_pushheader_byname(hp, HTTP_PSEUDO_SCHEME); if(v) { + if(!via_ssl_conn) { + /* PUSH over an insecure connection, accept only insecure schemes. */ + const struct Curl_scheme *scheme = Curl_get_scheme(v); + if(!scheme || (scheme->flags & PROTOPT_SSL)) { + rc = 1; + goto fail; + } + } uc = curl_url_set(u, CURLUPART_SCHEME, v, 0); if(uc) { rc = 1; @@ -921,10 +773,7 @@ fail: if(rc) return rc; - if(data->state.url_alloc) - free(data->state.url); - data->state.url_alloc = TRUE; - data->state.url = url; + Curl_bufref_set(&data->state.url, url, 0, curl_free); return 0; } @@ -948,7 +797,7 @@ static int push_promise(struct Curl_cfilter *cf, struct h2_stream_ctx *stream; struct h2_stream_ctx *newstream; struct curl_pushheaders heads; - CURLMcode rc; + CURLMcode mresult; CURLcode result; /* clone the parent */ struct Curl_easy *newhandle = h2_duphandle(cf, data); @@ -970,9 +819,10 @@ static int push_promise(struct Curl_cfilter *cf, heads.stream = stream; heads.frame = frame; - rv = set_transfer_url(newhandle, &heads); + rv = set_transfer_url(newhandle, + Curl_conn_is_ssl(cf->conn, cf->sockindex), &heads); if(rv) { - CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, failed to set url -> %d", + CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, failed to set URL -> %d", frame->promised_stream_id, rv); discard_newhandle(cf, newhandle); rv = CURL_PUSH_DENY; @@ -1000,8 +850,8 @@ static int push_promise(struct Curl_cfilter *cf, /* approved, add to the multi handle for processing. This * assigns newhandle->mid. For the new `mid` we assign the * h2_stream instance and remember the stream_id already known. */ - rc = Curl_multi_add_perform(data->multi, newhandle, cf->conn); - if(rc) { + mresult = Curl_multi_add_perform(data->multi, newhandle, cf->conn); + if(mresult) { infof(data, "failed to add handle to multi"); discard_newhandle(cf, newhandle); rv = CURL_PUSH_DENY; @@ -1030,6 +880,7 @@ static int push_promise(struct Curl_cfilter *cf, infof(data, "failed to set user_data for stream %u", newstream->id); DEBUGASSERT(0); + discard_newhandle(cf, newhandle); rv = CURL_PUSH_DENY; goto fail; } @@ -1051,7 +902,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); @@ -1068,7 +918,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); @@ -1145,7 +994,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; @@ -1169,14 +1019,12 @@ 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: - if(CURL_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) { + if(CURL_REQ_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) { /* need more data, force processing of transfer */ Curl_multi_mark_dirty(data); } @@ -1209,75 +1057,75 @@ 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: { - return 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_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]"); } - case NGHTTP2_HEADERS: { - return 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 msnprintf(buffer, blen, - "FRAME[PRIORITY, len=%d, flags=%d]", - (int)frame->hd.length, frame->hd.flags); - } - case NGHTTP2_RST_STREAM: { - return 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 msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]"); - } - return msnprintf(buffer, blen, - "FRAME[SETTINGS, len=%d]", (int)frame->hd.length); - } - case NGHTTP2_PUSH_PROMISE: { - return 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 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 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 msnprintf(buffer, blen, - "FRAME[WINDOW_UPDATE, incr=%d]", - frame->window_update.window_size_increment); - } - default: - return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]", - frame->hd.type, (int)frame->hd.length, - frame->hd.flags); + 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); } } @@ -1290,10 +1138,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); } @@ -1306,7 +1154,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) @@ -1317,15 +1165,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); + 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 */ @@ -1335,9 +1183,9 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, if(!(frame->hd.flags & NGHTTP2_FLAG_ACK)) { uint32_t max_conn = ctx->max_concurrent_streams; ctx->max_concurrent_streams = nghttp2_session_get_remote_settings( - session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); + session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); ctx->enable_push = nghttp2_session_get_remote_settings( - session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0; + session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0; CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %d", ctx->max_concurrent_streams); CURL_TRC_CF(data, cf, "[0] ENABLE_PUSH: %s", @@ -1353,7 +1201,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, * window and *assume* that we treat this like a WINDOW_UPDATE. Some * servers send an explicit WINDOW_UPDATE, but not all seem to do that. * To be safe, we UNHOLD a stream in order not to stall. */ - if(CURL_WANT_SEND(data)) + if(CURL_REQ_WANT_SEND(data)) Curl_multi_mark_dirty(data); } break; @@ -1364,7 +1212,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, ctx->remote_max_sid = frame->goaway.last_stream_id; if(data) { infof(data, "received GOAWAY, error=%u, last_stream=%u", - ctx->goaway_error, ctx->remote_max_sid); + ctx->goaway_error, ctx->remote_max_sid); Curl_multi_connchanged(data->multi); } break; @@ -1395,14 +1243,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, @@ -1435,8 +1283,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags, /* Receiving a Stream ID not in the hash should not happen - unless we have aborted a transfer artificially and there were more data in the pipeline. Silently ignore. */ - CURL_TRC_CF(CF_DATA_CURRENT(cf), cf, "[%d] Data for unknown", - stream_id); + CURL_TRC_CF(CF_DATA_CURRENT(cf), cf, "[%d] Data for unknown", stream_id); /* consumed explicitly as no one will read it */ nghttp2_session_consume(session, stream_id, len); return 0; @@ -1490,9 +1337,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)", @@ -1589,11 +1435,12 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(frame->hd.type == NGHTTP2_PUSH_PROMISE) { char *h; - if(!strcmp(HTTP_PSEUDO_AUTHORITY, (const char *)name)) { + if((namelen == (sizeof(HTTP_PSEUDO_AUTHORITY) - 1)) && + !strncmp(HTTP_PSEUDO_AUTHORITY, (const char *)name, namelen)) { /* pseudo headers are lower case */ int rc = 0; - char *check = aprintf("%s:%d", cf->conn->host.name, - cf->conn->remote_port); + char *check = curl_maprintf("%s:%d", cf->conn->host.name, + cf->conn->remote_port); if(!check) /* no memory */ return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -1609,15 +1456,15 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, stream_id, NGHTTP2_PROTOCOL_ERROR); rc = NGHTTP2_ERR_CALLBACK_FAILURE; } - free(check); + curlx_free(check); if(rc) return rc; } if(!stream->push_headers) { stream->push_headers_alloc = 10; - stream->push_headers = malloc(stream->push_headers_alloc * - sizeof(char *)); + stream->push_headers = curlx_malloc(stream->push_headers_alloc * + sizeof(char *)); if(!stream->push_headers) return NGHTTP2_ERR_CALLBACK_FAILURE; stream->push_headers_used = 0; @@ -1632,17 +1479,20 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, return NGHTTP2_ERR_CALLBACK_FAILURE; } stream->push_headers_alloc *= 2; - headp = realloc(stream->push_headers, - stream->push_headers_alloc * sizeof(char *)); + headp = curlx_realloc(stream->push_headers, + stream->push_headers_alloc * sizeof(char *)); if(!headp) { free_push_headers(stream); return NGHTTP2_ERR_CALLBACK_FAILURE; } stream->push_headers = headp; } - h = aprintf("%s:%s", name, value); - if(h) - stream->push_headers[stream->push_headers_used++] = h; + h = curl_maprintf("%s:%s", name, value); + if(!h) { + free_push_headers(stream); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + stream->push_headers[stream->push_headers_used++] = h; return 0; } @@ -1665,15 +1515,16 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { /* nghttp2 guarantees :status is received first and only once. */ char buffer[32]; + size_t hlen; result = Curl_http_decode_status(&stream->status_code, (const char *)value, valuelen); if(result) { cf_h2_header_error(cf, data_s, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; } - msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%u\r", - stream->status_code); - result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO); + hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%u\r", + stream->status_code); + result = Curl_headers_push(data_s, buffer, hlen, CURLH_PSEUDO); if(result) { cf_h2_header_error(cf, data_s, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -1779,7 +1630,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, @@ -1804,17 +1655,17 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, size_t blen; struct SingleRequest *k = &data->req; uint8_t binsettings[H2_BINSETTINGS_LEN]; - ssize_t binlen; /* length of the binsettings data */ + ssize_t rc; + size_t binlen; /* length of the binsettings data */ - binlen = populate_binsettings(binsettings, data); - if(binlen <= 0) { + rc = populate_binsettings(binsettings, data); + if(!curlx_sztouz(rc, &binlen) || !binlen) { failf(data, "nghttp2 unexpectedly failed on pack_settings_payload"); curlx_dyn_free(req); return CURLE_FAILED_INIT; } - result = curlx_base64url_encode((const char *)binsettings, (size_t)binlen, - &base64, &blen); + result = curlx_base64url_encode(binsettings, binlen, &base64, &blen); if(result) { curlx_dyn_free(req); return result; @@ -1826,10 +1677,10 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, "Upgrade: %s\r\n" "HTTP2-Settings: %s\r\n", NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64); - free(base64); + curlx_free(base64); k->upgr101 = UPGR101_H2; - data->conn->bits.asks_multiplex = TRUE; + data->conn->bits.upgrade_in_progress = TRUE; return result; } @@ -1842,36 +1693,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; + 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 */ } - 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->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 reset by %s (error 0x%x %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->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); + 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; } @@ -1888,13 +1734,14 @@ static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf, break; curlx_dyn_reset(&dbuf); result = curlx_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a", - (int)e->namelen, e->name, - (int)e->valuelen, e->value); + (int)e->namelen, e->name, + (int)e->valuelen, e->value); if(result) break; Curl_debug(data, CURLINFO_HEADER_IN, curlx_dyn_ptr(&dbuf), curlx_dyn_len(&dbuf)); - result = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER, + result = Curl_client_write(data, + CLIENTWRITE_HEADER | CLIENTWRITE_TRAILER, curlx_dyn_ptr(&dbuf), curlx_dyn_len(&dbuf)); if(result) break; @@ -1961,7 +1808,7 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, if(stream && stream->id > 0 && ((sweight_wanted(data) != sweight_in_effect(data)) || (data->set.priority.exclusive != data->state.priority.exclusive) || - (data->set.priority.parent != data->state.priority.parent)) ) { + (data->set.priority.parent != data->state.priority.parent))) { /* send new weight and/or dependency */ nghttp2_priority_spec pri_spec; @@ -1987,7 +1834,7 @@ out: /* Defer flushing during the connect phase so that the SETTINGS and * other initial frames are sent together with the first request. * Unless we are 'connect_only' where the request will never come. */ - if(!cf->connected && !cf->conn->connect_only) + if(!cf->connected && !cf->conn->bits.connect_only) return CURLE_OK; return nw_out_flush(cf, data); } @@ -2003,6 +1850,9 @@ static CURLcode stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, (void)len; *pnread = 0; + if(!stream->xfer_result) + stream->xfer_result = cf_h2_update_local_win(cf, data, stream); + if(stream->xfer_result) { CURL_TRC_CF(data, cf, "[%d] xfer write failed", stream->id); result = stream->xfer_result; @@ -2034,32 +1884,39 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, size_t nread; if(should_close_session(ctx)) { - CURL_TRC_CF(data, cf, "progress ingress, session is closed"); + CURL_TRC_CF(data, cf, "[0] ingress: session is closed"); return CURLE_HTTP2; } - /* Process network input buffer fist */ + /* Process network input buffer first */ if(!Curl_bufq_is_empty(&ctx->inbufq)) { CURL_TRC_CF(data, cf, "Process %zu bytes in connection buffer", Curl_bufq_len(&ctx->inbufq)); - if(h2_process_pending_input(cf, data, &result) < 0) + result = h2_process_pending_input(cf, data); + if(result) return result; } + if(!data_max_bytes) + data_max_bytes = H2_CHUNK_SIZE; + /* Receive data from the "lower" filters, e.g. network until * it is time to stop due to connection close or us not processing * all network input */ 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; } + else if(!stream) { + DEBUGASSERT(0); + break; + } result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); if(result) { @@ -2080,17 +1937,19 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, data_max_bytes = (data_max_bytes > nread) ? (data_max_bytes - nread) : 0; } - if(h2_process_pending_input(cf, data, &result)) + result = h2_process_pending_input(cf, data); + if(result) return result; - CURL_TRC_CF(data, cf, "[0] progress ingress: inbufg=%zu", + CURL_TRC_CF(data, cf, "[0] ingress: nw-in buffered %zu", Curl_bufq_len(&ctx->inbufq)); } if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) { - connclose(cf->conn, "GOAWAY received"); + connclose(cf->conn, ctx->rcvd_goaway ? "server closed with GOAWAY" : + "server closed abruptly"); } - CURL_TRC_CF(data, cf, "[0] progress ingress: done"); + CURL_TRC_CF(data, cf, "[0] ingress: done"); return CURLE_OK; } @@ -2098,10 +1957,15 @@ static CURLcode cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, size_t *pnread) { struct cf_h2_ctx *ctx = cf->ctx; - struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + struct h2_stream_ctx *stream; CURLcode result, r2; struct cf_call_data save; + if(!data) + return CURLE_HTTP2; + + stream = H2_STREAM_CTX(ctx, data); + *pnread = 0; if(!stream) { /* Abnormal call sequence: either this transfer has never opened a stream @@ -2144,7 +2008,7 @@ out: /* pending data to send, need to be called again. Ideally, we * monitor the socket for POLLOUT, but when not SENDING * any more, we force processing of the transfer. */ - if(!CURL_WANT_SEND(data)) + if(!CURL_REQ_WANT_SEND(data)) Curl_multi_mark_dirty(data); } else if(r2) { @@ -2164,15 +2028,16 @@ out: return result; } -static ssize_t cf_h2_body_send(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h2_stream_ctx *stream, - const void *buf, size_t blen, bool eos, - CURLcode *err) +static CURLcode cf_h2_body_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h2_stream_ctx *stream, + const void *buf, size_t blen, bool eos, + size_t *pnwritten) { struct cf_h2_ctx *ctx = cf->ctx; - size_t nwritten; + CURLcode result; + *pnwritten = 0; if(stream->closed) { if(stream->resp_hds_complete) { /* Server decided to close the stream after having sent us a final @@ -2184,36 +2049,34 @@ static ssize_t cf_h2_body_send(struct Curl_cfilter *cf, "on closed stream with response", stream->id); if(eos) stream->body_eos = TRUE; - *err = CURLE_OK; - return (ssize_t)blen; + *pnwritten = blen; + return CURLE_OK; } /* Server closed before we got a response, this is an error */ infof(data, "stream %u closed", stream->id); - *err = CURLE_SEND_ERROR; - return -1; + return CURLE_SEND_ERROR; } - *err = Curl_bufq_write(&stream->sendbuf, buf, blen, &nwritten); - if(*err) - return -1; + result = Curl_bufq_write(&stream->sendbuf, buf, blen, pnwritten); + if(result) + return result; - if(eos && (blen == nwritten)) + if(eos && (blen == *pnwritten)) stream->body_eos = TRUE; if(eos || !Curl_bufq_is_empty(&stream->sendbuf)) { /* resume the potentially suspended stream */ int rv = nghttp2_session_resume_data(ctx->h2, stream->id); - if(nghttp2_is_fatal(rv)) { - *err = CURLE_SEND_ERROR; - return -1; - } + if(nghttp2_is_fatal(rv)) + return CURLE_SEND_ERROR; } - return (ssize_t)nwritten; + + return CURLE_OK; } static CURLcode h2_submit(struct h2_stream_ctx **pstream, struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) { struct cf_h2_ctx *ctx = cf->ctx; @@ -2225,8 +2088,9 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream, nghttp2_data_provider data_prd; int32_t stream_id; nghttp2_priority_spec pri_spec; - ssize_t nwritten; + size_t nwritten; CURLcode result = CURLE_OK; + uint32_t initial_win_size; *pnwritten = 0; Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); @@ -2235,10 +2099,13 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream, if(result) goto out; - nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, &result); - if(nwritten < 0) + result = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, + !data->state.http_ignorecustom ? + data->set.str[STRING_CUSTOMREQUEST] : NULL, + 0, &nwritten); + if(result) goto out; - *pnwritten = (size_t)nwritten; + *pnwritten = nwritten; if(!stream->h1.done) { /* need more data */ goto out; @@ -2261,6 +2128,15 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream, if(!nghttp2_session_check_request_allowed(ctx->h2)) CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)"); + /* Check the initial windows size of the transfer (rate-limits?) and + * send an updated settings on changes from previous value. */ + initial_win_size = cf_h2_initial_win_size(data); + if(initial_win_size != ctx->initial_win_size) { + result = cf_h2_update_settings(ctx, initial_win_size); + if(result) + goto out; + } + switch(data->state.httpreq) { case HTTPREQ_POST: case HTTPREQ_POST_FORM: @@ -2283,13 +2159,13 @@ 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; infof(data, "[HTTP/2] [%d] OPENED stream for %s", - stream_id, data->state.url); + stream_id, Curl_bufref_ptr(&data->state.url)); for(i = 0; i < nheader; ++i) { acc += nva[i].namelen + nva[i].valuelen; @@ -2312,8 +2188,9 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream, bodylen = len - *pnwritten; if(bodylen || eos) { - ssize_t n = cf_h2_body_send(cf, data, stream, body, bodylen, eos, &result); - if(n >= 0) + size_t n; + result = cf_h2_body_send(cf, data, stream, body, bodylen, eos, &n); + if(!result) *pnwritten += n; else if(result == CURLE_AGAIN) result = CURLE_OK; @@ -2325,20 +2202,19 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream, out: CURL_TRC_CF(data, cf, "[%d] submit -> %d, %zu", stream ? stream->id : -1, result, *pnwritten); - Curl_safefree(nva); + curlx_safefree(nva); *pstream = stream; Curl_dynhds_free(&h2_headers); return result; } static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) { struct cf_h2_ctx *ctx = cf->ctx; struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); struct cf_call_data save; - ssize_t nwritten; CURLcode result = CURLE_OK, r2; CF_DATA_SAVE(save, cf, data); @@ -2355,21 +2231,19 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, * being able to flush stream->sendbuf. Make a 0-length write * to trigger flushing again. * If this works, we report to have written `len` bytes. */ + size_t n; DEBUGASSERT(eos); - nwritten = cf_h2_body_send(cf, data, stream, buf, 0, eos, &result); - CURL_TRC_CF(data, cf, "[%d] cf_body_send last CHUNK -> %zd, %d, eos=%d", - stream->id, nwritten, result, eos); - if(nwritten < 0) { + result = cf_h2_body_send(cf, data, stream, buf, 0, eos, &n); + CURL_TRC_CF(data, cf, "[%d] cf_body_send last CHUNK -> %d, %zu, eos=%d", + stream->id, result, n, eos); + if(result) goto out; - } *pnwritten = len; } else { - nwritten = cf_h2_body_send(cf, data, stream, buf, len, eos, &result); - CURL_TRC_CF(data, cf, "[%d] cf_body_send(len=%zu) -> %zd, %d, eos=%d", - stream->id, len, nwritten, result, eos); - if(nwritten >= 0) - *pnwritten = (size_t)nwritten; + result = cf_h2_body_send(cf, data, stream, buf, len, eos, pnwritten); + CURL_TRC_CF(data, cf, "[%d] cf_body_send(len=%zu) -> %d, %zu, eos=%d", + stream->id, len, result, *pnwritten, eos); } /* Call the nghttp2 send loop and flush to write ALL buffered data, @@ -2510,6 +2384,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) @@ -2543,13 +2536,13 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf, } if(!first_time) { - result = h2_progress_ingress(cf, data, H2_CHUNK_SIZE); + result = h2_progress_ingress(cf, data, 0); if(result) goto out; } /* Send out our SETTINGS and ACKs and such. If that blocks, we - * have it buffered and can count this filter as being connected */ + * have it buffered and can count this filter as being connected */ result = h2_progress_egress(cf, data); if(result && (result != CURLE_AGAIN)) goto out; @@ -2696,6 +2689,12 @@ static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf, case CF_CTRL_DATA_DONE: http2_data_done(cf, data); break; + case CF_CTRL_CONN_INFO_UPDATE: + if(!cf->sockindex && cf->connected) { + cf->conn->httpversion_seen = 20; + Curl_conn_set_multiplex(cf->conn); + } + break; default: break; } @@ -2757,7 +2756,7 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); if(!ctx->h2 || !nghttp2_session_check_request_allowed(ctx->h2)) { /* the limit is what we have in use right now */ - effective_max = CONN_ATTACHED(cf->conn); + effective_max = cf->conn->attached_xfers; } else { effective_max = ctx->max_concurrent_streams; @@ -2819,7 +2818,7 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf, CURLcode result = CURLE_OUT_OF_MEMORY; DEBUGASSERT(data->conn); - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) goto out; cf_h2_ctx_init(ctx, via_h1_upgrade); @@ -2847,7 +2846,7 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf, CURLcode result = CURLE_OUT_OF_MEMORY; (void)data; - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) goto out; cf_h2_ctx_init(ctx, via_h1_upgrade); @@ -2895,9 +2894,6 @@ CURLcode Curl_http2_switch(struct Curl_easy *data) return result; CURL_TRC_CF(data, cf, "switching connection to HTTP/2"); - data->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - Curl_multi_connchanged(data->multi); - if(cf->next) { bool done; return Curl_conn_cf_connect(cf, data, &done); @@ -2917,8 +2913,6 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data) return result; cf_h2 = cf->next; - cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - Curl_multi_connchanged(data->multi); if(cf_h2->next) { bool done; @@ -2935,8 +2929,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, struct cf_h2_ctx *ctx; CURLcode result; - DEBUGASSERT(Curl_conn_http_version(data, conn) < 20); - DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED); + DEBUGASSERT(Curl_conn_http_version(data, conn) < 20); result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE); if(result) @@ -2946,6 +2939,10 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, DEBUGASSERT(cf->cft == &Curl_cft_nghttp2); ctx = cf->ctx; + data->req.httpversion_sent = 20; /* it is an h2 request now */ + data->req.header = TRUE; /* we expect the real response to come in h2 */ + data->req.headerline = 0; /* restart the header line counter */ + if(nread > 0) { /* Remaining data from the protocol switch reply is already using * the switched protocol, ie. HTTP/2. We add that to the network @@ -2968,14 +2965,13 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, " after upgrade: len=%zu", nread); } - conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - Curl_multi_connchanged(data->multi); - if(cf->next) { bool done; - return Curl_conn_cf_connect(cf, data, &done); + result = Curl_conn_cf_connect(cf, data, &done); + if(!result) + cf->cft->cntrl(cf, data, CF_CTRL_CONN_INFO_UPDATE, 0, NULL); } - return CURLE_OK; + return result; } /* Only call this function for a transfer that already got an HTTP/2 @@ -3016,7 +3012,6 @@ void *Curl_nghttp2_realloc(void *ptr, size_t size, void *user_data) #else /* CURL_DISABLE_HTTP || !USE_NGHTTP2 */ /* Satisfy external references even if http2 is not compiled in. */ -#include char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num) { @@ -3025,10 +3020,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/lib/http2.h b/lib/http2.h index 93cc2d44f2..e38dc5745c 100644 --- a/lib/http2.h +++ b/lib/http2.h @@ -23,11 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef USE_NGHTTP2 -#include "http.h" /* value for MAX_CONCURRENT_STREAMS we use until we get an updated setting from the peer */ @@ -38,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); @@ -52,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); @@ -65,10 +68,9 @@ extern struct Curl_cftype Curl_cft_nghttp2; #define Curl_http2_may_switch(a) FALSE -#define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_switch(a) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_upgrade(a,b,c,d,e) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_h2_http_1_1_error(x) 0 +#define Curl_http2_request_upgrade(x, y) CURLE_UNSUPPORTED_PROTOCOL +#define Curl_http2_switch(a) CURLE_UNSUPPORTED_PROTOCOL +#define Curl_h2_http_1_1_error(x) 0 #endif #endif /* HEADER_CURL_HTTP2_H */ diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index c62db499ca..a3d8cfeb2a 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -21,47 +21,39 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) #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" -#include "parsedate.h" -#include "sendf.h" +#include "curl_trc.h" #include "escape.h" #include "curlx/strparse.h" +#include "slist.h" #include -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#include "slist.h" - -#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 @@ -70,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, @@ -137,8 +115,173 @@ 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 MAX_SIGV4_LEN 64 #define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date")) /* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */ @@ -190,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); @@ -211,12 +354,12 @@ static CURLcode merge_duplicate_headers(struct curl_slist *head) if(result) return result; - free(curr->data); + curlx_free(curr->data); curr->data = curlx_dyn_ptr(&buf); curr->next = next->next; - free(next->data); - free(next); + curlx_free(next->data); + curlx_free(next); } else { curr = curr->next; @@ -245,14 +388,14 @@ static CURLcode make_headers(struct Curl_easy *data, struct curl_slist *l; bool again = TRUE; - msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%.*s-Date", - (int)plen, provider1); + curl_msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%.*s-Date", + (int)plen, provider1); /* provider1 ucfirst */ Curl_strntolower(&date_hdr_key[2], provider1, plen); date_hdr_key[2] = Curl_raw_toupper(provider1[0]); - msnprintf(date_full_hdr, DATE_FULL_HDR_LEN, - "x-%.*s-date:%s", (int)plen, provider1, timestamp); + curl_msnprintf(date_full_hdr, DATE_FULL_HDR_LEN, + "x-%.*s-date:%s", (int)plen, provider1, timestamp); /* provider1 lowercase */ Curl_strntolower(&date_full_hdr[2], provider1, plen); @@ -262,20 +405,19 @@ 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 = aprintf("host:%s", hostname); + fullhost = curl_maprintf("host:%s", hostname); if(fullhost) head = Curl_slist_append_nodup(NULL, fullhost); if(!head) { - free(fullhost); + curlx_free(fullhost); goto fail; } } - if(*content_sha256_header) { tmp_head = curl_slist_append(head, content_sha256_header); if(!tmp_head) @@ -299,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))) @@ -309,13 +452,13 @@ static CURLcode make_headers(struct Curl_easy *data, ; if(!*ptr && ptr != sep + 1) /* a value of whitespace only */ continue; - dupdata = strdup(l->data); + dupdata = curlx_strdup(l->data); if(!dupdata) goto fail; dupdata[sep - l->data] = ':'; tmp_head = Curl_slist_append_nodup(head, dupdata); if(!tmp_head) { - free(dupdata); + curlx_free(dupdata); goto fail; } head = tmp_head; @@ -329,7 +472,9 @@ static CURLcode make_headers(struct Curl_easy *data, if(!tmp_head) goto fail; head = tmp_head; - *date_header = aprintf("%s: %s\r\n", date_hdr_key, timestamp); + *date_header = curl_maprintf("%s: %s\r\n", date_hdr_key, timestamp); + if(!*date_header) + goto fail; } else { const char *value; @@ -404,21 +549,21 @@ fail: #define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256")) /* add 2 for ": " between header name and value */ -#define CONTENT_SHA256_HDR_LEN (CONTENT_SHA256_KEY_LEN + 2 + \ - SHA256_HEX_LENGTH) +#define CONTENT_SHA256_HDR_LEN (CONTENT_SHA256_KEY_LEN + 2 + SHA256_HEX_LENGTH) /* try to parse a payload hash from the content-sha256 header */ static const char *parse_content_sha_hdr(struct Curl_easy *data, const char *provider1, size_t plen, - size_t *value_len) { + size_t *value_len) +{ char key[CONTENT_SHA256_KEY_LEN]; size_t key_len; const char *value; size_t len; - key_len = msnprintf(key, sizeof(key), "x-%.*s-content-sha256", - (int)plen, provider1); + key_len = curl_msnprintf(key, sizeof(key), "x-%.*s-content-sha256", + (int)plen, provider1); value = Curl_checkheaders(data, key, key_len); if(!value) @@ -432,7 +577,7 @@ static const char *parse_content_sha_hdr(struct Curl_easy *data, curlx_str_passblanks(&value); len = strlen(value); - while(len > 0 && ISBLANK(value[len-1])) + while(len > 0 && ISBLANK(value[len - 1])) --len; *value_len = len; @@ -452,7 +597,7 @@ static CURLcode calc_payload_hash(struct Curl_easy *data, else post_data_len = (size_t)data->set.postfieldsize; } - result = Curl_sha256it(sha_hash, (const unsigned char *) post_data, + result = Curl_sha256it(sha_hash, (const unsigned char *)post_data, post_data_len); if(!result) sha256_to_hex(sha_hex, sha_hash); @@ -490,8 +635,8 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data, } /* format the required content-sha256 header */ - msnprintf(header, CONTENT_SHA256_HDR_LEN, - "x-%.*s-content-sha256: %s", (int)plen, provider1, sha_hex); + curl_msnprintf(header, CONTENT_SHA256_HDR_LEN, + "x-%.*s-content-sha256: %s", (int)plen, provider1, sha_hex); ret = CURLE_OK; fail: @@ -500,7 +645,6 @@ fail: 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); @@ -533,9 +677,11 @@ static int compare_func(const void *a, const void *b) compare = strcmp(curlx_dyn_ptr(&aa->value), curlx_dyn_ptr(&bb->value)); return compare; - } +UNITTEST CURLcode canon_path(const char *q, size_t len, + struct dynbuf *new_path, + bool do_uri_encode); UNITTEST CURLcode canon_path(const char *q, size_t len, struct dynbuf *new_path, bool do_uri_encode) @@ -562,6 +708,7 @@ UNITTEST CURLcode canon_path(const char *q, size_t len, return result; } +UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq); UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq) { CURLcode result = CURLE_OK; @@ -575,21 +722,20 @@ UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq) if(!query) return result; - result = split_to_dyn_array(query, &query_array[0], - &num_query_components); + result = split_to_dyn_array(query, &query_array[0], &num_query_components); if(result) { goto fail; } /* Create list of pairs, each pair containing an encoded query - * component */ + * component */ 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; @@ -602,13 +748,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; } @@ -618,8 +766,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; } @@ -645,8 +793,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); @@ -675,8 +823,8 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) const char *line; struct Curl_str provider0; struct Curl_str provider1; - struct Curl_str region = { NULL, 0}; - struct Curl_str service = { NULL, 0}; + struct Curl_str region = { NULL, 0 }; + struct Curl_str service = { NULL, 0 }; const char *hostname = conn->host.name; time_t clock; struct tm tm; @@ -700,8 +848,8 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) char *str_to_sign = NULL; const char *user = data->state.aptr.user ? data->state.aptr.user : ""; char *secret = NULL; - unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = {0}; - unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = {0}; + unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 }; + unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 }; char *auth_headers = NULL; if(data->set.path_as_is) { @@ -785,7 +933,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) /* AWS S3 requires a x-amz-content-sha256 header, and supports special * values like UNSIGNED-PAYLOAD */ bool sign_as_s3 = curlx_str_casecompare(&provider0, "aws") && - curlx_str_casecompare(&service, "s3"); + curlx_str_casecompare(&service, "s3"); if(sign_as_s3) result = calc_s3_payload_hash(data, httpreq, curlx_str(&provider1), @@ -812,7 +960,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) #else clock = time(NULL); #endif - result = Curl_gmtime(clock, &tm); + result = curlx_gmtime(clock, &tm); if(result) { goto fail; } @@ -843,49 +991,52 @@ 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; canonical_request = - aprintf("%s\n" /* HTTPRequestMethod */ - "%s\n" /* CanonicalURI */ - "%s\n" /* CanonicalQueryString */ - "%s\n" /* CanonicalHeaders */ - "%s\n" /* SignedHeaders */ - "%.*s", /* HashedRequestPayload in hex */ - method, - curlx_dyn_ptr(&canonical_path), - curlx_dyn_ptr(&canonical_query) ? - curlx_dyn_ptr(&canonical_query) : "", - curlx_dyn_ptr(&canonical_headers), - curlx_dyn_ptr(&signed_headers), - (int)payload_hash_len, payload_hash); + curl_maprintf("%s\n" /* HTTPRequestMethod */ + "%s\n" /* CanonicalURI */ + "%s\n" /* CanonicalQueryString */ + "%s\n" /* CanonicalHeaders */ + "%s\n" /* SignedHeaders */ + "%.*s", /* HashedRequestPayload in hex */ + method, + curlx_dyn_ptr(&canonical_path), + curlx_dyn_ptr(&canonical_query) ? + curlx_dyn_ptr(&canonical_query) : "", + curlx_dyn_ptr(&canonical_headers), + curlx_dyn_ptr(&signed_headers), + (int)payload_hash_len, payload_hash); if(!canonical_request) goto fail; infof(data, "aws_sigv4: Canonical request (enclosed in []) - [%s]", - canonical_request); + canonical_request); - request_type = aprintf("%.*s4_request", - (int)curlx_strlen(&provider0), curlx_str(&provider0)); + request_type = curl_maprintf("%.*s4_request", + (int)curlx_strlen(&provider0), + curlx_str(&provider0)); if(!request_type) goto fail; - /* provider0 is lowercased *after* aprintf() so that the buffer can be - written to */ + /* provider0 is lowercased *after* curl_maprintf() so that the buffer + can be written to */ Curl_strntolower(request_type, request_type, curlx_strlen(&provider0)); - credential_scope = aprintf("%s/%.*s/%.*s/%s", date, - (int)curlx_strlen(®ion), curlx_str(®ion), - (int)curlx_strlen(&service), curlx_str(&service), - request_type); + credential_scope = curl_maprintf("%s/%.*s/%.*s/%s", date, + (int)curlx_strlen(®ion), + curlx_str(®ion), + (int)curlx_strlen(&service), + curlx_str(&service), + request_type); if(!credential_scope) goto fail; - if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request, + if(Curl_sha256it(sha_hash, (unsigned char *)canonical_request, strlen(canonical_request))) goto fail; @@ -895,14 +1046,15 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) * Google allows using RSA key instead of HMAC, so this code might change * in the future. For now we only support HMAC. */ - str_to_sign = aprintf("%.*s4-HMAC-SHA256\n" /* Algorithm */ - "%s\n" /* RequestDateTime */ - "%s\n" /* CredentialScope */ - "%s", /* HashedCanonicalRequest in hex */ - (int)curlx_strlen(&provider0), curlx_str(&provider0), - timestamp, - credential_scope, - sha_hex); + str_to_sign = curl_maprintf("%.*s4-HMAC-SHA256\n" /* Algorithm */ + "%s\n" /* RequestDateTime */ + "%s\n" /* CredentialScope */ + "%s", /* HashedCanonicalRequest in hex */ + (int)curlx_strlen(&provider0), + curlx_str(&provider0), + timestamp, + credential_scope, + sha_hex); if(!str_to_sign) goto fail; @@ -911,11 +1063,11 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) curlx_strlen(&provider0)); infof(data, "aws_sigv4: String to sign (enclosed in []) - [%s]", - str_to_sign); + str_to_sign); - secret = aprintf("%.*s4%s", (int)curlx_strlen(&provider0), - curlx_str(&provider0), data->state.aptr.passwd ? - data->state.aptr.passwd : ""); + secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(&provider0), + curlx_str(&provider0), data->state.aptr.passwd ? + data->state.aptr.passwd : ""); if(!secret) goto fail; /* make provider0 part done uppercase */ @@ -933,24 +1085,25 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) infof(data, "aws_sigv4: Signature - %s", sha_hex); - auth_headers = aprintf("Authorization: %.*s4-HMAC-SHA256 " - "Credential=%s/%s, " - "SignedHeaders=%s, " - "Signature=%s\r\n" - /* - * date_header is added here, only if it was not - * user-specified (using CURLOPT_HTTPHEADER). - * date_header includes \r\n - */ - "%s" - "%s", /* optional sha256 header includes \r\n */ - (int)curlx_strlen(&provider0), curlx_str(&provider0), - user, - credential_scope, - curlx_dyn_ptr(&signed_headers), - sha_hex, - date_header ? date_header : "", - content_sha256_hdr); + auth_headers = curl_maprintf("Authorization: %.*s4-HMAC-SHA256 " + "Credential=%s/%s, " + "SignedHeaders=%s, " + "Signature=%s\r\n" + /* + * date_header is added here, only if it was not + * user-specified (using CURLOPT_HTTPHEADER). + * date_header includes \r\n + */ + "%s" + "%s", /* optional sha256 header includes \r\n */ + (int)curlx_strlen(&provider0), + curlx_str(&provider0), + user, + credential_scope, + curlx_dyn_ptr(&signed_headers), + sha_hex, + date_header ? date_header : "", + content_sha256_hdr); if(!auth_headers) { goto fail; } @@ -958,8 +1111,8 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1], curlx_str(&provider0), curlx_strlen(&provider0)); - free(data->state.aptr.userpwd); - data->state.aptr.userpwd = auth_headers; + curlx_free(data->req.userpwd); + data->req.userpwd = auth_headers; data->state.authhost.done = TRUE; result = CURLE_OK; @@ -968,185 +1121,13 @@ fail: curlx_dyn_free(&canonical_path); curlx_dyn_free(&canonical_headers); curlx_dyn_free(&signed_headers); - free(canonical_request); - free(request_type); - free(credential_scope); - free(str_to_sign); - free(secret); - free(date_header); + curlx_free(canonical_request); + curlx_free(request_type); + curlx_free(credential_scope); + curlx_free(str_to_sign); + curlx_free(secret); + curlx_free(date_header); 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/lib/http_aws_sigv4.h b/lib/http_aws_sigv4.h index 9747c948a6..7dd002476a 100644 --- a/lib/http_aws_sigv4.h +++ b/lib/http_aws_sigv4.h @@ -24,18 +24,11 @@ * ***************************************************************************/ #include "curl_setup.h" + #include "curlx/dynbuf.h" #include "urldata.h" -#include "curlx/strparse.h" /* this is for creating aws_sigv4 header output */ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data); -#ifdef UNITTESTS -UNITTEST CURLcode canon_path(const char *q, size_t len, - struct dynbuf *new_path, - bool normalize); -UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq); -#endif - #endif /* HEADER_CURL_HTTP_AWS_SIGV4_H */ diff --git a/lib/http_chunks.c b/lib/http_chunks.c index f735a820c7..aa45c79e38 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -21,33 +21,24 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifndef CURL_DISABLE_HTTP #include "urldata.h" /* it includes http_chunks.h */ -#include "curl_printf.h" #include "curl_trc.h" #include "sendf.h" /* for the client write stuff */ #include "curlx/dynbuf.h" -#include "content_encoding.h" -#include "http.h" #include "multiif.h" #include "curlx/strparse.h" -#include "curlx/warnless.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.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, @@ -237,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; @@ -251,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 */ @@ -267,12 +258,12 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, size_t trlen = curlx_dyn_len(&ch->trailer); if(cw_next) result = Curl_cwriter_write(data, cw_next, - CLIENTWRITE_HEADER| + CLIENTWRITE_HEADER | CLIENTWRITE_TRAILER, tr, trlen); else result = Curl_client_write(data, - CLIENTWRITE_HEADER| + CLIENTWRITE_HEADER | CLIENTWRITE_TRAILER, tr, trlen); if(result) { @@ -362,7 +353,6 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, case CHUNK_FAILED: return CURLE_RECV_ERROR; } - } return CURLE_OK; } @@ -470,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 { @@ -527,14 +517,13 @@ 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; } - result = Curl_bufq_cwrite(&ctx->chunkbuf, tr->data, - strlen(tr->data), &n); + result = Curl_bufq_cwrite(&ctx->chunkbuf, tr->data, strlen(tr->data), &n); if(!result) result = Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("\r\n"), &n); if(result) @@ -584,7 +573,7 @@ static CURLcode add_chunk(struct Curl_easy *data, int hdlen; size_t n; - hdlen = msnprintf(hd, sizeof(hd), "%zx\r\n", nread); + hdlen = curl_msnprintf(hd, sizeof(hd), "%zx\r\n", nread); if(hdlen <= 0) return CURLE_READ_ERROR; /* On a soft-limited bufq, we do not need to check that all was written */ @@ -594,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; } @@ -613,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/lib/http_chunks.h b/lib/http_chunks.h index b84e479fcd..d8e5982e6d 100644 --- a/lib/http_chunks.h +++ b/lib/http_chunks.h @@ -23,6 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" #ifndef CURL_DISABLE_HTTP diff --git a/lib/http_digest.c b/lib/http_digest.c index f4e920706c..b7007071e7 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) @@ -32,11 +31,6 @@ #include "http_digest.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - /* Test example headers: WWW-Authenticate: Digest realm="testrealm", nonce="1053604598" @@ -75,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; @@ -97,7 +91,7 @@ CURLcode Curl_output_digest(struct Curl_easy *data, return CURLE_NOT_BUILT_IN; #else digest = &data->state.proxydigest; - allocuserpwd = &data->state.aptr.proxyuserpwd; + allocuserpwd = &data->req.proxyuserpwd; userp = data->state.aptr.proxyuser; passwdp = data->state.aptr.proxypasswd; authp = &data->state.authproxy; @@ -105,13 +99,13 @@ CURLcode Curl_output_digest(struct Curl_easy *data, } else { digest = &data->state.digest; - allocuserpwd = &data->state.aptr.userpwd; + allocuserpwd = &data->req.userpwd; userp = data->state.aptr.user; passwdp = data->state.aptr.passwd; authp = &data->state.authhost; } - Curl_safefree(*allocuserpwd); + curlx_safefree(*allocuserpwd); /* not set means empty */ if(!userp) @@ -131,7 +125,7 @@ CURLcode Curl_output_digest(struct Curl_easy *data, return CURLE_OK; } - /* So IE browsers < v7 cut off the URI part at the query part when they + /* IE browsers < v7 cut off the URI part at the query part when they evaluate the MD5 and some (IIS?) servers work with them so we may need to do the Digest IE-style. Note that the different ways cause different MD5 sums to get sent. @@ -149,25 +143,24 @@ CURLcode Curl_output_digest(struct Curl_easy *data, if(tmp) { size_t urilen = tmp - (const char *)uripath; /* typecast is fine here since the value is always less than 32 bits */ - path = (unsigned char *) aprintf("%.*s", (int)urilen, uripath); + path = (unsigned char *)curl_maprintf("%.*s", (int)urilen, uripath); } } if(!tmp) - path = (unsigned char *) strdup((const char *) uripath); + path = (unsigned char *)curlx_strdup((const char *)uripath); if(!path) return CURLE_OUT_OF_MEMORY; result = Curl_auth_create_digest_http_message(data, userp, passwdp, request, path, digest, &response, &len); - free(path); + curlx_free(path); if(result) return result; - *allocuserpwd = aprintf("%sAuthorization: Digest %s\r\n", - proxy ? "Proxy-" : "", - response); - free(response); + *allocuserpwd = curl_maprintf("%sAuthorization: Digest %s\r\n", + proxy ? "Proxy-" : "", response); + curlx_free(response); if(!*allocuserpwd) return CURLE_OUT_OF_MEMORY; diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 2c0b7e16d7..74d63d6cc0 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -21,24 +21,18 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO) #include "urldata.h" #include "cfilters.h" -#include "sendf.h" +#include "curl_trc.h" #include "http_negotiate.h" #include "vauth/vauth.h" #include "vtls/vtls.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - static void http_auth_nego_reset(struct connectdata *conn, struct negotiatedata *neg_ctx, @@ -52,7 +46,6 @@ static void http_auth_nego_reset(struct connectdata *conn, Curl_auth_cleanup_spnego(neg_ctx); } - CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, bool proxy, const char *header) { @@ -125,12 +118,12 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, neg_ctx->sslContext = conn->sslContext; #endif /* Check if the connection is using SSL and get the channel binding data */ -#ifdef HAVE_GSSAPI +#ifdef GSS_C_CHANNEL_BOUND_FLAG #ifdef USE_SSL curlx_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE + 1); if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_get_channel_binding( - data, FIRSTSOCKET, &neg_ctx->channel_binding_data); + result = Curl_ssl_get_channel_binding(data, FIRSTSOCKET, + &neg_ctx->channel_binding_data); if(result) { http_auth_nego_reset(conn, neg_ctx, proxy); return result; @@ -139,13 +132,13 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, #else curlx_dyn_init(&neg_ctx->channel_binding_data, 1); #endif /* USE_SSL */ -#endif /* HAVE_GSSAPI */ +#endif /* GSS_C_CHANNEL_BOUND_FLAG */ /* Initialize the security context and decode our challenge */ result = Curl_auth_decode_spnego_message(data, userp, passwdp, service, host, header, neg_ctx); -#ifdef HAVE_GSSAPI +#ifdef GSS_C_CHANNEL_BOUND_FLAG curlx_dyn_free(&neg_ctx->channel_binding_data); #endif @@ -219,40 +212,40 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data, if(result) return result; - userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "", - base64); + userp = curl_maprintf("%sAuthorization: Negotiate %s\r\n", + proxy ? "Proxy-" : "", base64); if(proxy) { #ifndef CURL_DISABLE_PROXY - free(data->state.aptr.proxyuserpwd); - data->state.aptr.proxyuserpwd = userp; + curlx_free(data->req.proxyuserpwd); + data->req.proxyuserpwd = userp; #endif } else { - free(data->state.aptr.userpwd); - data->state.aptr.userpwd = userp; + curlx_free(data->req.userpwd); + data->req.userpwd = userp; } - free(base64); + curlx_free(base64); if(!userp) { return CURLE_OUT_OF_MEMORY; } *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/lib/http_negotiate.h b/lib/http_negotiate.h index ad7c43d833..6c285f152f 100644 --- a/lib/http_negotiate.h +++ b/lib/http_negotiate.h @@ -23,6 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO) diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index a172eb848d..b2adae48c0 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) @@ -34,7 +33,7 @@ */ #include "urldata.h" -#include "sendf.h" +#include "curl_trc.h" #include "strcase.h" #include "http_ntlm.h" #include "curl_ntlm_core.h" @@ -49,11 +48,6 @@ #include "curl_sspi.h" #endif -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - CURLcode Curl_input_ntlm(struct Curl_easy *data, bool proxy, /* if proxy or not */ const char *header) /* rest of the www-authenticate: @@ -69,7 +63,7 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data, if(checkprefix("NTLM", header)) { struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, proxy); if(!ntlm) - return CURLE_FAILED_INIT; + return CURLE_OUT_OF_MEMORY; header += strlen("NTLM"); curlx_str_passblanks(&header); @@ -145,11 +139,11 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) if(proxy) { #ifndef CURL_DISABLE_PROXY - allocuserpwd = &data->state.aptr.proxyuserpwd; + allocuserpwd = &data->req.proxyuserpwd; userp = data->state.aptr.proxyuser; passwdp = data->state.aptr.proxypasswd; service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; + data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; hostname = conn->http_proxy.host.name; state = &conn->proxy_ntlm_state; authp = &data->state.authproxy; @@ -158,11 +152,11 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) #endif } else { - allocuserpwd = &data->state.aptr.userpwd; + allocuserpwd = &data->req.userpwd; userp = data->state.aptr.user; passwdp = data->state.aptr.passwd; service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : "HTTP"; + data->set.str[STRING_SERVICE_NAME] : "HTTP"; hostname = conn->host.name; state = &conn->http_ntlm_state; authp = &data->state.authhost; @@ -181,7 +175,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) #ifdef USE_WINDOWS_SSPI if(!Curl_pSecFn) { - /* not thread safe and leaks - use curl_global_init() to avoid */ + /* not thread-safe and leaks - use curl_global_init() to avoid */ CURLcode err = Curl_sspi_global_init(); if(!Curl_pSecFn) return err; @@ -206,14 +200,13 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) hostname, ntlm, &ntlmmsg); if(!result) { DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0); - result = curlx_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg), - Curl_bufref_len(&ntlmmsg), &base64, &len); + result = curlx_base64_encode(Curl_bufref_uptr(&ntlmmsg), + Curl_bufref_len(&ntlmmsg), &base64, &len); if(!result) { - free(*allocuserpwd); - *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", - proxy ? "Proxy-" : "", - base64); - free(base64); + curlx_free(*allocuserpwd); + *allocuserpwd = curl_maprintf("%sAuthorization: NTLM %s\r\n", + proxy ? "Proxy-" : "", base64); + curlx_free(base64); if(!*allocuserpwd) result = CURLE_OUT_OF_MEMORY; } @@ -225,14 +218,13 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) result = Curl_auth_create_ntlm_type3_message(data, userp, passwdp, ntlm, &ntlmmsg); if(!result && Curl_bufref_len(&ntlmmsg)) { - result = curlx_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg), + result = curlx_base64_encode(Curl_bufref_uptr(&ntlmmsg), Curl_bufref_len(&ntlmmsg), &base64, &len); if(!result) { - free(*allocuserpwd); - *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n", - proxy ? "Proxy-" : "", - base64); - free(base64); + curlx_free(*allocuserpwd); + *allocuserpwd = curl_maprintf("%sAuthorization: NTLM %s\r\n", + proxy ? "Proxy-" : "", base64); + curlx_free(base64); if(!*allocuserpwd) result = CURLE_OUT_OF_MEMORY; else { @@ -250,7 +242,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) data->info.proxyauthpicked = CURLAUTH_NTLM; else data->info.httpauthpicked = CURLAUTH_NTLM; - Curl_safefree(*allocuserpwd); + curlx_safefree(*allocuserpwd); authp->done = TRUE; break; } diff --git a/lib/http_ntlm.h b/lib/http_ntlm.h index b38ff82dba..ff5218d89f 100644 --- a/lib/http_ntlm.h +++ b/lib/http_ntlm.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 2d742856ce..a4bdd7e361 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -21,46 +21,27 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #include "http_proxy.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) -#include -#include "sendf.h" +#include "curl_trc.h" #include "http.h" #include "url.h" -#include "select.h" -#include "progress.h" #include "cfilters.h" #include "cf-h1-proxy.h" #include "cf-h2-proxy.h" #include "connect.h" -#include "vtls/vtls.h" -#include "transfer.h" -#include "multiif.h" #include "vauth/vauth.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -static bool hd_name_eq(const char *n1, size_t n1len, - const char *n2, size_t n2len) -{ - return (n1len == n2len) ? curl_strnequal(n1, n2, n1len) : FALSE; -} - static CURLcode dynhds_add_custom(struct Curl_easy *data, bool is_connect, int httpversion, struct dynhds *hds) { struct connectdata *conn = data->conn; - const char *ptr; struct curl_slist *h[2]; struct curl_slist *headers; int numlists = 1; /* by default */ @@ -96,86 +77,82 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, /* loop through one or two lists */ for(i = 0; i < numlists; i++) { for(headers = h[i]; headers; headers = headers->next) { - const char *name, *value; - size_t namelen, valuelen; + struct Curl_str name; + const char *value = NULL; + size_t valuelen = 0; + const char *ptr = headers->data; /* There are 2 quirks in place for custom headers: * 1. setting only 'name:' to suppress a header from being sent * 2. setting only 'name;' to send an empty (illegal) header */ - ptr = strchr(headers->data, ':'); - if(ptr) { - name = headers->data; - namelen = ptr - headers->data; - ptr++; /* pass the colon */ - curlx_str_passblanks(&ptr); - if(*ptr) { - value = ptr; - valuelen = strlen(value); + if(!curlx_str_cspn(&ptr, &name, ";:")) { + if(!curlx_str_single(&ptr, ':')) { + curlx_str_passblanks(&ptr); + if(*ptr) { + value = ptr; + valuelen = strlen(value); + } + else { + /* quirk #1, suppress this header */ + continue; + } } - else { - /* quirk #1, suppress this header */ + else if(!curlx_str_single(&ptr, ';')) { + curlx_str_passblanks(&ptr); + if(!*ptr) { + /* quirk #2, send an empty header */ + value = ""; + valuelen = 0; + } + else { + /* this may be used for something else in the future, + * ignore this for now */ + continue; + } + } + else + /* neither : nor ; in provided header value. We ignore this + * silently */ continue; - } } - else { - ptr = strchr(headers->data, ';'); + else + /* no name, move on */ + continue; - if(!ptr) { - /* neither : nor ; in provided header value. We seem - * to ignore this silently */ - continue; - } - - name = headers->data; - namelen = ptr - headers->data; - ptr++; /* pass the semicolon */ - curlx_str_passblanks(&ptr); - if(!*ptr) { - /* quirk #2, send an empty header */ - value = ""; - valuelen = 0; - } - else { - /* this may be used for something else in the future, - * ignore this for now */ - continue; - } - } - - DEBUGASSERT(name && value); + DEBUGASSERT(curlx_strlen(&name) && value); if(data->state.aptr.host && /* a Host: header was sent already, do not pass on any custom Host: header as that will produce *two* in the same request! */ - hd_name_eq(name, namelen, STRCONST("Host:"))) + curlx_str_casecompare(&name, "Host")) ; else if(data->state.httpreq == HTTPREQ_POST_FORM && /* this header (extended by formdata.c) is sent later */ - hd_name_eq(name, namelen, STRCONST("Content-Type:"))) + curlx_str_casecompare(&name, "Content-Type")) ; else if(data->state.httpreq == HTTPREQ_POST_MIME && /* this header is sent later */ - hd_name_eq(name, namelen, STRCONST("Content-Type:"))) + curlx_str_casecompare(&name, "Content-Type")) ; else if(data->req.authneg && /* while doing auth neg, do not allow the custom length since we will force length zero then */ - hd_name_eq(name, namelen, STRCONST("Content-Length:"))) + curlx_str_casecompare(&name, "Content-Length")) ; else if((httpversion >= 20) && - hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:"))) - /* HTTP/2 and HTTP/3 do not support chunked requests */ + curlx_str_casecompare(&name, "Transfer-Encoding")) ; - else if((hd_name_eq(name, namelen, STRCONST("Authorization:")) || - hd_name_eq(name, namelen, STRCONST("Cookie:"))) && + /* HTTP/2 and HTTP/3 do not support chunked requests */ + else if((curlx_str_casecompare(&name, "Authorization") || + curlx_str_casecompare(&name, "Cookie")) && /* be careful of sending this potentially sensitive header to other hosts */ !Curl_auth_allowed_to_host(data)) ; else { - CURLcode result; - - result = Curl_dynhds_add(hds, name, namelen, value, valuelen); + CURLcode result = + Curl_dynhds_add(hds, curlx_str(&name), curlx_strlen(&name), + value, valuelen); if(result) return result; } @@ -185,9 +162,9 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, return CURLE_OK; } -CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf, - const char **phostname, - int *pport, bool *pipv6_ip) +void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, + const char **phostname, + uint16_t *pport, bool *pipv6_ip) { DEBUGASSERT(cf); DEBUGASSERT(cf->conn); @@ -206,18 +183,12 @@ CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf, else *pport = cf->conn->remote_port; - if(*phostname != cf->conn->host.name) - *pipv6_ip = (strchr(*phostname, ':') != NULL); - else - *pipv6_ip = cf->conn->bits.ipv6_ip; - - return CURLE_OK; + *pipv6_ip = (strchr(*phostname, ':') != NULL); } struct cf_proxy_ctx { - /* the protocol specific sub-filter we install during connect */ - struct Curl_cfilter *cf_protocol; int httpversion; /* HTTP version used to CONNECT */ + BIT(sub_filter_installed); }; CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, @@ -228,23 +199,21 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, struct cf_proxy_ctx *ctx = cf->ctx; const char *hostname = NULL; char *authority = NULL; - int port; + uint16_t port; bool ipv6_ip; CURLcode result; struct httpreq *req = NULL; - result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); - if(result) - goto out; + Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); - authority = aprintf("%s%s%s:%d", ipv6_ip ? "[" : "", hostname, - ipv6_ip ?"]" : "", port); + authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, + ipv6_ip ? "]" : "", port); if(!authority) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1, + result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT") - 1, NULL, 0, authority, strlen(authority), NULL, 0); if(result) @@ -264,9 +233,9 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, goto out; } - if(data->state.aptr.proxyuserpwd) { + if(data->req.proxyuserpwd) { result = Curl_dynhds_h1_cadd_line(&req->headers, - data->state.aptr.proxyuserpwd); + data->req.proxyuserpwd); if(result) goto out; } @@ -280,7 +249,7 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, } if(http_version_major == 1 && - !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { + !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive"); if(result) goto out; @@ -293,7 +262,7 @@ out: Curl_http_req_free(req); req = NULL; } - free(authority); + curlx_free(authority); *preq = req; return result; } @@ -317,8 +286,7 @@ connect_sub: return result; *done = FALSE; - if(!ctx->cf_protocol) { - struct Curl_cfilter *cf_protocol = NULL; + if(!ctx->sub_filter_installed) { int httpversion = 0; const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data); @@ -332,7 +300,6 @@ connect_sub: result = Curl_cf_h1_proxy_insert_after(cf, data); if(result) goto out; - cf_protocol = cf->next; httpversion = 10; } else if(!alpn || !strcmp(alpn, "http/1.1")) { @@ -340,7 +307,6 @@ connect_sub: result = Curl_cf_h1_proxy_insert_after(cf, data); if(result) goto out; - cf_protocol = cf->next; /* Assume that without an ALPN, we are talking to an ancient one */ httpversion = 11; } @@ -350,7 +316,6 @@ connect_sub: result = Curl_cf_h2_proxy_insert_after(cf, data); if(result) goto out; - cf_protocol = cf->next; httpversion = 20; } #endif @@ -360,7 +325,7 @@ connect_sub: goto out; } - ctx->cf_protocol = cf_protocol; + ctx->sub_filter_installed = TRUE; ctx->httpversion = httpversion; /* after we installed the filter "below" us, we call connect * on out sub-chain again. @@ -371,7 +336,7 @@ connect_sub: /* subchain connected and we had already installed the protocol filter. * This means the protocol tunnel is established, we are done. */ - DEBUGASSERT(ctx->cf_protocol); + DEBUGASSERT(ctx->sub_filter_installed); result = CURLE_OK; } @@ -411,39 +376,22 @@ 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"); - free(ctx); + curlx_free(ctx); } static void http_proxy_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct cf_proxy_ctx *ctx = cf->ctx; - CURL_TRC_CF(data, cf, "close"); cf->connected = FALSE; - if(ctx->cf_protocol) { - struct Curl_cfilter *f; - /* if someone already removed it, we assume he also - * took care of destroying it. */ - for(f = cf->next; f; f = f->next) { - if(f == ctx->cf_protocol) { - /* still in our sub-chain */ - Curl_conn_cf_discard_sub(cf, ctx->cf_protocol, data, FALSE); - break; - } - } - ctx->cf_protocol = NULL; - } if(cf->next) cf->next->cft->do_close(cf->next, data); } - struct Curl_cftype Curl_cft_http_proxy = { "HTTP-PROXY", - CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, + CF_TYPE_IP_CONNECT | CF_TYPE_PROXY | CF_TYPE_SETUP, 0, http_proxy_cf_destroy, http_proxy_cf_connect, @@ -467,7 +415,7 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, CURLcode result; (void)data; - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -479,8 +427,8 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, Curl_conn_cf_insert_after(cf_at, cf); out: - free(ctx); + curlx_free(ctx); return result; } -#endif /* ! CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */ +#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */ diff --git a/lib/http_proxy.h b/lib/http_proxy.h index e6ab2bacf3..155b222edc 100644 --- a/lib/http_proxy.h +++ b/lib/http_proxy.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) @@ -36,9 +35,9 @@ enum Curl_proxy_use { HEADER_CONNECT /* sending CONNECT to a proxy */ }; -CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf, - const char **phostname, - int *pport, bool *pipv6_ip); +void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, + const char **phostname, + uint16_t *pport, bool *pipv6_ip); CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, struct Curl_cfilter *cf, @@ -46,7 +45,7 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, int http_version_major); /* Default proxy timeout in milliseconds */ -#define PROXY_TIMEOUT (3600*1000) +#define PROXY_TIMEOUT (3600 * 1000) CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, struct Curl_easy *data, diff --git a/lib/httpsrr.c b/lib/httpsrr.c index df93ac34ac..8d02d24542 100644 --- a/lib/httpsrr.c +++ b/lib/httpsrr.c @@ -21,26 +21,18 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef USE_HTTPSRR #include "urldata.h" -#include "curl_addrinfo.h" #include "httpsrr.h" #include "connect.h" -#include "sendf.h" -#include "strdup.h" +#include "curl_trc.h" +#include "curlx/strdup.h" +#include "curlx/inet_ntop.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#define MAX_ALPN_LENGTH 255 - -static CURLcode httpsrr_decode_alpn(const char *cp, size_t len, +static CURLcode httpsrr_decode_alpn(const uint8_t *cp, size_t len, unsigned char *alpns) { /* @@ -52,7 +44,7 @@ static CURLcode httpsrr_decode_alpn(const char *cp, size_t len, int idnum = 0; while(len > 0) { - size_t tlen = (size_t) *cp++; + size_t tlen = *cp++; enum alpnid id; len--; if(tlen > len) @@ -77,70 +69,135 @@ static CURLcode httpsrr_decode_alpn(const char *cp, size_t len, return CURLE_OK; } -CURLcode Curl_httpsrr_set(struct Curl_easy *data, - struct Curl_https_rrinfo *hi, +#ifdef CURLVERBOSE +static void httpsrr_report_addr(struct Curl_easy *data, int ai_family, + const uint8_t *addr, size_t total_len) +{ + char buf[MAX_IPADR_LEN]; + struct dynbuf tmp; + size_t i, alen = (ai_family == AF_INET6) ? 16 : 4; + const char *sep = ""; + bool incomplete = FALSE; + CURLcode result; + + if(!CURL_TRC_DNS_is_verbose(data)) + return; + + curlx_dyn_init(&tmp, 1024); + for(i = 0; i < (total_len / alen); ++i) { + if(!curlx_inet_ntop(ai_family, addr + (i * alen), buf, sizeof(buf))) { + CURL_TRC_DNS(data, "[HTTPS-RR] error parsing address #%zu", i); + goto out; + } + result = curlx_dyn_addf(&tmp, "%s%s", sep, buf); + if(result) { + incomplete = TRUE; + break; + } + sep = ", "; + } + + CURL_TRC_DNS(data, "[HTTPS-RR] IPv%d: %s%s", + (ai_family == AF_INET6) ? 6 : 4, + curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)", + incomplete ? " ..." : ""); +out: + curlx_dyn_free(&tmp); +} + +void Curl_httpsrr_trace(struct Curl_easy *data, + struct Curl_https_rrinfo *hi) +{ + if(!hi || !hi->complete) { + CURL_TRC_DNS(data, "[HTTPS-RR] not available"); + return; + } + + if(hi->target) + CURL_TRC_DNS(data, "[HTTPS-RR] target: %s", hi->target); + if(hi->priority) + CURL_TRC_DNS(data, "[HTTPS-RR] priority: %u", hi->priority); + if(hi->mandatory) + CURL_TRC_DNS(data, "[HTTPS-RR] MANDATORY present, but not supported"); + if(hi->alpns[0]) + CURL_TRC_DNS(data, "[HTTPS-RR] ALPN: %u %u %u %u", + hi->alpns[0], hi->alpns[1], hi->alpns[2], hi->alpns[3]); + if(hi->port_set) + CURL_TRC_DNS(data, "[HTTPS-RR] port %u", hi->port); + if(hi->no_def_alpn) + CURL_TRC_DNS(data, "[HTTPS-RR] no-def-alpn"); + if(hi->ipv6hints_len) + httpsrr_report_addr(data, AF_INET6, hi->ipv6hints, hi->ipv6hints_len); + if(hi->ipv4hints_len) + httpsrr_report_addr(data, AF_INET, hi->ipv4hints, hi->ipv4hints_len); + if(hi->echconfiglist_len) + CURL_TRC_DNS(data, "[HTTPS-RR] ECH"); +} + +#else +#define httpsrr_report_addr(a, b, c, d) Curl_nop_stmt +#endif /* CURLVERBOSE */ + +CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *hi, uint16_t rrkey, const uint8_t *val, size_t vlen) { CURLcode result = CURLE_OK; switch(rrkey) { case HTTPS_RR_CODE_MANDATORY: - CURL_TRC_DNS(data, "HTTPS RR MANDATORY left to implement"); + hi->mandatory = TRUE; break; case HTTPS_RR_CODE_ALPN: /* str_list */ - result = httpsrr_decode_alpn((const char *)val, vlen, hi->alpns); - CURL_TRC_DNS(data, "HTTPS RR ALPN: %u %u %u %u", - hi->alpns[0], hi->alpns[1], hi->alpns[2], hi->alpns[3]); + result = httpsrr_decode_alpn(val, vlen, hi->alpns); break; case HTTPS_RR_CODE_NO_DEF_ALPN: if(vlen) /* no data */ return CURLE_BAD_FUNCTION_ARGUMENT; hi->no_def_alpn = TRUE; - CURL_TRC_DNS(data, "HTTPS RR no-def-alpn"); break; case HTTPS_RR_CODE_IPV4: /* addr4 list */ if(!vlen || (vlen & 3)) /* the size must be 4-byte aligned */ return CURLE_BAD_FUNCTION_ARGUMENT; - hi->ipv4hints = Curl_memdup(val, vlen); + curlx_free(hi->ipv4hints); + hi->ipv4hints = curlx_memdup(val, vlen); if(!hi->ipv4hints) return CURLE_OUT_OF_MEMORY; hi->ipv4hints_len = vlen; - CURL_TRC_DNS(data, "HTTPS RR IPv4"); break; case HTTPS_RR_CODE_ECH: if(!vlen) return CURLE_BAD_FUNCTION_ARGUMENT; - hi->echconfiglist = Curl_memdup(val, vlen); + curlx_free(hi->echconfiglist); + hi->echconfiglist = curlx_memdup(val, vlen); if(!hi->echconfiglist) return CURLE_OUT_OF_MEMORY; hi->echconfiglist_len = vlen; - CURL_TRC_DNS(data, "HTTPS RR ECH"); break; case HTTPS_RR_CODE_IPV6: /* addr6 list */ if(!vlen || (vlen & 15)) /* the size must be 16-byte aligned */ return CURLE_BAD_FUNCTION_ARGUMENT; - hi->ipv6hints = Curl_memdup(val, vlen); + curlx_free(hi->ipv6hints); + hi->ipv6hints = curlx_memdup(val, vlen); if(!hi->ipv6hints) return CURLE_OUT_OF_MEMORY; hi->ipv6hints_len = vlen; - CURL_TRC_DNS(data, "HTTPS RR IPv6"); break; case HTTPS_RR_CODE_PORT: if(vlen != 2) return CURLE_BAD_FUNCTION_ARGUMENT; - hi->port = (unsigned short)((val[0] << 8) | val[1]); - CURL_TRC_DNS(data, "HTTPS RR port %u", hi->port); + hi->port = (uint16_t)((val[0] << 8) | val[1]); + hi->port_set = TRUE; break; default: - CURL_TRC_DNS(data, "HTTPS RR unknown code"); + /* unknown code */ break; } return result; } -struct Curl_https_rrinfo * -Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo) +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; @@ -148,17 +205,17 @@ Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo) void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo) { - Curl_safefree(rrinfo->target); - Curl_safefree(rrinfo->echconfiglist); - Curl_safefree(rrinfo->ipv4hints); - Curl_safefree(rrinfo->ipv6hints); + curlx_safefree(rrinfo->target); + curlx_safefree(rrinfo->echconfiglist); + curlx_safefree(rrinfo->ipv4hints); + curlx_safefree(rrinfo->ipv6hints); + curlx_safefree(rrinfo->rrname); + rrinfo->complete = FALSE; } - #ifdef USE_ARES -static CURLcode httpsrr_opt(struct Curl_easy *data, - const ares_dns_rr_t *rr, +static CURLcode httpsrr_opt(const ares_dns_rr_t *rr, ares_dns_rr_key_t key, size_t idx, struct Curl_https_rrinfo *hinfo) { @@ -166,12 +223,11 @@ static CURLcode httpsrr_opt(struct Curl_easy *data, unsigned short code; size_t len = 0; - code = ares_dns_rr_get_opt(rr, key, idx, &val, &len); - return Curl_httpsrr_set(data, hinfo, code, val, len); + code = ares_dns_rr_get_opt(rr, key, idx, &val, &len); + return Curl_httpsrr_set(hinfo, code, val, len); } -CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data, - const ares_dns_record_t *dnsrec, +CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, struct Curl_https_rrinfo *hinfo) { CURLcode result = CURLE_OK; @@ -188,23 +244,24 @@ CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data, is in ServiceMode */ target = ares_dns_rr_get_str(rr, ARES_RR_HTTPS_TARGET); if(target && target[0]) { - hinfo->target = strdup(target); + curlx_free(hinfo->target); + hinfo->target = curlx_strdup(target); if(!hinfo->target) { result = CURLE_OUT_OF_MEMORY; goto out; } - CURL_TRC_DNS(data, "HTTPS RR target: %s", hinfo->target); } - CURL_TRC_DNS(data, "HTTPS RR priority: %u", - ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY)); + hinfo->priority = ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY); for(opt = 0; opt < ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS); opt++) { - result = httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt, hinfo); + result = httpsrr_opt(rr, ARES_RR_HTTPS_PARAMS, opt, hinfo); if(result) break; } } out: + hinfo->complete = !result; + curlx_safefree(hinfo->rrname); return result; } diff --git a/lib/httpsrr.h b/lib/httpsrr.h index 563d1c8e51..68f5dad875 100644 --- a/lib/httpsrr.h +++ b/lib/httpsrr.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef USE_ARES @@ -33,11 +32,12 @@ #ifdef USE_HTTPSRR #define CURL_MAXLEN_host_name 253 -#define MAX_HTTPSRR_ALPNS 4 +#define MAX_HTTPSRR_ALPNS 4 struct Curl_easy; struct Curl_https_rrinfo { + char *rrname; /* if NULL, the same as the URL hostname */ /* * Fields from HTTPS RR. The only mandatory fields are priority and target. * See https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2 @@ -51,17 +51,19 @@ struct Curl_https_rrinfo { size_t ipv6hints_len; unsigned char alpns[MAX_HTTPSRR_ALPNS]; /* keytag = 1 */ /* store parsed alpnid entries in the array, end with ALPN_none */ - int port; /* -1 means not set */ + uint16_t port; /* -1 means not set */ uint16_t priority; BIT(no_def_alpn); /* keytag = 2 */ + BIT(mandatory); /* keytag = 0 */ + BIT(port_set); /* port value has been assigned */ + BIT(complete); /* values have been successfully assigned */ }; -CURLcode Curl_httpsrr_set(struct Curl_easy *data, - struct Curl_https_rrinfo *hi, +CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *hi, uint16_t rrkey, const uint8_t *val, size_t vlen); -struct Curl_https_rrinfo * -Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo); +struct Curl_https_rrinfo *Curl_httpsrr_dup_move( + struct Curl_https_rrinfo *rrinfo); void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo); @@ -77,10 +79,17 @@ void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo); #define HTTPS_RR_CODE_IPV6 0x06 #ifdef USE_ARES -CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data, - const ares_dns_record_t *dnsrec, +CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, struct Curl_https_rrinfo *hinfo); #endif /* USE_ARES */ + +#ifdef CURLVERBOSE +void Curl_httpsrr_trace(struct Curl_easy *data, + struct Curl_https_rrinfo *hi); +#else +#define Curl_httpsrr_trace(a, b) Curl_nop_stmt +#endif + #endif /* USE_HTTPSRR */ #endif /* HEADER_CURL_HTTPSRR_H */ diff --git a/lib/idn.c b/lib/idn.c index bb6fd2cffc..f2b954e2d0 100644 --- a/lib/idn.c +++ b/lib/idn.c @@ -21,34 +21,25 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - - /* - * IDN conversions - */ - +/* + * IDN conversions + */ #include "curl_setup.h" + #include "urldata.h" #include "idn.h" -#include "sendf.h" -#include "curlx/multibyte.h" -#include "curlx/warnless.h" #ifdef USE_LIBIDN2 #include #if defined(_WIN32) && defined(UNICODE) -#define IDN2_LOOKUP(name, host, flags) \ +#define IDN2_LOOKUP(name, host, flags) \ 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) +#define IDN2_LOOKUP(name, host, flags) \ + idn2_lookup_ul((const char *)(name), (char **)(host), flags) #endif -#endif /* USE_LIBIDN2 */ - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#endif /* USE_LIBIDN2 */ /* for macOS and iOS targets */ #ifdef USE_APPLE_IDN @@ -93,23 +84,23 @@ static CURLcode mac_idn_to_ascii(const char *in, char **out) { size_t inlen = strlen(in); if(inlen < MAX_HOST_LENGTH) { - char iconv_buffer[MAX_HOST_LENGTH] = {0}; + char iconv_buffer[MAX_HOST_LENGTH] = { 0 }; char *iconv_outptr = iconv_buffer; size_t iconv_outlen = sizeof(iconv_buffer); CURLcode iconv_result = iconv_to_utf8(in, inlen, &iconv_outptr, &iconv_outlen); if(!iconv_result) { UErrorCode err = U_ZERO_ERROR; - UIDNA* idna = uidna_openUTS46( - UIDNA_CHECK_BIDI|UIDNA_NONTRANSITIONAL_TO_ASCII, &err); + UIDNA *idna = uidna_openUTS46( + UIDNA_CHECK_BIDI | UIDNA_NONTRANSITIONAL_TO_ASCII, &err); if(!U_FAILURE(err)) { UIDNAInfo info = UIDNA_INFO_INITIALIZER; - char buffer[MAX_HOST_LENGTH] = {0}; + char buffer[MAX_HOST_LENGTH] = { 0 }; (void)uidna_nameToASCII_UTF8(idna, iconv_buffer, (int)iconv_outlen, buffer, sizeof(buffer) - 1, &info, &err); uidna_close(idna); if(!U_FAILURE(err) && !info.errors) { - *out = strdup(buffer); + *out = curlx_strdup(buffer); if(*out) return CURLE_OK; else @@ -128,16 +119,16 @@ static CURLcode mac_ascii_to_idn(const char *in, char **out) size_t inlen = strlen(in); if(inlen < MAX_HOST_LENGTH) { UErrorCode err = U_ZERO_ERROR; - UIDNA* idna = uidna_openUTS46( - UIDNA_CHECK_BIDI|UIDNA_NONTRANSITIONAL_TO_UNICODE, &err); + UIDNA *idna = uidna_openUTS46( + UIDNA_CHECK_BIDI | UIDNA_NONTRANSITIONAL_TO_UNICODE, &err); if(!U_FAILURE(err)) { UIDNAInfo info = UIDNA_INFO_INITIALIZER; - char buffer[MAX_HOST_LENGTH] = {0}; + char buffer[MAX_HOST_LENGTH] = { 0 }; (void)uidna_nameToUnicodeUTF8(idna, in, -1, buffer, sizeof(buffer) - 1, &info, &err); uidna_close(idna); if(!U_FAILURE(err)) { - *out = strdup(buffer); + *out = curlx_strdup(buffer); if(*out) return CURLE_OK; else @@ -152,40 +143,41 @@ 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) +{ + char *str_utf8 = NULL; + int bytes = WideCharToMultiByte(CP_UTF8, 0, str_w, chars, NULL, 0, + NULL, NULL); + if(bytes > 0) { + str_utf8 = curlx_malloc(bytes); + if(str_utf8) { + if(WideCharToMultiByte(CP_UTF8, 0, str_w, chars, str_utf8, bytes, + NULL, NULL) == 0) { + curlx_free(str_utf8); + return NULL; + } + } + } + return str_utf8; +} + static CURLcode win32_idn_to_ascii(const char *in, char **out) { - wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); + wchar_t in_w[IDN_MAX_LENGTH]; + int in_w_len; *out = NULL; - if(in_w) { + /* Returned in_w_len includes the null-terminator, which then gets + preserved across the calls that follow, ending up terminating + the buffer returned to the caller. */ + in_w_len = MultiByteToWideChar(CP_UTF8, 0, in, -1, in_w, IDN_MAX_LENGTH); + if(in_w_len) { wchar_t punycode[IDN_MAX_LENGTH]; - int chars = IdnToAscii(0, in_w, (int)(wcslen(in_w) + 1), punycode, - IDN_MAX_LENGTH); - curlx_unicodefree(in_w); - if(chars) { - char *mstr = curlx_convert_wchar_to_UTF8(punycode); - if(mstr) { - *out = strdup(mstr); - curlx_unicodefree(mstr); - if(!*out) - return CURLE_OUT_OF_MEMORY; - } - else + int chars = IdnToAscii(0, in_w, in_w_len, punycode, IDN_MAX_LENGTH); + if(chars > 0) { + *out = idn_curlx_convert_wchar_to_UTF8(punycode, chars); + if(!*out) return CURLE_OUT_OF_MEMORY; } else @@ -197,31 +189,29 @@ static CURLcode win32_idn_to_ascii(const char *in, char **out) return CURLE_OK; } -static CURLcode win32_ascii_to_idn(const char *in, char **output) +static CURLcode win32_ascii_to_idn(const char *in, char **out) { - char *out = NULL; - - wchar_t *in_w = curlx_convert_UTF8_to_wchar(in); - if(in_w) { + wchar_t in_w[IDN_MAX_LENGTH]; + int in_w_len; + *out = NULL; + /* Returned in_w_len includes the null-terminator, which then gets + preserved across the calls that follow, ending up terminating + the buffer returned to the caller. */ + in_w_len = MultiByteToWideChar(CP_UTF8, 0, in, -1, in_w, IDN_MAX_LENGTH); + if(in_w_len) { WCHAR idn[IDN_MAX_LENGTH]; /* stores a UTF-16 string */ - int chars = IdnToUnicode(0, in_w, (int)(wcslen(in_w) + 1), idn, - IDN_MAX_LENGTH); - if(chars) { - /* 'chars' is "the number of characters retrieved" */ - char *mstr = curlx_convert_wchar_to_UTF8(idn); - if(mstr) { - out = strdup(mstr); - curlx_unicodefree(mstr); - if(!out) - return CURLE_OUT_OF_MEMORY; - } + int chars = IdnToUnicode(0, in_w, in_w_len, idn, IDN_MAX_LENGTH); + if(chars > 0) { /* 'chars' is "the number of characters retrieved" */ + *out = idn_curlx_convert_wchar_to_UTF8(idn, chars); + if(!*out) + return CURLE_OUT_OF_MEMORY; } else return CURLE_URL_MALFORMAT; } else return CURLE_URL_MALFORMAT; - *output = out; + return CURLE_OK; } @@ -315,7 +305,7 @@ CURLcode Curl_idn_decode(const char *input, char **output) CURLcode result = idn_decode(input, &d); #ifdef USE_LIBIDN2 if(!result) { - char *c = strdup(d); + char *c = curlx_strdup(d); idn2_free(d); if(c) d = c; @@ -326,7 +316,7 @@ CURLcode Curl_idn_decode(const char *input, char **output) if(!result) { if(!d[0]) { /* ended up zero length, not acceptable */ result = CURLE_URL_MALFORMAT; - free(d); + curlx_free(d); } else *output = d; @@ -340,7 +330,7 @@ CURLcode Curl_idn_encode(const char *puny, char **output) CURLcode result = idn_encode(puny, &d); #ifdef USE_LIBIDN2 if(!result) { - char *c = strdup(d); + char *c = curlx_strdup(d); idn2_free(d); if(c) d = c; @@ -358,7 +348,7 @@ CURLcode Curl_idn_encode(const char *puny, char **output) */ void Curl_free_idnconverted_hostname(struct hostname *host) { - Curl_safefree(host->encalloc); + curlx_safefree(host->encalloc); } #endif /* USE_IDN */ diff --git a/lib/idn.h b/lib/idn.h index 2bdce8927f..90d8e811b1 100644 --- a/lib/idn.h +++ b/lib/idn.h @@ -23,17 +23,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - bool Curl_is_ASCII_name(const char *hostname); CURLcode Curl_idnconvert_hostname(struct hostname *host); + #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) #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 #endif + #endif /* HEADER_CURL_IDN_H */ diff --git a/lib/if2ip.c b/lib/if2ip.c index 91ee59c02a..b71254ada0 100644 --- a/lib/if2ip.c +++ b/lib/if2ip.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_NETINET_IN_H @@ -54,10 +53,6 @@ #include "curlx/inet_ntop.h" #include "if2ip.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" /* ------------------------------------------------------------------ */ @@ -66,10 +61,10 @@ unsigned int Curl_ipv6_scope(const struct sockaddr *sa) { if(sa->sa_family == AF_INET6) { - const struct sockaddr_in6 * sa6 = - (const struct sockaddr_in6 *)(const void *) sa; + const struct sockaddr_in6 *sa6 = + (const struct sockaddr_in6 *)(const void *)sa; const unsigned char *b = sa6->sin6_addr.s6_addr; - unsigned short w = (unsigned short) ((b[0] << 8) | b[1]); + unsigned short w = (unsigned short)((b[0] << 8) | b[1]); if((b[0] & 0xFE) == 0xFC) /* Handle ULAs */ return IPV6_SCOPE_UNIQUELOCAL; @@ -107,8 +102,7 @@ if2ip_result_t Curl_if2ip(int af, struct ifaddrs *iface, *head; if2ip_result_t res = IF2IP_NOT_FOUND; -#if defined(USE_IPV6) && \ - !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) +#if defined(USE_IPV6) && !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) (void)local_scope_id; #endif @@ -142,7 +136,7 @@ if2ip_result_t Curl_if2ip(int af, #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID /* Include the scope of this interface as part of the address */ scopeid = ((struct sockaddr_in6 *)(void *)iface->ifa_addr) - ->sin6_scope_id; + ->sin6_scope_id; /* If given, scope id should match. */ if(local_scope_id && scopeid != local_scope_id) { @@ -153,7 +147,7 @@ if2ip_result_t Curl_if2ip(int af, } if(scopeid) - msnprintf(scope, sizeof(scope), "%%%u", scopeid); + curl_msnprintf(scope, sizeof(scope), "%%%u", scopeid); #endif } else @@ -162,7 +156,7 @@ if2ip_result_t Curl_if2ip(int af, &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr; res = IF2IP_FOUND; ip = curlx_inet_ntop(af, addr, ipstr, sizeof(ipstr)); - msnprintf(buf, buf_size, "%s%s", ip, scope); + curl_msnprintf(buf, buf_size, "%s%s", ip, scope); break; } } @@ -208,21 +202,21 @@ if2ip_result_t Curl_if2ip(int af, if(len >= sizeof(req.ifr_name)) return IF2IP_NOT_FOUND; - dummy = socket(AF_INET, SOCK_STREAM, 0); - if(CURL_SOCKET_BAD == dummy) + dummy = CURL_SOCKET(AF_INET, SOCK_STREAM, 0); + if(dummy == CURL_SOCKET_BAD) return IF2IP_NOT_FOUND; memset(&req, 0, sizeof(req)); memcpy(req.ifr_name, interf, len + 1); req.ifr_addr.sa_family = AF_INET; -#if defined(__GNUC__) && defined(_AIX) +#if defined(CURL_HAVE_DIAG) && defined(_AIX) /* Suppress warning inside system headers */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshift-sign-overflow" #endif if(ioctl(dummy, SIOCGIFADDR, &req) < 0) { -#if defined(__GNUC__) && defined(_AIX) +#if defined(CURL_HAVE_DIAG) && defined(_AIX) #pragma GCC diagnostic pop #endif sclose(dummy); @@ -252,15 +246,15 @@ if2ip_result_t Curl_if2ip(int af, const char *interf, char *buf, size_t buf_size) { - (void)af; + (void)af; #ifdef USE_IPV6 - (void)remote_scope; - (void)local_scope_id; + (void)remote_scope; + (void)local_scope_id; #endif - (void)interf; - (void)buf; - (void)buf_size; - return IF2IP_NOT_FOUND; + (void)interf; + (void)buf; + (void)buf_size; + return IF2IP_NOT_FOUND; } #endif diff --git a/lib/if2ip.h b/lib/if2ip.h index f4b2f4c15d..12fdaabd73 100644 --- a/lib/if2ip.h +++ b/lib/if2ip.h @@ -56,34 +56,33 @@ if2ip_result_t Curl_if2ip(int af, /* Nedelcho Stanev's work-around for SFU 3.0 */ struct ifreq { -#define IFNAMSIZ 16 +#define IFNAMSIZ 16 #define IFHWADDRLEN 6 union { char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ } ifr_ifrn; - union { - struct sockaddr ifru_addr; - struct sockaddr ifru_broadaddr; - struct sockaddr ifru_netmask; - struct sockaddr ifru_hwaddr; - short ifru_flags; - int ifru_metric; - int ifru_mtu; - } ifr_ifru; + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_metric; + int ifru_mtu; + } ifr_ifru; }; -/* This define was added by Daniel to avoid an extra #ifdef INTERIX in the - C code. */ +/* This define exists to avoid an extra #ifdef INTERIX in the C code. */ -#define ifr_name ifr_ifrn.ifrn_name /* interface name */ -#define ifr_addr ifr_ifru.ifru_addr /* address */ +#define ifr_name ifr_ifrn.ifrn_name /* interface name */ +#define ifr_addr ifr_ifru.ifru_addr /* address */ #define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ -#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ -#define ifr_flags ifr_ifru.ifru_flags /* flags */ -#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ -#define ifr_metric ifr_ifru.ifru_metric /* metric */ -#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ +#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ +#define ifr_flags ifr_ifru.ifru_flags /* flags */ +#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ +#define ifr_metric ifr_ifru.ifru_metric /* metric */ +#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ #define SIOCGIFADDR _IOW('s', 102, struct ifreq) /* Get if addr */ diff --git a/lib/imap.c b/lib/imap.c index 41aec8ffab..ccfbeb72c4 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -34,9 +34,9 @@ * Draft LOGIN SASL Mechanism * ***************************************************************************/ - #include "curl_setup.h" -#include "curlx/dynbuf.h" +#include "urldata.h" +#include "imap.h" #ifndef CURL_DISABLE_IMAP @@ -54,16 +54,14 @@ #include #endif -#include -#include "urldata.h" +#include "curlx/dynbuf.h" #include "sendf.h" +#include "curl_trc.h" #include "hostip.h" #include "progress.h" #include "transfer.h" #include "escape.h" -#include "http.h" /* for HTTP proxy tunnel stuff */ -#include "socks.h" -#include "imap.h" +#include "pingpong.h" #include "mime.h" #include "curlx/strparse.h" #include "strcase.h" @@ -71,18 +69,10 @@ #include "cfilters.h" #include "connect.h" #include "select.h" -#include "multiif.h" #include "url.h" #include "bufref.h" #include "curl_sasl.h" -#include "curlx/warnless.h" -#include "curl_ctype.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - +#include "curlx/strcopy.h" /* meta key for storing protocol meta at easy handle */ #define CURL_META_IMAP_EASY "meta:proto:imap:easy" @@ -116,8 +106,8 @@ struct imap_conn { struct SASL sasl; /* SASL-related parameters */ struct dynbuf dyn; /* for the IMAP commands */ char *mailbox; /* The last selected mailbox */ - char *mailbox_uidvalidity; /* UIDVALIDITY parsed from select response */ imapstate state; /* Always use imap.c:state() to change state! */ + unsigned int mb_uidvalidity; /* UIDVALIDITY parsed from select response */ char resptag[5]; /* Response tag to wait for */ unsigned char preftype; /* Preferred authentication type */ unsigned char cmdid; /* Last used command ID */ @@ -126,6 +116,7 @@ struct imap_conn { BIT(tls_supported); /* StartTLS capability supported by server */ BIT(login_disabled); /* LOGIN command disabled by server */ BIT(ir_supported); /* Initial response supported by server */ + BIT(mb_uidvalidity_set); }; /* This IMAP struct is used in the Curl_easy. All IMAP data that is @@ -135,7 +126,6 @@ struct imap_conn { struct IMAP { curl_pp_transfer transfer; char *mailbox; /* Mailbox to select */ - char *uidvalidity; /* UIDVALIDITY to check in select */ char *uid; /* Message UID to fetch */ char *mindex; /* Index in mail box of mail to fetch */ char *section; /* Message SECTION to fetch */ @@ -143,131 +133,137 @@ struct IMAP { char *query; /* Query to search for */ char *custom; /* Custom request */ char *custom_params; /* Parameters for the custom request */ + unsigned int uidvalidity; /* UIDVALIDITY to check in select */ + 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); -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); - -/* - * IMAP protocol handler. - */ - -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 -}; - -#ifdef USE_SSL -/* - * IMAPS protocol handler. - */ - -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 -}; -#endif - #define IMAP_RESP_OK 1 #define IMAP_RESP_NOT_OK 2 #define IMAP_RESP_PREAUTH 3 -/* 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 */ -}; - 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, 0); +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); +} + +/* + * 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; +} + /*********************************************************************** * * imap_matchresp() @@ -349,46 +345,46 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn, /* Do we have an untagged command response? */ if(len >= 2 && !memcmp("* ", line, 2)) { switch(imapc->state) { - /* States which are interested in untagged responses */ - case IMAP_CAPABILITY: - if(!imap_matchresp(line, len, "CAPABILITY")) - return FALSE; - break; - - case IMAP_LIST: - if((!imap->custom && !imap_matchresp(line, len, "LIST")) || - (imap->custom && !imap_matchresp(line, len, imap->custom) && - (!curl_strequal(imap->custom, "STORE") || - !imap_matchresp(line, len, "FETCH")) && - !curl_strequal(imap->custom, "SELECT") && - !curl_strequal(imap->custom, "EXAMINE") && - !curl_strequal(imap->custom, "SEARCH") && - !curl_strequal(imap->custom, "EXPUNGE") && - !curl_strequal(imap->custom, "LSUB") && - !curl_strequal(imap->custom, "UID") && - !curl_strequal(imap->custom, "GETQUOTAROOT") && - !curl_strequal(imap->custom, "NOOP"))) - return FALSE; - break; - - case IMAP_SELECT: - /* SELECT is special in that its untagged responses do not have a - common prefix so accept anything! */ - break; - - case IMAP_FETCH: - if(!imap_matchresp(line, len, "FETCH")) - return FALSE; - break; - - case IMAP_SEARCH: - if(!imap_matchresp(line, len, "SEARCH")) - return FALSE; - break; - - /* Ignore other untagged responses */ - default: + /* States which are interested in untagged responses */ + case IMAP_CAPABILITY: + if(!imap_matchresp(line, len, "CAPABILITY")) return FALSE; + break; + + case IMAP_LIST: + if((!imap->custom && !imap_matchresp(line, len, "LIST")) || + (imap->custom && !imap_matchresp(line, len, imap->custom) && + (!curl_strequal(imap->custom, "STORE") || + !imap_matchresp(line, len, "FETCH")) && + !curl_strequal(imap->custom, "SELECT") && + !curl_strequal(imap->custom, "EXAMINE") && + !curl_strequal(imap->custom, "SEARCH") && + !curl_strequal(imap->custom, "EXPUNGE") && + !curl_strequal(imap->custom, "LSUB") && + !curl_strequal(imap->custom, "UID") && + !curl_strequal(imap->custom, "GETQUOTAROOT") && + !curl_strequal(imap->custom, "NOOP"))) + return FALSE; + break; + + case IMAP_SELECT: + /* SELECT is special in that its untagged responses do not have a + common prefix so accept anything! */ + break; + + case IMAP_FETCH: + if(!imap_matchresp(line, len, "FETCH")) + return FALSE; + break; + + case IMAP_SEARCH: + if(!imap_matchresp(line, len, "SEARCH")) + return FALSE; + break; + + /* Ignore other untagged responses */ + default: + return FALSE; } *resp = '*'; @@ -402,16 +398,16 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn, if(!imap->custom && ((len == 3 && line[0] == '+') || (len >= 2 && !memcmp("+ ", line, 2)))) { switch(imapc->state) { - /* States which are interested in continuation responses */ - case IMAP_AUTHENTICATE: - case IMAP_APPEND: - *resp = '+'; - break; + /* States which are interested in continuation responses */ + case IMAP_AUTHENTICATE: + case IMAP_APPEND: + *resp = '+'; + break; - default: - failf(data, "Unexpected continuation response"); - *resp = -1; - break; + default: + failf(data, "Unexpected continuation response"); + *resp = -1; + break; } return TRUE; @@ -441,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 */ @@ -471,9 +466,9 @@ 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[]={ + static const char * const names[] = { "STOP", "SERVERGREET", "CAPABILITY", @@ -495,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; } @@ -563,17 +559,17 @@ 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 */ - result = imap_perform_capability(data, imapc); + /* perform CAPA now, changes imapc->state out of IMAP_UPGRADETLS */ + result = imap_perform_capability(data, imapc); } out: return result; @@ -615,8 +611,8 @@ static CURLcode imap_perform_login(struct Curl_easy *data, result = imap_sendf(data, imapc, "LOGIN %s %s", user ? user : "", passwd ? passwd : ""); - free(user); - free(passwd); + curlx_free(user); + curlx_free(passwd); if(!result) imap_state(data, imapc, IMAP_LOGIN); @@ -638,7 +634,7 @@ static CURLcode imap_perform_authenticate(struct Curl_easy *data, struct imap_conn *imapc = Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN); CURLcode result = CURLE_OK; - const char *ir = (const char *) Curl_bufref_ptr(initresp); + const char *ir = Curl_bufref_ptr(initresp); if(!imapc) return CURLE_FAILED_INIT; @@ -670,8 +666,7 @@ static CURLcode imap_continue_authenticate(struct Curl_easy *data, (void)mech; if(!imapc) return CURLE_FAILED_INIT; - return Curl_pp_sendf(data, &imapc->pp, - "%s", (const char *) Curl_bufref_ptr(resp)); + return Curl_pp_sendf(data, &imapc->pp, "%s", Curl_bufref_ptr(resp)); } /*********************************************************************** @@ -715,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) @@ -749,14 +745,14 @@ static CURLcode imap_perform_list(struct Curl_easy *data, else { /* Make sure the mailbox is in the correct atom format if necessary */ char *mailbox = imap->mailbox ? imap_atom(imap->mailbox, TRUE) - : strdup(""); + : curlx_strdup(""); if(!mailbox) return CURLE_OUT_OF_MEMORY; /* Send the LIST command */ result = imap_sendf(data, imapc, "LIST \"%s\" *", mailbox); - free(mailbox); + curlx_free(mailbox); } if(!result) @@ -779,8 +775,8 @@ static CURLcode imap_perform_select(struct Curl_easy *data, char *mailbox; /* Invalidate old information as we are switching mailboxes */ - Curl_safefree(imapc->mailbox); - Curl_safefree(imapc->mailbox_uidvalidity); + curlx_safefree(imapc->mailbox); + imapc->mb_uidvalidity_set = FALSE; /* Check we have a mailbox */ if(!imap->mailbox) { @@ -796,7 +792,7 @@ static CURLcode imap_perform_select(struct Curl_easy *data, /* Send the SELECT command */ result = imap_sendf(data, imapc, "SELECT %s", mailbox); - free(mailbox); + curlx_free(mailbox); if(!result) imap_state(data, imapc, IMAP_SELECT); @@ -869,22 +865,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); @@ -946,7 +944,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data, cleanup: curlx_dyn_free(&flags); - free(mailbox); + curlx_free(mailbox); if(!result) imap_state(data, imapc, IMAP_APPEND); @@ -1027,7 +1025,6 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, imapstate instate) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf); (void)instate; @@ -1039,36 +1036,31 @@ 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? */ - if(wordlen == 8 && !memcmp(line, "STARTTLS", 8)) + if(wordlen == 8 && curl_strnequal(line, "STARTTLS", 8)) imapc->tls_supported = TRUE; /* Has the server explicitly disabled clear text authentication? */ - else if(wordlen == 13 && !memcmp(line, "LOGINDISABLED", 13)) + else if(wordlen == 13 && curl_strnequal(line, "LOGINDISABLED", 13)) imapc->login_disabled = TRUE; /* Does the server support the SASL-IR capability? */ - else if(wordlen == 7 && !memcmp(line, "SASL-IR", 7)) + else if(wordlen == 7 && curl_strnequal(line, "SASL-IR", 7)) imapc->ir_supported = TRUE; /* Do we have a SASL based authentication mechanism? */ - else if(wordlen > 5 && !memcmp(line, "AUTH=", 5)) { + else if(wordlen > 5 && curl_strnequal(line, "AUTH=", 5)) { size_t llen; unsigned short mechbit; @@ -1084,7 +1076,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, line += wordlen; } } - else if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + else if(data->set.use_ssl && !Curl_xfer_is_secure(data)) { /* PREAUTH is not compatible with STARTTLS. */ if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) { /* Switch to TLS connection now */ @@ -1149,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 { @@ -1184,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, @@ -1191,13 +1220,117 @@ 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 == '*') - result = Curl_client_write(data, CLIENTWRITE_BODY, line, len); + 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 = imap_find_literal(line, line_len); + if(ptr) { + curl_off_t size = 0; + bool parsed = FALSE; + ptr++; + if(!curlx_str_number(&ptr, &size, CURL_OFF_T_MAX) && + !curlx_str_single(&ptr, '}')) + parsed = TRUE; + + if(parsed) { + struct pingpong *pp = &imapc->pp; + size_t buffer_len = curlx_dyn_len(&pp->recvbuf); + size_t after_header = buffer_len - pp->nfinal; + + /* This is a literal response, setup to receive the body data */ + infof(data, "Found %" FMT_OFF_T " bytes to download", size); + + /* First write the header line */ + result = Curl_client_write(data, CLIENTWRITE_BODY, line, len); + if(result) + return result; + + /* Handle data already in buffer after the header line */ + if(after_header > 0) { + /* There is already data in the buffer that is part of the literal + body or subsequent responses */ + size_t chunk = after_header; + + /* Keep only the data after the header line */ + curlx_dyn_tail(&pp->recvbuf, chunk); + pp->nfinal = 0; /* done */ + + /* Limit chunk to the literal size */ + if(chunk > (size_t)size) + chunk = (size_t)size; + + if(chunk) { + /* Write the literal body data */ + result = Curl_client_write(data, CLIENTWRITE_BODY, + curlx_dyn_ptr(&pp->recvbuf), chunk); + if(result) + return result; + } + + /* Handle remaining data in buffer (either more literal data or + subsequent responses) */ + if(after_header > chunk) { + /* Keep the data after the literal body */ + pp->overflow = after_header - chunk; + curlx_dyn_tail(&pp->recvbuf, pp->overflow); + } + else { + pp->overflow = 0; + curlx_dyn_reset(&pp->recvbuf); + } + } + else { + /* No data in buffer yet, reset overflow */ + pp->overflow = 0; + } + + if((CURL_OFF_T_MAX - size) < (curl_off_t)len) + /* unlikely to actually be a transfer this big, but avoid integer + overflow */ + size = CURL_OFF_T_MAX; + else + size += len; + + /* Progress size includes both header line and literal body */ + Curl_pgrsSetDownloadSize(data, size); + + if(data->req.bytecount == size) + /* All data already transferred (header + literal body) */ + Curl_xfer_setup_nop(data); + else { + /* Setup to receive the literal body data. + maxdownload and transfer size include both header line and + literal body */ + data->req.maxdownload = size; + Curl_xfer_setup_recv(data, FIRSTSOCKET, size); + } + /* End of DO phase */ + imap_state(data, imapc, IMAP_STOP); + } + else { + /* Failed to parse literal, write the line */ + result = Curl_client_write(data, CLIENTWRITE_BODY, line, len); + } + } + else { + /* No literal, write the line as-is */ + result = Curl_client_write(data, CLIENTWRITE_BODY, line, len); + } + } else if(imapcode != IMAP_RESP_OK) result = CURLE_QUOTE_ERROR; else @@ -1215,38 +1348,32 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, imapstate instate) { CURLcode result = CURLE_OK; - const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf); - (void)instate; if(imapcode == '*') { /* See if this is an UIDVALIDITY response */ - if(checkprefix("OK [UIDVALIDITY ", line + 2)) { - size_t len = 0; + const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf); + size_t len = curlx_dyn_len(&imapc->pp.recvbuf); + if((len >= 18) && checkprefix("OK [UIDVALIDITY ", &line[2])) { + curl_off_t value; const char *p = &line[2] + strlen("OK [UIDVALIDITY "); - while((len < 20) && p[len] && ISDIGIT(p[len])) - len++; - if(len && (p[len] == ']')) { - struct dynbuf uid; - curlx_dyn_init(&uid, 20); - if(curlx_dyn_addn(&uid, p, len)) - return CURLE_OUT_OF_MEMORY; - free(imapc->mailbox_uidvalidity); - imapc->mailbox_uidvalidity = curlx_dyn_ptr(&uid); + if(!curlx_str_number(&p, &value, UINT_MAX)) { + imapc->mb_uidvalidity = (unsigned int)value; + imapc->mb_uidvalidity_set = TRUE; } } } else if(imapcode == IMAP_RESP_OK) { /* Check if the UIDVALIDITY has been specified and matches */ - if(imap->uidvalidity && imapc->mailbox_uidvalidity && - !curl_strequal(imap->uidvalidity, imapc->mailbox_uidvalidity)) { + if(imap->uidvalidity_set && imapc->mb_uidvalidity_set && + (imap->uidvalidity != imapc->mb_uidvalidity)) { failf(data, "Mailbox UIDVALIDITY has changed"); result = CURLE_REMOTE_FILE_NOT_FOUND; } else { /* Note the currently opened mailbox on this connection */ DEBUGASSERT(!imapc->mailbox); - imapc->mailbox = strdup(imap->mailbox); + imapc->mailbox = curlx_strdup(imap->mailbox); if(!imapc->mailbox) return CURLE_OUT_OF_MEMORY; @@ -1289,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) && @@ -1328,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; @@ -1436,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; @@ -1568,451 +1694,21 @@ static CURLcode imap_pollset(struct Curl_easy *data, return imapc ? Curl_pp_pollset(data, &imapc->pp, ps) : CURLE_OK; } -/*********************************************************************** - * - * 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; - - /* We always support persistent connections in IMAP */ - connkeep(data->conn, "IMAP default"); - - /* Parse the URL options */ - result = imap_parse_url_options(data->conn, imapc); - if(result) - return result; - - /* Start off waiting for the server greeting response */ - imap_state(data, imapc, IMAP_SERVERGREET); - - /* Start off with an response id of '*' */ - strcpy(imapc->resptag, "*"); - - result = imap_multi_statemach(data, done); - - return result; -} - -/*********************************************************************** - * - * imap_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 imap_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN); - struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); - - (void)premature; - - if(!imapc) - return CURLE_FAILED_INIT; - if(!imap) - return CURLE_OK; - - if(status) { - connclose(conn, "IMAP done with bad status"); /* marked for closure */ - result = status; /* use the already set error code */ - } - else if(!data->set.connect_only && !imap->custom && - (imap->uid || imap->mindex || data->state.upload || - IS_MIME_POST(data))) { - /* Handle responses after FETCH or APPEND transfer has finished */ - - if(!data->state.upload && !IS_MIME_POST(data)) - imap_state(data, imapc, IMAP_FETCH_FINAL); - else { - /* End the APPEND command first by sending an empty line */ - result = Curl_pp_sendf(data, &imapc->pp, "%s", ""); - if(!result) - imap_state(data, imapc, IMAP_APPEND_FINAL); - } - - /* Run the state-machine */ - if(!result) - result = imap_block_statemach(data, imapc, FALSE); - } - - imap_easy_reset(imap); - return result; -} - -/*********************************************************************** - * - * imap_perform() - * - * This is the actual DO function for IMAP. Fetch or append a message, or do - * other things according to the options previously setup. - */ -static CURLcode imap_perform(struct Curl_easy *data, bool *connected, - bool *dophase_done) -{ - /* This is IMAP and no proxy */ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN); - struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); - bool selected = FALSE; - - DEBUGF(infof(data, "DO phase starts")); - if(!imapc || !imap) - return CURLE_FAILED_INIT; - - if(data->req.no_body) { - /* Requested no body means no transfer */ - imap->transfer = PPTRANSFER_INFO; - } - - *dophase_done = FALSE; /* not done yet */ - - /* Determine if the requested mailbox (with the same UIDVALIDITY if set) - has already been selected on this connection */ - if(imap->mailbox && imapc->mailbox && - curl_strequal(imap->mailbox, imapc->mailbox) && - (!imap->uidvalidity || !imapc->mailbox_uidvalidity || - curl_strequal(imap->uidvalidity, imapc->mailbox_uidvalidity))) - selected = TRUE; - - /* Start the first command in the DO phase */ - if(data->state.upload || IS_MIME_POST(data)) - /* APPEND can be executed directly */ - result = imap_perform_append(data, imapc, imap); - else if(imap->custom && (selected || !imap->mailbox)) - /* Custom command using the same mailbox or no mailbox */ - result = imap_perform_list(data, imapc, imap); - else if(!imap->custom && selected && (imap->uid || imap->mindex)) - /* FETCH from the same mailbox */ - result = imap_perform_fetch(data, imapc, imap); - else if(!imap->custom && selected && imap->query) - /* SEARCH the current mailbox */ - result = imap_perform_search(data, imapc, imap); - else if(imap->mailbox && !selected && - (imap->custom || imap->uid || imap->mindex || imap->query)) - /* SELECT the mailbox */ - result = imap_perform_select(data, imapc, imap); - else - /* LIST */ - result = imap_perform_list(data, imapc, imap); - - if(result) - return result; - - /* Run the state-machine */ - result = imap_multi_statemach(data, dophase_done); - - *connected = Curl_conn_is_connected(conn, FIRSTSOCKET); - - if(*dophase_done) - DEBUGF(infof(data, "DO phase is complete")); - - 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 */ - - if(!imap) - return CURLE_FAILED_INIT; - /* Parse the URL path */ - result = imap_parse_url_path(data, imap); - if(result) - return result; - - /* Parse the custom request */ - result = imap_parse_custom_request(data, imap); - if(result) - return result; - - result = imap_regular_transfer(data, imap, done); - - return result; -} - -/*********************************************************************** - * - * imap_disconnect() - * - * Disconnect from an IMAP server. Cleanup protocol-specific per-connection - * resources. BLOCKING. - */ -static CURLcode imap_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) -{ - 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), - 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, &imapc->pp)) { - if(!imap_perform_logout(data, imapc)) - (void)imap_block_statemach(data, imapc, TRUE); /* ignore errors */ - } - } - 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) -{ - struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); - CURLcode result; - - if(!imap) - return CURLE_FAILED_INIT; - - result = imap_multi_statemach(data, dophase_done); - if(result) - DEBUGF(infof(data, "DO phase failed")); - else if(*dophase_done) { - result = imap_dophase_done(data, imap, FALSE /* not connected */); - - DEBUGF(infof(data, "DO phase is complete")); - } - - 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_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - /* 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->uidvalidity); - 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); + curlx_safefree(imap->mailbox); + curlx_safefree(imap->uid); + curlx_safefree(imap->mindex); + curlx_safefree(imap->section); + curlx_safefree(imap->partial); + curlx_safefree(imap->query); + curlx_safefree(imap->custom); + curlx_safefree(imap->custom_params); + imap->uidvalidity_set = FALSE; /* 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; - (void)key; - (void)klen; - imap_easy_reset(imap); - free(imap); -} - -static void imap_conn_dtor(void *key, size_t klen, void *entry) -{ - struct imap_conn *imapc = entry; - (void)key; - (void)klen; - Curl_pp_disconnect(&imapc->pp); - curlx_dyn_free(&imapc->dyn); - Curl_safefree(imapc->mailbox); - Curl_safefree(imapc->mailbox_uidvalidity); - free(imapc); -} - -static CURLcode imap_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - struct imap_conn *imapc; - struct pingpong *pp; - struct IMAP *imap; - - imapc = calloc(1, sizeof(*imapc)); - if(!imapc) - return CURLE_OUT_OF_MEMORY; - - pp = &imapc->pp; - PINGPONG_SETUP(pp, imap_pp_statemachine, imap_endofresp); - - /* Set the default preferred authentication type and mechanism */ - imapc->preftype = IMAP_TYPE_ANY; - Curl_sasl_init(&imapc->sasl, data, &saslimap); - - curlx_dyn_init(&imapc->dyn, DYN_IMAP_CMD); - Curl_pp_init(pp); - - if(Curl_conn_meta_set(conn, CURL_META_IMAP_CONN, imapc, imap_conn_dtor)) - return CURLE_OUT_OF_MEMORY; - - imap = calloc(1, sizeof(struct IMAP)); - if(!imap || - Curl_meta_set(data, CURL_META_IMAP_EASY, imap, imap_easy_dtor)) - return CURLE_OUT_OF_MEMORY; - - 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 */ - 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 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() @@ -2022,28 +1718,9 @@ static char *imap_atom(const char *str, bool escape_only) */ static bool imap_is_bchar(char ch) { - /* Performing the alnum check with this macro is faster because of ASCII + /* Performing the alnum check first with 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; - } + return ch && (ISALNUM(ch) || strchr(":@/&=-._~!$\'()*+,%", ch)); } /*********************************************************************** @@ -2171,68 +1848,61 @@ static CURLcode imap_parse_url_path(struct Curl_easy *data, result = Curl_urldecode(begin, ptr - begin, &value, &valuelen, REJECT_CTRL); if(result) { - free(name); + 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. + /* 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(curl_strequal(name, "UIDVALIDITY") && !imap->uidvalidity) { - if(valuelen > 0 && value[valuelen - 1] == '/') - value[valuelen - 1] = '\0'; - - imap->uidvalidity = value; - value = NULL; + 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 if(curl_strequal(name, "UID") && !imap->uid) { - if(valuelen > 0 && value[valuelen - 1] == '/') - value[valuelen - 1] = '\0'; - - imap->uid = value; - value = NULL; - } - else if(curl_strequal(name, "MAILINDEX") && !imap->mindex) { - if(valuelen > 0 && value[valuelen - 1] == '/') - value[valuelen - 1] = '\0'; - - imap->mindex = value; - value = NULL; - } - else if(curl_strequal(name, "SECTION") && !imap->section) { - if(valuelen > 0 && value[valuelen - 1] == '/') - value[valuelen - 1] = '\0'; - - imap->section = value; - value = NULL; - } - else if(curl_strequal(name, "PARTIAL") && !imap->partial) { - if(valuelen > 0 && value[valuelen - 1] == '/') - value[valuelen - 1] = '\0'; - - imap->partial = value; - value = NULL; - } - else { - free(name); - free(value); - - return CURLE_URL_MALFORMAT; - } - - free(name); - free(value); + 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 */ - (void)curl_url_get(data->state.uh, CURLUPART_QUERY, &imap->query, - CURLU_URLDECODE); + 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 */ @@ -2266,7 +1936,7 @@ static CURLcode imap_parse_custom_request(struct Curl_easy *data, params++; if(*params) { - imap->custom_params = strdup(params); + imap->custom_params = curlx_strdup(params); imap->custom[params - imap->custom] = '\0'; if(!imap->custom_params) @@ -2278,4 +1948,379 @@ static CURLcode imap_parse_custom_request(struct Curl_easy *data, 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); + if(result) + return result; + + /* Start off waiting for the server greeting response */ + imap_state(data, imapc, IMAP_SERVERGREET); + + /* Start off with an response id of '*' */ + curlx_strcopy(imapc->resptag, sizeof(imapc->resptag), STRCONST("*")); + + result = imap_multi_statemach(data, done); + + return result; +} + +/*********************************************************************** + * + * imap_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 imap_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN); + struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); + + (void)premature; + + if(!imapc) + return CURLE_FAILED_INIT; + if(!imap) + return CURLE_OK; + + if(status) { + connclose(conn, "IMAP done with bad status"); /* marked for closure */ + result = status; /* use the already set error code */ + } + else if(!data->set.connect_only && + ((!imap->custom && (imap->uid || imap->mindex)) || + (imap->custom && data->req.maxdownload > 0) || + data->state.upload || IS_MIME_POST(data))) { + /* Handle responses after FETCH or APPEND transfer has finished. + For custom commands, check if we set up a download which indicates + a FETCH-like command with literal data. */ + + if(!data->state.upload && !IS_MIME_POST(data)) + imap_state(data, imapc, IMAP_FETCH_FINAL); + else { + /* End the APPEND command first by sending an empty line */ + result = Curl_pp_sendf(data, &imapc->pp, "%s", ""); + if(!result) + imap_state(data, imapc, IMAP_APPEND_FINAL); + } + + /* Run the state-machine */ + if(!result) + result = imap_block_statemach(data, imapc, FALSE); + } + + imap_easy_reset(imap); + return result; +} + +/*********************************************************************** + * + * imap_perform() + * + * This is the actual DO function for IMAP. Fetch or append a message, or do + * other things according to the options previously setup. + */ +static CURLcode imap_perform(struct Curl_easy *data, bool *connected, + bool *dophase_done) +{ + /* This is IMAP and no proxy */ + CURLcode result = CURLE_OK; + struct connectdata *conn = data->conn; + struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN); + struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); + bool selected = FALSE; + + DEBUGF(infof(data, "DO phase starts")); + if(!imapc || !imap) + return CURLE_FAILED_INIT; + + if(data->req.no_body) { + /* Requested no body means no transfer */ + imap->transfer = PPTRANSFER_INFO; + } + + *dophase_done = FALSE; /* not done yet */ + + /* Determine if the requested mailbox (with the same UIDVALIDITY if set) + has already been selected on this connection */ + if(imap->mailbox && imapc->mailbox && + curl_strequal(imap->mailbox, imapc->mailbox) && + (!imap->uidvalidity_set || !imapc->mb_uidvalidity_set || + (imap->uidvalidity == imapc->mb_uidvalidity))) + selected = TRUE; + + /* Start the first command in the DO phase */ + if(data->state.upload || IS_MIME_POST(data)) + /* APPEND can be executed directly */ + result = imap_perform_append(data, imapc, imap); + else if(imap->custom && (selected || !imap->mailbox)) + /* Custom command using the same mailbox or no mailbox */ + result = imap_perform_list(data, imapc, imap); + else if(!imap->custom && selected && (imap->uid || imap->mindex)) + /* FETCH from the same mailbox */ + result = imap_perform_fetch(data, imapc, imap); + else if(!imap->custom && selected && imap->query) + /* SEARCH the current mailbox */ + result = imap_perform_search(data, imapc, imap); + else if(imap->mailbox && !selected && + (imap->custom || imap->uid || imap->mindex || imap->query)) + /* SELECT the mailbox */ + result = imap_perform_select(data, imapc, imap); + else + /* LIST */ + result = imap_perform_list(data, imapc, imap); + + if(result) + return result; + + /* Run the state-machine */ + result = imap_multi_statemach(data, dophase_done); + + *connected = Curl_conn_is_connected(conn, FIRSTSOCKET); + + if(*dophase_done) + DEBUGF(infof(data, "DO phase is complete")); + + return result; +} + +/* 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 */ + + if(!imap) + return CURLE_FAILED_INIT; + /* Parse the URL path */ + result = imap_parse_url_path(data, imap); + if(result) + return result; + + /* Parse the custom request */ + result = imap_parse_custom_request(data, imap); + if(result) + return result; + + result = imap_regular_transfer(data, imap, done); + + return result; +} + +/*********************************************************************** + * + * imap_disconnect() + * + * Disconnect from an IMAP server. Cleanup protocol-specific per-connection + * resources. BLOCKING. + */ +static CURLcode imap_disconnect(struct Curl_easy *data, + struct connectdata *conn, bool dead_connection) +{ + struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN); + + if(imapc) { + /* We cannot send quit unconditionally. If this connection is stale or + bad in any way (pingpong has pending data to send), + 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, &imapc->pp)) { + if(!imap_perform_logout(data, imapc)) + (void)imap_block_statemach(data, imapc, TRUE); /* ignore errors */ + } + } + return CURLE_OK; +} + +/* Called from multi.c while DOing */ +static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done) +{ + struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY); + CURLcode result; + + if(!imap) + return CURLE_FAILED_INIT; + + result = imap_multi_statemach(data, dophase_done); + if(result) + DEBUGF(infof(data, "DO phase failed")); + else if(*dophase_done) { + result = imap_dophase_done(data, imap, FALSE /* not connected */); + + DEBUGF(infof(data, "DO phase is complete")); + } + + return result; +} + +static void imap_easy_dtor(void *key, size_t klen, void *entry) +{ + struct IMAP *imap = entry; + (void)key; + (void)klen; + imap_easy_reset(imap); + curlx_free(imap); +} + +static void imap_conn_dtor(void *key, size_t klen, void *entry) +{ + struct imap_conn *imapc = entry; + (void)key; + (void)klen; + Curl_pp_disconnect(&imapc->pp); + curlx_dyn_free(&imapc->dyn); + curlx_safefree(imapc->mailbox); + 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) +{ + struct imap_conn *imapc; + struct pingpong *pp; + struct IMAP *imap; + + imapc = curlx_calloc(1, sizeof(*imapc)); + if(!imapc) + return CURLE_OUT_OF_MEMORY; + + pp = &imapc->pp; + PINGPONG_SETUP(pp, imap_pp_statemachine, imap_endofresp); + + /* Set the default preferred authentication type and mechanism */ + imapc->preftype = IMAP_TYPE_ANY; + Curl_sasl_init(&imapc->sasl, data, &saslimap); + + curlx_dyn_init(&imapc->dyn, DYN_IMAP_CMD); + Curl_pp_init(pp, Curl_pgrs_now(data)); + + if(Curl_conn_meta_set(conn, CURL_META_IMAP_CONN, imapc, imap_conn_dtor)) + return CURLE_OUT_OF_MEMORY; + + imap = curlx_calloc(1, sizeof(struct IMAP)); + if(!imap || + Curl_meta_set(data, CURL_META_IMAP_EASY, imap, imap_easy_dtor)) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +/* + * IMAP protocol. + */ +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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* CURL_DISABLE_IMAP */ diff --git a/lib/imap.h b/lib/imap.h index f802ed5c0c..ecfb1ec9b9 100644 --- a/lib/imap.h +++ b/lib/imap.h @@ -24,19 +24,16 @@ * ***************************************************************************/ -#include "pingpong.h" -#include "curl_sasl.h" - - -extern const struct Curl_handler Curl_handler_imap; -extern const struct Curl_handler Curl_handler_imaps; - /* Authentication type flags */ #define IMAP_TYPE_CLEARTEXT (1 << 0) #define IMAP_TYPE_SASL (1 << 1) /* Authentication type values */ #define IMAP_TYPE_NONE 0 -#define IMAP_TYPE_ANY (IMAP_TYPE_CLEARTEXT|IMAP_TYPE_SASL) +#define IMAP_TYPE_ANY (IMAP_TYPE_CLEARTEXT | IMAP_TYPE_SASL) + +#ifndef CURL_DISABLE_IMAP +extern const struct Curl_protocol Curl_protocol_imap; +#endif #endif /* HEADER_CURL_IMAP_H */ diff --git a/lib/krb5.c b/lib/krb5.c deleted file mode 100644 index 563d724bbb..0000000000 --- a/lib/krb5.c +++ /dev/null @@ -1,947 +0,0 @@ -/* GSSAPI/krb5 support for FTP - loosely based on old krb4.c - * - * Copyright (c) 1995, 1996, 1997, 1998, 1999 Kungliga Tekniska Högskolan - * (Royal Institute of Technology, Stockholm, Sweden). - * Copyright (C) Daniel Stenberg - * All rights reserved. - * - * SPDX-License-Identifier: BSD-3-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the Institute nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. */ - -#include "curl_setup.h" - -#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP) - -#ifdef HAVE_NETDB_H -#include -#endif -#ifdef HAVE_ARPA_INET_H -#include -#endif - -#include "urldata.h" -#include "url.h" -#include "cfilters.h" -#include "cf-socket.h" -#include "curlx/base64.h" -#include "ftp.h" -#include "curl_gssapi.h" -#include "sendf.h" -#include "transfer.h" -#include "curl_krb5.h" -#include "curlx/warnless.h" -#include "strdup.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if defined(__GNUC__) && defined(__APPLE__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - -static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn, - const char *cmd) -{ - size_t bytes_written; -#define SBUF_SIZE 1024 - char s[SBUF_SIZE]; - size_t write_len; - char *sptr = s; - CURLcode result = CURLE_OK; -#ifdef HAVE_GSSAPI - unsigned char data_sec = conn->data_prot; -#endif - - DEBUGASSERT(cmd); - - write_len = strlen(cmd); - if(!write_len || write_len > (sizeof(s) -3)) - return CURLE_BAD_FUNCTION_ARGUMENT; - - memcpy(&s, cmd, write_len); - strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */ - write_len += 2; - bytes_written = 0; - - for(;;) { -#ifdef HAVE_GSSAPI - conn->data_prot = PROT_CMD; -#endif - result = Curl_xfer_send(data, sptr, write_len, FALSE, &bytes_written); -#ifdef HAVE_GSSAPI - DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); - conn->data_prot = data_sec; -#endif - - if(result) - break; - - Curl_debug(data, CURLINFO_HEADER_OUT, sptr, bytes_written); - - if(bytes_written != write_len) { - write_len -= bytes_written; - sptr += bytes_written; - } - else - break; - } - - return result; -} - -static int -krb5_init(void *app_data) -{ - gss_ctx_id_t *context = app_data; - /* Make sure our context is initialized for krb5_end. */ - *context = GSS_C_NO_CONTEXT; - return 0; -} - -static int -krb5_check_prot(void *app_data, int level) -{ - (void)app_data; - if(level == PROT_CONFIDENTIAL) - return -1; - return 0; -} - -static int -krb5_decode(void *app_data, void *buf, int len, - int level, struct connectdata *conn) -{ - gss_ctx_id_t *context = app_data; - OM_uint32 maj, min; - gss_buffer_desc enc, dec; - - (void)level; - (void)conn; - - enc.value = buf; - enc.length = len; - maj = gss_unwrap(&min, *context, &enc, &dec, NULL, NULL); - if(maj != GSS_S_COMPLETE) - return -1; - - memcpy(buf, dec.value, dec.length); - len = curlx_uztosi(dec.length); - gss_release_buffer(&min, &dec); - - return len; -} - -static int -krb5_encode(void *app_data, const void *from, int length, int level, void **to) -{ - gss_ctx_id_t *context = app_data; - gss_buffer_desc dec, enc; - OM_uint32 maj, min; - int state; - int len; - - /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal - * libraries modify the input buffer in gss_wrap() - */ - dec.value = CURL_UNCONST(from); - dec.length = (size_t)length; - maj = gss_wrap(&min, *context, - level == PROT_PRIVATE, - GSS_C_QOP_DEFAULT, - &dec, &state, &enc); - - if(maj != GSS_S_COMPLETE) - return -1; - - /* malloc a new buffer, in case gss_release_buffer does not work as - expected */ - *to = malloc(enc.length); - if(!*to) - return -1; - memcpy(*to, enc.value, enc.length); - len = curlx_uztosi(enc.length); - gss_release_buffer(&min, &enc); - return len; -} - -static int -krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) -{ - int ret = AUTH_OK; - char *p; - const char *host = conn->host.name; - ssize_t nread; - curl_socklen_t l = sizeof(conn->local_addr); - CURLcode result; - const char *service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : - "ftp"; - const char *srv_host = "host"; - gss_buffer_desc input_buffer, output_buffer, *gssresp; - gss_buffer_desc _gssresp = GSS_C_EMPTY_BUFFER; - OM_uint32 maj, min; - gss_name_t gssname; - gss_ctx_id_t *context = app_data; - struct gss_channel_bindings_struct chan; - size_t base64_sz = 0; - const struct Curl_sockaddr_ex *remote_addr = - Curl_conn_get_remote_addr(data, FIRSTSOCKET); - struct sockaddr_in *remote_in_addr = remote_addr ? - (struct sockaddr_in *)CURL_UNCONST(&remote_addr->curl_sa_addr) : NULL; - char *stringp; - struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN); - - if(!ftpc || !remote_in_addr) - return -2; - - if(getsockname(conn->sock[FIRSTSOCKET], - (struct sockaddr *)&conn->local_addr, &l) < 0) - perror("getsockname()"); - - chan.initiator_addrtype = GSS_C_AF_INET; - chan.initiator_address.length = l - 4; - chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr; - chan.acceptor_addrtype = GSS_C_AF_INET; - chan.acceptor_address.length = l - 4; - chan.acceptor_address.value = &remote_in_addr->sin_addr.s_addr; - chan.application_data.length = 0; - chan.application_data.value = NULL; - - /* this loop will execute twice (once for service, once for host) */ - for(;;) { - /* this really should not be repeated here, but cannot help it */ - if(service == srv_host) { - result = ftpsend(data, conn, "AUTH GSSAPI"); - if(result) - return -2; - - if(Curl_GetFTPResponse(data, &nread, NULL)) - return -1; - else { - char *line = curlx_dyn_ptr(&ftpc->pp.recvbuf); - if(line[0] != '3') - return -1; - } - } - - stringp = aprintf("%s@%s", service, host); - if(!stringp) - return -2; - - input_buffer.value = stringp; - input_buffer.length = strlen(stringp); - maj = gss_import_name(&min, &input_buffer, GSS_C_NT_HOSTBASED_SERVICE, - &gssname); - free(stringp); - if(maj != GSS_S_COMPLETE) { - gss_release_name(&min, &gssname); - if(service == srv_host) { - failf(data, "Error importing service name %s@%s", service, host); - return AUTH_ERROR; - } - service = srv_host; - continue; - } - /* We pass NULL as |output_name_type| to avoid a leak. */ - gss_display_name(&min, gssname, &output_buffer, NULL); - infof(data, "Trying against %s", (char *)output_buffer.value); - gssresp = GSS_C_NO_BUFFER; - *context = GSS_C_NO_CONTEXT; - - do { - /* Release the buffer at each iteration to avoid leaking: the first time - we are releasing the memory from gss_display_name. The last item is - taken care by a final gss_release_buffer. */ - gss_release_buffer(&min, &output_buffer); - ret = AUTH_OK; - maj = Curl_gss_init_sec_context(data, - &min, - context, - gssname, - &Curl_krb5_mech_oid, - &chan, - gssresp, - &output_buffer, - TRUE, - NULL); - - if(gssresp) { - free(_gssresp.value); - gssresp = NULL; - } - - if(GSS_ERROR(maj)) { - infof(data, "Error creating security context"); - ret = AUTH_ERROR; - break; - } - - if(output_buffer.length) { - char *cmd; - - result = curlx_base64_encode((char *)output_buffer.value, - output_buffer.length, &p, &base64_sz); - if(result) { - infof(data, "base64-encoding: %s", curl_easy_strerror(result)); - ret = AUTH_ERROR; - break; - } - - cmd = aprintf("ADAT %s", p); - if(cmd) - result = ftpsend(data, conn, cmd); - else - result = CURLE_OUT_OF_MEMORY; - - free(p); - free(cmd); - - if(result) { - ret = -2; - break; - } - - if(Curl_GetFTPResponse(data, &nread, NULL)) { - ret = -1; - break; - } - else { - size_t len = curlx_dyn_len(&ftpc->pp.recvbuf); - p = curlx_dyn_ptr(&ftpc->pp.recvbuf); - if((len < 4) || (p[0] != '2' && p[0] != '3')) { - infof(data, "Server did not accept auth data"); - ret = AUTH_ERROR; - break; - } - } - - _gssresp.value = NULL; /* make sure it is initialized */ - _gssresp.length = 0; - p += 4; /* over '789 ' */ - p = strstr(p, "ADAT="); - if(p) { - unsigned char *outptr; - size_t outlen; - result = curlx_base64_decode(p + 5, &outptr, &outlen); - if(result) { - failf(data, "base64-decoding: %s", curl_easy_strerror(result)); - ret = AUTH_CONTINUE; - break; - } - _gssresp.value = outptr; - _gssresp.length = outlen; - } - - gssresp = &_gssresp; - } - } while(maj == GSS_S_CONTINUE_NEEDED); - - gss_release_name(&min, &gssname); - gss_release_buffer(&min, &output_buffer); - - if(gssresp) - free(_gssresp.value); - - if(ret == AUTH_OK || service == srv_host) - break; - - service = srv_host; - } - return ret; -} - -static void krb5_end(void *app_data) -{ - OM_uint32 min; - gss_ctx_id_t *context = app_data; - if(*context != GSS_C_NO_CONTEXT) { - OM_uint32 maj = Curl_gss_delete_sec_context(&min, context, - GSS_C_NO_BUFFER); - (void)maj; - DEBUGASSERT(maj == GSS_S_COMPLETE); - } -} - -static const struct Curl_sec_client_mech Curl_krb5_client_mech = { - "GSSAPI", - sizeof(gss_ctx_id_t), - krb5_init, - krb5_auth, - krb5_end, - krb5_check_prot, - - krb5_encode, - krb5_decode -}; - -static const struct { - unsigned char level; - const char *name; -} level_names[] = { - { PROT_CLEAR, "clear" }, - { PROT_SAFE, "safe" }, - { PROT_CONFIDENTIAL, "confidential" }, - { PROT_PRIVATE, "private" } -}; - -static unsigned char name_to_level(const char *name) -{ - int i; - for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++) - if(curl_strequal(name, level_names[i].name)) - return level_names[i].level; - return PROT_NONE; -} - -/* Convert a protocol |level| to its char representation. - We take an int to catch programming mistakes. */ -static char level_to_char(int level) -{ - switch(level) { - case PROT_CLEAR: - return 'C'; - case PROT_SAFE: - return 'S'; - case PROT_CONFIDENTIAL: - return 'E'; - case PROT_PRIVATE: - return 'P'; - case PROT_CMD: - default: - /* Those 2 cases should not be reached! */ - break; - } - DEBUGASSERT(0); - /* Default to the most secure alternative. */ - return 'P'; -} - -/* Send an FTP command defined by |message| and the optional arguments. The - function returns the ftp_code. If an error occurs, -1 is returned. */ -static int ftp_send_command(struct Curl_easy *data, const char *message, ...) - CURL_PRINTF(2, 3); - -static int ftp_send_command(struct Curl_easy *data, const char *message, ...) -{ - int ftp_code; - ssize_t nread = 0; - va_list args; - char print_buffer[50]; - - va_start(args, message); - mvsnprintf(print_buffer, sizeof(print_buffer), message, args); - va_end(args); - - if(ftpsend(data, data->conn, print_buffer)) { - ftp_code = -1; - } - else { - if(Curl_GetFTPResponse(data, &nread, &ftp_code)) - ftp_code = -1; - } - - (void)nread; - return ftp_code; -} - -/* Read |len| from the socket |fd| and store it in |to|. Return a CURLcode - saying whether an error occurred or CURLE_OK if |len| was read. */ -static CURLcode -socket_read(struct Curl_easy *data, int sockindex, void *to, size_t len) -{ - char *to_p = to; - CURLcode result; - size_t nread = 0; - - while(len > 0) { - result = Curl_conn_recv(data, sockindex, to_p, len, &nread); - if(result == CURLE_AGAIN) - continue; - if(result) - return result; - if(nread > len) - return CURLE_RECV_ERROR; - len -= nread; - to_p += nread; - } - return CURLE_OK; -} - - -/* Write |len| bytes from the buffer |to| to the socket |fd|. Return a - CURLcode saying whether an error occurred or CURLE_OK if |len| was - written. */ -static CURLcode -socket_write(struct Curl_easy *data, int sockindex, const void *to, - size_t len) -{ - const char *to_p = to; - CURLcode result; - size_t written; - - while(len > 0) { - result = Curl_conn_send(data, sockindex, to_p, len, FALSE, &written); - if(!result && written > 0) { - len -= written; - to_p += written; - } - else { - if(result == CURLE_AGAIN) - continue; - return result; - } - } - return CURLE_OK; -} - -static CURLcode krb5_read_data(struct Curl_easy *data, int sockindex, - struct krb5buffer *buf) -{ - struct connectdata *conn = data->conn; - int len; - CURLcode result; - int nread; - - result = socket_read(data, sockindex, &len, sizeof(len)); - if(result) - return result; - - if(len) { - len = (int)ntohl((uint32_t)len); - if(len > CURL_MAX_INPUT_LENGTH) - return CURLE_TOO_LARGE; - - curlx_dyn_reset(&buf->buf); - } - else - return CURLE_RECV_ERROR; - - do { - char buffer[1024]; - nread = CURLMIN(len, (int)sizeof(buffer)); - result = socket_read(data, sockindex, buffer, (size_t)nread); - if(result) - return result; - result = curlx_dyn_addn(&buf->buf, buffer, nread); - if(result) - return result; - len -= nread; - } while(len); - /* this decodes the dynbuf *in place* */ - nread = conn->mech->decode(conn->app_data, - curlx_dyn_ptr(&buf->buf), - len, conn->data_prot, conn); - if(nread < 0) - return CURLE_RECV_ERROR; - curlx_dyn_setlen(&buf->buf, nread); - buf->index = 0; - return CURLE_OK; -} - -static size_t -buffer_read(struct krb5buffer *buf, void *data, size_t len) -{ - size_t size = curlx_dyn_len(&buf->buf); - if(size - buf->index < len) - len = size - buf->index; - memcpy(data, curlx_dyn_ptr(&buf->buf) + buf->index, len); - buf->index += len; - return len; -} - -/* Matches Curl_recv signature */ -static CURLcode sec_recv(struct Curl_easy *data, int sockindex, - char *buffer, size_t len, size_t *pnread) -{ - struct connectdata *conn = data->conn; - CURLcode result = CURLE_OK; - size_t bytes_read; - - /* Handle clear text response. */ - if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) - return Curl_conn_recv(data, sockindex, buffer, len, pnread); - - if(conn->in_buffer.eof_flag) { - conn->in_buffer.eof_flag = 0; - *pnread = 0; - return CURLE_OK; - } - - bytes_read = buffer_read(&conn->in_buffer, buffer, len); - buffer += bytes_read; - len -= bytes_read; - *pnread += bytes_read; - - while(len > 0) { - result = krb5_read_data(data, sockindex, &conn->in_buffer); - if(result) - return result; - if(curlx_dyn_len(&conn->in_buffer.buf) == 0) { - if(*pnread > 0) - conn->in_buffer.eof_flag = 1; - return result; - } - bytes_read = buffer_read(&conn->in_buffer, buffer, len); - buffer += bytes_read; - len -= bytes_read; - *pnread += bytes_read; - } - return result; -} - -/* Send |length| bytes from |from| to the |sockindex| socket taking care of - encoding and negotiating with the server. |from| can be NULL. */ -static void do_sec_send(struct Curl_easy *data, struct connectdata *conn, - int sockindex, const char *from, size_t length) -{ - int bytes, htonl_bytes; /* 32-bit integers for htonl */ - char *buffer = NULL; - char *cmd_buffer; - size_t cmd_size = 0; - CURLcode error; - enum protection_level prot_level = conn->data_prot; - bool iscmd = (prot_level == PROT_CMD); - - DEBUGASSERT(prot_level > PROT_NONE && prot_level < PROT_LAST); - - if(iscmd) { - if(!strncmp(from, "PASS ", 5) || !strncmp(from, "ACCT ", 5)) - prot_level = PROT_PRIVATE; - else - prot_level = conn->command_prot; - } - bytes = conn->mech->encode(conn->app_data, from, (int)length, - (int)prot_level, (void **)&buffer); - if(!buffer || bytes <= 0) - return; /* error */ - - if(iscmd) { - error = curlx_base64_encode(buffer, curlx_sitouz(bytes), - &cmd_buffer, &cmd_size); - if(error) { - free(buffer); - return; /* error */ - } - if(cmd_size > 0) { - static const char *enc = "ENC "; - static const char *mic = "MIC "; - if(prot_level == PROT_PRIVATE) - socket_write(data, sockindex, enc, 4); - else - socket_write(data, sockindex, mic, 4); - - socket_write(data, sockindex, cmd_buffer, cmd_size); - socket_write(data, sockindex, "\r\n", 2); - infof(data, "Send: %s%s", prot_level == PROT_PRIVATE ? enc : mic, - cmd_buffer); - free(cmd_buffer); - } - } - else { - htonl_bytes = (int)htonl((OM_uint32)bytes); - socket_write(data, sockindex, &htonl_bytes, sizeof(htonl_bytes)); - socket_write(data, sockindex, buffer, curlx_sitouz(bytes)); - } - free(buffer); -} - -static CURLcode sec_write(struct Curl_easy *data, int sockindex, - const char *buffer, size_t length, - size_t *pnwritten) -{ - struct connectdata *conn = data->conn; - size_t len = conn->buffer_size; - - *pnwritten = 0; - if(len <= 0) - len = length; - while(length) { - if(length < len) - len = length; - - /* WTF: this ignores all errors writing to the socket */ - do_sec_send(data, conn, sockindex, buffer, len); - length -= len; - buffer += len; - *pnwritten += len; - } - return CURLE_OK; -} - -/* Matches Curl_send signature */ -static CURLcode sec_send(struct Curl_easy *data, int sockindex, - const void *buffer, size_t len, bool eos, - size_t *pnwritten) -{ - (void)eos; - return sec_write(data, sockindex, buffer, len, pnwritten); -} - -int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, - char *buffer, enum protection_level level) -{ - /* decoded_len should be size_t or ssize_t but conn->mech->decode returns an - int */ - int decoded_len; - char *buf; - int ret_code = 0; - size_t decoded_sz = 0; - CURLcode error; - - (void)data; - - if(!conn->mech) - /* not initialized, return error */ - return -1; - - DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); - - error = curlx_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz); - if(error || decoded_sz == 0) - return -1; - - if(decoded_sz > (size_t)INT_MAX) { - free(buf); - return -1; - } - decoded_len = curlx_uztosi(decoded_sz); - - decoded_len = conn->mech->decode(conn->app_data, buf, decoded_len, - (int)level, conn); - if(decoded_len <= 0) { - free(buf); - return -1; - } - - { - buf[decoded_len] = '\n'; - Curl_debug(data, CURLINFO_HEADER_IN, buf, decoded_len + 1); - } - - buf[decoded_len] = '\0'; - if(decoded_len <= 3) - /* suspiciously short */ - return 0; - - if(buf[3] != '-') - ret_code = atoi(buf); - - if(buf[decoded_len - 1] == '\n') - buf[decoded_len - 1] = '\0'; - strcpy(buffer, buf); - free(buf); - return ret_code; -} - -static int sec_set_protection_level(struct Curl_easy *data) -{ - int code; - struct connectdata *conn = data->conn; - unsigned char level = conn->request_data_prot; - - DEBUGASSERT(level > PROT_NONE && level < PROT_LAST); - - if(!conn->sec_complete) { - infof(data, "Trying to change the protection level after the" - " completion of the data exchange."); - return -1; - } - - /* Bail out if we try to set up the same level */ - if(conn->data_prot == level) - return 0; - - if(level) { - char *pbsz; - unsigned int buffer_size = 1 << 20; /* 1048576 */ - struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN); - char *line; - - if(!ftpc) - return -2; - - code = ftp_send_command(data, "PBSZ %u", buffer_size); - if(code < 0) - return -1; - - if(code/100 != 2) { - failf(data, "Failed to set the protection's buffer size."); - return -1; - } - conn->buffer_size = buffer_size; - - line = curlx_dyn_ptr(&ftpc->pp.recvbuf); - pbsz = strstr(line, "PBSZ="); - if(pbsz) { - /* stick to default value if the check fails */ - if(ISDIGIT(pbsz[5])) - buffer_size = (unsigned int)atoi(&pbsz[5]); - if(buffer_size < conn->buffer_size) - conn->buffer_size = buffer_size; - } - } - - /* Now try to negotiate the protection level. */ - code = ftp_send_command(data, "PROT %c", level_to_char(level)); - - if(code < 0) - return -1; - - if(code/100 != 2) { - failf(data, "Failed to set the protection level."); - return -1; - } - - conn->data_prot = level; - if(level == PROT_PRIVATE) - conn->command_prot = level; - - return 0; -} - -int -Curl_sec_request_prot(struct connectdata *conn, const char *level) -{ - unsigned char l = name_to_level(level); - if(l == PROT_NONE) - return -1; - DEBUGASSERT(l > PROT_NONE && l < PROT_LAST); - conn->request_data_prot = l; - return 0; -} - -static CURLcode choose_mech(struct Curl_easy *data, struct connectdata *conn) -{ - int ret; - void *tmp_allocation; - const struct Curl_sec_client_mech *mech = &Curl_krb5_client_mech; - - tmp_allocation = realloc(conn->app_data, mech->size); - if(!tmp_allocation) { - failf(data, "Failed realloc of size %zu", mech->size); - mech = NULL; - return CURLE_OUT_OF_MEMORY; - } - conn->app_data = tmp_allocation; - - if(mech->init) { - ret = mech->init(conn->app_data); - if(ret) { - infof(data, "Failed initialization for %s. Skipping it.", - mech->name); - return CURLE_FAILED_INIT; - } - } - - infof(data, "Trying mechanism %s...", mech->name); - ret = ftp_send_command(data, "AUTH %s", mech->name); - if(ret < 0) - return CURLE_COULDNT_CONNECT; - - if(ret/100 != 3) { - switch(ret) { - case 504: - infof(data, "Mechanism %s is not supported by the server (server " - "returned ftp code: 504).", mech->name); - break; - case 534: - infof(data, "Mechanism %s was rejected by the server (server returned " - "ftp code: 534).", mech->name); - break; - default: - if(ret/100 == 5) { - infof(data, "server does not support the security extensions"); - return CURLE_USE_SSL_FAILED; - } - break; - } - return CURLE_LOGIN_DENIED; - } - - /* Authenticate */ - ret = mech->auth(conn->app_data, data, conn); - - if(ret != AUTH_CONTINUE) { - if(ret != AUTH_OK) { - /* Mechanism has dumped the error to stderr, do not error here. */ - return CURLE_USE_SSL_FAILED; - } - DEBUGASSERT(ret == AUTH_OK); - - conn->mech = mech; - conn->sec_complete = 1; - conn->recv[FIRSTSOCKET] = sec_recv; - conn->send[FIRSTSOCKET] = sec_send; - conn->recv[SECONDARYSOCKET] = sec_recv; - conn->send[SECONDARYSOCKET] = sec_send; - conn->command_prot = PROT_SAFE; - /* Set the requested protection level */ - /* BLOCKING */ - (void)sec_set_protection_level(data); - } - - return CURLE_OK; -} - -CURLcode -Curl_sec_login(struct Curl_easy *data, struct connectdata *conn) -{ - return choose_mech(data, conn); -} - -void -Curl_sec_conn_init(struct connectdata *conn) -{ - curlx_dyn_init(&conn->in_buffer.buf, CURL_MAX_INPUT_LENGTH); - conn->in_buffer.index = 0; - conn->in_buffer.eof_flag = 0; -} - -void -Curl_sec_conn_destroy(struct connectdata *conn) -{ - if(conn->mech && conn->mech->end) - conn->mech->end(conn->app_data); - Curl_safefree(conn->app_data); - curlx_dyn_free(&conn->in_buffer.buf); - conn->in_buffer.index = 0; - conn->in_buffer.eof_flag = 0; - conn->sec_complete = 0; - conn->data_prot = PROT_CLEAR; - conn->mech = NULL; -} - -#if defined(__GNUC__) && defined(__APPLE__) -#pragma GCC diagnostic pop -#endif - -#endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */ diff --git a/lib/ldap.c b/lib/ldap.c index c66a56d7bb..f8e7ff6a70 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -21,12 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" +#include "urldata.h" +#include "curl_ldap.h" #if !defined(CURL_DISABLE_LDAP) && !defined(USE_OPENLDAP) -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -42,61 +43,46 @@ * 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 -# ifndef LDAP_VENDOR_NAME -# error Your Platform SDK is NOT sufficient for LDAP support! \ - Update your Platform SDK, or disable LDAP support! -# else +# 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 -# endif #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 #include "cfilters.h" #include "sendf.h" +#include "curl_trc.h" #include "escape.h" #include "progress.h" #include "transfer.h" #include "curlx/strparse.h" -#include "curl_ldap.h" +#include "bufref.h" #include "curlx/multibyte.h" #include "curlx/base64.h" #include "connect.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" + +#ifdef USE_WIN32_LDAP +#define FREE_ON_WINLDAP(x) curlx_free(x) +#define curl_ldap_num_t ULONG +#else +#define FREE_ON_WINLDAP(x) do {} while(0) +#define curl_ldap_num_t int +#endif #ifndef HAVE_LDAP_URL_PARSE @@ -119,32 +105,34 @@ struct ldap_urldesc { char *lud_filter; #endif char **lud_exts; - size_t lud_attrs_dups; /* how many were dup'ed, this field is not in the - "real" struct so can only be used in code - without HAVE_LDAP_URL_PARSE defined */ + size_t lud_attrs_dups; /* how many were dup'ed, this field is not in the + "real" struct so can only be used in code + without HAVE_LDAP_URL_PARSE defined */ }; #undef LDAPURLDesc #define LDAPURLDesc struct ldap_urldesc -static int _ldap_url_parse(struct Curl_easy *data, - const struct connectdata *conn, - LDAPURLDesc **ludp); -static void _ldap_free_urldesc(LDAPURLDesc *ludp); +static curl_ldap_num_t ldap_url_parse_low(struct Curl_easy *data, + const struct connectdata *conn, + LDAPURLDesc **ludpp); +static void ldap_free_urldesc_low(LDAPURLDesc *ludp); #undef ldap_free_urldesc -#define ldap_free_urldesc _ldap_free_urldesc -#endif +#define ldap_free_urldesc ldap_free_urldesc_low + +#endif /* !HAVE_LDAP_URL_PARSE */ #ifdef DEBUG_LDAP - #define LDAP_TRACE(x) do { \ - _ldap_trace("%u: ", __LINE__); \ - _ldap_trace x; \ - } while(0) +#define LDAP_TRACE(x) \ + do { \ + ldap_trace_low("%u: ", __LINE__); \ + ldap_trace_low x; \ + } while(0) - static void _ldap_trace(const char *fmt, ...) CURL_PRINTF(1, 2); +static void ldap_trace_low(const char *fmt, ...) CURL_PRINTF(1, 2); #else - #define LDAP_TRACE(x) Curl_nop_stmt +#define LDAP_TRACE(x) Curl_nop_stmt #endif #if defined(USE_WIN32_LDAP) && defined(ldap_err2string) @@ -162,77 +150,15 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp); #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_NONE /* 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 -static int ldap_win_bind_auth(LDAP *server, const char *user, - const char *passwd, unsigned long authflags) +static ULONG ldap_win_bind_auth(LDAP *server, const char *user, + const char *passwd, unsigned long authflags) { ULONG method = 0; SEC_WINNT_AUTH_IDENTITY cred; - int rc = LDAP_AUTH_METHOD_NOT_SUPPORTED; + ULONG rc = LDAP_AUTH_METHOD_NOT_SUPPORTED; memset(&cred, 0, sizeof(cred)); @@ -260,25 +186,27 @@ static int ldap_win_bind_auth(LDAP *server, const char *user, if(method && user && passwd) { CURLcode res = Curl_create_sspi_identity(user, passwd, &cred); - rc = (int)res; - if(!rc) { - rc = (int)ldap_bind_s(server, NULL, (TCHAR *)&cred, method); + if(!res) { + rc = ldap_bind_s(server, NULL, (TCHAR *)&cred, method); Curl_sspi_free_identity(&cred); } + else { + rc = LDAP_NO_MEMORY; + } } else { /* proceed with current user credentials */ method = LDAP_AUTH_NEGOTIATE; - rc = (int)ldap_bind_s(server, NULL, NULL, method); + rc = ldap_bind_s(server, NULL, NULL, method); } return rc; } #endif /* USE_WINDOWS_SSPI */ -static int ldap_win_bind(struct Curl_easy *data, LDAP *server, - const char *user, const char *passwd) +static ULONG ldap_win_bind(struct Curl_easy *data, LDAP *server, + const char *user, const char *passwd) { - int rc = LDAP_INVALID_CREDENTIALS; + ULONG rc = LDAP_INVALID_CREDENTIALS; PTCHAR inuser = NULL; PTCHAR inpass = NULL; @@ -287,14 +215,14 @@ static int ldap_win_bind(struct Curl_easy *data, LDAP *server, inuser = curlx_convert_UTF8_to_tchar(user); inpass = curlx_convert_UTF8_to_tchar(passwd); - rc = (int)ldap_simple_bind_s(server, inuser, inpass); + rc = ldap_simple_bind_s(server, inuser, inpass); - curlx_unicodefree(inuser); - curlx_unicodefree(inpass); + curlx_free(inuser); + curlx_free(inpass); } #ifdef USE_WINDOWS_SSPI else { - rc = (int)ldap_win_bind_auth(server, user, passwd, data->set.httpauth); + rc = ldap_win_bind_auth(server, user, passwd, data->set.httpauth); } #endif @@ -302,19 +230,10 @@ static int ldap_win_bind(struct Curl_easy *data, LDAP *server, } #endif /* USE_WIN32_LDAP */ -#ifdef USE_WIN32_LDAP -#define FREE_ON_WINLDAP(x) curlx_unicodefree(x) -#define curl_ldap_num_t ULONG -#else -#define FREE_ON_WINLDAP(x) -#define curl_ldap_num_t int -#endif - - static CURLcode ldap_do(struct Curl_easy *data, bool *done) { CURLcode result = CURLE_OK; - int rc = 0; + curl_ldap_num_t rc = LDAP_SUCCESS; LDAP *server = NULL; LDAPURLDesc *ludp = NULL; LDAPMessage *ldapmsg = NULL; @@ -341,15 +260,15 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) *done = TRUE; /* unconditionally */ infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d", LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION); - infof(data, "LDAP local: %s", data->state.url); + infof(data, "LDAP local: %s", Curl_bufref_ptr(&data->state.url)); #ifdef HAVE_LDAP_URL_PARSE - rc = ldap_url_parse(data->state.url, &ludp); + rc = ldap_url_parse(Curl_bufref_ptr(&data->state.url), &ludp); #else - rc = _ldap_url_parse(data, conn, &ludp); + rc = ldap_url_parse_low(data, conn, &ludp); #endif if(rc) { - failf(data, "Bad LDAP URL: %s", ldap_err2string((curl_ldap_num_t)rc)); + failf(data, "Bad LDAP URL: %s", ldap_err2string(rc)); result = CURLE_URL_MALFORMAT; goto quit; } @@ -380,25 +299,37 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) passwd = conn->passwd; } -#ifdef LDAP_OPT_NETWORK_TIMEOUT - ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &ldap_timeout); +#ifdef USE_WIN32_LDAP + if(ldap_ssl) + server = ldap_sslinit(host, (curl_ldap_num_t)ipquad.remote_port, 1); + else #endif - ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); + server = ldap_init(host, (curl_ldap_num_t)ipquad.remote_port); + if(!server) { + failf(data, "LDAP: cannot setup connect to %s:%u", + conn->host.dispname, ipquad.remote_port); + result = CURLE_COULDNT_CONNECT; + goto quit; + } + +#ifdef LDAP_OPT_NETWORK_TIMEOUT + ldap_set_option(server, LDAP_OPT_NETWORK_TIMEOUT, &ldap_timeout); +#endif + ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); if(ldap_ssl) { #ifdef HAVE_LDAP_SSL #ifdef USE_WIN32_LDAP /* Win32 LDAP SDK does not support insecure mode without CA! */ - server = ldap_sslinit(host, (curl_ldap_num_t)ipquad.remote_port, 1); ldap_set_option(server, LDAP_OPT_SSL, LDAP_OPT_ON); -#else +#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. */ - if((data->set.ssl.cert_type) && - (!curl_strequal(data->set.ssl.cert_type, "PEM"))) { + if(data->set.ssl.cert_type && + !curl_strequal(data->set.ssl.cert_type, "PEM")) { failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type"); result = CURLE_SSL_CERTPROBLEM; goto quit; @@ -409,10 +340,10 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } infof(data, "LDAP local: using PEM CA cert: %s", ldap_ca); - rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_ca); + rc = ldap_set_option(server, LDAP_OPT_X_TLS_CACERTFILE, ldap_ca); if(rc != LDAP_SUCCESS) { failf(data, "LDAP local: ERROR setting PEM CA cert: %s", - ldap_err2string(rc)); + ldap_err2string(rc)); result = CURLE_SSL_CERTPROBLEM; goto quit; } @@ -421,71 +352,46 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) else ldap_option = LDAP_OPT_X_TLS_NEVER; - rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &ldap_option); + rc = ldap_set_option(server, LDAP_OPT_X_TLS_REQUIRE_CERT, &ldap_option); if(rc != LDAP_SUCCESS) { failf(data, "LDAP local: ERROR setting cert verify mode: %s", - ldap_err2string(rc)); + ldap_err2string(rc)); result = CURLE_SSL_CERTPROBLEM; goto quit; } - server = ldap_init(host, ipquad.remote_port); - if(!server) { - failf(data, "LDAP local: Cannot connect to %s:%u", - conn->host.dispname, ipquad.remote_port); - result = CURLE_COULDNT_CONNECT; - goto quit; - } ldap_option = LDAP_OPT_X_TLS_HARD; rc = ldap_set_option(server, LDAP_OPT_X_TLS, &ldap_option); if(rc != LDAP_SUCCESS) { failf(data, "LDAP local: ERROR setting SSL/TLS mode: %s", - ldap_err2string(rc)); + ldap_err2string(rc)); result = CURLE_SSL_CERTPROBLEM; goto quit; } -/* - rc = ldap_start_tls_s(server, NULL, NULL); - if(rc != LDAP_SUCCESS) { - failf(data, "LDAP local: ERROR starting SSL/TLS mode: %s", - ldap_err2string(rc)); - result = CURLE_SSL_CERTPROBLEM; - goto quit; - } -*/ -#else +#else /* !LDAP_OPT_X_TLS */ (void)ldap_option; (void)ldap_ca; /* we should probably never come up to here since configure should check in first place if we can support LDAP SSL/TLS */ failf(data, "LDAP local: SSL/TLS not supported with this version " - "of the OpenLDAP toolkit\n"); + "of the OpenLDAP toolkit"); result = CURLE_SSL_CERTPROBLEM; goto quit; -#endif -#endif -#endif /* CURL_LDAP_USE_SSL */ +#endif /* LDAP_OPT_X_TLS */ +#endif /* USE_WIN32_LDAP */ +#endif /* HAVE_LDAP_SSL */ } else if(data->set.use_ssl > CURLUSESSL_TRY) { failf(data, "LDAP local: explicit TLS not supported"); result = CURLE_NOT_BUILT_IN; goto quit; } - else { - server = ldap_init(host, (curl_ldap_num_t)ipquad.remote_port); - if(!server) { - failf(data, "LDAP local: Cannot connect to %s:%u", - conn->host.dispname, ipquad.remote_port); - result = CURLE_COULDNT_CONNECT; - goto quit; - } - } + #ifdef USE_WIN32_LDAP - ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); rc = ldap_win_bind(data, server, user, passwd); #else rc = ldap_simple_bind_s(server, user, passwd); #endif - if(!ldap_ssl && rc) { + if(!ldap_ssl && rc != LDAP_SUCCESS) { ldap_proto = LDAP_VERSION2; ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); #ifdef USE_WIN32_LDAP @@ -494,10 +400,10 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) rc = ldap_simple_bind_s(server, user, passwd); #endif } - if(rc) { + if(rc != LDAP_SUCCESS) { #ifdef USE_WIN32_LDAP failf(data, "LDAP local: bind via ldap_win_bind %s", - ldap_err2string((ULONG)rc)); + ldap_err2string(rc)); #else failf(data, "LDAP local: bind via ldap_simple_bind_s %s", ldap_err2string(rc)); @@ -506,13 +412,13 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - Curl_pgrsSetDownloadCounter(data, 0); - rc = (int)ldap_search_s(server, ludp->lud_dn, - (curl_ldap_num_t)ludp->lud_scope, - ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg); + Curl_pgrsReset(data); + rc = ldap_search_s(server, ludp->lud_dn, + ludp->lud_scope, + ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg); - if(rc && rc != LDAP_SIZELIMIT_EXCEEDED) { - failf(data, "LDAP remote: %s", ldap_err2string((curl_ldap_num_t)rc)); + if(rc != LDAP_SUCCESS && rc != LDAP_SIZELIMIT_EXCEEDED) { + failf(data, "LDAP remote: %s", ldap_err2string(rc)); result = CURLE_LDAP_SEARCH_FAILED; goto quit; } @@ -532,7 +438,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) /* Get the DN and write it to the client */ { char *name; - size_t name_len; + size_t name_len = 0; #ifdef USE_WIN32_LDAP TCHAR *dn = ldap_get_dn(server, entryIterator); name = curlx_convert_tchar_to_UTF8(dn); @@ -546,32 +452,20 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #else char *dn = name = ldap_get_dn(server, entryIterator); #endif - name_len = strlen(name); - - result = Curl_client_write(data, CLIENTWRITE_BODY, "DN: ", 4); - if(result) { - FREE_ON_WINLDAP(name); - ldap_memfree(dn); - goto quit; + if(!name) + result = CURLE_FAILED_INIT; + else { + name_len = strlen(name); + result = Curl_client_write(data, CLIENTWRITE_BODY, "DN: ", 4); } - - result = Curl_client_write(data, CLIENTWRITE_BODY, name, name_len); - if(result) { - FREE_ON_WINLDAP(name); - ldap_memfree(dn); - goto quit; - } - - result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1); - if(result) { - FREE_ON_WINLDAP(name); - ldap_memfree(dn); - - goto quit; - } - + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, name, name_len); + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1); FREE_ON_WINLDAP(name); ldap_memfree(dn); + if(result) + goto quit; } /* Get the attributes and write them to the client */ @@ -632,24 +526,12 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) } if((attr_len > 7) && - (strcmp(";binary", attr + (attr_len - 7)) == 0)) { + curl_strequal(";binary", attr + (attr_len - 7))) { /* Binary attribute, encode to base64. */ - result = curlx_base64_encode(vals[i]->bv_val, vals[i]->bv_len, - &val_b64, &val_b64_sz); - if(result) { - ldap_value_free_len(vals); - FREE_ON_WINLDAP(attr); - ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - - goto quit; - } - - if(val_b64_sz > 0) { - result = Curl_client_write(data, CLIENTWRITE_BODY, val_b64, - val_b64_sz); - free(val_b64); + if(vals[i]->bv_len) { + result = curlx_base64_encode((uint8_t *)vals[i]->bv_val, + vals[i]->bv_len, + &val_b64, &val_b64_sz); if(result) { ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); @@ -659,6 +541,21 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } + + if(val_b64_sz > 0) { + result = Curl_client_write(data, CLIENTWRITE_BODY, val_b64, + val_b64_sz); + curlx_free(val_b64); + if(result) { + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); + if(ber) + ber_free(ber, 0); + + goto quit; + } + } } } else { @@ -726,7 +623,7 @@ quit: } #ifdef DEBUG_LDAP -static void _ldap_trace(const char *fmt, ...) +static void ldap_trace_low(const char *fmt, ...) { static int do_trace = -1; va_list args; @@ -741,10 +638,10 @@ static void _ldap_trace(const char *fmt, ...) return; va_start(args, fmt); - vfprintf(stderr, fmt, args); + curl_mvfprintf(stderr, fmt, args); va_end(args); } -#endif +#endif /* DEBUG_LDAP */ #ifndef HAVE_LDAP_URL_PARSE @@ -793,10 +690,11 @@ static size_t num_entries(const char *s) * * Defined in RFC4516 section 2. */ -static int _ldap_url_parse2(struct Curl_easy *data, - const struct connectdata *conn, LDAPURLDesc *ludp) +static curl_ldap_num_t ldap_url_parse2_low(struct Curl_easy *data, + const struct connectdata *conn, + LDAPURLDesc *ludp) { - int rc = LDAP_SUCCESS; + curl_ldap_num_t rc = LDAP_SUCCESS; char *p; char *path; char *q = NULL; @@ -814,15 +712,15 @@ static int _ldap_url_parse2(struct Curl_easy *data, ludp->lud_host = conn->host.name; /* Duplicate the path */ - p = path = strdup(data->state.up.path + 1); + p = path = curlx_strdup(data->state.up.path + 1); if(!path) return LDAP_NO_MEMORY; /* Duplicate the query if present */ if(data->state.up.query) { - q = query = strdup(data->state.up.query); + q = query = curlx_strdup(data->state.up.query); if(!query) { - free(path); + curlx_free(path); return LDAP_NO_MEMORY; } } @@ -848,7 +746,7 @@ static int _ldap_url_parse2(struct Curl_easy *data, ludp->lud_dn = curlx_convert_UTF8_to_tchar(unescaped); /* Free the unescaped string as we are done with it */ - free(unescaped); + curlx_free(unescaped); if(!ludp->lud_dn) { rc = LDAP_NO_MEMORY; @@ -875,9 +773,9 @@ static int _ldap_url_parse2(struct Curl_easy *data, /* Allocate our array (+1 for the NULL entry) */ #ifdef USE_WIN32_LDAP - ludp->lud_attrs = calloc(count + 1, sizeof(TCHAR *)); + ludp->lud_attrs = curlx_calloc(count + 1, sizeof(TCHAR *)); #else - ludp->lud_attrs = calloc(count + 1, sizeof(char *)); + ludp->lud_attrs = curlx_calloc(count + 1, sizeof(char *)); #endif if(!ludp->lud_attrs) { rc = LDAP_NO_MEMORY; @@ -895,8 +793,7 @@ static int _ldap_url_parse2(struct Curl_easy *data, LDAP_TRACE(("attr[%zu] '%.*s'\n", i, (int)out.len, out.str)); /* Unescape the attribute */ - result = Curl_urldecode(out.str, out.len, &unescaped, NULL, - REJECT_ZERO); + result = Curl_urldecode(out.str, out.len, &unescaped, NULL, REJECT_ZERO); if(result) { rc = LDAP_NO_MEMORY; goto quit; @@ -907,7 +804,7 @@ static int _ldap_url_parse2(struct Curl_easy *data, ludp->lud_attrs[i] = curlx_convert_UTF8_to_tchar(unescaped); /* Free the unescaped string as we are done with it */ - free(unescaped); + curlx_free(unescaped); if(!ludp->lud_attrs[i]) { rc = LDAP_NO_MEMORY; @@ -971,7 +868,7 @@ static int _ldap_url_parse2(struct Curl_easy *data, ludp->lud_filter = curlx_convert_UTF8_to_tchar(unescaped); /* Free the unescaped string as we are done with it */ - free(unescaped); + curlx_free(unescaped); if(!ludp->lud_filter) { rc = LDAP_NO_MEMORY; @@ -991,63 +888,114 @@ static int _ldap_url_parse2(struct Curl_easy *data, } quit: - free(path); - free(query); + curlx_free(path); + curlx_free(query); return rc; } -static int _ldap_url_parse(struct Curl_easy *data, - const struct connectdata *conn, - LDAPURLDesc **ludpp) +static curl_ldap_num_t ldap_url_parse_low(struct Curl_easy *data, + const struct connectdata *conn, + LDAPURLDesc **ludpp) { - LDAPURLDesc *ludp = calloc(1, sizeof(*ludp)); - int rc; + LDAPURLDesc *ludp = curlx_calloc(1, sizeof(*ludp)); + curl_ldap_num_t rc; *ludpp = NULL; if(!ludp) return LDAP_NO_MEMORY; - rc = _ldap_url_parse2(data, conn, ludp); + rc = ldap_url_parse2_low(data, conn, ludp); if(rc != LDAP_SUCCESS) { - _ldap_free_urldesc(ludp); + ldap_free_urldesc_low(ludp); ludp = NULL; } *ludpp = ludp; return rc; } -static void _ldap_free_urldesc(LDAPURLDesc *ludp) +static void ldap_free_urldesc_low(LDAPURLDesc *ludp) { if(!ludp) return; -#ifdef USE_WIN32_LDAP - curlx_unicodefree(ludp->lud_dn); - curlx_unicodefree(ludp->lud_filter); -#else - free(ludp->lud_dn); - free(ludp->lud_filter); -#endif + curlx_free(ludp->lud_dn); + curlx_free(ludp->lud_filter); if(ludp->lud_attrs) { size_t i; for(i = 0; i < ludp->lud_attrs_dups; i++) { -#ifdef USE_WIN32_LDAP - curlx_unicodefree(ludp->lud_attrs[i]); -#else - free(ludp->lud_attrs[i]); -#endif + curlx_free(ludp->lud_attrs[i]); } - free(ludp->lud_attrs); + curlx_free(ludp->lud_attrs); } - free(ludp); + curlx_free(ludp); } -#endif /* !HAVE_LDAP_URL_PARSE */ +#endif /* !HAVE_LDAP_URL_PARSE */ -#if defined(__GNUC__) && defined(__APPLE__) +void Curl_ldap_version(char *buf, size_t bufsz) +{ +#ifdef USE_WIN32_LDAP + curl_msnprintf(buf, bufsz, "WinLDAP"); +#else +#ifdef LDAP_OPT_X_TLS_PASSPHRASE + static const char *flavor = "/Apple"; +#else + static const char *flavor = ""; +#endif + LDAPAPIInfo api; + api.ldapai_info_version = LDAP_API_INFO_VERSION; + + /* Comparing against 0, as different platforms + disagree on the success define name */ + if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) == 0) { + 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)) + - patch) / 100; +#ifdef __OS400__ + 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 /* 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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif -#endif /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */ +#endif /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */ diff --git a/lib/libcurl.def b/lib/libcurl.def index d2f5d8318f..803f372041 100644 --- a/lib/libcurl.def +++ b/lib/libcurl.def @@ -57,6 +57,8 @@ curl_multi_get_handles curl_multi_get_offt curl_multi_info_read curl_multi_init +curl_multi_notify_disable +curl_multi_notify_enable curl_multi_perform curl_multi_poll curl_multi_remove_handle diff --git a/lib/libcurl.rc b/lib/libcurl.rc index 1ceb4691fb..81cd8d71b4 100644 --- a/lib/libcurl.rc +++ b/lib/libcurl.rc @@ -32,7 +32,7 @@ VS_VERSION_INFO VERSIONINFO FILEVERSION RC_VERSION PRODUCTVERSION RC_VERSION FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#if defined(DEBUGBUILD) || defined(UNITTESTS) || defined(CURLDEBUG) || defined(_DEBUG) +#if defined(DEBUGBUILD) || defined(UNITTESTS) || defined(_DEBUG) FILEFLAGS VS_FF_DEBUG #else FILEFLAGS 0L diff --git a/lib/llist.c b/lib/llist.c index c9a7d4a8a7..6543b7b922 100644 --- a/lib/llist.c +++ b/lib/llist.c @@ -21,16 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "llist.h" -#include "curl_memory.h" - -/* this must be the last include file */ -#include "memdebug.h" #ifdef DEBUGBUILD #define LLISTINIT 0x100cc001 /* random pattern */ @@ -49,8 +42,7 @@ static struct Curl_llist_node *verifynode(struct Curl_llist_node *n) /* * @unittest: 1300 */ -void -Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) +void Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) { l->_size = 0; l->_dtor = dtor; @@ -72,11 +64,10 @@ Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) * * @unittest: 1300 */ -void -Curl_llist_insert_next(struct Curl_llist *list, - struct Curl_llist_node *e, /* may be NULL */ - const void *p, - struct Curl_llist_node *ne) +void Curl_llist_insert_next(struct Curl_llist *list, + struct Curl_llist_node *e, /* may be NULL */ + const void *p, + struct Curl_llist_node *ne) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -123,9 +114,8 @@ Curl_llist_insert_next(struct Curl_llist *list, * * @unittest: 1300 */ -void -Curl_llist_append(struct Curl_llist *list, const void *p, - struct Curl_llist_node *ne) +void Curl_llist_append(struct Curl_llist *list, const void *p, + struct Curl_llist_node *ne) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -181,9 +171,8 @@ void *Curl_node_take_elem(struct Curl_llist_node *e) /* * @unittest: 1300 */ -UNITTEST void Curl_node_uremove(struct Curl_llist_node *, void *); -UNITTEST void -Curl_node_uremove(struct Curl_llist_node *e, void *user) +UNITTEST void Curl_node_uremove(struct Curl_llist_node *e, void *user); +UNITTEST void Curl_node_uremove(struct Curl_llist_node *e, void *user) { struct Curl_llist *list; void *ptr; @@ -204,8 +193,7 @@ void Curl_node_remove(struct Curl_llist_node *e) Curl_node_uremove(e, NULL); } -void -Curl_llist_destroy(struct Curl_llist *list, void *user) +void Curl_llist_destroy(struct Curl_llist *list, void *user) { if(list) { DEBUGASSERT(list->_init == LLISTINIT); @@ -226,7 +214,8 @@ struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list) #ifdef UNITTESTS /* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which might be NULL */ -struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list) +UNITTEST struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list); +UNITTEST struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -260,16 +249,15 @@ struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n) } #ifdef UNITTESTS - /* Curl_node_prev() returns the previous element in a list from a given Curl_llist_node */ -struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n) +UNITTEST struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n); +UNITTEST struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(n->_init == NODEINIT); return VERIFYNODE(n->_prev); } - #endif struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n) diff --git a/lib/llist.h b/lib/llist.h index 0a2bc9a62f..28e958d5c5 100644 --- a/lib/llist.h +++ b/lib/llist.h @@ -23,9 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include typedef void (*Curl_llist_dtor)(void *user, void *elem); @@ -52,22 +50,18 @@ 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 */ struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list); -/* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which - might be NULL */ -struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list); - /* Curl_llist_count() returns a size_t the number of nodes in the list */ size_t Curl_llist_count(struct Curl_llist *list); @@ -75,17 +69,13 @@ size_t Curl_llist_count(struct Curl_llist *list); 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 *); + * from a Curl_llist_node. Does NOT invoke a registered `dtor`. */ +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 */ struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n); -/* Curl_node_prev() returns the previous element in a list from a given - Curl_llist_node */ -struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n); - /* Curl_node_llist() return the list the node is in or NULL. */ struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n); diff --git a/lib/macos.c b/lib/macos.c index daf2ab94f6..334dbc7d1d 100644 --- a/lib/macos.c +++ b/lib/macos.c @@ -21,13 +21,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef CURL_MACOS_CALL_COPYPROXIES -#include - #include "macos.h" #include diff --git a/lib/macos.h b/lib/macos.h index 637860e80f..0a72a30272 100644 --- a/lib/macos.h +++ b/lib/macos.h @@ -23,17 +23,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef CURL_MACOS_CALL_COPYPROXIES - CURLcode Curl_macos_init(void); - #else - #define Curl_macos_init() CURLE_OK - #endif #endif /* HEADER_CURL_MACOS_H */ diff --git a/lib/md4.c b/lib/md4.c index b9c98d6c4a..0213483ad3 100644 --- a/lib/md4.c +++ b/lib/md4.c @@ -21,16 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef USE_CURL_NTLM_CORE -#include - -#include "strdup.h" #include "curl_md4.h" -#include "curlx/warnless.h" #ifdef USE_OPENSSL #include @@ -38,32 +33,53 @@ /* OpenSSL 3.0.0 marks the MD4 functions as deprecated */ #define OPENSSL_NO_MD4 #else -/* Cover also OPENSSL_NO_MD4 configured in openssl */ +/* Cover also OPENSSL_NO_MD4 configured in OpenSSL */ #include #endif #endif /* USE_OPENSSL */ #ifdef USE_WOLFSSL #include -#define VOID_MD4_INIT -#ifdef NO_MD4 -#define WOLFSSL_NO_MD4 #endif -#endif - -#ifdef USE_MBEDTLS -#include -#if MBEDTLS_VERSION_NUMBER < 0x03020000 - #error "mbedTLS 3.2.0 or later required" -#endif -#include -#endif /* USE_MBEDTLS */ /* When OpenSSL or wolfSSL is available, we use their MD4 functions. */ -#if defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4) -#include -#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) + +#if defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) #include + +/* Map directly to OpenSSL implementation */ +#define my_md4_ctx MD4_CTX +#define my_md4_init MD4_Init +#define my_md4_update MD4_Update +#define my_md4_final MD4_Final + +#elif defined(USE_WOLFSSL) && !defined(NO_MD4) +#include +#include + +#if LIBWOLFSSL_VERSION_HEX >= 0x05007006 +typedef wc_Md4 my_md4_ctx; +#else +typedef Md4 my_md4_ctx; +#endif + +static int my_md4_init(my_md4_ctx *ctx) +{ + wc_InitMd4(ctx); + return 1; +} + +static void my_md4_update(my_md4_ctx *ctx, + const void *input, unsigned long len) +{ + wc_Md4Update(ctx, input, (word32)len); +} + +static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) +{ + wc_Md4Final(ctx, digest); +} + #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \ @@ -72,60 +88,36 @@ (__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 -#elif(defined(USE_MBEDTLS) && defined(MBEDTLS_MD4_C)) -#include -#endif -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +typedef CC_MD4_CTX my_md4_ctx; - -#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) +static int my_md4_init(my_md4_ctx *ctx) { return CC_MD4_Init(ctx); } -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +static void my_md4_update(my_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 my_md4_final(unsigned char *digest, my_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; HCRYPTHASH hHash; }; -typedef struct md4_ctx MD4_CTX; +typedef struct md4_ctx my_md4_ctx; -static int MD4_Init(MD4_CTX *ctx) +static int my_md4_init(my_md4_ctx *ctx) { ctx->hCryptProv = 0; ctx->hHash = 0; @@ -143,23 +135,19 @@ static int MD4_Init(MD4_CTX *ctx) return 1; } -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +static void my_md4_update(my_md4_ctx *ctx, + const void *input, unsigned long len) { -#ifdef __MINGW32CE__ - CryptHashData(ctx->hHash, (BYTE *)CURL_UNCONST(data), - (unsigned int) size, 0); -#else - CryptHashData(ctx->hHash, (const BYTE *)data, (unsigned int) size, 0); -#endif + CryptHashData(ctx->hHash, (const BYTE *)input, (unsigned int)len, 0); } -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) +static void my_md4_final(unsigned char *digest, my_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); @@ -169,56 +157,25 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) } #elif defined(USE_GNUTLS) +#include -typedef struct md4_ctx MD4_CTX; +typedef struct md4_ctx my_md4_ctx; -static int MD4_Init(MD4_CTX *ctx) +static int my_md4_init(my_md4_ctx *ctx) { md4_init(ctx); return 1; } -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) +static void my_md4_update(my_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 my_md4_final(unsigned char *digest, my_md4_ctx *ctx) { - md4_digest(ctx, MD4_DIGEST_SIZE, result); -} - -#elif(defined(USE_MBEDTLS) && defined(MBEDTLS_MD4_C)) - -struct md4_ctx { - void *data; - unsigned long size; -}; -typedef struct md4_ctx MD4_CTX; - -static int MD4_Init(MD4_CTX *ctx) -{ - ctx->data = NULL; - ctx->size = 0; - return 1; -} - -static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size) -{ - if(!ctx->data) { - ctx->data = Curl_memdup(data, size); - if(ctx->data) - ctx->size = size; - } -} - -static void MD4_Final(unsigned char *result, MD4_CTX *ctx) -{ - if(ctx->data) { - mbedtls_md4(ctx->data, ctx->size, result); - Curl_safefree(ctx->data); - ctx->size = 0; - } + md4_digest(ctx, MD4_DIGEST_SIZE, digest); } #else @@ -229,7 +186,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 @@ -238,8 +195,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. @@ -247,34 +204,15 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) * There is ABSOLUTELY NO WARRANTY, express or implied. * * (This is a heavily cut-down "BSD license".) - * - * This differs from Colin Plumb's older public domain implementation in that - * no exactly 32-bit integer data type is required (any 32-bit or wider - * unsigned integer data type will do), there is no compile-time endianness - * configuration, and the function prototypes match OpenSSL's. No code from - * Colin Plumb's implementation has been reused; this comment merely compares - * the properties of the two independent implementations. - * - * The primary goals of this implementation are portability and ease of use. - * It is meant to be fast, but not as fast as possible. Some known - * optimizations are not included to reduce source code size and avoid - * compile-time configuration. */ -/* 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); +typedef struct md4_ctx my_md4_ctx; /* * The basic MD4 functions. @@ -290,44 +228,39 @@ 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) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); + (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_GET(n) \ - MD4_SET(n) +#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 /* * This processes one or more 64-byte data blocks, but does NOT update * the bit counters. There are no alignment requirements. */ -static const void *my_md4_body(MD4_CTX *ctx, - const void *data, unsigned long size) +static const void *my_md4_body(my_md4_ctx *ctx, + 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; @@ -335,14 +268,14 @@ 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; saved_c = c; saved_d = d; -/* Round 1 */ + /* Round 1 */ MD4_STEP(MD4_F, a, b, c, d, MD4_SET(0), 3) MD4_STEP(MD4_F, d, a, b, c, MD4_SET(1), 7) MD4_STEP(MD4_F, c, d, a, b, MD4_SET(2), 11) @@ -360,7 +293,7 @@ static const void *my_md4_body(MD4_CTX *ctx, MD4_STEP(MD4_F, c, d, a, b, MD4_SET(14), 11) MD4_STEP(MD4_F, b, c, d, a, MD4_SET(15), 19) -/* Round 2 */ + /* Round 2 */ MD4_STEP(MD4_G, a, b, c, d, MD4_GET(0) + 0x5a827999, 3) MD4_STEP(MD4_G, d, a, b, c, MD4_GET(4) + 0x5a827999, 5) MD4_STEP(MD4_G, c, d, a, b, MD4_GET(8) + 0x5a827999, 9) @@ -378,7 +311,7 @@ static const void *my_md4_body(MD4_CTX *ctx, MD4_STEP(MD4_G, c, d, a, b, MD4_GET(11) + 0x5a827999, 9) MD4_STEP(MD4_G, b, c, d, a, MD4_GET(15) + 0x5a827999, 13) -/* Round 3 */ + /* Round 3 */ MD4_STEP(MD4_H, a, b, c, d, MD4_GET(0) + 0x6ed9eba1, 3) MD4_STEP(MD4_H, d, a, b, c, MD4_GET(8) + 0x6ed9eba1, 9) MD4_STEP(MD4_H, c, d, a, b, MD4_GET(4) + 0x6ed9eba1, 11) @@ -412,7 +345,7 @@ static const void *my_md4_body(MD4_CTX *ctx, return ptr; } -static int MD4_Init(MD4_CTX *ctx) +static int my_md4_init(my_md4_ctx *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; @@ -421,45 +354,47 @@ 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 my_md4_update(my_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 my_md4_final(unsigned char *digest, my_md4_ctx *ctx) { unsigned long used, available; @@ -479,53 +414,47 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx) memset(&ctx->buffer[used], 0, available - 8); ctx->lo <<= 3; - ctx->buffer[56] = curlx_ultouc((ctx->lo)&0xff); - ctx->buffer[57] = curlx_ultouc((ctx->lo >> 8)&0xff); - ctx->buffer[58] = curlx_ultouc((ctx->lo >> 16)&0xff); - ctx->buffer[59] = curlx_ultouc((ctx->lo >> 24)&0xff); - ctx->buffer[60] = curlx_ultouc((ctx->hi)&0xff); - ctx->buffer[61] = curlx_ultouc((ctx->hi >> 8)&0xff); - ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16)&0xff); + ctx->buffer[56] = curlx_ultouc((ctx->lo) & 0xff); + ctx->buffer[57] = curlx_ultouc((ctx->lo >> 8) & 0xff); + ctx->buffer[58] = curlx_ultouc((ctx->lo >> 16) & 0xff); + ctx->buffer[59] = curlx_ultouc((ctx->lo >> 24) & 0xff); + ctx->buffer[60] = curlx_ultouc((ctx->hi) & 0xff); + ctx->buffer[61] = curlx_ultouc((ctx->hi >> 8) & 0xff); + ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16) & 0xff); ctx->buffer[63] = curlx_ultouc(ctx->hi >> 24); 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; - -#ifdef VOID_MD4_INIT - MD4_Init(&ctx); -#else - if(!MD4_Init(&ctx)) + my_md4_ctx ctx; + if(!my_md4_init(&ctx)) return CURLE_FAILED_INIT; -#endif - - MD4_Update(&ctx, input, curlx_uztoui(len)); - MD4_Final(output, &ctx); + my_md4_update(&ctx, input, curlx_uztoui(len)); + my_md4_final(output, &ctx); return CURLE_OK; } diff --git a/lib/md5.c b/lib/md5.c index 625670965a..4dd0d7c278 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -21,68 +21,32 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ !defined(CURL_DISABLE_DIGEST_AUTH) -#include -#include - #include "curl_md5.h" #include "curl_hmac.h" -#include "curlx/warnless.h" + +#ifdef USE_OPENSSL +#include +#endif + +#ifdef USE_WOLFSSL +#include +#endif #ifdef USE_MBEDTLS #include #if MBEDTLS_VERSION_NUMBER < 0x03020000 - #error "mbedTLS 3.2.0 or later required" +#error "mbedTLS 3.2.0 or later required" #endif -#endif /* USE_MBEDTLS */ - -#ifdef USE_OPENSSL - #include - #if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0) - #define USE_OPENSSL_MD5 - #endif -#endif - -#ifdef USE_WOLFSSL - #include - #ifndef NO_MD5 - #define USE_WOLFSSL_MD5 - #endif +#include #endif #ifdef USE_GNUTLS #include -#elif defined(USE_OPENSSL_MD5) -#include -#elif defined(USE_WOLFSSL_MD5) -#include -#elif defined(USE_MBEDTLS) -#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 -#endif - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#ifdef USE_GNUTLS typedef struct md5_ctx my_md5_ctx; @@ -93,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) @@ -104,8 +67,9 @@ 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) +#include typedef MD5_CTX my_md5_ctx; @@ -118,8 +82,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); } @@ -129,54 +92,64 @@ 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; +typedef wc_Md5 my_md5_ctx; static CURLcode my_md5_init(void *ctx) { - if(!wolfSSL_MD5_Init(ctx)) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} - -static void my_md5_update(void *ctx, - const unsigned char *input, - unsigned int len) -{ - (void)wolfSSL_MD5_Update(ctx, input, len); -} - -static void my_md5_final(unsigned char *digest, void *ctx) -{ - (void)wolfSSL_MD5_Final(digest, ctx); -} - -#elif defined(USE_MBEDTLS) - -typedef mbedtls_md5_context my_md5_ctx; - -static CURLcode my_md5_init(void *ctx) -{ - if(mbedtls_md5_starts(ctx)) + if(wc_InitMd5(ctx)) return CURLE_OUT_OF_MEMORY; return CURLE_OK; } static void my_md5_update(void *ctx, - const unsigned char *data, - unsigned int length) + const unsigned char *input, unsigned int len) { - (void)mbedtls_md5_update(ctx, data, length); + (void)wc_Md5Update(ctx, input, (word32)len); } static void my_md5_final(unsigned char *digest, void *ctx) { - (void)mbedtls_md5_finish(ctx, digest); + (void)wc_Md5Final(ctx, digest); } -#elif defined(AN_APPLE_OS) +#elif defined(USE_MBEDTLS) && \ + defined(PSA_WANT_ALG_MD5) && PSA_WANT_ALG_MD5 /* mbedTLS 4+ */ +#include + +typedef psa_hash_operation_t my_md5_ctx; + +static CURLcode my_md5_init(void *ctx) +{ + memset(ctx, 0, sizeof(my_md5_ctx)); + if(psa_hash_setup(ctx, PSA_ALG_MD5) != PSA_SUCCESS) + return CURLE_OUT_OF_MEMORY; + return CURLE_OK; +} + +static void my_md5_update(void *ctx, + const unsigned char *input, unsigned int len) +{ + (void)psa_hash_update(ctx, input, len); +} + +static void my_md5_final(unsigned char *digest, void *ctx) +{ + size_t actual_length; + (void)psa_hash_finish(ctx, digest, 16, &actual_length); +} + +#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 @@ -195,10 +168,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) @@ -207,6 +179,7 @@ static void my_md5_final(unsigned char *digest, void *ctx) } #elif defined(USE_WIN32_CRYPTO) +#include struct md5_ctx { HCRYPTPROV hCryptProv; @@ -231,15 +204,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; -#ifdef __MINGW32CE__ - CryptHashData(ctx->hHash, (BYTE *)CURL_UNCONST(input), inputLen, 0); -#else - CryptHashData(ctx->hHash, (const BYTE *)input, inputLen, 0); -#endif + CryptHashData(ctx->hHash, (const BYTE *)input, len, 0); } static void my_md5_final(unsigned char *digest, void *in) @@ -264,17 +232,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. @@ -282,41 +250,21 @@ static void my_md5_final(unsigned char *digest, void *in) * There is ABSOLUTELY NO WARRANTY, express or implied. * * (This is a heavily cut-down "BSD license".) - * - * This differs from Colin Plumb's older public domain implementation in that - * no exactly 32-bit integer data type is required (any 32-bit or wider - * unsigned integer data type will do), there is no compile-time endianness - * configuration, and the function prototypes match OpenSSL's. No code from - * Colin Plumb's implementation has been reused; this comment merely compares - * the properties of the two independent implementations. - * - * The primary goals of this implementation are portability and ease of use. - * It is meant to be fast, but not as fast as possible. Some known - * optimizations are not included to reduce source code size and avoid - * compile-time configuration. */ -/* 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)))) @@ -329,32 +277,27 @@ 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) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ - (a) += (b); + (a) += f(b, c, d) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); /* * 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_GET(n) \ - MD5_SET(n) +#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 /* @@ -365,7 +308,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; @@ -375,14 +318,14 @@ 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; saved_c = c; saved_d = d; -/* Round 1 */ + /* Round 1 */ MD5_STEP(MD5_F, a, b, c, d, MD5_SET(0), 0xd76aa478, 7) MD5_STEP(MD5_F, d, a, b, c, MD5_SET(1), 0xe8c7b756, 12) MD5_STEP(MD5_F, c, d, a, b, MD5_SET(2), 0x242070db, 17) @@ -400,7 +343,7 @@ static const void *my_md5_body(my_md5_ctx *ctx, MD5_STEP(MD5_F, c, d, a, b, MD5_SET(14), 0xa679438e, 17) MD5_STEP(MD5_F, b, c, d, a, MD5_SET(15), 0x49b40821, 22) -/* Round 2 */ + /* Round 2 */ MD5_STEP(MD5_G, a, b, c, d, MD5_GET(1), 0xf61e2562, 5) MD5_STEP(MD5_G, d, a, b, c, MD5_GET(6), 0xc040b340, 9) MD5_STEP(MD5_G, c, d, a, b, MD5_GET(11), 0x265e5a51, 14) @@ -418,7 +361,7 @@ static const void *my_md5_body(my_md5_ctx *ctx, MD5_STEP(MD5_G, c, d, a, b, MD5_GET(7), 0x676f02d9, 14) MD5_STEP(MD5_G, b, c, d, a, MD5_GET(12), 0x8d2a4c8a, 20) -/* Round 3 */ + /* Round 3 */ MD5_STEP(MD5_H, a, b, c, d, MD5_GET(5), 0xfffa3942, 4) MD5_STEP(MD5_H2, d, a, b, c, MD5_GET(8), 0x8771f681, 11) MD5_STEP(MD5_H, c, d, a, b, MD5_GET(11), 0x6d9d6122, 16) @@ -436,7 +379,7 @@ static const void *my_md5_body(my_md5_ctx *ctx, MD5_STEP(MD5_H, c, d, a, b, MD5_GET(15), 0x1fa27cf8, 16) MD5_STEP(MD5_H2, b, c, d, a, MD5_GET(2), 0xc4ac5665, 23) -/* Round 4 */ + /* Round 4 */ MD5_STEP(MD5_I, a, b, c, d, MD5_GET(0), 0xf4292244, 6) MD5_STEP(MD5_I, d, a, b, c, MD5_GET(7), 0x432aff97, 10) MD5_STEP(MD5_I, c, d, a, b, MD5_GET(14), 0xab9423a7, 15) @@ -484,44 +427,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; @@ -542,33 +485,33 @@ static void my_md5_final(unsigned char *result, void *in) memset(&ctx->buffer[used], 0, available - 8); ctx->lo <<= 3; - ctx->buffer[56] = curlx_ultouc((ctx->lo)&0xff); - ctx->buffer[57] = curlx_ultouc((ctx->lo >> 8)&0xff); - ctx->buffer[58] = curlx_ultouc((ctx->lo >> 16)&0xff); + ctx->buffer[56] = curlx_ultouc((ctx->lo) & 0xff); + ctx->buffer[57] = curlx_ultouc((ctx->lo >> 8) & 0xff); + ctx->buffer[58] = curlx_ultouc((ctx->lo >> 16) & 0xff); ctx->buffer[59] = curlx_ultouc(ctx->lo >> 24); - ctx->buffer[60] = curlx_ultouc((ctx->hi)&0xff); - ctx->buffer[61] = curlx_ultouc((ctx->hi >> 8)&0xff); - ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16)&0xff); + ctx->buffer[60] = curlx_ultouc((ctx->hi) & 0xff); + ctx->buffer[61] = curlx_ultouc((ctx->hi >> 8) & 0xff); + ctx->buffer[62] = curlx_ultouc((ctx->hi >> 16) & 0xff); ctx->buffer[63] = curlx_ultouc(ctx->hi >> 24); 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)); } @@ -596,16 +539,21 @@ 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, size_t len) { CURLcode result; my_md5_ctx ctx; result = my_md5_init(&ctx); if(!result) { - my_md5_update(&ctx, input, curlx_uztoui(len)); - my_md5_final(outbuffer, &ctx); + do { + unsigned int ilen = (unsigned int)CURLMIN(len, UINT_MAX); + my_md5_update(&ctx, input, ilen); + input += ilen; + len -= ilen; + } while(len); + my_md5_final(output, &ctx); } return result; } @@ -615,23 +563,23 @@ struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params) struct MD5_context *ctxt; /* Create MD5 context */ - ctxt = malloc(sizeof(*ctxt)); + ctxt = curlx_malloc(sizeof(*ctxt)); if(!ctxt) return ctxt; - ctxt->md5_hashctx = malloc(md5params->md5_ctxtsize); + ctxt->md5_hashctx = curlx_malloc(md5params->md5_ctxtsize); if(!ctxt->md5_hashctx) { - free(ctxt); + curlx_free(ctxt); return NULL; } ctxt->md5_hash = md5params; if((*md5params->md5_init_func)(ctxt->md5_hashctx)) { - free(ctxt->md5_hashctx); - free(ctxt); + curlx_free(ctxt->md5_hashctx); + curlx_free(ctxt); return NULL; } @@ -639,10 +587,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; } @@ -651,8 +598,8 @@ CURLcode Curl_MD5_final(struct MD5_context *context, unsigned char *result) { (*context->md5_hash->md5_final_func)(result, context->md5_hashctx); - free(context->md5_hashctx); - free(context); + curlx_free(context->md5_hashctx); + curlx_free(context); return CURLE_OK; } diff --git a/lib/memdebug.c b/lib/memdebug.c index c1121713ea..6eda7a8236 100644 --- a/lib/memdebug.c +++ b/lib/memdebug.c @@ -21,19 +21,19 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#ifdef CURLDEBUG +#ifdef CURL_MEMDEBUG -#include +#include /* for offsetof() */ #include "urldata.h" +#include "curl_threads.h" +#include "curlx/fopen.h" /* for CURLX_FOPEN_LOW(), CURLX_FREOPEN_LOW() */ -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#ifdef USE_BACKTRACE +#include +#endif struct memdebug { size_t size; @@ -51,13 +51,47 @@ struct memdebug { * For advanced analysis, record a log file and write perl scripts to analyze * them! * - * Do not use these with multithreaded test programs! + * Do not use these with multi-threaded test programs! */ FILE *curl_dbg_logfile = NULL; static bool registered_cleanup = FALSE; /* atexit registered cleanup */ static bool memlimit = FALSE; /* enable memory limit */ static long memsize = 0; /* set number of mallocs allowed */ +#ifdef USE_BACKTRACE +static struct backtrace_state *btstate; +#endif + +static char membuf[10000]; +static size_t memwidx = 0; /* write index */ + +#ifdef USE_MUTEX +static bool dbg_mutex_init = 0; +static curl_mutex_t dbg_mutex; +#endif + +static bool curl_dbg_lock(void) +{ +#ifdef USE_MUTEX + if(dbg_mutex_init) { + Curl_mutex_acquire(&dbg_mutex); + return TRUE; + } +#endif + return FALSE; +} + +static void curl_dbg_unlock(bool was_locked) +{ +#ifdef USE_MUTEX + if(was_locked) + Curl_mutex_release(&dbg_mutex); +#else + (void)was_locked; +#endif +} + +static void curl_dbg_log_locked(const char *format, ...) CURL_PRINTF(1, 2); /* LeakSantizier (LSAN) calls _exit() instead of exit() when a leak is detected on exit so the logfile must be closed explicitly or data could be lost. @@ -65,32 +99,75 @@ static long memsize = 0; /* set number of mallocs allowed */ _exit() comes after the atexit handlers are called. curl/curl#6620 */ static void curl_dbg_cleanup(void) { + bool locked = curl_dbg_lock(); if(curl_dbg_logfile && curl_dbg_logfile != stderr && curl_dbg_logfile != stdout) { - (fclose)(curl_dbg_logfile); + if(memwidx) + fwrite(membuf, 1, memwidx, curl_dbg_logfile); + /* !checksrc! disable BANNEDFUNC 1 */ + fclose(curl_dbg_logfile); } curl_dbg_logfile = NULL; + curl_dbg_unlock(locked); +#ifdef USE_MUTEX + if(dbg_mutex_init) { + Curl_mutex_destroy(&dbg_mutex); + dbg_mutex_init = FALSE; + } +#endif } +#ifdef USE_BACKTRACE +static void error_bt_callback(void *data, const char *message, + int error_number) +{ + (void)data; + if(error_number == -1) + curl_dbg_log("compile with -g\n\n"); + else + curl_dbg_log("Backtrace error %d: %s\n", error_number, message); +} + +static int full_callback(void *data, uintptr_t pc, const char *pathname, + int line_number, const char *function) +{ + (void)data; + (void)pc; + if(pathname || function || line_number) + curl_dbg_log("BT %s:%d -- %s\n", pathname, line_number, function); + return 0; +} + +static void dump_bt(void) +{ + backtrace_full(btstate, 0, full_callback, error_bt_callback, NULL); +} +#else +#define dump_bt() /* nothing to do */ +#endif + /* this sets the log filename */ void curl_dbg_memdebug(const char *logname) { if(!curl_dbg_logfile) { if(logname && *logname) -#ifdef CURL_FOPEN - curl_dbg_logfile = CURL_FOPEN(logname, FOPEN_WRITETEXT); -#else - curl_dbg_logfile = (fopen)(logname, FOPEN_WRITETEXT); -#endif - else - curl_dbg_logfile = stderr; + curl_dbg_logfile = CURLX_FOPEN_LOW(logname, FOPEN_WRITETEXT); #ifdef MEMDEBUG_LOG_SYNC /* Flush the log file after every line so the log is not lost in a crash */ if(curl_dbg_logfile) setbuf(curl_dbg_logfile, (char *)NULL); #endif } +#ifdef USE_MUTEX + if(!dbg_mutex_init) { + dbg_mutex_init = TRUE; + Curl_mutex_init(&dbg_mutex); + } +#endif +#ifdef USE_BACKTRACE + btstate = backtrace_create_state(NULL, 0, error_bt_callback, NULL); +#endif if(!registered_cleanup) registered_cleanup = !atexit(curl_dbg_cleanup); } @@ -113,14 +190,14 @@ static bool countcheck(const char *func, int line, const char *source) if(memlimit && source) { if(!memsize) { /* log to file */ - curl_dbg_log("LIMIT %s:%d %s reached memlimit\n", - source, line, func); + curl_dbg_log("LIMIT %s:%d %s reached memlimit\n", source, line, func); /* log to stderr also */ - fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n", - source, line, func); + curl_mfprintf(stderr, "LIMIT %s:%d %s reached memlimit\n", + source, line, func); + dump_bt(); fflush(curl_dbg_logfile); /* because it might crash now */ /* !checksrc! disable ERRNOVAR 1 */ - CURL_SETERRNO(ENOMEM); + errno = ENOMEM; return TRUE; /* RETURN ERROR! */ } else @@ -144,7 +221,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; } @@ -174,7 +251,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; @@ -243,6 +320,7 @@ void *curl_dbg_realloc(void *ptr, size_t wantedsize, int line, const char *source) { struct memdebug *mem = NULL; + bool was_locked; size_t size = sizeof(struct memdebug) + wantedsize; @@ -251,6 +329,10 @@ void *curl_dbg_realloc(void *ptr, size_t wantedsize, if(countcheck("realloc", line, source)) return NULL; + /* need to realloc under lock, as we get out-of-order log + * entries otherwise, since another thread might alloc the + * memory released by realloc() before otherwise would log it. */ + was_locked = curl_dbg_lock(); #ifdef __INTEL_COMPILER # pragma warning(push) # pragma warning(disable:1684) @@ -264,12 +346,13 @@ 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("MEM %s:%d realloc(%p, %zu) = %p\n", - source, line, (void *)ptr, wantedsize, - mem ? (void *)mem->mem : (void *)0); + curl_dbg_log_locked("MEM %s:%d realloc(%p, %zu) = %p\n", + source, line, (void *)ptr, wantedsize, + mem ? (void *)mem->mem : (void *)0); + curl_dbg_unlock(was_locked); if(mem) { mem->size = wantedsize; return mem->mem; @@ -283,6 +366,9 @@ void curl_dbg_free(void *ptr, int line, const char *source) if(ptr) { struct memdebug *mem; + if(source) + curl_dbg_log("MEM %s:%d free(%p)\n", source, line, (void *)ptr); + #ifdef __INTEL_COMPILER # pragma warning(push) # pragma warning(disable:1684) @@ -296,11 +382,8 @@ void curl_dbg_free(void *ptr, int line, const char *source) #endif /* free for real */ - (Curl_cfree)(mem); + Curl_cfree(mem); } - - if(source && ptr) - curl_dbg_log("MEM %s:%d free(%p)\n", source, line, (void *)ptr); } curl_socket_t curl_dbg_socket(int domain, int type, int protocol, @@ -311,7 +394,8 @@ curl_socket_t curl_dbg_socket(int domain, int type, int protocol, if(countcheck("socket", line, source)) return CURL_SOCKET_BAD; - sockfd = (socket)(domain, type, protocol); + /* !checksrc! disable BANNEDFUNC 1 */ + sockfd = socket(domain, type, protocol); if(source && (sockfd != CURL_SOCKET_BAD)) curl_dbg_log("FD %s:%d socket() = %" FMT_SOCKET_T "\n", @@ -320,41 +404,13 @@ curl_socket_t curl_dbg_socket(int domain, int type, int protocol, return sockfd; } -SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd, - SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf, - SEND_TYPE_ARG3 len, SEND_TYPE_ARG4 flags, int line, - const char *source) -{ - SEND_TYPE_RETV rc; - if(countcheck("send", line, source)) - return -1; - rc = (send)(sockfd, buf, len, flags); - if(source) - curl_dbg_log("SEND %s:%d send(%lu) = %ld\n", - source, line, (unsigned long)len, (long)rc); - return rc; -} - -RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf, - RECV_TYPE_ARG3 len, RECV_TYPE_ARG4 flags, int line, - const char *source) -{ - RECV_TYPE_RETV rc; - if(countcheck("recv", line, source)) - return -1; - rc = (recv)(sockfd, buf, len, flags); - if(source) - curl_dbg_log("RECV %s:%d recv(%lu) = %ld\n", - source, line, (unsigned long)len, (long)rc); - return rc; -} - #ifdef HAVE_SOCKETPAIR int curl_dbg_socketpair(int domain, int type, int protocol, curl_socket_t socket_vector[2], int line, const char *source) { - int res = (socketpair)(domain, type, protocol, socket_vector); + /* !checksrc! disable BANNEDFUNC 1 */ + int res = socketpair(domain, type, protocol, socket_vector); if(source && (res == 0)) curl_dbg_log("FD %s:%d socketpair() = " @@ -371,6 +427,7 @@ curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen, struct sockaddr *addr = (struct sockaddr *)saddr; curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen; + /* !checksrc! disable BANNEDFUNC 1 */ curl_socket_t sockfd = accept(s, addr, addrlen); if(source && (sockfd != CURL_SOCKET_BAD)) @@ -388,7 +445,8 @@ curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr, void *saddrlen, struct sockaddr *addr = (struct sockaddr *)saddr; curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen; - curl_socket_t sockfd = (accept4)(s, addr, addrlen, flags); + /* !checksrc! disable BANNEDFUNC 1 */ + curl_socket_t sockfd = accept4(s, addr, addrlen, flags); if(source && (sockfd != CURL_SOCKET_BAD)) curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n", @@ -409,25 +467,30 @@ void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source) /* this is our own defined way to close sockets on *ALL* platforms */ int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source) { - int res = CURL_SCLOSE(sockfd); curl_dbg_mark_sclose(sockfd, line, source); - return res; + return CURL_SCLOSE(sockfd); } ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode, int line, const char *source) { - FILE *res; -#ifdef CURL_FOPEN - res = CURL_FOPEN(file, mode); -#else - res = (fopen)(file, mode); -#endif - + FILE *res = CURLX_FOPEN_LOW(file, mode); if(source) curl_dbg_log("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n", - source, line, file, mode, (void *)res); + source, line, file, mode, (void *)res); + + return res; +} + +ALLOC_FUNC +FILE *curl_dbg_freopen(const char *file, const char *mode, FILE *fh, + int line, const char *source) +{ + FILE *res = CURLX_FREOPEN_LOW(file, mode, fh); + if(source) + curl_dbg_log("FILE %s:%d freopen(\"%s\",\"%s\",%p) = %p\n", + source, line, file, mode, (void *)fh, (void *)res); return res; } @@ -436,7 +499,7 @@ ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode, int line, const char *source) { - 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); @@ -450,33 +513,67 @@ int curl_dbg_fclose(FILE *file, int line, const char *source) DEBUGASSERT(file != NULL); if(source) - curl_dbg_log("FILE %s:%d fclose(%p)\n", - source, line, (void *)file); + curl_dbg_log("FILE %s:%d fclose(%p)\n", source, line, (void *)file); - res = (fclose)(file); + /* !checksrc! disable BANNEDFUNC 1 */ + res = fclose(file); return res; } -/* this does the writing to the memory tracking log file */ -void curl_dbg_log(const char *format, ...) +static void curl_dbg_vlog(const char * const fmt, + va_list ap) CURL_PRINTF(1, 0); + +static void curl_dbg_vlog(const char * const fmt, va_list ap) { char buf[1024]; - int nchars; + size_t nchars = curl_mvsnprintf(buf, sizeof(buf), fmt, ap); + + if(nchars > (int)sizeof(buf) - 1) + nchars = (int)sizeof(buf) - 1; + + if(nchars > 0) { + if(sizeof(membuf) - nchars < memwidx) { + /* flush */ + fwrite(membuf, 1, memwidx, curl_dbg_logfile); + fflush(curl_dbg_logfile); + memwidx = 0; + } + if(memwidx) { + /* the previous line ends with a newline */ + DEBUGASSERT(membuf[memwidx - 1] == '\n'); + } + memcpy(&membuf[memwidx], buf, nchars); + memwidx += nchars; + } +} + +static void curl_dbg_log_locked(const char *format, ...) +{ va_list ap; if(!curl_dbg_logfile) return; va_start(ap, format); - nchars = mvsnprintf(buf, sizeof(buf), format, ap); + curl_dbg_vlog(format, ap); va_end(ap); - - if(nchars > (int)sizeof(buf) - 1) - nchars = (int)sizeof(buf) - 1; - - if(nchars > 0) - (fwrite)(buf, 1, (size_t)nchars, curl_dbg_logfile); } -#endif /* CURLDEBUG */ +/* this does the writing to the memory tracking log file */ +void curl_dbg_log(const char *format, ...) +{ + bool was_locked; + va_list ap; + + if(!curl_dbg_logfile) + return; + + was_locked = curl_dbg_lock(); + va_start(ap, format); + curl_dbg_vlog(format, ap); + va_end(ap); + curl_dbg_unlock(was_locked); +} + +#endif /* CURL_MEMDEBUG */ diff --git a/lib/memdebug.h b/lib/memdebug.h deleted file mode 100644 index 30469b99a5..0000000000 --- a/lib/memdebug.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef HEADER_CURL_MEMDEBUG_H -#define HEADER_CURL_MEMDEBUG_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 - * - ***************************************************************************/ - -/* - * CAUTION: this header is designed to work when included by the app-side - * as well as the library. Do not mix with library internals! - */ - -#ifdef CURLDEBUG - -/* Set this symbol on the command-line, recompile all lib-sources */ -#undef strdup -#define strdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__) -#undef malloc -#define malloc(size) curl_dbg_malloc(size, __LINE__, __FILE__) -#undef calloc -#define calloc(nbelem,size) curl_dbg_calloc(nbelem, size, __LINE__, __FILE__) -#undef realloc -#define realloc(ptr,size) curl_dbg_realloc(ptr, size, __LINE__, __FILE__) -#undef free -#define free(ptr) curl_dbg_free(ptr, __LINE__, __FILE__) -#undef send -#define send(a,b,c,d) curl_dbg_send(a,b,c,d, __LINE__, __FILE__) -#undef recv -#define recv(a,b,c,d) curl_dbg_recv(a,b,c,d, __LINE__, __FILE__) - -#ifdef _WIN32 -#undef _tcsdup -#ifdef UNICODE -#define _tcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) -#else -#define _tcsdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__) -#endif -#endif /* _WIN32 */ - -#undef socket -#define socket(domain,type,protocol) \ - curl_dbg_socket((int)domain, type, protocol, __LINE__, __FILE__) -#ifdef HAVE_ACCEPT4 -#undef accept4 /* for those with accept4 as a macro */ -#define accept4(sock,addr,len,flags) \ - curl_dbg_accept4(sock, addr, len, flags, __LINE__, __FILE__) -#endif -#ifdef HAVE_SOCKETPAIR -#define socketpair(domain,type,protocol,socket_vector) \ - curl_dbg_socketpair((int)domain, type, protocol, socket_vector, \ - __LINE__, __FILE__) -#endif - -#undef fopen -#define fopen(file,mode) curl_dbg_fopen(file,mode,__LINE__,__FILE__) -#undef fdopen -#define fdopen(file,mode) curl_dbg_fdopen(file,mode,__LINE__,__FILE__) -#undef fclose -#define fclose(file) curl_dbg_fclose(file,__LINE__,__FILE__) - -#endif /* CURLDEBUG */ -#endif /* HEADER_CURL_MEMDEBUG_H */ diff --git a/lib/mime.c b/lib/mime.c index 894413be17..157884a838 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -21,73 +21,36 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - struct Curl_easy; #include "mime.h" -#include "curlx/warnless.h" #include "urldata.h" #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" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" -#ifdef _WIN32 -# ifndef R_OK -# define R_OK 4 -# endif -#endif - -#define READ_ERROR ((size_t) -1) -#define STOP_FILLING ((size_t) -2) +#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,15 +86,13 @@ static const unsigned char qp_class[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */ }; - /* Binary --> hexadecimal ASCII table. */ 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 fopen_read fopen +#define filesize(name, stat_data) stat_data.st_size +#define fopen_read curlx_fopen #else @@ -146,15 +107,14 @@ 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; int ret_stat; - FILE * file; + FILE *file; - file = fopen(name, FOPEN_READTEXT); /* VMS */ + file = curlx_fopen(name, FOPEN_READTEXT); /* VMS */ if(!file) return 0; @@ -165,7 +125,7 @@ curl_off_t VmsRealFileSize(const char *name, if(ret_stat) count += ret_stat; } - fclose(file); + curlx_fclose(file); return count; } @@ -177,7 +137,7 @@ curl_off_t VmsRealFileSize(const char *name, * */ static curl_off_t VmsSpecialSize(const char *name, - const struct_stat *stat_buf) + const curlx_struct_stat *stat_buf) { switch(stat_buf->st_fab_rfm) { case FAB$C_VAR: @@ -199,77 +159,27 @@ static curl_off_t VmsSpecialSize(const char *name, * record format of the file. * */ -static FILE * vmsfopenread(const char *file, const char *mode) +static FILE *vmsfopenread(const char *file, const char *mode) { - struct_stat statbuf; - int result; + curlx_struct_stat statbuf; + int res = curlx_stat(file, &statbuf); - result = stat(file, &statbuf); - - switch(statbuf.st_fab_rfm) { - case FAB$C_VAR: - case FAB$C_VFC: - case FAB$C_STMCR: - return fopen(file, FOPEN_READTEXT); /* VMS */ - break; - default: - return fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm"); + if(res != -1) { + switch(statbuf.st_fab_rfm) { + case FAB$C_VAR: + case FAB$C_VFC: + case FAB$C_STMCR: + return curlx_fopen(file, FOPEN_READTEXT); /* VMS */ + break; + default: + return curlx_fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm"); + } } + return NULL; } #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, @@ -280,7 +190,6 @@ static void mimesetstate(struct mime_state *state, state->offset = 0; } - /* Escape header string into allocated memory. */ static char *escape_string(struct Curl_easy *data, const char *src, enum mimestrategy strategy) @@ -356,13 +265,13 @@ static char *strippath(const char *fullfile) { char *filename; char *base; - filename = strdup(fullfile); /* duplicate since basename() may ruin the - buffer it works on */ + filename = curlx_strdup(fullfile); /* duplicate since basename() may ruin + the buffer it works on */ if(!filename) return NULL; - base = strdup(basename(filename)); + base = curlx_strdup(curlx_basename(filename)); - free(filename); /* free temporary buffer */ + curlx_free(filename); /* free temporary buffer */ return base; /* returns an allocated string or NULL ! */ } @@ -375,8 +284,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) { @@ -403,8 +311,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) { @@ -429,7 +336,6 @@ static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof, return cursize; } - /* Base64 content encoder. */ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, curl_mimepart *part) @@ -468,10 +374,10 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, i = st->buf[st->bufbeg++] & 0xFF; i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF); i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF); - *ptr++ = Curl_base64encdec[(i >> 18) & 0x3F]; - *ptr++ = Curl_base64encdec[(i >> 12) & 0x3F]; - *ptr++ = Curl_base64encdec[(i >> 6) & 0x3F]; - *ptr++ = Curl_base64encdec[i & 0x3F]; + *ptr++ = curlx_base64encdec[(i >> 18) & 0x3F]; + *ptr++ = curlx_base64encdec[(i >> 12) & 0x3F]; + *ptr++ = curlx_base64encdec[(i >> 6) & 0x3F]; + *ptr++ = curlx_base64encdec[i & 0x3F]; cursize += 4; st->pos += 4; size -= 4; @@ -495,10 +401,10 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, i = (st->buf[st->bufbeg + 1] & 0xFF) << 8; i |= (st->buf[st->bufbeg] & 0xFF) << 16; - ptr[0] = Curl_base64encdec[(i >> 18) & 0x3F]; - ptr[1] = Curl_base64encdec[(i >> 12) & 0x3F]; + ptr[0] = curlx_base64encdec[(i >> 18) & 0x3F]; + ptr[1] = curlx_base64encdec[(i >> 12) & 0x3F]; if(++st->bufbeg != st->bufend) { - ptr[2] = Curl_base64encdec[(i >> 6) & 0x3F]; + ptr[2] = curlx_base64encdec[(i >> 6) & 0x3F]; st->bufbeg++; } cursize += 4; @@ -518,13 +424,12 @@ 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. * * Check if a CRLF or end of data is in input buffer at current position + n. @@ -560,8 +465,8 @@ 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]; - buf[0] = (char) i; + int i = (unsigned char)st->buf[st->bufbeg]; + buf[0] = (char)i; buf[1] = aschex[(i >> 4) & 0xF]; buf[2] = aschex[i & 0xF]; @@ -618,7 +523,7 @@ static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, } } if(softlinebreak) { - strcpy(buf, "\x3D\x0D\x0A"); /* "=\r\n" */ + curlx_strcopy(buf, sizeof(buf), STRCONST("\x3D\x0D\x0A")); /* =\r\n */ len = 3; consumed = 0; } @@ -652,13 +557,12 @@ static curl_off_t encoder_qp_size(curl_mimepart *part) return part->datasize ? -1 : 0; } - /* In-memory data callbacks. */ /* Argument is a pointer to the mime part. */ static size_t mime_mem_read(char *buffer, size_t size, size_t nitems, void *instream) { - curl_mimepart *part = (curl_mimepart *) instream; + curl_mimepart *part = (curl_mimepart *)instream; size_t sz = curlx_sotouz(part->datasize - part->state.offset); (void)size; /* Always 1 */ @@ -676,7 +580,7 @@ static size_t mime_mem_read(char *buffer, size_t size, size_t nitems, static int mime_mem_seek(void *instream, curl_off_t offset, int whence) { - curl_mimepart *part = (curl_mimepart *) instream; + curl_mimepart *part = (curl_mimepart *)instream; switch(whence) { case SEEK_CUR: @@ -696,26 +600,25 @@ static int mime_mem_seek(void *instream, curl_off_t offset, int whence) static void mime_mem_free(void *ptr) { - Curl_safefree(((curl_mimepart *) ptr)->data); + curlx_safefree(((curl_mimepart *)ptr)->data); } - /* Named file callbacks. */ /* Argument is a pointer to the mime part. */ -static int mime_open_file(curl_mimepart *part) +static bool mime_open_file(curl_mimepart *part) { /* Open a MIMEKIND_FILE part. */ if(part->fp) - return 0; + return FALSE; part->fp = fopen_read(part->data, "rb"); - return part->fp ? 0 : -1; + return part->fp ? FALSE : TRUE; } static size_t mime_file_read(char *buffer, size_t size, size_t nitems, void *instream) { - curl_mimepart *part = (curl_mimepart *) instream; + curl_mimepart *part = (curl_mimepart *)instream; if(!nitems) return STOP_FILLING; @@ -728,7 +631,7 @@ static size_t mime_file_read(char *buffer, size_t size, size_t nitems, static int mime_file_seek(void *instream, curl_off_t offset, int whence) { - curl_mimepart *part = (curl_mimepart *) instream; + curl_mimepart *part = (curl_mimepart *)instream; if(whence == SEEK_SET && !offset && !part->fp) return CURL_SEEKFUNC_OK; /* Not open: implicitly already at BOF. */ @@ -736,22 +639,21 @@ static int mime_file_seek(void *instream, curl_off_t offset, int whence) if(mime_open_file(part)) return CURL_SEEKFUNC_FAIL; - return fseek(part->fp, (long) offset, whence) ? + return curlx_fseek(part->fp, offset, whence) ? CURL_SEEKFUNC_CANTSEEK : CURL_SEEKFUNC_OK; } static void mime_file_free(void *ptr) { - curl_mimepart *part = (curl_mimepart *) ptr; + curl_mimepart *part = (curl_mimepart *)ptr; if(part->fp) { - fclose(part->fp); + curlx_fclose(part->fp); part->fp = NULL; } - Curl_safefree(part->data); + curlx_safefree(part->data); } - /* Subparts callbacks. */ /* Argument is a pointer to the mime structure. */ @@ -801,7 +703,7 @@ static size_t read_part_content(curl_mimepart *part, } /* If we can determine we are at end of part data, spare a read. */ - if(part->datasize != (curl_off_t) -1 && + if(part->datasize != (curl_off_t)-1 && part->state.offset >= part->datasize) { /* sz is already zero. */ } @@ -812,8 +714,8 @@ static size_t read_part_content(curl_mimepart *part, * Cannot be processed as other kinds since read function requires * an additional parameter and is highly recursive. */ - sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread); - break; + sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread); + break; case MIMEKIND_FILE: if(part->fp && feof(part->fp)) break; /* At EOF. */ @@ -919,7 +821,7 @@ static size_t readback_part(curl_mimepart *part, while(bufsize) { size_t sz = 0; - struct curl_slist *hdr = (struct curl_slist *) part->state.ptr; + struct curl_slist *hdr = (struct curl_slist *)part->state.ptr; switch(part->state.state) { case MIMESTATE_BEGIN: mimesetstate(&part->state, @@ -967,7 +869,7 @@ static size_t readback_part(curl_mimepart *part, mimesetstate(&part->state, MIMESTATE_END, NULL); /* Try sparing open file descriptors. */ if(part->kind == MIMEKIND_FILE && part->fp) { - fclose(part->fp); + curlx_fclose(part->fp); part->fp = NULL; } FALLTHROUGH(); @@ -997,7 +899,7 @@ static size_t readback_part(curl_mimepart *part, static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, void *instream, bool *hasread) { - curl_mime *mime = (curl_mime *) instream; + curl_mime *mime = (curl_mime *)instream; size_t cursize = 0; (void)size; /* Always 1 */ @@ -1073,7 +975,7 @@ static int mime_part_rewind(curl_mimepart *part) if(part->state.state > targetstate) { res = CURL_SEEKFUNC_CANTSEEK; if(part->seekfunc) { - res = part->seekfunc(part->arg, (curl_off_t) 0, SEEK_SET); + res = part->seekfunc(part->arg, (curl_off_t)0, SEEK_SET); switch(res) { case CURL_SEEKFUNC_OK: case CURL_SEEKFUNC_FAIL: @@ -1098,9 +1000,9 @@ static int mime_part_rewind(curl_mimepart *part) static int mime_subparts_seek(void *instream, curl_off_t offset, int whence) { - curl_mime *mime = (curl_mime *) instream; + curl_mime *mime = (curl_mime *)instream; curl_mimepart *part; - int result = CURL_SEEKFUNC_OK; + int rc = CURL_SEEKFUNC_OK; if(whence != SEEK_SET || offset) return CURL_SEEKFUNC_CANTSEEK; /* Only support full rewind. */ @@ -1111,13 +1013,13 @@ static int mime_subparts_seek(void *instream, curl_off_t offset, int whence) for(part = mime->firstpart; part; part = part->nextpart) { int res = mime_part_rewind(part); if(res != CURL_SEEKFUNC_OK) - result = res; + rc = res; } - if(result == CURL_SEEKFUNC_OK) + if(rc == CURL_SEEKFUNC_OK) mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL); - return result; + return rc; } /* Release part content. */ @@ -1129,10 +1031,10 @@ static void cleanup_part_content(curl_mimepart *part) part->readfunc = NULL; part->seekfunc = NULL; part->freefunc = NULL; - part->arg = (void *) part; /* Defaults to part itself. */ + part->arg = (void *)part; /* Defaults to part itself. */ part->data = NULL; part->fp = NULL; - part->datasize = (curl_off_t) 0; /* No size yet. */ + part->datasize = (curl_off_t)0; /* No size yet. */ cleanup_encoder_state(&part->encstate); part->kind = MIMEKIND_NONE; part->flags &= ~(unsigned int)MIME_FAST_READ; @@ -1142,7 +1044,7 @@ static void cleanup_part_content(curl_mimepart *part) static void mime_subparts_free(void *ptr) { - curl_mime *mime = (curl_mime *) ptr; + curl_mime *mime = (curl_mime *)ptr; if(mime && mime->parent) { mime->parent->freefunc = NULL; /* Be sure we will not be called again. */ @@ -1154,7 +1056,7 @@ static void mime_subparts_free(void *ptr) /* Do not free subparts: unbind them. This is used for the top level only. */ static void mime_subparts_unbind(void *ptr) { - curl_mime *mime = (curl_mime *) ptr; + curl_mime *mime = (curl_mime *)ptr; if(mime && mime->parent) { mime->parent->freefunc = NULL; /* Be sure we will not be called again. */ @@ -1163,7 +1065,6 @@ static void mime_subparts_unbind(void *ptr) } } - void Curl_mime_cleanpart(curl_mimepart *part) { if(part) { @@ -1171,9 +1072,9 @@ void Curl_mime_cleanpart(curl_mimepart *part) curl_slist_free_all(part->curlheaders); if(part->flags & MIME_USERHEADERS_OWNER) curl_slist_free_all(part->userheaders); - Curl_safefree(part->mimetype); - Curl_safefree(part->name); - Curl_safefree(part->filename); + curlx_safefree(part->mimetype); + curlx_safefree(part->name); + curlx_safefree(part->filename); Curl_mime_initpart(part); } } @@ -1189,9 +1090,9 @@ void curl_mime_free(curl_mime *mime) part = mime->firstpart; mime->firstpart = part->nextpart; Curl_mime_cleanpart(part); - free(part); + curlx_free(part); } - free(mime); + curlx_free(mime); } } @@ -1210,7 +1111,7 @@ CURLcode Curl_mime_duppart(struct Curl_easy *data, case MIMEKIND_NONE: break; case MIMEKIND_DATA: - res = curl_mime_data(dst, src->data, (size_t) src->datasize); + res = curl_mime_data(dst, src->data, (size_t)src->datasize); break; case MIMEKIND_FILE: res = curl_mime_filedata(dst, src->data); @@ -1229,7 +1130,7 @@ CURLcode Curl_mime_duppart(struct Curl_easy *data, res = mime ? curl_mime_subparts(dst, mime) : CURLE_OUT_OF_MEMORY; /* Duplicate subparts. */ - for(s = ((curl_mime *) src->arg)->firstpart; !res && s; s = s->nextpart) { + for(s = ((curl_mime *)src->arg)->firstpart; !res && s; s = s->nextpart) { d = curl_mime_addpart(mime); res = d ? Curl_mime_duppart(data, d, s) : CURLE_OUT_OF_MEMORY; } @@ -1281,7 +1182,7 @@ curl_mime *curl_mime_init(void *easy) { curl_mime *mime; - mime = (curl_mime *) malloc(sizeof(*mime)); + mime = (curl_mime *)curlx_malloc(sizeof(*mime)); if(mime) { mime->parent = NULL; @@ -1290,10 +1191,10 @@ curl_mime *curl_mime_init(void *easy) memset(mime->boundary, '-', MIME_BOUNDARY_DASHES); if(Curl_rand_alnum(easy, - (unsigned char *) &mime->boundary[MIME_BOUNDARY_DASHES], + (unsigned char *)&mime->boundary[MIME_BOUNDARY_DASHES], MIME_RAND_BOUNDARY_CHARS + 1)) { /* failed to get random separator, bail out */ - free(mime); + curlx_free(mime); return NULL; } mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL); @@ -1305,7 +1206,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); } @@ -1318,7 +1219,7 @@ curl_mimepart *curl_mime_addpart(curl_mime *mime) if(!mime) return NULL; - part = (curl_mimepart *) malloc(sizeof(*part)); + part = (curl_mimepart *)curlx_malloc(sizeof(*part)); if(part) { Curl_mime_initpart(part); @@ -1341,10 +1242,10 @@ CURLcode curl_mime_name(curl_mimepart *part, const char *name) if(!part) return CURLE_BAD_FUNCTION_ARGUMENT; - Curl_safefree(part->name); + curlx_safefree(part->name); if(name) { - part->name = strdup(name); + part->name = curlx_strdup(name); if(!part->name) return CURLE_OUT_OF_MEMORY; } @@ -1358,10 +1259,10 @@ CURLcode curl_mime_filename(curl_mimepart *part, const char *filename) if(!part) return CURLE_BAD_FUNCTION_ARGUMENT; - Curl_safefree(part->filename); + curlx_safefree(part->filename); if(filename) { - part->filename = strdup(filename); + part->filename = curlx_strdup(filename); if(!part->filename) return CURLE_OUT_OF_MEMORY; } @@ -1370,19 +1271,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; @@ -1409,12 +1309,12 @@ CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename) if(filename) { char *base; - struct_stat sbuf; + curlx_struct_stat sbuf; - if(stat(filename, &sbuf)) + if(curlx_stat(filename, &sbuf)) result = CURLE_READ_ERROR; else { - part->data = strdup(filename); + part->data = curlx_strdup(filename); if(!part->data) result = CURLE_OUT_OF_MEMORY; else { @@ -1437,7 +1337,7 @@ CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename) result = CURLE_OUT_OF_MEMORY; else { result = curl_mime_filename(part, base); - free(base); + curlx_free(base); } } } @@ -1451,10 +1351,10 @@ CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype) if(!part) return CURLE_BAD_FUNCTION_ARGUMENT; - Curl_safefree(part->mimetype); + curlx_safefree(part->mimetype); if(mimetype) { - part->mimetype = strdup(mimetype); + part->mimetype = curlx_strdup(mimetype); if(!part->mimetype) return CURLE_OUT_OF_MEMORY; } @@ -1462,6 +1362,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) { @@ -1561,7 +1470,7 @@ CURLcode Curl_mime_set_subparts(curl_mimepart *part, they might not be positioned at start. Rewind them now, as a future check while rewinding the parent may cause this content to be skipped. */ - if(mime_subparts_seek(subparts, (curl_off_t) 0, SEEK_SET) != + if(mime_subparts_seek(subparts, (curl_off_t)0, SEEK_SET) != CURL_SEEKFUNC_OK) return CURLE_SEND_FAIL_REWIND; @@ -1583,12 +1492,11 @@ CURLcode curl_mime_subparts(curl_mimepart *part, curl_mime *subparts) return Curl_mime_set_subparts(part, subparts, TRUE); } - /* Readback from top mime. */ /* Argument is the dummy top part. */ size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream) { - curl_mimepart *part = (curl_mimepart *) instream; + curl_mimepart *part = (curl_mimepart *)instream; size_t ret; bool hasread; @@ -1671,8 +1579,7 @@ static curl_off_t mime_size(curl_mimepart *part) if(size >= 0 && !(part->flags & MIME_BODY_ONLY)) { /* Compute total part size. */ size += slist_size(part->curlheaders, 2, NULL, 0); - size += slist_size(part->userheaders, 2, - STRCONST("Content-Type")); + size += slist_size(part->userheaders, 2, STRCONST("Content-Type")); size += 2; /* CRLF after headers. */ } return size; @@ -1687,7 +1594,7 @@ CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...) va_list ap; va_start(ap, fmt); - s = vaprintf(fmt, ap); + s = curl_mvaprintf(fmt, ap); va_end(ap); if(s) { @@ -1695,7 +1602,7 @@ CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...) if(hdr) *slp = hdr; else - free(s); + curlx_free(s); } return hdr ? CURLE_OK : CURLE_OUT_OF_MEMORY; @@ -1721,16 +1628,16 @@ const char *Curl_mime_contenttype(const char *filename) const char *type; }; static const struct ContentType ctts[] = { - {".gif", "image/gif"}, - {".jpg", "image/jpeg"}, - {".jpeg", "image/jpeg"}, - {".png", "image/png"}, - {".svg", "image/svg+xml"}, - {".txt", "text/plain"}, - {".htm", "text/html"}, - {".html", "text/html"}, - {".pdf", "application/pdf"}, - {".xml", "application/xml"} + { ".gif", "image/gif" }, + { ".jpg", "image/jpeg" }, + { ".jpeg", "image/jpeg" }, + { ".png", "image/png" }, + { ".svg", "image/svg+xml" }, + { ".txt", "text/plain" }, + { ".htm", "text/html" }, + { ".html", "text/html" }, + { ".pdf", "application/pdf" }, + { ".xml", "application/xml" } }; if(filename) { @@ -1811,7 +1718,7 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, } if(part->kind == MIMEKIND_MULTIPART) { - mime = (curl_mime *) part->arg; + mime = (curl_mime *)part->arg; if(mime) boundary = mime->boundary; } @@ -1824,10 +1731,10 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, if(!search_header(part->userheaders, STRCONST("Content-Disposition"))) { if(!disposition) if(part->filename || part->name || - (contenttype && !curl_strnequal(contenttype, "multipart/", 10))) - disposition = DISPOSITION_DEFAULT; + (contenttype && !curl_strnequal(contenttype, "multipart/", 10))) + disposition = DISPOSITION_DEFAULT; if(disposition && curl_strequal(disposition, "attachment") && - !part->name && !part->filename) + !part->name && !part->filename) disposition = NULL; if(disposition) { char *name = NULL; @@ -1853,12 +1760,12 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, filename ? "; filename=\"" : "", filename ? filename : "", filename ? "\"" : ""); - Curl_safefree(name); - Curl_safefree(filename); + curlx_safefree(name); + curlx_safefree(filename); if(ret) return ret; - } } + } /* Issue Content-Type header. */ if(contenttype) { @@ -1873,7 +1780,7 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, if(part->encoder) cte = part->encoder->name; else if(contenttype && strategy == MIMESTRATEGY_MAIL && - part->kind != MIMEKIND_MULTIPART) + part->kind != MIMEKIND_MULTIPART) cte = "8bit"; if(cte) { ret = Curl_mime_add_header(&part->curlheaders, @@ -1912,7 +1819,7 @@ static void mime_unpause(curl_mimepart *part) if(part->lastreadstatus == CURL_READFUNC_PAUSE) part->lastreadstatus = 1; /* Successful read status. */ if(part->kind == MIMEKIND_MULTIPART) { - curl_mime *mime = (curl_mime *) part->arg; + curl_mime *mime = (curl_mime *)part->arg; if(mime) { curl_mimepart *subpart; @@ -1965,7 +1872,6 @@ static CURLcode cr_mime_read(struct Curl_easy *data, size_t nread; char tmp[256]; - /* Once we have errored, we will return the same error forever */ if(ctx->errored) { CURL_TRC_READ(data, "cr_mime_read(len=%zu) is errored -> %d, eos=0", @@ -2031,7 +1937,7 @@ static CURLcode cr_mime_read(struct Curl_easy *data, case 0: if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) { failf(data, "client mime read EOF fail, " - "only %"FMT_OFF_T"/%"FMT_OFF_T + "only %" FMT_OFF_T "/%" FMT_OFF_T " of needed bytes read", ctx->read_len, ctx->total_len); return CURLE_READ_ERROR; } @@ -2081,12 +1987,12 @@ 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; } CURL_TRC_READ(data, "cr_mime_read(len=%zu, total=%" FMT_OFF_T - ", read=%"FMT_OFF_T") -> %d, %zu, %d", + ", read=%" FMT_OFF_T ") -> %d, %zu, %d", blen, ctx->total_len, ctx->read_len, result, *pnread, *peos); return result; } @@ -2117,7 +2023,7 @@ static CURLcode cr_mime_resume_from(struct Curl_easy *data, curl_off_t passed = 0; do { - char scratch[4*1024]; + char scratch[4 * 1024]; size_t readthisamountnow = (offset - passed > (curl_off_t)sizeof(scratch)) ? sizeof(scratch) : @@ -2218,7 +2124,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) @@ -2266,8 +2173,7 @@ CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding) return CURLE_NOT_BUILT_IN; } -CURLcode curl_mime_data(curl_mimepart *part, - const char *data, size_t datasize) +CURLcode curl_mime_data(curl_mimepart *part, const char *data, size_t datasize) { (void)part; (void)data; @@ -2282,8 +2188,7 @@ CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename) return CURLE_NOT_BUILT_IN; } -CURLcode curl_mime_data_cb(curl_mimepart *part, - curl_off_t datasize, +CURLcode curl_mime_data_cb(curl_mimepart *part, curl_off_t datasize, curl_read_callback readfunc, curl_seek_callback seekfunc, curl_free_callback freefunc, diff --git a/lib/mime.h b/lib/mime.h index 5073a38f70..e84f04051b 100644 --- a/lib/mime.h +++ b/lib/mime.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #define MIME_BOUNDARY_DASHES 24 /* leading boundary dashes */ @@ -161,16 +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_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) #endif - #endif /* HEADER_CURL_MIME_H */ diff --git a/lib/mprintf.c b/lib/mprintf.c index 176f8a3e4b..66ccae2190 100644 --- a/lib/mprintf.c +++ b/lib/mprintf.c @@ -20,77 +20,47 @@ * * SPDX-License-Identifier: curl * - */ - + ***************************************************************************/ #include "curl_setup.h" + #include "curlx/dynbuf.h" #include "curl_printf.h" #include "curlx/strparse.h" - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.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)) \ - return TRUE; \ - (*donep)++; \ +#define OUTCHAR(x) \ + do { \ + 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 */ @@ -132,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; }; @@ -162,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; @@ -175,8 +145,8 @@ static int dollarstring(const char *p, const char **end) return (int)num - 1; } -#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 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))) /* * Parse the format string. @@ -210,7 +180,7 @@ static int parsefmt(const char *format, int max_param = -1; int i; int ocount = 0; - unsigned char usedinput[MAX_PARAMETERS/8]; + unsigned char usedinput[MAX_PARAMETERS / 8]; size_t outlen = 0; struct outsegment *optr; int use_dollar = DOLLAR_UNKNOWN; @@ -254,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; } @@ -368,8 +338,15 @@ static int parsefmt(const char *format, if(!(flags & FLAGS_LEFT)) flags |= FLAGS_PAD_NIL; FALLTHROUGH(); - case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { curl_off_t num; flags |= FLAGS_WIDTH; fmt--; @@ -402,81 +379,81 @@ 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; - flags |= FLAGS_OCTAL|FLAGS_UNSIGNED; + 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; - flags |= FLAGS_HEX|FLAGS_UNSIGNED; + 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; - flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED; + 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; - flags |= FLAGS_FLOATE|FLAGS_UPPER; + 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; - flags |= FLAGS_FLOATG|FLAGS_UPPER; + type = MTYPE_DOUBLE; + flags |= FLAGS_FLOATG | FLAGS_UPPER; break; default: /* invalid instruction, disregard and continue */ @@ -496,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); } @@ -514,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); } @@ -569,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; @@ -620,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; }; @@ -631,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; @@ -693,26 +670,22 @@ 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 +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef _WIN32 + curlx_win32_snprintf(work, BUFFSIZE, fmt, dnum); +#else + /* !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; + snprintf(work, BUFFSIZE, fmt, dnum); #endif -#else - (sprintf)(work, formatbuf, dnum); -#endif -#ifdef __clang__ -#pragma clang diagnostic pop +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic pop #endif DEBUGASSERT(strlen(work) < BUFFSIZE); while(*work) { @@ -726,8 +699,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; @@ -745,11 +718,11 @@ static bool out_number(void *userp, char *w; if(flags & FLAGS_CHAR) { - /* Character. */ + /* Character. */ if(!(flags & FLAGS_LEFT)) while(--width > 0) OUTCHAR(' '); - OUTCHAR((char) num); + OUTCHAR((char)num); if(flags & FLAGS_LEFT) while(--width > 0) OUTCHAR(' '); @@ -769,23 +742,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) { @@ -845,7 +818,7 @@ static bool out_number(void *userp, while(width-- > 0) OUTCHAR('0'); - /* Write the number. */ + /* Write the number. */ while(++w <= workend) { OUTCHAR(*w); } @@ -871,8 +844,8 @@ static bool out_string(void *userp, size_t len; if(!str) { - /* Write null string if there is space. */ - if(prec == -1 || prec >= (int) sizeof(nilstr) - 1) { + /* Write null string if there is space. */ + if(prec == -1 || prec >= (int)sizeof(nilstr) - 1) { str = nilstr; len = sizeof(nilstr) - 1; /* Disable quotes around (nil) */ @@ -918,17 +891,17 @@ static bool out_pointer(void *userp, char *work, int *donep) { - /* Generic pointer. */ + /* Generic pointer. */ if(ptr) { - size_t num = (size_t) ptr; + size_t num = (size_t)ptr; - /* If the pointer is not NULL, write it as a %#x spec. */ - p->flags |= FLAGS_HEX|FLAGS_ALT; + /* 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; @@ -962,15 +935,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 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 */ +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 */ @@ -997,7 +969,7 @@ static int formatf( done++; } if(optr->flags & FLAGS_SUBSTR) - /* this is just a substring */ + /* this is a substring */ continue; } @@ -1034,50 +1006,48 @@ static int formatf( 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; + *(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; + *(short *)iptr->val.ptr = (short)done; break; default: @@ -1101,7 +1071,7 @@ static int addbyter(unsigned char outc, void *f) } int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, - va_list ap_save) + va_list args) { int retcode; struct nsprintf info; @@ -1110,7 +1080,7 @@ int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, info.length = 0; info.max = maxlength; - retcode = formatf(&info, addbyter, format, ap_save); + retcode = formatf(&info, addbyter, format, args); if(info.max) { /* we terminate this with a zero byte */ if(info.max == info.length) { @@ -1128,10 +1098,10 @@ int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...) { int retcode; - va_list ap_save; /* argument pointer */ - va_start(ap_save, format); - retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save); - va_end(ap_save); + va_list args; /* argument pointer */ + va_start(args, format); + retcode = curl_mvsnprintf(buffer, maxlength, format, args); + va_end(args); return retcode; } @@ -1142,19 +1112,19 @@ static int alloc_addbyter(unsigned char outc, void *f) CURLcode result = curlx_dyn_addn(infop->b, &outc, 1); if(result) { infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM; - return 1 ; /* fail */ + return 1; /* fail */ } return 0; } /* appends the formatted string, returns MERR error code */ -int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save) +int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list args) { struct asprintf info; info.b = dyn; info.merr = MERR_OK; - (void)formatf(&info, alloc_addbyter, format, ap_save); + (void)formatf(&info, alloc_addbyter, format, args); if(info.merr) { curlx_dyn_free(info.b); return info.merr; @@ -1162,7 +1132,7 @@ int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save) return 0; } -char *curl_mvaprintf(const char *format, va_list ap_save) +char *curl_mvaprintf(const char *format, va_list args) { struct asprintf info; struct dynbuf dyn; @@ -1170,23 +1140,23 @@ char *curl_mvaprintf(const char *format, va_list ap_save) curlx_dyn_init(info.b, DYN_APRINTF); info.merr = MERR_OK; - (void)formatf(&info, alloc_addbyter, format, ap_save); + (void)formatf(&info, alloc_addbyter, format, args); if(info.merr) { curlx_dyn_free(info.b); return NULL; } if(curlx_dyn_len(info.b)) return curlx_dyn_ptr(info.b); - return strdup(""); + return curlx_strdup(""); } char *curl_maprintf(const char *format, ...) { - va_list ap_save; + va_list args; char *s; - va_start(ap_save, format); - s = curl_mvaprintf(format, ap_save); - va_end(ap_save); + va_start(args, format); + s = curl_mvaprintf(format, args); + va_end(args); return s; } @@ -1200,11 +1170,11 @@ static int storebuffer(unsigned char outc, void *f) int curl_msprintf(char *buffer, const char *format, ...) { - va_list ap_save; /* argument pointer */ + va_list args; /* argument pointer */ int retcode; - va_start(ap_save, format); - retcode = formatf(&buffer, storebuffer, format, ap_save); - va_end(ap_save); + va_start(args, format); + retcode = formatf(&buffer, storebuffer, format, args); + va_end(args); *buffer = 0; /* we terminate this with a zero byte */ return retcode; } @@ -1220,36 +1190,36 @@ static int fputc_wrapper(unsigned char outc, void *f) int curl_mprintf(const char *format, ...) { int retcode; - va_list ap_save; /* argument pointer */ - va_start(ap_save, format); - retcode = formatf(stdout, fputc_wrapper, format, ap_save); - va_end(ap_save); + va_list args; /* argument pointer */ + va_start(args, format); + retcode = formatf(stdout, fputc_wrapper, format, args); + va_end(args); return retcode; } -int curl_mfprintf(FILE *whereto, const char *format, ...) +int curl_mfprintf(FILE *fd, const char *format, ...) { int retcode; - va_list ap_save; /* argument pointer */ - va_start(ap_save, format); - retcode = formatf(whereto, fputc_wrapper, format, ap_save); - va_end(ap_save); + va_list args; /* argument pointer */ + va_start(args, format); + retcode = formatf(fd, fputc_wrapper, format, args); + va_end(args); return retcode; } -int curl_mvsprintf(char *buffer, const char *format, va_list ap_save) +int curl_mvsprintf(char *buffer, const char *format, va_list args) { - int retcode = formatf(&buffer, storebuffer, format, ap_save); + int retcode = formatf(&buffer, storebuffer, format, args); *buffer = 0; /* we terminate this with a zero byte */ return retcode; } -int curl_mvprintf(const char *format, va_list ap_save) +int curl_mvprintf(const char *format, va_list args) { - return formatf(stdout, fputc_wrapper, format, ap_save); + return formatf(stdout, fputc_wrapper, format, args); } -int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save) +int curl_mvfprintf(FILE *fd, const char *format, va_list args) { - return formatf(whereto, fputc_wrapper, format, ap_save); + return formatf(fd, fputc_wrapper, format, args); } diff --git a/lib/mqtt.c b/lib/mqtt.c index 35afe01207..84fd272e21 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -22,29 +22,22 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" +#include "urldata.h" #ifndef CURL_DISABLE_MQTT -#include "urldata.h" -#include #include "transfer.h" #include "sendf.h" +#include "curl_trc.h" #include "progress.h" #include "mqtt.h" #include "select.h" -#include "strdup.h" #include "url.h" #include "escape.h" -#include "curlx/warnless.h" -#include "curl_printf.h" -#include "curl_memory.h" -#include "multiif.h" #include "rand.h" - -/* The last #include file should be: */ -#include "memdebug.h" +#include "cfilters.h" +#include "connect.h" /* first byte is command. second byte is for flags. */ @@ -54,11 +47,11 @@ #define MQTT_MSG_SUBSCRIBE 0x82 #define MQTT_MSG_SUBACK 0x90 #define MQTT_MSG_DISCONNECT 0xe0 -#define MQTT_MSG_PINGREQ 0xC0 +/* #define MQTT_MSG_PINGREQ 0xC0 */ #define MQTT_MSG_PINGRESP 0xD0 -#define MQTT_CONNACK_LEN 2 -#define MQTT_SUBACK_LEN 3 +#define MQTT_CONNACK_LEN 2 +#define MQTT_SUBACK_LEN 3 #define MQTT_CLIENTID_LEN 12 /* "curl0123abcd" */ /* meta key for storing protocol meta at easy handle */ @@ -98,49 +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; @@ -148,14 +98,14 @@ static void mqtt_easy_dtor(void *key, size_t klen, void *entry) (void)klen; curlx_dyn_free(&mq->sendbuf); curlx_dyn_free(&mq->recvbuf); - free(mq); + curlx_free(mq); } static void mqtt_conn_dtor(void *key, size_t klen, void *entry) { (void)key; (void)klen; - free(entry); + curlx_free(entry); } static CURLcode mqtt_setup_conn(struct Curl_easy *data, @@ -165,12 +115,12 @@ static CURLcode mqtt_setup_conn(struct Curl_easy *data, struct mqtt_conn *mqtt; struct MQTT *mq; - mqtt = calloc(1, sizeof(*mqtt)); + mqtt = curlx_calloc(1, sizeof(*mqtt)); if(!mqtt || Curl_conn_meta_set(conn, CURL_META_MQTT_CONN, mqtt, mqtt_conn_dtor)) return CURLE_OUT_OF_MEMORY; - mq = calloc(1, sizeof(struct MQTT)); + mq = curlx_calloc(1, sizeof(struct MQTT)); if(!mq) return CURLE_OUT_OF_MEMORY; curlx_dyn_init(&mq->recvbuf, DYN_MQTT_RECV); @@ -193,8 +143,8 @@ static CURLcode mqtt_send(struct Curl_easy *data, result = Curl_xfer_send(data, buf, len, FALSE, &n); if(result) return result; - mq->lastTime = curlx_now(); - Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n); + mq->lastTime = *Curl_pgrs_now(data); + Curl_debug(data, CURLINFO_HEADER_OUT, buf, n); if(len != n) { size_t nsend = len - n; if(curlx_dyn_len(&mq->sendbuf)) { @@ -321,7 +271,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) int rc = 0; /* remain length */ int remain_pos = 0; - char remain[4] = {0}; + char remain[4] = { 0 }; size_t packetlen = 0; size_t start_user = 0; size_t start_pwd = 0; @@ -330,12 +280,10 @@ static CURLcode mqtt_connect(struct Curl_easy *data) char *packet = NULL; /* extracting username from request */ - const char *username = data->state.aptr.user ? - data->state.aptr.user : ""; + const char *username = data->state.aptr.user ? data->state.aptr.user : ""; const size_t ulen = strlen(username); /* extracting password from request */ - const char *passwd = data->state.aptr.passwd ? - data->state.aptr.passwd : ""; + const char *passwd = data->state.aptr.passwd ? data->state.aptr.passwd : ""; const size_t plen = strlen(passwd); const size_t payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2 + /* The plus 2s below are for the MSB and LSB describing the length of the @@ -352,7 +300,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) /* allocating packet */ if(packetlen > 0xFFFFFFF) return CURLE_WEIRD_SERVER_REPLY; - packet = calloc(1, packetlen); + packet = curlx_calloc(1, packetlen); if(!packet) return CURLE_OUT_OF_MEMORY; @@ -402,9 +350,9 @@ static CURLcode mqtt_connect(struct Curl_easy *data) end: if(packet) - free(packet); - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); + curlx_free(packet); + curlx_safefree(data->state.aptr.user); + curlx_safefree(data->state.aptr.passwd); return result; } @@ -431,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); @@ -455,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); @@ -474,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); @@ -526,7 +479,7 @@ static CURLcode mqtt_subscribe(struct Curl_easy *data) n = mqtt_encode_len((char *)encodedsize, packetlen); packetlen += n + 1; /* add one for the control packet type byte */ - packet = malloc(packetlen); + packet = curlx_malloc(packetlen); if(!packet) { result = CURLE_OUT_OF_MEMORY; goto fail; @@ -537,15 +490,15 @@ static CURLcode mqtt_subscribe(struct Curl_easy *data) packet[1 + n] = (mqtt->packetid >> 8) & 0xff; packet[2 + n] = mqtt->packetid & 0xff; packet[3 + n] = (topiclen >> 8) & 0xff; - packet[4 + n ] = topiclen & 0xff; + packet[4 + n] = topiclen & 0xff; memcpy(&packet[5 + n], topic, topiclen); packet[5 + n + topiclen] = 0; /* QoS zero */ result = mqtt_send(data, (const char *)packet, packetlen); fail: - free(topic); - free(packet); + curlx_free(topic); + curlx_free(packet); return result; } @@ -558,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; @@ -584,6 +543,8 @@ fail: return result; } +#define MAX_MQTT_MESSAGE_SIZE 0xFFFFFFF + static CURLcode mqtt_publish(struct Curl_easy *data) { CURLcode result; @@ -602,10 +563,11 @@ static CURLcode mqtt_publish(struct Curl_easy *data) DEBUGF(infof(data, "mqtt_publish without payload, return bad arg")); return CURLE_BAD_FUNCTION_ARGUMENT; } - if(postfieldsize < 0) + if(!curlx_sotouz_fits(postfieldsize, &payloadlen)) { + if(postfieldsize > 0) /* off_t does not fit into size_t */ + return CURLE_BAD_FUNCTION_ARGUMENT; payloadlen = strlen(payload); - else - payloadlen = (size_t)postfieldsize; + } result = mqtt_get_topic(data, &topic, &topiclen); if(result) @@ -613,9 +575,13 @@ static CURLcode mqtt_publish(struct Curl_easy *data) remaininglength = payloadlen + 2 + topiclen; encodelen = mqtt_encode_len(encodedbytes, remaininglength); + if(remaininglength > (MAX_MQTT_MESSAGE_SIZE - encodelen - 1)) { + result = CURLE_TOO_LARGE; + goto fail; + } /* add the control byte and the encoded remaining length */ - pkt = malloc(remaininglength + 1 + encodelen); + pkt = curlx_malloc(remaininglength + 1 + encodelen); if(!pkt) { result = CURLE_OUT_OF_MEMORY; goto fail; @@ -634,13 +600,14 @@ static CURLcode mqtt_publish(struct Curl_easy *data) result = mqtt_send(data, (const char *)pkt, i); fail: - free(pkt); - free(topic); + curlx_free(pkt); + curlx_free(topic); return result; } -static size_t mqtt_decode_len(unsigned char *buf, - size_t buflen, size_t *lenbytes) +/* return 0 on success, non-zero on error */ +static int mqtt_decode_len(size_t *lenp, const unsigned char *buf, + size_t buflen) { size_t len = 0; size_t mult = 1; @@ -648,19 +615,19 @@ static size_t mqtt_decode_len(unsigned char *buf, unsigned char encoded = 128; for(i = 0; (i < buflen) && (encoded & 128); i++) { + if(i == 4) + return 1; /* bad size */ encoded = buf[i]; len += (encoded & 127) * mult; mult *= 128; } - if(lenbytes) - *lenbytes = i; - - return len; + *lenp = len; + return 0; } -#ifdef DEBUGBUILD -static const char *statenames[]={ +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) +static const char *statenames[] = { "MQTT_FIRST", "MQTT_REMAINING_LENGTH", "MQTT_CONNACK", @@ -694,7 +661,6 @@ static void mqstate(struct Curl_easy *data, mqtt->nextstate = nextstate; } - static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done) { CURLcode result = CURLE_OK; @@ -755,13 +721,13 @@ MQTT_SUBACK_COMING: FALLTHROUGH(); case MQTT_PUB_REMAIN: { /* read rest of packet, but no more. Cap to buffer size */ - char buffer[4*1024]; + char buffer[4 * 1024]; size_t rest = mq->npacket; if(rest > sizeof(buffer)) 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; @@ -773,7 +739,7 @@ MQTT_SUBACK_COMING: } /* we received something */ - mq->lastTime = curlx_now(); + mq->lastTime = *Curl_pgrs_now(data); /* if QoS is set, message contains packet id */ result = Curl_client_write(data, CLIENTWRITE_BODY, buffer, nread); @@ -803,7 +769,7 @@ static CURLcode mqtt_do(struct Curl_easy *data, bool *done) if(!mq) return CURLE_FAILED_INIT; - mq->lastTime = curlx_now(); + mq->lastTime = *Curl_pgrs_now(data); mq->pingsent = FALSE; result = mqtt_connect(data); @@ -842,8 +808,8 @@ static CURLcode mqtt_ping(struct Curl_easy *data) if(mqtt->state == MQTT_FIRST && !mq->pingsent && data->set.upkeep_interval_ms > 0) { - struct curltime t = curlx_now(); - timediff_t diff = curlx_timediff(t, mq->lastTime); + struct curltime t = *Curl_pgrs_now(data); + timediff_t diff = curlx_ptimediff_ms(&t, &mq->lastTime); if(diff > data->set.upkeep_interval_ms) { /* 0xC0 is PINGREQ, and 0x00 is remaining length */ @@ -885,7 +851,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) if(result) return result; - infof(data, "mqtt_doing: state [%d]", (int) mqtt->state); + infof(data, "mqtt_doing: state [%d]", (int)mqtt->state); switch(mqtt->state) { case MQTT_FIRST: /* Read the initial byte only */ @@ -901,7 +867,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) Curl_debug(data, CURLINFO_HEADER_IN, (const char *)&mq->firstbyte, 1); /* we received something */ - mq->lastTime = curlx_now(); + mq->lastTime = *Curl_pgrs_now(data); /* remember the first byte */ mq->npacket = 0; @@ -921,7 +887,10 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) result = CURLE_WEIRD_SERVER_REPLY; if(result) break; - mq->remaining_length = mqtt_decode_len(mq->pkt_hd, mq->npacket, NULL); + if(mqtt_decode_len(&mq->remaining_length, mq->pkt_hd, mq->npacket)) { + result = CURLE_WEIRD_SERVER_REPLY; + break; + } mq->npacket = 0; if(mq->remaining_length) { mqstate(data, mqtt->nextstate, MQTT_NOSTATE); @@ -979,4 +948,65 @@ 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. + */ +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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#endif + +/* + * MQTT protocol. + */ +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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif /* CURL_DISABLE_MQTT */ diff --git a/lib/mqtt.h b/lib/mqtt.h index 8fb8a33c02..3d95293f12 100644 --- a/lib/mqtt.h +++ b/lib/mqtt.h @@ -23,9 +23,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #ifndef CURL_DISABLE_MQTT -extern const struct Curl_handler Curl_handler_mqtt; +extern const struct Curl_protocol Curl_protocol_mqtt; +#ifdef USE_SSL +extern const struct Curl_protocol Curl_protocol_mqtts; +#endif #endif #endif /* HEADER_CURL_MQTT_H */ diff --git a/lib/multi.c b/lib/multi.c index 918928d03c..f6d719287f 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -21,29 +21,23 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "urldata.h" #include "transfer.h" #include "url.h" #include "cfilters.h" #include "connect.h" #include "progress.h" -#include "easyif.h" -#include "share.h" +#include "curl_share.h" #include "psl.h" #include "multiif.h" #include "multi_ev.h" #include "sendf.h" -#include "curlx/timeval.h" +#include "curl_trc.h" #include "http.h" #include "select.h" -#include "curlx/warnless.h" #include "curlx/wait.h" -#include "speedcheck.h" #include "conncache.h" #include "multihandle.h" #include "sigpipe.h" @@ -52,15 +46,10 @@ #include "http_proxy.h" #include "http2.h" #include "socketpair.h" -#include "socks.h" -#include "urlapi-int.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "bufref.h" /* initial multi->xfers table size for a full multi */ -#define CURL_XFER_TABLE_SIZE 512 +#define CURL_XFER_TABLE_SIZE 512 /* CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97 @@ -99,18 +88,24 @@ static void move_pending_to_connect(struct Curl_multi *multi, struct Curl_easy *data); -static CURLMcode add_next_timeout(struct curltime now, +static CURLMcode add_next_timeout(const struct curltime *pnow, struct Curl_multi *multi, struct Curl_easy *d); -static CURLMcode multi_timeout(struct Curl_multi *multi, - struct curltime *expire_time, - long *timeout_ms); +static void multi_timeout(struct Curl_multi *multi, + struct curltime *expire_time, + long *timeout_ms); static void process_pending_handles(struct Curl_multi *multi); static void multi_xfer_bufs_free(struct Curl_multi *multi); #ifdef DEBUGBUILD static void multi_xfer_tbl_dump(struct Curl_multi *multi); #endif +static const struct curltime *multi_now(struct Curl_multi *multi) +{ + curlx_pnow(&multi->now); + return &multi->now; +} + /* function pointer called once when switching TO a state */ typedef void (*init_multistate_func)(struct Curl_easy *data); @@ -144,9 +139,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state NULL, /* PENDING */ NULL, /* SETUP */ Curl_init_CONNECT, /* CONNECT */ - NULL, /* RESOLVING */ NULL, /* CONNECTING */ - NULL, /* TUNNELING */ NULL, /* PROTOCONNECT */ NULL, /* PROTOCONNECTING */ NULL, /* DO */ @@ -165,23 +158,35 @@ 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)); #endif + /* really switching state */ data->mstate = state; - - if(state == MSTATE_COMPLETED) { + switch(state) { + case MSTATE_DONE: + CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); + break; + case MSTATE_COMPLETED: + /* we sometimes directly jump to COMPLETED, trigger also a notification + * in that case. */ + if(oldstate < MSTATE_DONE) + CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); /* changing to COMPLETED means it is in process and needs to go */ - DEBUGASSERT(Curl_uint_bset_contains(&data->multi->process, data->mid)); - Curl_uint_bset_remove(&data->multi->process, data->mid); - Curl_uint_bset_remove(&data->multi->pending, data->mid); /* to be sure */ + DEBUGASSERT(Curl_uint32_bset_contains(&data->multi->process, data->mid)); + Curl_uint32_bset_remove(&data->multi->process, data->mid); + Curl_uint32_bset_remove(&data->multi->pending, data->mid); /* to be sure */ - if(Curl_uint_bset_empty(&data->multi->process)) { + if(Curl_uint32_bset_empty(&data->multi->process)) { /* free the transfer buffer when we have no more active transfers */ multi_xfer_bufs_free(data->multi); } + break; + default: + break; } /* if this state has an init-function, run it */ @@ -190,18 +195,17 @@ static void mstate(struct Curl_easy *data, CURLMstate state } #ifndef DEBUGBUILD -#define multistate(x,y) mstate(x,y) +#define multistate(x, y) mstate(x, y) #else -#define multistate(x,y) mstate(x,y, __LINE__) +#define multistate(x, y) mstate(x, y, __LINE__) #endif - /* multi->proto_hash destructor. Should never be called as elements * MUST be added with their own destructor */ static void ph_freeentry(void *p) { (void)p; - /* Will always be FALSE. Cannot use a 0 assert here since compilers + /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ DEBUGASSERT(p == NULL); @@ -215,16 +219,18 @@ static void ph_freeentry(void *p) */ static void multi_addmsg(struct Curl_multi *multi, struct Curl_message *msg) { + if(!Curl_llist_count(&multi->msglist)) + CURLM_NTFY(multi->admin, CURLMNOTIFY_INFO_READ); Curl_llist_append(&multi->msglist, msg, &msg->list); } -struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size, +struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, size_t ev_hashsize, /* event hash */ size_t chashsize, /* connection hash */ size_t dnssize, /* dns hash */ size_t sesssize) /* TLS session cache */ { - struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi)); + struct Curl_multi *multi = curlx_calloc(1, sizeof(struct Curl_multi)); if(!multi) return NULL; @@ -232,12 +238,13 @@ struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size, multi->magic = CURL_MULTI_HANDLE; Curl_dnscache_init(&multi->dnscache, dnssize); + Curl_mntfy_init(multi); Curl_multi_ev_init(multi, ev_hashsize); - Curl_uint_tbl_init(&multi->xfers, NULL); - Curl_uint_bset_init(&multi->process); - Curl_uint_bset_init(&multi->dirty); - Curl_uint_bset_init(&multi->pending); - Curl_uint_bset_init(&multi->msgsent); + Curl_uint32_tbl_init(&multi->xfers, NULL); + Curl_uint32_bset_init(&multi->process); + Curl_uint32_bset_init(&multi->dirty); + Curl_uint32_bset_init(&multi->pending); + Curl_uint32_bset_init(&multi->msgsent); Curl_hash_init(&multi->proto_hash, 23, Curl_hash_str, curlx_str_key_compare, ph_freeentry); Curl_llist_init(&multi->msglist, NULL); @@ -245,12 +252,17 @@ struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size, multi->multiplexing = TRUE; multi->max_concurrent_streams = 100; multi->last_timeout_ms = -1; +#ifdef ENABLE_WAKEUP + multi->wakeup_pair[0] = CURL_SOCKET_BAD; + multi->wakeup_pair[1] = CURL_SOCKET_BAD; +#endif - if(Curl_uint_bset_resize(&multi->process, xfer_table_size) || - Curl_uint_bset_resize(&multi->pending, xfer_table_size) || - Curl_uint_bset_resize(&multi->dirty, xfer_table_size) || - Curl_uint_bset_resize(&multi->msgsent, xfer_table_size) || - Curl_uint_tbl_resize(&multi->xfers, xfer_table_size)) + if(Curl_mntfy_resize(multi) || + Curl_uint32_bset_resize(&multi->process, xfer_table_size) || + Curl_uint32_bset_resize(&multi->pending, xfer_table_size) || + Curl_uint32_bset_resize(&multi->dirty, xfer_table_size) || + Curl_uint32_bset_resize(&multi->msgsent, xfer_table_size) || + Curl_uint32_tbl_resize(&multi->xfers, xfer_table_size)) goto error; multi->admin = curl_easy_init(); @@ -260,11 +272,13 @@ struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size, multi->admin->multi = multi; multi->admin->state.internal = TRUE; Curl_llist_init(&multi->admin->state.timeoutlist, NULL); + #ifdef DEBUGBUILD if(getenv("CURL_DEBUG")) multi->admin->set.verbose = TRUE; #endif - Curl_uint_tbl_add(&multi->xfers, multi->admin, &multi->admin->mid); + Curl_uint32_tbl_add(&multi->xfers, multi->admin, &multi->admin->mid); + Curl_uint32_bset_add(&multi->process, multi->admin->mid); if(Curl_cshutdn_init(&multi->cshutdn, multi)) goto error; @@ -282,10 +296,27 @@ struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size, multi->wsa_event = WSACreateEvent(); if(multi->wsa_event == WSA_INVALID_EVENT) goto error; -#elif defined(ENABLE_WAKEUP) - if(wakeup_create(multi->wakeup_pair, TRUE) < 0) { - multi->wakeup_pair[0] = CURL_SOCKET_BAD; - multi->wakeup_pair[1] = CURL_SOCKET_BAD; +#endif +#ifdef ENABLE_WAKEUP + /* When enabled, rely on this to work. We ignore this in previous + * versions, but that seems an unnecessary complication. */ + if(Curl_wakeup_init(multi->wakeup_pair, TRUE) < 0) + goto error; +#endif + +#ifdef USE_IPV6 + if(Curl_probeipv6(multi)) + goto error; +#endif + +#ifdef USE_RESOLV_THREADED + if(xfer_table_size < CURL_XFER_TABLE_SIZE) { /* easy multi */ + if(Curl_async_thrdd_multi_init(multi, 0, 2, 10)) + goto error; + } + else { /* real multi handle */ + if(Curl_async_thrdd_multi_init(multi, 0, 20, 2000)) + goto error; } #endif @@ -293,6 +324,9 @@ struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size, error: +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_multi_destroy(multi, TRUE); +#endif Curl_multi_ev_cleanup(multi); Curl_hash_destroy(&multi->proto_hash); Curl_dnscache_destroy(&multi->dnscache); @@ -302,17 +336,22 @@ error: Curl_ssl_scache_destroy(multi->ssl_scache); #endif if(multi->admin) { + Curl_multi_ev_xfer_done(multi, multi->admin); multi->admin->multi = NULL; Curl_close(&multi->admin); } + Curl_mntfy_cleanup(multi); - Curl_uint_bset_destroy(&multi->process); - Curl_uint_bset_destroy(&multi->dirty); - Curl_uint_bset_destroy(&multi->pending); - Curl_uint_bset_destroy(&multi->msgsent); - Curl_uint_tbl_destroy(&multi->xfers); + Curl_uint32_bset_destroy(&multi->process); + Curl_uint32_bset_destroy(&multi->dirty); + Curl_uint32_bset_destroy(&multi->pending); + Curl_uint32_bset_destroy(&multi->msgsent); + Curl_uint32_tbl_destroy(&multi->xfers); +#ifdef ENABLE_WAKEUP + Curl_wakeup_destroy(multi->wakeup_pair); +#endif - free(multi); + curlx_free(multi); return NULL; } @@ -325,40 +364,55 @@ 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) { infof(data, "!!! WARNING !!!"); infof(data, "This is a debug build of libcurl, " - "do not use in production."); + "do not use in production."); multi->warned = TRUE; } } #else -#define multi_warn_debug(x,y) Curl_nop_stmt +#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_assess_wakeup(struct Curl_multi *multi) +{ +#ifdef ENABLE_WAKEUP + if(multi->socket_cb) + return Curl_multi_ev_assess_xfer(multi, multi->admin); +#else + (void)multi; +#endif + return CURLM_OK; +} static CURLMcode multi_xfers_add(struct Curl_multi *multi, struct Curl_easy *data) { - unsigned int capacity = Curl_uint_tbl_capacity(&multi->xfers); - unsigned int new_size = 0; + uint32_t capacity = Curl_uint32_tbl_capacity(&multi->xfers); + uint32_t new_size = 0; /* Prepare to make this into a CURLMOPT_MAX_TRANSFERS, because some * applications may want to prevent a run-away of their memory use. */ /* UINT_MAX is our "invalid" id, do not let the table grow up to that. */ - const unsigned int max_capacity = UINT_MAX - 1; + const uint32_t max_capacity = UINT_MAX - 1; 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). */ - unsigned int used = Curl_uint_tbl_count(&multi->xfers); - unsigned int unused = capacity - used; - unsigned int min_unused = CURLMAX(capacity >> 2, 4); + uint32_t used = Curl_uint32_tbl_count(&multi->xfers); + uint32_t unused = capacity - used; + uint32_t min_unused = CURLMAX(capacity >> 2, 4); if(unused <= min_unused) { /* Make sure the uint arithmetic here works on the corner * cases where we are close to max_capacity or UINT_MAX */ @@ -368,8 +422,8 @@ static CURLMcode multi_xfers_add(struct Curl_multi *multi, new_size = max_capacity; /* can not be larger than this */ } else { - /* make it a 64 multiple, since our bitsets frow by that and - * small (easy_multi) grows to at least 64 on first resize. */ + /* make it a 64 multiple, since our bitsets frow by that and + * small (easy_multi) grows to at least 64 on first resize. */ new_size = (((used + min_unused) + 63) / 64) * 64; } } @@ -381,30 +435,30 @@ static CURLMcode multi_xfers_add(struct Curl_multi *multi, * to work properly when larger than the table, but not * the other way around. */ CURL_TRC_M(data, "increasing xfer table size to %u", new_size); - if(Curl_uint_bset_resize(&multi->process, new_size) || - Curl_uint_bset_resize(&multi->dirty, new_size) || - Curl_uint_bset_resize(&multi->pending, new_size) || - Curl_uint_bset_resize(&multi->msgsent, new_size) || - Curl_uint_tbl_resize(&multi->xfers, new_size)) + if(Curl_uint32_bset_resize(&multi->process, new_size) || + Curl_uint32_bset_resize(&multi->dirty, new_size) || + Curl_uint32_bset_resize(&multi->pending, new_size) || + Curl_uint32_bset_resize(&multi->msgsent, new_size) || + Curl_uint32_tbl_resize(&multi->xfers, new_size)) return CURLM_OUT_OF_MEMORY; } /* Insert the easy into the table now */ - if(!Curl_uint_tbl_add(&multi->xfers, data, &data->mid)) { + if(!Curl_uint32_tbl_add(&multi->xfers, data, &data->mid)) { /* MUST only happen when table is full */ - DEBUGASSERT(Curl_uint_tbl_capacity(&multi->xfers) <= - Curl_uint_tbl_count(&multi->xfers)); + DEBUGASSERT(Curl_uint32_tbl_capacity(&multi->xfers) <= + Curl_uint32_tbl_count(&multi->xfers)); return CURLM_OUT_OF_MEMORY; } return CURLM_OK; } - -CURLMcode curl_multi_add_handle(CURLM *m, CURL *d) +CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) { - CURLMcode rc; + CURLMcode mresult; struct Curl_multi *multi = m; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; + /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -426,14 +480,14 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d) handles are still alive - but if there are none alive anymore, it is fine to start over and unmark the "deadness" of this handle. This means only the admin handle MUST be present. */ - if((Curl_uint_tbl_count(&multi->xfers) != 1) || - !Curl_uint_tbl_contains(&multi->xfers, 0)) + if((Curl_uint32_tbl_count(&multi->xfers) != 1) || + !Curl_uint32_tbl_contains(&multi->xfers, 0)) return CURLM_ABORTED_BY_CALLBACK; multi->dead = FALSE; - Curl_uint_bset_clear(&multi->process); - Curl_uint_bset_clear(&multi->dirty); - Curl_uint_bset_clear(&multi->pending); - Curl_uint_bset_clear(&multi->msgsent); + Curl_uint32_bset_clear(&multi->process); + Curl_uint32_bset_clear(&multi->dirty); + Curl_uint32_bset_clear(&multi->pending); + Curl_uint32_bset_clear(&multi->msgsent); } if(data->multi_easy) { @@ -477,7 +531,7 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d) #endif /* add the easy handle to the process set */ - Curl_uint_bset_add(&multi->process, data->mid); + Curl_uint32_bset_add(&multi->process, data->mid); ++multi->xfers_alive; ++multi->xfers_total_ever; @@ -486,14 +540,15 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d) /* Make sure the new handle will run */ Curl_multi_mark_dirty(data); + /* Necessary in event based processing, where dirty handles trigger * a timeout callback invocation. */ - rc = Curl_update_timer(multi); - if(rc) { + mresult = Curl_update_timer(multi); + if(mresult) { data->multi = NULL; /* not anymore */ - Curl_uint_tbl_remove(&multi->xfers, data->mid); - data->mid = UINT_MAX; - return rc; + Curl_uint32_tbl_remove(&multi->xfers, data->mid); + data->mid = UINT32_MAX; + return mresult; } /* The admin handle only ever has default timeouts set. To improve the @@ -505,9 +560,15 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d) data->set.server_response_timeout; multi->admin->set.no_signal = data->set.no_signal; + mresult = multi_assess_wakeup(multi); + if(mresult) { + failf(data, "error enabling wakeup listening: %d", mresult); + return mresult; + } + CURL_TRC_M(data, "added to multi, mid=%u, running=%u, total=%u", data->mid, Curl_multi_xfers_running(multi), - Curl_uint_tbl_count(&multi->xfers)); + Curl_uint32_tbl_count(&multi->xfers)); return CURLM_OK; } @@ -522,8 +583,8 @@ static void debug_print_sock_hash(void *p) { struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p; - fprintf(stderr, " [readers %u][writers %u]", - sh->readers, sh->writers); + curl_mfprintf(stderr, " [readers %u][writers %u]", + sh->readers, sh->writers); } #endif @@ -531,99 +592,91 @@ struct multi_done_ctx { BIT(premature); }; +static bool multi_conn_should_close(struct connectdata *conn, + struct Curl_easy *data, + bool premature) +{ + /* if conn->bits.close is TRUE, it means that the connection should be + closed in spite of everything else. */ + if(conn->bits.close) + return TRUE; + + /* if data->set.reuse_forbid is TRUE, it means the libcurl client has + forced us to close this connection. This is ignored for requests taking + place in a NTLM/NEGOTIATE authentication handshake. */ + if(data->set.reuse_forbid +#ifdef USE_NTLM + && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 || + conn->proxy_ntlm_state == NTLMSTATE_TYPE2) +#endif +#ifdef USE_SPNEGO + && !(conn->http_negotiate_state == GSS_AUTHRECV || + conn->proxy_negotiate_state == GSS_AUTHRECV) +#endif + ) + return TRUE; + + /* 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->scheme && + !(conn->scheme->flags & PROTOPT_CONN_REUSE)) + return TRUE; + + /* if premature is TRUE, it means this connection was said to be DONE before + the entire request operation is complete and thus we cannot know in what + state it is for reusing, so we are forced to close it. In a perfect world + we can add code that keep track of if we really must close it here or not, + but currently we have no such detail knowledge. */ + if(premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET)) + return TRUE; + + return FALSE; +} + static void multi_done_locked(struct connectdata *conn, struct Curl_easy *data, void *userdata) { struct multi_done_ctx *mdctx = userdata; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - const char *host = -#ifndef CURL_DISABLE_PROXY - conn->bits.socksproxy ? - conn->socks_proxy.host.dispname : - conn->bits.httpproxy ? conn->http_proxy.host.dispname : -#endif - conn->bits.conn_to_host ? conn->conn_to_host.dispname : - conn->host.dispname; - int port = -#ifndef CURL_DISABLE_PROXY - conn->bits.httpproxy ? conn->http_proxy.port : -#endif - conn->bits.conn_to_port ? conn->conn_to_port : - conn->remote_port; -#endif Curl_detach_connection(data); - CURL_TRC_M(data, "multi_done_locked, in use=%u", - Curl_uint_spbset_count(&conn->xfers_attached)); + CURL_TRC_M(data, "multi_done_locked, in use=%u", conn->attached_xfers); if(CONN_INUSE(conn)) { /* Stop if still used. */ CURL_TRC_M(data, "Connection still in use %u, no more multi_done now!", - Curl_uint_spbset_count(&conn->xfers_attached)); + conn->attached_xfers); 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 data->set.reuse_forbid is TRUE, it means the libcurl client has - forced us to close this connection. This is ignored for requests taking - place in a NTLM/NEGOTIATE authentication handshake - - if conn->bits.close is TRUE, it means that the connection should be - closed in spite of all our efforts to be nice, due to protocol - restrictions in our or the server's end - - if premature is TRUE, it means this connection was said to be DONE before - the entire request operation is complete and thus we cannot know in what - state it is for reusing, so we are forced to close it. In a perfect world - we can add code that keep track of if we really must close it here or not, - but currently we have no such detail knowledge. - */ - - if((data->set.reuse_forbid -#ifdef USE_NTLM - && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 || - conn->proxy_ntlm_state == NTLMSTATE_TYPE2) -#endif -#ifdef USE_SPNEGO - && !(conn->http_negotiate_state == GSS_AUTHRECV || - conn->proxy_negotiate_state == GSS_AUTHRECV) -#endif - ) || conn->bits.close - || (mdctx->premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s:%d, " + if(multi_conn_should_close(conn, data, (bool)mdctx->premature)) { + CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s, " "forbid=%d, close=%d, premature=%d, conn_multiplex=%d", - conn->connection_id, host, port, data->set.reuse_forbid, - conn->bits.close, mdctx->premature, + conn->connection_id, conn->destination, + 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 + CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s was shutdown" + " by server, not reusing", conn->connection_id, + conn->destination); 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 + infof(data, "Connection #%" FMT_OFF_T " to host %s left intact", + conn->connection_id, conn->destination); } else { /* connection was removed from the cpool and destroyed. */ @@ -639,9 +692,6 @@ static CURLcode multi_done(struct Curl_easy *data, { CURLcode result; struct connectdata *conn = data->conn; - struct multi_done_ctx mdctx; - - memset(&mdctx, 0, sizeof(mdctx)); CURL_TRC_M(data, "multi_done: status: %d prem: %d done: %d", (int)status, (int)premature, data->state.done); @@ -651,20 +701,20 @@ static CURLcode multi_done(struct Curl_easy *data, return CURLE_OK; /* Shut down any ongoing async resolver operation. */ - Curl_async_shutdown(data); + Curl_resolv_shutdown_all(data); /* Cleanup possible redirect junk */ - Curl_safefree(data->req.newurl); - Curl_safefree(data->req.location); + curlx_safefree(data->req.newurl); + curlx_safefree(data->req.location); switch(status) { 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: @@ -672,34 +722,43 @@ 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 && 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) { - /* avoid this if we already aborted by callback to avoid this calling - another callback */ + if(data->mstate > MSTATE_CONNECTING && + (result != CURLE_ABORTED_BY_CALLBACK)) { + /* avoid this if + * - the transfer has not connected + * - we already aborted by callback to avoid this calling another callback + */ int rc = Curl_pgrsDone(data); if(!result && rc) result = CURLE_ABORTED_BY_CALLBACK; } /* Make sure that transfer client writes are really done now. */ - result = Curl_1st_err(result, Curl_xfer_write_done(data, premature)); + result = Curl_1st_fatal(result, Curl_xfer_write_done(data, premature)); /* Inform connection filters that this transfer is done */ - Curl_conn_ev_data_done(data, premature); + if(conn) + Curl_conn_ev_data_done(data, premature); process_pending_handles(data->multi); /* connection / multiplex */ if(!result) result = Curl_req_done(&data->req, data, premature); - /* Under the potential connection pool's share lock, decide what to - * do with the transfer's connection. */ - mdctx.premature = premature; - Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx); + if(conn) { + /* Under the potential connection pool's share lock, decide what to + * do with the transfer's connection. */ + struct multi_done_ctx mdctx; + + memset(&mdctx, 0, sizeof(mdctx)); + mdctx.premature = premature; + Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx); + } /* flush the netrc cache */ Curl_netrc_cleanup(&data->state.netrc); @@ -712,19 +771,18 @@ static void close_connect_only(struct connectdata *conn, { (void)userdata; (void)data; - if(conn->connect_only) + if(conn->bits.connect_only) connclose(conn, "Removing connect-only easy handle"); } -CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) +CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) { struct Curl_multi *multi = m; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; bool premature; struct Curl_llist_node *e; - CURLMcode rc; - bool removed_timer = FALSE; - unsigned int mid; + CURLMcode mresult; + uint32_t mid; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -742,11 +800,11 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) if(data->multi != multi) return CURLM_BAD_EASY_HANDLE; - if(data->mid == UINT_MAX) { + if(data->mid == UINT32_MAX) { DEBUGASSERT(0); return CURLM_INTERNAL_ERROR; } - if(Curl_uint_tbl_get(&multi->xfers, data->mid) != data) { + if(Curl_uint32_tbl_get(&multi->xfers, data->mid) != data) { DEBUGASSERT(0); return CURLM_INTERNAL_ERROR; } @@ -770,7 +828,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); } @@ -778,10 +836,10 @@ 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_uint_bset_contains(&multi->msgsent, data->mid)) + if(!Curl_uint32_bset_contains(&multi->msgsent, data->mid)) --multi->xfers_alive; Curl_wildcard_dtor(&data->wildcard); @@ -814,7 +872,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) if(data->state.lastconnect_id != -1) { /* Mark any connect-only connection for closure */ Curl_cpool_do_by_id(data, data->state.lastconnect_id, - close_connect_only, NULL); + close_connect_only, NULL); } #ifdef USE_LIBPSL @@ -837,29 +895,33 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) /* clear the association to this multi handle */ mid = data->mid; - DEBUGASSERT(Curl_uint_tbl_contains(&multi->xfers, mid)); - Curl_uint_tbl_remove(&multi->xfers, mid); - Curl_uint_bset_remove(&multi->process, mid); - Curl_uint_bset_remove(&multi->dirty, mid); - Curl_uint_bset_remove(&multi->pending, mid); - Curl_uint_bset_remove(&multi->msgsent, mid); + DEBUGASSERT(Curl_uint32_tbl_contains(&multi->xfers, mid)); + Curl_uint32_tbl_remove(&multi->xfers, mid); + Curl_uint32_bset_remove(&multi->process, mid); + Curl_uint32_bset_remove(&multi->dirty, mid); + Curl_uint32_bset_remove(&multi->pending, mid); + Curl_uint32_bset_remove(&multi->msgsent, mid); data->multi = NULL; - data->mid = UINT_MAX; - data->master_mid = UINT_MAX; + data->mid = UINT32_MAX; + data->master_mid = UINT32_MAX; /* NOTE NOTE NOTE We do not touch the easy handle here! */ process_pending_handles(multi); - if(removed_timer) { - rc = Curl_update_timer(multi); - if(rc) - return rc; + mresult = Curl_update_timer(multi); + if(mresult) + return mresult; + + mresult = multi_assess_wakeup(multi); + if(mresult) { + failf(data, "error enabling wakeup listening: %d", mresult); + return mresult; } CURL_TRC_M(data, "removed from multi, mid=%u, running=%u, total=%u", mid, Curl_multi_xfers_running(multi), - Curl_uint_tbl_count(&multi->xfers)); + Curl_uint32_tbl_count(&multi->xfers)); return CURLM_OK; } @@ -879,9 +941,13 @@ void Curl_detach_connection(struct Curl_easy *data) { struct connectdata *conn = data->conn; if(conn) { - Curl_uint_spbset_remove(&conn->xfers_attached, data->mid); - if(Curl_uint_spbset_empty(&conn->xfers_attached)) - conn->attached_multi = NULL; + /* this should never happen, prevent underflow */ + DEBUGASSERT(conn->attached_xfers); + if(conn->attached_xfers) { + conn->attached_xfers--; + if(!conn->attached_xfers) + conn->attached_multi = NULL; + } } data->conn = NULL; } @@ -897,262 +963,287 @@ void Curl_attach_connection(struct Curl_easy *data, DEBUGASSERT(data); DEBUGASSERT(!data->conn); DEBUGASSERT(conn); + DEBUGASSERT(conn->attached_xfers < UINT32_MAX); data->conn = conn; - Curl_uint_spbset_add(&conn->xfers_attached, data->mid); + conn->attached_xfers++; /* all attached transfers must be from the same multi */ if(!conn->attached_multi) 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 */ +static CURLcode multi_adjust_pollset(struct Curl_easy *data, + struct easy_pollset *ps) +{ + CURLcode result = CURLE_OK; + + if(ps->n) { + const struct curltime *pnow = Curl_pgrs_now(data); + bool send_blocked, recv_blocked; + + recv_blocked = (Curl_rlimit_avail(&data->progress.dl.rlimit, pnow) <= 0); + send_blocked = (Curl_rlimit_avail(&data->progress.ul.rlimit, pnow) <= 0); + if(send_blocked || recv_blocked) { + int i; + for(i = 0; i <= SECONDARYSOCKET; ++i) { + curl_socket_t sock = data->conn->sock[i]; + if(sock == CURL_SOCKET_BAD) + continue; + if(recv_blocked && Curl_pollset_want_recv(data, ps, sock)) { + result = Curl_pollset_remove_in(data, ps, sock); + if(result) + break; + } + if(send_blocked && Curl_pollset_want_send(data, ps, sock)) { + result = Curl_pollset_remove_out(data, ps, sock); + if(result) + break; + } + } + } + + /* Not blocked and wanting to receive. If there is data pending + * in the connection filters, make transfer run again. */ + if(!recv_blocked && + ((Curl_pollset_want_recv(data, ps, data->conn->sock[FIRSTSOCKET]) && + Curl_conn_data_pending(data, FIRSTSOCKET)) || + (Curl_pollset_want_recv(data, ps, data->conn->sock[SECONDARYSOCKET]) && + Curl_conn_data_pending(data, SECONDARYSOCKET)))) { + CURL_TRC_M(data, "pollset[] has POLLIN, but there is still " + "buffered input -> mark as dirty"); + Curl_multi_mark_dirty(data); + } + } + return result; } static CURLcode mstate_connecting_pollset(struct Curl_easy *data, struct easy_pollset *ps) { - if(data->conn) { - curl_socket_t sockfd = Curl_conn_get_first_socket(data); - if(sockfd != CURL_SOCKET_BAD) { - /* Default is to wait to something from the server */ - return Curl_pollset_change(data, ps, sockfd, CURL_POLL_IN, 0); - } + struct connectdata *conn = data->conn; + curl_socket_t sockfd; + CURLcode result = CURLE_OK; + + if(Curl_xfer_recv_is_paused(data)) + return CURLE_OK; + /* If a socket is set, receiving is default. If the socket + * has not been determined yet (eyeballing), always ask the + * connection filters for what to monitor. */ + sockfd = Curl_conn_get_first_socket(data); + if(sockfd != CURL_SOCKET_BAD) { + result = Curl_pollset_change(data, ps, sockfd, CURL_POLL_IN, 0); + if(!result) + result = multi_adjust_pollset(data, ps); } - return CURLE_OK; + if(!result) + result = Curl_conn_adjust_pollset(data, conn, ps); + return result; } static CURLcode mstate_protocol_pollset(struct Curl_easy *data, struct easy_pollset *ps) { - if(data->conn) { - curl_socket_t sockfd; - if(data->conn->handler->proto_pollset) - return data->conn->handler->proto_pollset(data, ps); - sockfd = data->conn->sock[FIRSTSOCKET]; + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + + 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) { /* Default is to wait to something from the server */ - return Curl_pollset_change(data, ps, sockfd, CURL_POLL_IN, 0); + result = Curl_pollset_change(data, ps, sockfd, CURL_POLL_IN, 0); } } - return CURLE_OK; + if(!result) + result = multi_adjust_pollset(data, ps); + if(!result) + result = Curl_conn_adjust_pollset(data, conn, ps); + return result; } static CURLcode mstate_do_pollset(struct Curl_easy *data, struct easy_pollset *ps) { - if(data->conn) { - if(data->conn->handler->doing_pollset) - return data->conn->handler->doing_pollset(data, ps); - else if(CONN_SOCK_IDX_VALID(data->conn->send_idx)) { - /* Default is that we want to send something to the server */ - return Curl_pollset_add_out( - data, ps, data->conn->sock[data->conn->send_idx]); - } + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + + 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]); } - return CURLE_OK; + if(!result) + result = multi_adjust_pollset(data, ps); + if(!result) + result = Curl_conn_adjust_pollset(data, conn, ps); + return result; } static CURLcode mstate_domore_pollset(struct Curl_easy *data, struct easy_pollset *ps) { - if(data->conn) { - if(data->conn->handler->domore_pollset) - return data->conn->handler->domore_pollset(data, ps); - else if(CONN_SOCK_IDX_VALID(data->conn->send_idx)) { - /* Default is that we want to send something to the server */ - return Curl_pollset_add_out( - data, ps, data->conn->sock[data->conn->send_idx]); - } + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + + 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]); } - return CURLE_OK; + if(!result) + result = multi_adjust_pollset(data, ps); + if(!result) + result = Curl_conn_adjust_pollset(data, conn, ps); + return result; } static CURLcode mstate_perform_pollset(struct Curl_easy *data, struct easy_pollset *ps) { - if(!data->conn) - return CURLE_OK; - else if(data->conn->handler->perform_pollset) - return data->conn->handler->perform_pollset(data, ps); - else { - /* Default is to obey the data->req.keepon flags for send/recv */ - CURLcode result = CURLE_OK; - if(CURL_WANT_RECV(data) && CONN_SOCK_IDX_VALID(data->conn->recv_idx)) { - result = Curl_pollset_add_in( - data, ps, data->conn->sock[data->conn->recv_idx]); - } + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; - if(!result && Curl_req_want_send(data) && - CONN_SOCK_IDX_VALID(data->conn->send_idx)) { - result = Curl_pollset_add_out( - data, ps, data->conn->sock[data->conn->send_idx]); + if(conn->scheme->run->perform_pollset) + result = conn->scheme->run->perform_pollset(data, ps); + else { + /* Default is to obey the request flags for send/recv */ + if(Curl_req_want_recv(data) && CONN_SOCK_IDX_VALID(conn->recv_idx)) { + result = Curl_pollset_add_in(data, ps, conn->sock[conn->recv_idx]); + } + if(!result && Curl_req_want_send(data) && + CONN_SOCK_IDX_VALID(conn->send_idx)) { + result = Curl_pollset_add_out(data, ps, conn->sock[conn->send_idx]); } - return result; } + if(!result) + result = multi_adjust_pollset(data, ps); + if(!result) + result = Curl_conn_adjust_pollset(data, conn, ps); + return result; } /* Initializes `poll_set` with the current socket poll actions needed * for transfer `data`. */ CURLMcode Curl_multi_pollset(struct Curl_easy *data, - struct easy_pollset *ps, - const char *caller) + struct easy_pollset *ps) { - CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; - bool expect_sockets = TRUE; + Curl_pollset_reset(ps); +#ifdef ENABLE_WAKEUP + /* The admin handle always listens on the wakeup socket when there + * are transfers alive. */ + if(data->multi && (data == data->multi->admin) && + data->multi->xfers_alive) { + result = Curl_pollset_add_in(data, ps, data->multi->wakeup_pair[0]); + } +#endif /* If the transfer has no connection, this is fine. Happens when called via curl_multi_remove_handle() => Curl_multi_ev_assess() => Curl_multi_pollset(). */ - Curl_pollset_reset(ps); - if(!data->conn) - return CURLM_OK; + if(!result && data->conn) { + switch(data->mstate) { + case MSTATE_INIT: + case MSTATE_PENDING: + case MSTATE_SETUP: + case MSTATE_CONNECT: + /* nothing to poll for yet */ + break; - switch(data->mstate) { - case MSTATE_INIT: - case MSTATE_PENDING: - case MSTATE_SETUP: - case MSTATE_CONNECT: - /* nothing to poll for yet */ - expect_sockets = FALSE; - break; - - case MSTATE_RESOLVING: - result = Curl_resolv_pollset(data, ps); - /* connection filters are not involved in this phase. It's ok if we get no - * sockets to wait for. Resolving can wake up from other sources. */ - expect_sockets = FALSE; - break; - - case MSTATE_CONNECTING: - case MSTATE_TUNNELING: - if(!Curl_xfer_recv_is_paused(data)) { - result = mstate_connecting_pollset(data, ps); + case MSTATE_CONNECTING: + if(data->conn && !data->conn->bits.dns_resolved) + result = Curl_resolv_pollset(data, ps); if(!result) - result = Curl_conn_adjust_pollset(data, data->conn, ps); + result = mstate_connecting_pollset(data, ps); + break; + + case MSTATE_PROTOCONNECT: + case MSTATE_PROTOCONNECTING: + result = mstate_protocol_pollset(data, ps); + break; + + case MSTATE_DO: + case MSTATE_DOING: + result = mstate_do_pollset(data, ps); + break; + + case MSTATE_DOING_MORE: + result = mstate_domore_pollset(data, ps); + break; + + case MSTATE_DID: /* same as PERFORMING in regard to polling */ + case MSTATE_PERFORMING: + result = mstate_perform_pollset(data, ps); + break; + + case MSTATE_RATELIMITING: + /* we need to let time pass, ignore socket(s) */ + break; + + case MSTATE_DONE: + case MSTATE_COMPLETED: + case MSTATE_MSGSENT: + /* nothing more to poll for */ + break; + + default: + failf(data, "multi_getsock: unexpected multi state %d", data->mstate); + DEBUGASSERT(0); + break; } - else - expect_sockets = FALSE; - break; - - case MSTATE_PROTOCONNECT: - case MSTATE_PROTOCONNECTING: - result = mstate_protocol_pollset(data, ps); - if(!result) - result = Curl_conn_adjust_pollset(data, data->conn, ps); - break; - - case MSTATE_DO: - case MSTATE_DOING: - result = mstate_do_pollset(data, ps); - if(!result) - result = Curl_conn_adjust_pollset(data, data->conn, ps); - break; - - case MSTATE_DOING_MORE: - result = mstate_domore_pollset(data, ps); - if(!result) - result = Curl_conn_adjust_pollset(data, data->conn, ps); - break; - - case MSTATE_DID: /* same as PERFORMING in regard to polling */ - case MSTATE_PERFORMING: - result = mstate_perform_pollset(data, ps); - if(!result) - result = Curl_conn_adjust_pollset(data, data->conn, ps); - break; - - case MSTATE_RATELIMITING: - /* we need to let time pass, ignore socket(s) */ - expect_sockets = FALSE; - break; - - case MSTATE_DONE: - case MSTATE_COMPLETED: - case MSTATE_MSGSENT: - /* nothing more to poll for */ - expect_sockets = FALSE; - break; - - default: - failf(data, "multi_getsock: unexpected multi state %d", data->mstate); - DEBUGASSERT(0); - expect_sockets = FALSE; - break; } if(result) { if(result == CURLE_OUT_OF_MEMORY) - mresult = CURLM_OUT_OF_MEMORY; - else { - failf(data, "error determining pollset: %d", result); - mresult = CURLM_INTERNAL_ERROR; - } - goto out; - } - - /* Unblocked and waiting to receive with buffered input. - * Make transfer run again at next opportunity. */ - if(!Curl_xfer_is_blocked(data) && !Curl_xfer_is_too_fast(data) && - ((Curl_pollset_want_read(data, ps, data->conn->sock[FIRSTSOCKET]) && - Curl_conn_data_pending(data, FIRSTSOCKET)) || - (Curl_pollset_want_read(data, ps, data->conn->sock[SECONDARYSOCKET]) && - Curl_conn_data_pending(data, SECONDARYSOCKET)))) { - CURL_TRC_M(data, "%s pollset[] has POLLIN, but there is still " - "buffered input to consume -> mark as dirty", caller); - Curl_multi_mark_dirty(data); + return CURLM_OUT_OF_MEMORY; + failf(data, "error determining pollset: %d", result); + return CURLM_INTERNAL_ERROR; } +#ifdef CURLVERBOSE if(CURL_TRC_M_is_verbose(data)) { size_t timeout_count = Curl_llist_count(&data->state.timeoutlist); switch(ps->n) { - case 0: - CURL_TRC_M(data, "%s pollset[], timeouts=%zu, paused %d/%d (r/w)", - caller, timeout_count, - Curl_xfer_send_is_paused(data), - Curl_xfer_recv_is_paused(data)); - break; - case 1: - CURL_TRC_M(data, "%s pollset[fd=%" FMT_SOCKET_T " %s%s], timeouts=%zu", - caller, ps->sockets[0], - (ps->actions[0] & CURL_POLL_IN) ? "IN" : "", - (ps->actions[0] & CURL_POLL_OUT) ? "OUT" : "", - timeout_count); - break; - case 2: - CURL_TRC_M(data, "%s pollset[fd=%" FMT_SOCKET_T " %s%s, " - "fd=%" FMT_SOCKET_T " %s%s], timeouts=%zu", - caller, ps->sockets[0], - (ps->actions[0] & CURL_POLL_IN) ? "IN" : "", - (ps->actions[0] & CURL_POLL_OUT) ? "OUT" : "", - ps->sockets[1], - (ps->actions[1] & CURL_POLL_IN) ? "IN" : "", - (ps->actions[1] & CURL_POLL_OUT) ? "OUT" : "", - timeout_count); - break; - default: - CURL_TRC_M(data, "%s pollset[fds=%u], timeouts=%zu", - caller, ps->n, timeout_count); - break; + case 0: + CURL_TRC_M(data, "pollset[], timeouts=%zu, paused %d/%d (r/w)", + timeout_count, + Curl_xfer_send_is_paused(data), + Curl_xfer_recv_is_paused(data)); + break; + case 1: + CURL_TRC_M(data, "pollset[fd=%" FMT_SOCKET_T " %s%s], timeouts=%zu", + ps->sockets[0], + (ps->actions[0] & CURL_POLL_IN) ? "IN" : "", + (ps->actions[0] & CURL_POLL_OUT) ? "OUT" : "", + timeout_count); + break; + case 2: + CURL_TRC_M(data, "pollset[fd=%" FMT_SOCKET_T " %s%s, " + "fd=%" FMT_SOCKET_T " %s%s], timeouts=%zu", + ps->sockets[0], + (ps->actions[0] & CURL_POLL_IN) ? "IN" : "", + (ps->actions[0] & CURL_POLL_OUT) ? "OUT" : "", + ps->sockets[1], + (ps->actions[1] & CURL_POLL_IN) ? "IN" : "", + (ps->actions[1] & CURL_POLL_OUT) ? "OUT" : "", + timeout_count); + break; + default: + CURL_TRC_M(data, "pollset[fds=%u], timeouts=%zu", ps->n, timeout_count); + break; } - CURL_TRC_M_TIMEOUTS(data); + CURL_TRC_EASY_TIMERS(data); } +#endif - if(expect_sockets && !ps->n && data->multi && - !Curl_uint_bset_contains(&data->multi->dirty, data->mid) && - !Curl_llist_count(&data->state.timeoutlist) && - !Curl_cwriter_is_paused(data) && !Curl_creader_is_paused(data) && - Curl_conn_is_ip_connected(data, FIRSTSOCKET)) { - /* We expected sockets for POLL monitoring, but none are set. - * We are not dirty (and run anyway). - * We are not waiting on any timer. - * None of the READ/WRITE directions are paused. - * We are connected to the server on IP level, at least. */ - infof(data, "WARNING: no socket in pollset or timer, transfer may stall!"); - DEBUGASSERT(0); - } -out: - return mresult; + return CURLM_OK; } CURLMcode curl_multi_fdset(CURLM *m, @@ -1165,7 +1256,8 @@ CURLMcode curl_multi_fdset(CURLM *m, int this_max_fd = -1; struct Curl_multi *multi = m; struct easy_pollset ps; - unsigned int i, mid; + unsigned int i; + uint32_t mid; (void)exc_fd_set; if(!GOOD_MULTI_HANDLE(multi)) @@ -1175,7 +1267,7 @@ CURLMcode curl_multi_fdset(CURLM *m, return CURLM_RECURSIVE_API_CALL; Curl_pollset_init(&ps); - if(Curl_uint_bset_first(&multi->process, &mid)) { + if(Curl_uint32_bset_first(&multi->process, &mid)) { do { struct Curl_easy *data = Curl_multi_get_easy(multi, mid); @@ -1184,27 +1276,19 @@ CURLMcode curl_multi_fdset(CURLM *m, continue; } - Curl_multi_pollset(data, &ps, "curl_multi_fdset"); + Curl_multi_pollset(data, &ps); for(i = 0; i < ps.n; i++) { 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]; } - } - while(Curl_uint_bset_next(&multi->process, mid, &mid)); + } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); } Curl_cshutdn_setfds(&multi->cshutdn, multi->admin, @@ -1222,10 +1306,11 @@ CURLMcode curl_multi_waitfds(CURLM *m, unsigned int *fd_count) { struct Curl_waitfds cwfds; - CURLMcode result = CURLM_OK; + CURLMcode mresult = CURLM_OK; struct Curl_multi *multi = m; struct easy_pollset ps; - unsigned int need = 0, mid; + unsigned int need = 0; + uint32_t mid; if(!ufds && (size || !fd_count)) return CURLM_BAD_FUNCTION_ARGUMENT; @@ -1238,31 +1323,29 @@ CURLMcode curl_multi_waitfds(CURLM *m, Curl_pollset_init(&ps); Curl_waitfds_init(&cwfds, ufds, size); - if(Curl_uint_bset_first(&multi->process, &mid)) { + if(Curl_uint32_bset_first(&multi->process, &mid)) { do { struct Curl_easy *data = Curl_multi_get_easy(multi, mid); if(!data) { DEBUGASSERT(0); - Curl_uint_bset_remove(&multi->process, mid); - Curl_uint_bset_remove(&multi->dirty, mid); + Curl_uint32_bset_remove(&multi->process, mid); + Curl_uint32_bset_remove(&multi->dirty, mid); continue; } - Curl_multi_pollset(data, &ps, "curl_multi_waitfds"); + Curl_multi_pollset(data, &ps); need += Curl_waitfds_add_ps(&cwfds, &ps); - } - while(Curl_uint_bset_next(&multi->process, mid, &mid)); + } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); } need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds); - if(need != cwfds.n && ufds) { - result = CURLM_OUT_OF_MEMORY; - } + if(need != cwfds.n && ufds) + mresult = CURLM_OUT_OF_MEMORY; if(fd_count) *fd_count = need; Curl_pollset_cleanup(&ps); - return result; + return mresult; } #ifdef USE_WINSOCK @@ -1277,9 +1360,169 @@ 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 + +static CURLMcode multi_winsock_select(struct Curl_multi *multi, + struct curl_pollfds *cpfds, + unsigned int curl_nfds, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + bool extrawait, + int *pnevents) +{ + CURLMcode mresult = CURLM_OK; + WSANETWORKEVENTS wsa_events; + int nevents = 0; + size_t i; + + DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT); + + /* Set the WSA events based on the collected pollds */ + for(i = 0; i < cpfds->n; i++) { + long mask = 0; + if(cpfds->pfds[i].events & POLLIN) + mask |= FD_READ | FD_ACCEPT | FD_CLOSE; + if(cpfds->pfds[i].events & POLLPRI) + mask |= FD_OOB; + if(cpfds->pfds[i].events & POLLOUT) { + mask |= FD_WRITE | FD_CONNECT | FD_CLOSE; + reset_socket_fdwrite(cpfds->pfds[i].fd); + } + if(mask) { + if(WSAEventSelect(cpfds->pfds[i].fd, multi->wsa_event, mask) != 0) { + mresult = CURLM_OUT_OF_MEMORY; + goto out; + } + } + } + + if(cpfds->n || extrawait) { + int pollrc = 0; + + if(cpfds->n) { /* pre-check with Winsock */ + pollrc = Curl_poll(cpfds->pfds, cpfds->n, 0); + if(pollrc < 0) { + mresult = CURLM_UNRECOVERABLE_POLL; + goto out; + } + nevents = pollrc; + } + + if(!nevents) { + /* now wait... if not ready during the pre-check (pollrc == 0) */ + WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms, + FALSE); + } + + /* With Winsock, we have to run the following section unconditionally + to call WSAEventSelect(fd, event, 0) on all the sockets */ + /* copy revents results from the poll to the curl_multi_wait poll + struct, the bit values of the actual underlying poll() implementation + may not be the same as the ones in the public libcurl API! */ + for(i = 0; i < extra_nfds; i++) { + unsigned short mask = 0; + curl_socket_t s = extra_fds[i].fd; + + wsa_events.lNetworkEvents = 0; + if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) { + if(wsa_events.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) + mask |= CURL_WAIT_POLLIN; + if(wsa_events.lNetworkEvents & (FD_WRITE | FD_CONNECT | FD_CLOSE)) + mask |= CURL_WAIT_POLLOUT; + if(wsa_events.lNetworkEvents & FD_OOB) + mask |= CURL_WAIT_POLLPRI; + if(!pollrc && wsa_events.lNetworkEvents) + nevents++; + } + WSAEventSelect(s, multi->wsa_event, 0); + if(!pollrc) { + extra_fds[i].revents = (short)mask; + continue; + } + else { + unsigned r = (unsigned)cpfds->pfds[curl_nfds + i].revents; + if(r & POLLIN) + mask |= CURL_WAIT_POLLIN; + if(r & POLLOUT) + mask |= CURL_WAIT_POLLOUT; + if(r & POLLPRI) + mask |= CURL_WAIT_POLLPRI; + extra_fds[i].revents = (short)mask; + } + } + + /* Count up all our own sockets that had activity, + and remove them from the event. */ + for(i = 0; i < curl_nfds; ++i) { + wsa_events.lNetworkEvents = 0; + if(WSAEnumNetworkEvents(cpfds->pfds[i].fd, NULL, &wsa_events) == 0) { + if(!pollrc && wsa_events.lNetworkEvents) + nevents++; + } + WSAEventSelect(cpfds->pfds[i].fd, multi->wsa_event, 0); + } + WSAResetEvent(multi->wsa_event); + } + +out: + *pnevents = nevents; + return mresult; +} + +#else /* USE_WINSOCK */ + +static CURLMcode multi_posix_poll(struct Curl_multi *multi, + struct curl_pollfds *cpfds, + unsigned int curl_nfds, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + bool extrawait, + int *pnevents) +{ + CURLMcode mresult = CURLM_OK; + int nevents = 0; + size_t i; + + (void)multi; + if(cpfds->n) { + int pollrc = Curl_poll(cpfds->pfds, cpfds->n, timeout_ms); /* wait... */ + if(pollrc < 0) { + mresult = CURLM_UNRECOVERABLE_POLL; + goto out; + } + nevents = pollrc; + + /* copy revents results from the poll to the curl_multi_wait poll + struct, the bit values of the actual underlying poll() implementation + may not be the same as the ones in the public libcurl API! */ + for(i = 0; i < extra_nfds; i++) { + unsigned r = (unsigned)cpfds->pfds[curl_nfds + i].revents; + unsigned short mask = 0; + if(r & POLLIN) + mask |= CURL_WAIT_POLLIN; + if(r & POLLOUT) + mask |= CURL_WAIT_POLLOUT; + if(r & POLLPRI) + mask |= CURL_WAIT_POLLPRI; + extra_fds[i].revents = (short)mask; + } + } + else if(extrawait) { + /* No fds to poll, but asked to obey timeout_ms anyway. We cannot + * use Curl_poll() as it, on some platforms, returns immediately + * without fds. */ + curlx_wait_ms(timeout_ms); + } + +out: + *pnevents = nevents; + return mresult; +} + +#endif /* !USE_WINSOCK */ #define NUM_POLLS_ON_STACK 10 @@ -1288,27 +1531,21 @@ static CURLMcode multi_wait(struct Curl_multi *multi, unsigned int extra_nfds, int timeout_ms, int *ret, - bool extrawait, /* when no socket, wait */ - bool use_wakeup) + bool extrawait) /* when no socket, wait */ { size_t i; struct curltime expire_time; long timeout_internal; - int retcode = 0; + int nevents = 0; struct easy_pollset ps; struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; struct curl_pollfds cpfds; unsigned int curl_nfds = 0; /* how many pfds are for curl transfers */ struct Curl_easy *data = NULL; - CURLMcode result = CURLM_OK; - unsigned int mid; - -#ifdef USE_WINSOCK - WSANETWORKEVENTS wsa_events; - DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT); -#endif -#ifndef ENABLE_WAKEUP - (void)use_wakeup; + CURLMcode mresult = CURLM_OK; + uint32_t mid; +#ifdef ENABLE_WAKEUP + int wakeup_idx = -1; #endif if(!GOOD_MULTI_HANDLE(multi)) @@ -1324,29 +1561,42 @@ static CURLMcode multi_wait(struct Curl_multi *multi, Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK); /* Add the curl handles to our pollfds first */ - if(Curl_uint_bset_first(&multi->process, &mid)) { + if(Curl_uint32_bset_first(&multi->process, &mid)) { do { data = Curl_multi_get_easy(multi, mid); if(!data) { DEBUGASSERT(0); - Curl_uint_bset_remove(&multi->process, mid); - Curl_uint_bset_remove(&multi->dirty, mid); + Curl_uint32_bset_remove(&multi->process, mid); + Curl_uint32_bset_remove(&multi->dirty, mid); continue; } - Curl_multi_pollset(data, &ps, "multi_wait"); + Curl_multi_pollset(data, &ps); if(Curl_pollfds_add_ps(&cpfds, &ps)) { - result = CURLM_OUT_OF_MEMORY; + mresult = CURLM_OUT_OF_MEMORY; goto out; } - } - while(Curl_uint_bset_next(&multi->process, mid, &mid)); + } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); } if(Curl_cshutdn_add_pollfds(&multi->cshutdn, multi->admin, &cpfds)) { - result = CURLM_OUT_OF_MEMORY; + mresult = CURLM_OUT_OF_MEMORY; goto out; } +#ifdef ENABLE_WAKEUP + /* If `extrawait` is TRUE *or* we have `extra_fds`to poll *or* we + * have transfer sockets to poll, we obey `timeout_ms`. + * Then we need to also monitor the multi's wakeup + * socket to catch calls to `curl_multi_wakeup()` during the wait. */ + if(extrawait || cpfds.n || extra_nfds) { + wakeup_idx = cpfds.n; + if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) { + mresult = CURLM_OUT_OF_MEMORY; + goto out; + } + } +#endif + curl_nfds = cpfds.n; /* what curl internally uses in cpfds */ /* Add external file descriptions from poll-like struct curl_waitfd */ for(i = 0; i < extra_nfds; i++) { @@ -1358,203 +1608,69 @@ static CURLMcode multi_wait(struct Curl_multi *multi, if(extra_fds[i].events & CURL_WAIT_POLLOUT) events |= POLLOUT; if(Curl_pollfds_add_sock(&cpfds, extra_fds[i].fd, events)) { - result = CURLM_OUT_OF_MEMORY; + mresult = CURLM_OUT_OF_MEMORY; goto out; } } -#ifdef USE_WINSOCK - /* Set the WSA events based on the collected pollds */ - for(i = 0; i < cpfds.n; i++) { - long mask = 0; - if(cpfds.pfds[i].events & POLLIN) - mask |= FD_READ|FD_ACCEPT|FD_CLOSE; - if(cpfds.pfds[i].events & POLLPRI) - mask |= FD_OOB; - if(cpfds.pfds[i].events & POLLOUT) { - mask |= FD_WRITE|FD_CONNECT|FD_CLOSE; - reset_socket_fdwrite(cpfds.pfds[i].fd); - } - if(mask) { - if(WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, mask) != 0) { - result = CURLM_OUT_OF_MEMORY; - goto out; - } - } - } -#endif - -#ifdef ENABLE_WAKEUP -#ifndef USE_WINSOCK - if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { - if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) { - result = CURLM_OUT_OF_MEMORY; - goto out; - } - } -#endif -#endif - /* We check the internal timeout *AFTER* we collected all sockets to * poll. Collecting the sockets may install new timers by protocols * and connection filters. - * Use the shorter one of the internal and the caller requested timeout. */ - (void)multi_timeout(multi, &expire_time, &timeout_internal); + * Use the shorter one of the internal and the caller requested timeout. + * If we are called with `!extrawait` and multi_timeout() reports no + * timeouts exist, do not wait. */ + multi_timeout(multi, &expire_time, &timeout_internal); if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) timeout_ms = (int)timeout_internal; if(data) CURL_TRC_M(data, "multi_wait(fds=%d, timeout=%d) tinternal=%ld", cpfds.n, timeout_ms, timeout_internal); -#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) - if(cpfds.n || use_wakeup) { -#else - if(cpfds.n) { -#endif - int pollrc; -#ifdef USE_WINSOCK - if(cpfds.n) /* just pre-check with Winsock */ - pollrc = Curl_poll(cpfds.pfds, cpfds.n, 0); - else - pollrc = 0; -#else - pollrc = Curl_poll(cpfds.pfds, cpfds.n, timeout_ms); /* wait... */ -#endif - if(pollrc < 0) { - result = CURLM_UNRECOVERABLE_POLL; - goto out; - } - - if(pollrc > 0) { - retcode = pollrc; -#ifdef USE_WINSOCK - } - else { /* now wait... if not ready during the pre-check (pollrc == 0) */ - WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms, - FALSE); - } - /* With Winsock, we have to run the following section unconditionally - to call WSAEventSelect(fd, event, 0) on all the sockets */ - { -#endif - /* copy revents results from the poll to the curl_multi_wait poll - struct, the bit values of the actual underlying poll() implementation - may not be the same as the ones in the public libcurl API! */ - for(i = 0; i < extra_nfds; i++) { - unsigned r = (unsigned)cpfds.pfds[curl_nfds + i].revents; - unsigned short mask = 0; -#ifdef USE_WINSOCK - curl_socket_t s = extra_fds[i].fd; - wsa_events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) { - if(wsa_events.lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE)) - mask |= CURL_WAIT_POLLIN; - if(wsa_events.lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE)) - mask |= CURL_WAIT_POLLOUT; - if(wsa_events.lNetworkEvents & FD_OOB) - mask |= CURL_WAIT_POLLPRI; - if(ret && !pollrc && wsa_events.lNetworkEvents) - retcode++; - } - WSAEventSelect(s, multi->wsa_event, 0); - if(!pollrc) { - extra_fds[i].revents = (short)mask; - continue; - } -#endif - if(r & POLLIN) - mask |= CURL_WAIT_POLLIN; - if(r & POLLOUT) - mask |= CURL_WAIT_POLLOUT; - if(r & POLLPRI) - mask |= CURL_WAIT_POLLPRI; - extra_fds[i].revents = (short)mask; - } #ifdef USE_WINSOCK - /* Count up all our own sockets that had activity, - and remove them from the event. */ - for(i = 0; i < curl_nfds; ++i) { - wsa_events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(cpfds.pfds[i].fd, NULL, &wsa_events) == 0) { - if(ret && !pollrc && wsa_events.lNetworkEvents) - retcode++; - } - WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, 0); - } - WSAResetEvent(multi->wsa_event); + mresult = multi_winsock_select(multi, &cpfds, curl_nfds, + extra_fds, extra_nfds, + timeout_ms, extrawait, &nevents); #else + mresult = multi_posix_poll(multi, &cpfds, curl_nfds, + extra_fds, extra_nfds, + timeout_ms, extrawait, &nevents); +#endif + #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; - } - } - /* do not count the wakeup socket into the returned value */ - retcode--; - } + if(nevents && (wakeup_idx >= 0)) { + if(cpfds.pfds[wakeup_idx].revents & POLLIN) { + (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE); + /* do not count the wakeup socket into the returned value */ + nevents--; } -#endif -#endif } - } - - if(ret) - *ret = retcode; -#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) - if(extrawait && !cpfds.n && !use_wakeup) { -#else - if(extrawait && !cpfds.n) { #endif - long sleep_ms = 0; - - /* Avoid busy-looping when there is nothing particular to wait for */ - if(!curl_multi_timeout(multi, &sleep_ms) && sleep_ms) { - if(sleep_ms > timeout_ms) - sleep_ms = timeout_ms; - /* when there are no easy handles in the multi, this holds a -1 - timeout */ - else if(sleep_ms < 0) - sleep_ms = timeout_ms; - curlx_wait_ms(sleep_ms); - } - } out: Curl_pollset_cleanup(&ps); Curl_pollfds_cleanup(&cpfds); - return result; + if(ret) + *ret = nevents; + return mresult; } -CURLMcode curl_multi_wait(CURLM *multi, +CURLMcode curl_multi_wait(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, int *ret) { - return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE, - FALSE); + return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, FALSE); } -CURLMcode curl_multi_poll(CURLM *multi, +CURLMcode curl_multi_poll(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, int *ret) { - return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE, - TRUE); + return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, TRUE); } CURLMcode curl_multi_wakeup(CURLM *m) @@ -1563,56 +1679,24 @@ CURLMcode curl_multi_wakeup(CURLM *m) it has to be careful only to access parts of the Curl_multi struct that are constant */ struct Curl_multi *multi = m; + CURLMcode mresult = CURLM_WAKEUP_FAILURE; /* GOOD_MULTI_HANDLE can be safely called */ if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; #ifdef ENABLE_WAKEUP -#ifdef USE_WINSOCK - if(WSASetEvent(multi->wsa_event)) - return CURLM_OK; -#else /* the wakeup_pair variable is only written during init and cleanup, 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 }; + if(!Curl_wakeup_signal(multi->wakeup_pair)) + mresult = CURLM_OK; #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; + if(WSASetEvent(multi->wsa_event)) + mresult = CURLM_OK; #endif - if(!return_success) - return CURLM_WAKEUP_FAILURE; - } - return CURLM_OK; - } - } -#endif -#endif - return CURLM_WAKEUP_FAILURE; + return mresult; } /* @@ -1625,7 +1709,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; @@ -1647,14 +1731,13 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, struct Curl_easy *data, struct connectdata *conn) { - CURLMcode rc; + CURLMcode mresult; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - rc = curl_multi_add_handle(multi, data); - if(!rc) { - struct SingleRequest *k = &data->req; + mresult = curl_multi_add_handle(multi, data); + if(!mresult) { CURLcode result; /* pass in NULL for 'conn' here since we do not want to init the @@ -1668,9 +1751,9 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, /* take this handle to the perform state right away */ multistate(data, MSTATE_PERFORMING); Curl_attach_connection(data, conn); - k->keepon |= KEEP_RECV; /* setup to receive! */ + CURL_REQ_SET_RECV(data); } - return rc; + return mresult; } static CURLcode multi_do(struct Curl_easy *data, bool *done) @@ -1679,18 +1762,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! @@ -1703,8 +1786,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; } @@ -1713,37 +1796,37 @@ static CURLcode multi_do_more(struct Curl_easy *data, int *complete) * Check whether a timeout occurred, and handle it if it did */ static bool multi_handle_timeout(struct Curl_easy *data, - struct curltime *now, bool *stream_error, CURLcode *result) { - bool connect_timeout = data->mstate < MSTATE_DO; - timediff_t timeout_ms = Curl_timeleft(data, now, connect_timeout); + timediff_t timeout_ms; + + 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; - if(data->mstate == MSTATE_RESOLVING) - failf(data, "Resolving timed out after %" FMT_TIMEDIFF_T - " milliseconds", curlx_timediff(*now, since)); - else if(data->mstate == MSTATE_CONNECTING) - failf(data, "Connection timed out after %" FMT_TIMEDIFF_T - " milliseconds", curlx_timediff(*now, since)); + if(data->mstate == MSTATE_CONNECTING) + failf(data, "%s timed out after %" FMT_TIMEDIFF_T " milliseconds", + data->conn->bits.dns_resolved ? "Connection" : "Resolving", + curlx_ptimediff_ms(Curl_pgrs_now(data), &since)); else { struct SingleRequest *k = &data->req; if(k->size != -1) { failf(data, "Operation timed out after %" FMT_TIMEDIFF_T " milliseconds with %" FMT_OFF_T " out of %" FMT_OFF_T " bytes received", - curlx_timediff(*now, since), k->bytecount, k->size); + curlx_ptimediff_ms(Curl_pgrs_now(data), &since), + k->bytecount, k->size); } else { failf(data, "Operation timed out after %" FMT_TIMEDIFF_T " milliseconds with %" FMT_OFF_T " bytes received", - curlx_timediff(*now, since), k->bytecount); + curlx_ptimediff_ms(Curl_pgrs_now(data), &since), + k->bytecount); } } *result = CURLE_OPERATION_TIMEDOUT; @@ -1772,9 +1855,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; @@ -1792,9 +1875,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; @@ -1807,47 +1890,31 @@ static CURLcode protocol_doing(struct Curl_easy *data, bool *done) * proceed with some action. * */ -static CURLcode protocol_connect(struct Curl_easy *data, - bool *protocol_done) +static CURLcode protocol_connect(struct Curl_easy *data, bool *protocol_done) { - CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + DEBUGASSERT(conn); DEBUGASSERT(protocol_done); + DEBUGASSERT(Curl_conn_is_connected(conn, FIRSTSOCKET)); *protocol_done = FALSE; - - if(Curl_conn_is_connected(conn, FIRSTSOCKET) - && conn->bits.protoconnstart) { - /* We already are connected, get back. This may happen when the connect - worked fine in the first call, like when we connect to a local server - or proxy. Note that we do not know if the protocol is actually done. - - Unless this protocol does not have any protocol-connect callback, as - then we know we are done. */ - if(!conn->handler->connecting) - *protocol_done = TRUE; - - return CURLE_OK; - } - if(!conn->bits.protoconnstart) { - if(conn->handler->connect_it) { - /* is there a protocol-specific connect() procedure? */ - + 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; } - else - *protocol_done = TRUE; - - /* it has started, possibly even completed but that knowledge is not stored - in this bit! */ - if(!result) - conn->bits.protoconnstart = TRUE; + conn->bits.protoconnstart = TRUE; } - return result; /* pass back status */ + /* Unless this protocol does not have any protocol-connect callback, as + then we know we are done. */ + if(!conn->scheme->run->connecting) + *protocol_done = TRUE; + return CURLE_OK; } static void set_in_callback(struct Curl_multi *multi, bool value) @@ -1860,7 +1927,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); @@ -1873,68 +1940,83 @@ static void multi_posttransfer(struct Curl_easy *data) * multi_follow() handles the URL redirect magic. Pass in the 'newurl' string * as given by the remote server and set up the new URL to request. * - * This function DOES NOT FREE the given url. + * 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); + + if(type == FOLLOW_RETRY) + /* Retries are generic and do not require protocol-specific redirect + handling. */ + return CURLE_OK; + return CURLE_TOO_MANY_REDIRECTS; } -static CURLcode mspeed_check(struct Curl_easy *data, - struct curltime *nowp) +static CURLcode mspeed_check(struct Curl_easy *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 over send speed */ - if(data->set.max_send_speed) - send_wait_ms = Curl_pgrsLimitWaitTime(&data->progress.ul, - data->set.max_send_speed, - *nowp); + send_ms = Curl_rlimit_wait_ms(&data->progress.ul.rlimit, pnow); + recv_ms = Curl_rlimit_wait_ms(&data->progress.dl.rlimit, pnow); - /* check if over recv speed */ - if(data->set.max_recv_speed) - recv_wait_ms = Curl_pgrsLimitWaitTime(&data->progress.dl, - data->set.max_recv_speed, - *nowp); - - if(send_wait_ms || recv_wait_ms) { - if(data->mstate != MSTATE_RATELIMITING) { - Curl_ratelimit(data, *nowp); - 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); - 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); - Curl_ratelimit(data, *nowp); } return CURLE_OK; } static CURLMcode state_performing(struct Curl_easy *data, - struct curltime *nowp, bool *stream_errorp, CURLcode *resultp) { char *newurl = NULL; bool retry = FALSE; - CURLMcode rc = CURLM_OK; + CURLMcode mresult = CURLM_OK; CURLcode result = *resultp = CURLE_OK; *stream_errorp = FALSE; - if(mspeed_check(data, nowp) == CURLE_AGAIN) + if(mspeed_check(data) == CURLE_AGAIN) return CURLM_OK; /* read/write data if it is ready to do so */ - result = Curl_sendrecv(data, nowp); + result = Curl_sendrecv(data); if(data->req.done || (result == CURLE_RECV_ERROR)) { /* If CURLE_RECV_ERROR happens early enough, we assume it was a race @@ -1955,7 +2037,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); @@ -1968,12 +2050,17 @@ static CURLMcode state_performing(struct Curl_easy *data, data->state.errorbuf = FALSE; if(!newurl) /* typically for HTTP_1_1_REQUIRED error on first flight */ - newurl = strdup(data->state.url); - /* if we are to retry, set the result to OK and consider the request - as done */ - retry = TRUE; - result = CURLE_OK; - data->req.done = TRUE; + newurl = Curl_bufref_dup(&data->state.url); + if(!newurl) { + result = CURLE_OUT_OF_MEMORY; + } + else { + /* if we are to retry, set the result to OK and consider the request + as done */ + retry = TRUE; + result = CURLE_OK; + data->req.done = TRUE; + } } else result = ret; @@ -1989,7 +2076,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"); @@ -1997,7 +2084,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); @@ -2007,9 +2094,9 @@ 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 */ - free(newurl); + curlx_free(newurl); newurl = data->req.newurl; data->req.newurl = NULL; follow = FOLLOW_REDIR; @@ -2021,7 +2108,7 @@ static CURLMcode state_performing(struct Curl_easy *data, result = multi_follow(data, handler, newurl, follow); if(!result) { multistate(data, MSTATE_SETUP); - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; } } else { @@ -2030,7 +2117,7 @@ static CURLMcode state_performing(struct Curl_easy *data, /* but first check to see if we got a location info even though we are not following redirects */ if(data->req.location) { - free(newurl); + curlx_free(newurl); newurl = data->req.location; data->req.location = NULL; result = multi_follow(data, handler, newurl, FOLLOW_FAKE); @@ -2042,23 +2129,23 @@ static CURLMcode state_performing(struct Curl_easy *data, if(!result) { multistate(data, MSTATE_DONE); - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; } } } else { /* not errored, not done */ - mspeed_check(data, nowp); + mspeed_check(data); } - free(newurl); + curlx_free(newurl); *resultp = result; - return rc; + return mresult; } static CURLMcode state_do(struct Curl_easy *data, bool *stream_errorp, CURLcode *resultp) { - CURLMcode rc = CURLM_OK; + CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; if(data->set.fprereq) { int prereq_rc; @@ -2083,10 +2170,8 @@ static CURLMcode state_do(struct Curl_easy *data, } if(data->set.connect_only && !data->set.connect_only_ws) { - /* keep connection open for application to use the socket */ - connkeep(data->conn, "CONNECT_ONLY"); multistate(data, MSTATE_DONE); - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; } else { bool dophase_done = FALSE; @@ -2106,9 +2191,8 @@ static CURLMcode state_do(struct Curl_easy *data, multi_done(data, CURLE_OK, FALSE); /* if there is no connection left, skip the DONE state */ - multistate(data, data->conn ? - MSTATE_DONE : MSTATE_COMPLETED); - rc = CURLM_CALL_MULTI_PERFORM; + multistate(data, data->conn ? MSTATE_DONE : MSTATE_COMPLETED); + mresult = CURLM_CALL_MULTI_PERFORM; goto end; } } @@ -2116,7 +2200,7 @@ static CURLMcode state_do(struct Curl_easy *data, /* DO was not completed in one function call, we must continue DOING... */ multistate(data, MSTATE_DOING); - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; } /* after DO, go DO_DONE... or DO_MORE */ @@ -2124,22 +2208,22 @@ static CURLMcode state_do(struct Curl_easy *data, /* we are supposed to do more, but we need to sit down, relax and wait a little while first */ multistate(data, MSTATE_DOING_MORE); - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; } else { /* we are done with the DO, now DID */ multistate(data, MSTATE_DID); - rc = CURLM_CALL_MULTI_PERFORM; + 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; @@ -2162,7 +2246,7 @@ static CURLMcode state_do(struct Curl_easy *data, drc = multi_follow(data, handler, newurl, follow); if(!drc) { multistate(data, MSTATE_SETUP); - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; result = CURLE_OK; } else { @@ -2179,7 +2263,7 @@ static CURLMcode state_do(struct Curl_easy *data, /* Have error handler disconnect conn if we cannot retry */ *stream_errorp = TRUE; } - free(newurl); + curlx_free(newurl); } else { /* failure detected */ @@ -2191,24 +2275,20 @@ static CURLMcode state_do(struct Curl_easy *data, } end: *resultp = result; - return rc; + return mresult; } static CURLMcode state_ratelimiting(struct Curl_easy *data, - struct curltime *nowp, CURLcode *resultp) { CURLcode result = CURLE_OK; - CURLMcode rc = CURLM_OK; + CURLMcode mresult = CURLM_OK; DEBUGASSERT(data->conn); /* if both rates are within spec, resume transfer */ - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, *nowp); + 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"); @@ -2216,125 +2296,169 @@ static CURLMcode state_ratelimiting(struct Curl_easy *data, multi_done(data, result, TRUE); } else { - if(!mspeed_check(data, nowp)) - rc = CURLM_CALL_MULTI_PERFORM; + if(!mspeed_check(data)) + mresult = CURLM_CALL_MULTI_PERFORM; } *resultp = result; - return rc; -} - -static CURLMcode state_resolving(struct Curl_multi *multi, - struct Curl_easy *data, - bool *stream_errorp, - CURLcode *resultp) -{ - struct Curl_dns_entry *dns = NULL; - CURLcode result; - CURLMcode rc = CURLM_OK; - - result = Curl_resolv_check(data, &dns); - CURL_TRC_DNS(data, "Curl_resolv_check() -> %d, %s", - result, dns ? "found" : "missing"); - /* Update sockets here, because the socket(s) may have been closed and the - application thus needs to be told, even if it is likely that the same - socket(s) will again be used further down. If the name has not yet been - resolved, it is likely that new sockets have been opened in an attempt to - contact another resolver. */ - rc = Curl_multi_ev_assess_xfer(multi, data); - if(rc) - return rc; - - if(dns) { - bool connected; - /* Perform the next step in the connection phase, and then move on to the - WAITCONNECT state */ - result = Curl_once_resolved(data, dns, &connected); - - if(result) - /* if Curl_once_resolved() returns failure, the connection struct is - already freed and gone */ - data->conn = NULL; /* no more connection */ - else { - /* call again please so that we get the next socket setup */ - rc = CURLM_CALL_MULTI_PERFORM; - if(connected) - multistate(data, MSTATE_PROTOCONNECT); - else { - multistate(data, MSTATE_CONNECTING); - } - } - } - - if(result) - /* failure detected */ - *stream_errorp = TRUE; - - *resultp = result; - return rc; + return mresult; } static CURLMcode state_connect(struct Curl_multi *multi, struct Curl_easy *data, - struct curltime *nowp, CURLcode *resultp) { /* Connect. We want to get a connection identifier filled in. This state can be entered from SETUP and from PENDING. */ bool connected; - bool async; - CURLMcode rc = CURLM_OK; - CURLcode result = Curl_connect(data, &async, &connected); - if(CURLE_NO_CONNECTION_AVAILABLE == result) { + CURLMcode mresult = CURLM_OK; + CURLcode result = Curl_connect(data, &connected); + 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); /* move from process to pending set */ - Curl_uint_bset_remove(&multi->process, data->mid); - Curl_uint_bset_remove(&multi->dirty, data->mid); - Curl_uint_bset_add(&multi->pending, data->mid); + Curl_uint32_bset_remove(&multi->process, data->mid); + Curl_uint32_bset_remove(&multi->dirty, data->mid); + Curl_uint32_bset_add(&multi->pending, data->mid); *resultp = CURLE_OK; - return rc; + return mresult; } else process_pending_handles(data->multi); if(!result) { - *nowp = Curl_pgrsTime(data, TIMER_POSTQUEUE); - if(async) - /* We are now waiting for an asynchronous name lookup */ - multistate(data, MSTATE_RESOLVING); - else { - /* after the connect has been sent off, go WAITCONNECT unless the - protocol connect is already done and we can go directly to WAITDO or - DO! */ - rc = CURLM_CALL_MULTI_PERFORM; + /* after the connect has been sent off, go WAITCONNECT unless the + protocol connect is already done and we can go directly to WAITDO or + DO! */ + mresult = CURLM_CALL_MULTI_PERFORM; - if(connected) { - if(!data->conn->bits.reuse && - Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { - /* new connection, can multiplex, wake pending handles */ - process_pending_handles(data->multi); - } - multistate(data, MSTATE_PROTOCONNECT); - } - else { - multistate(data, MSTATE_CONNECTING); + if(connected) { + if(!data->conn->bits.reuse && + Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { + /* new connection, can multiplex, wake pending handles */ + process_pending_handles(data->multi); } + multistate(data, MSTATE_PROTOCONNECT); + } + else { + multistate(data, MSTATE_CONNECTING); } } *resultp = result; - return rc; + 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 && Curl_conn_is_connected(data->conn, FIRSTSOCKET)) { + 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; + if(!multi->xfers_alive) + multi_assess_wakeup(multi); } static CURLMcode multi_runsingle(struct Curl_multi *multi, - struct curltime *nowp, - 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; - CURLMcode rc; + CURLMcode mresult; CURLcode result = CURLE_OK; int control; @@ -2354,13 +2478,22 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* transfer runs now, clear the dirty bit. This may be set * again during processing, triggering a re-run later. */ - Curl_uint_bset_remove(&multi->dirty, data->mid); + Curl_uint32_bset_remove(&multi->dirty, data->mid); + if(data == multi->admin) { +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_multi_process(multi); +#endif + 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 */ bool stream_error = FALSE; - rc = CURLM_OK; + mresult = CURLM_OK; if(multi_ischanged(multi, TRUE)) { CURL_TRC_M(data, "multi changed, check CONNECT_PEND queue"); @@ -2378,7 +2511,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* Wait for the connect state as only then is the start time stored, but we must not check already completed handles */ if((data->mstate >= MSTATE_CONNECT) && (data->mstate < MSTATE_COMPLETED) && - multi_handle_timeout(data, nowp, &stream_error, &result)) + multi_handle_timeout(data, &stream_error, &result)) /* Skip the statemachine and go directly to error handling section. */ goto statemachine_end; @@ -2392,13 +2525,13 @@ 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: /* Transitional state. Setup things for a new transfer. The handle can come back to this state on a redirect. */ - *nowp = Curl_pgrsTime(data, TIMER_STARTSINGLE); + Curl_pgrsTime(data, TIMER_STARTSINGLE); if(data->set.timeout) Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); if(data->set.connecttimeout) @@ -2411,33 +2544,17 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, FALLTHROUGH(); case MSTATE_CONNECT: - rc = state_connect(multi, data, nowp, &result); + mresult = state_connect(multi, data, &result); break; - case MSTATE_RESOLVING: - /* awaiting an asynch name resolve to complete */ - rc = state_resolving(multi, data, &stream_error, &result); - break; - -#ifndef CURL_DISABLE_HTTP - case MSTATE_TUNNELING: - /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ - DEBUGASSERT(data->conn); - result = Curl_http_connect(data, &protocol_connected); - if(!result) { - rc = CURLM_CALL_MULTI_PERFORM; - /* initiate protocol connect phase */ - multistate(data, MSTATE_PROTOCONNECT); - } - else - stream_error = TRUE; - break; -#endif - case MSTATE_CONNECTING: /* awaiting a completion of an asynch TCP connect */ - DEBUGASSERT(data->conn); - if(!Curl_xfer_recv_is_paused(data)) { + if(!data->conn) { + DEBUGASSERT(0); + result = CURLE_FAILED_INIT; + break; + } + else if(!Curl_xfer_recv_is_paused(data)) { result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); if(connected && !result) { if(!data->conn->bits.reuse && @@ -2445,11 +2562,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* new connection, can multiplex, wake pending handles */ process_pending_handles(data->multi); } - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; multistate(data, MSTATE_PROTOCONNECT); } else if(result) { /* failure detected */ + CURL_TRC_M(data, "connect failed -> %d", result); multi_posttransfer(data); multi_done(data, result, TRUE); stream_error = TRUE; @@ -2465,7 +2583,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * restart this on a reused connection. */ multistate(data, MSTATE_DO); - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; break; } if(!result) @@ -2473,12 +2591,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(!result && !protocol_connected) { /* switch to waiting state */ multistate(data, MSTATE_PROTOCONNECTING); - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; } else if(!result) { /* protocol connect has completed, go WAITDO or DO */ multistate(data, MSTATE_DO); - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; } else { /* failure detected */ @@ -2494,7 +2612,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(!result && protocol_connected) { /* after the connect has completed, go WAITDO or DO */ multistate(data, MSTATE_DO); - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; } else if(result) { /* failure detected */ @@ -2505,7 +2623,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case MSTATE_DO: - rc = state_do(data, &stream_error, &result); + mresult = state_do(data, &stream_error, &result); break; case MSTATE_DOING: @@ -2517,7 +2635,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, /* after DO, go DO_DONE or DO_MORE */ multistate(data, data->conn->bits.do_more ? MSTATE_DOING_MORE : MSTATE_DID); - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; } /* dophase_done */ } else { @@ -2539,9 +2657,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(control) { /* if positive, advance to DO_DONE if negative, go back to DOING */ - multistate(data, control == 1 ? - MSTATE_DID : MSTATE_DOING); - rc = CURLM_CALL_MULTI_PERFORM; + multistate(data, control == 1 ? MSTATE_DID : MSTATE_DOING); + mresult = CURLM_CALL_MULTI_PERFORM; } /* else stay in DO_MORE */ @@ -2558,7 +2675,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, DEBUGASSERT(data->conn); if(data->conn->bits.multiplex) /* Check if we can move pending requests to send pipe */ - process_pending_handles(multi); /* multiplexed */ + process_pending_handles(multi); /* multiplexed */ /* Only perform the transfer if there is a good socket to work with. Having both BAD is a signal to skip immediately to DONE */ @@ -2568,26 +2685,26 @@ 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 multistate(data, MSTATE_DONE); } - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; break; case MSTATE_RATELIMITING: /* limit-rate exceeded in either direction */ - rc = state_ratelimiting(data, nowp, &result); + mresult = state_ratelimiting(data, &result); break; case MSTATE_PERFORMING: - rc = state_performing(data, nowp, &stream_error, &result); + mresult = state_performing(data, &stream_error, &result); break; case MSTATE_DONE: /* this state is highly transient, so run another loop after this */ - rc = CURLM_CALL_MULTI_PERFORM; + mresult = CURLM_CALL_MULTI_PERFORM; if(data->conn) { CURLcode res; @@ -2629,7 +2746,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, if(data->mstate >= MSTATE_CONNECT && data->mstate < MSTATE_DO && - rc != CURLM_CALL_MULTI_PERFORM && + mresult != CURLM_CALL_MULTI_PERFORM && !multi_ischanged(multi, FALSE)) { /* We now handle stream timeouts if and only if this will be the last * loop iteration. We only check this on the last iteration to ensure @@ -2637,154 +2754,69 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, * (i.e. CURLM_CALL_MULTI_PERFORM == TRUE) then we should do that before * declaring the connection timed out as we may almost have a completed * connection. */ - multi_handle_timeout(data, nowp, &stream_error, &result); + multi_handle_timeout(data, &stream_error, &result); } 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); - rc = CURLM_CALL_MULTI_PERFORM; - } - /* if there is still a connection to use, call the progress function */ - else if(data->conn && Curl_pgrsUpdate(data)) { - /* aborted due to progress callback return code must close the - connection */ - result = CURLE_ABORTED_BY_CALLBACK; - 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); - rc = 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 != UINT_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_uint_bset_remove(&multi->process, data->mid); - Curl_uint_bset_remove(&multi->dirty, data->mid); - Curl_uint_bset_remove(&multi->pending, data->mid); - Curl_uint_bset_add(&multi->msgsent, data->mid); - --multi->xfers_alive; + handle_completed(multi, data, result); return CURLM_OK; } - } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE)); + } while((mresult == CURLM_CALL_MULTI_PERFORM) || + multi_ischanged(multi, FALSE)); data->result = result; - return rc; + return mresult; } - -CURLMcode curl_multi_perform(CURLM *m, int *running_handles) +static CURLMcode multi_perform(struct Curl_multi *multi, + int *running_handles) { CURLMcode returncode = CURLM_OK; - struct Curl_tree *t = NULL; - struct curltime now = curlx_now(); - struct Curl_multi *multi = m; - unsigned int mid; - SIGPIPE_VARIABLE(pipe_st); - - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; + struct curltime start = *multi_now(multi); + uint32_t mid; + struct Curl_sigpipe_ctx sigpipe_ctx; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - sigpipe_init(&pipe_st); - if(Curl_uint_bset_first(&multi->process, &mid)) { + if(multi->in_ntfy_callback) + return CURLM_RECURSIVE_API_CALL; + + 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)); do { struct Curl_easy *data = Curl_multi_get_easy(multi, mid); - CURLMcode result; + CURLMcode mresult; if(!data) { DEBUGASSERT(0); - Curl_uint_bset_remove(&multi->process, mid); - Curl_uint_bset_remove(&multi->dirty, mid); + Curl_uint32_bset_remove(&multi->process, mid); + Curl_uint32_bset_remove(&multi->dirty, mid); continue; } - if(data != multi->admin) { - /* admin handle is processed below */ - sigpipe_apply(data, &pipe_st); - result = multi_runsingle(multi, &now, data); - if(result) - returncode = result; - } - } - while(Curl_uint_bset_next(&multi->process, mid, &mid)); + mresult = multi_runsingle(multi, data, &sigpipe_ctx); + if(mresult) + returncode = mresult; + } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); } + sigpipe_restore(&sigpipe_ctx); - sigpipe_apply(multi->admin, &pipe_st); - Curl_cshutdn_perform(&multi->cshutdn, multi->admin, CURL_SOCKET_TIMEOUT); - sigpipe_restore(&pipe_st); + if(multi_ischanged(multi, TRUE)) + process_pending_handles(multi); - if(multi_ischanged(m, TRUE)) - process_pending_handles(m); + if(!returncode && CURL_MNTFY_HAS_ENTRIES(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. * @@ -2793,22 +2825,25 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles) * then and then we risk this loop to remove timers that actually have not * been handled! */ - do { - multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); - if(t) { - /* the removed may have another timeout in queue */ - struct Curl_easy *data = Curl_splayget(t); - (void)add_next_timeout(now, multi, data); - if(data->mstate == MSTATE_PENDING) { - bool stream_unused; - CURLcode result_unused; - if(multi_handle_timeout(data, &now, &stream_unused, &result_unused)) { - infof(data, "PENDING handle timeout"); - move_pending_to_connect(multi, data); + if(multi->timetree) { + struct Curl_tree *t = NULL; + do { + multi->timetree = Curl_splaygetbest(&start, multi->timetree, &t); + if(t) { + /* the removed may have another timeout in queue */ + struct Curl_easy *data = Curl_splayget(t); + (void)add_next_timeout(&start, multi, data); + if(data->mstate == MSTATE_PENDING) { + bool stream_unused; + CURLcode result_unused; + if(multi_handle_timeout(data, &stream_unused, &result_unused)) { + infof(data, "PENDING handle timeout"); + move_pending_to_connect(multi, data); + } } } - } - } while(t); + } while(t); + } if(running_handles) { unsigned int running = Curl_multi_xfers_running(multi); @@ -2821,18 +2856,30 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles) return returncode; } +CURLMcode curl_multi_perform(CURLM *m, int *running_handles) +{ + struct Curl_multi *multi = m; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + + return multi_perform(multi, running_handles); +} + CURLMcode curl_multi_cleanup(CURLM *m) { struct Curl_multi *multi = m; if(GOOD_MULTI_HANDLE(multi)) { void *entry; - unsigned int mid; + uint32_t mid; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; + if(multi->in_ntfy_callback) + return CURLM_RECURSIVE_API_CALL; /* First remove all remaining easy handles, * close internal ones. admin handle is special */ - if(Curl_uint_tbl_first(&multi->xfers, &mid, &entry)) { + if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) { do { struct Curl_easy *data = entry; if(!GOOD_EASY_HANDLE(data)) @@ -2854,8 +2901,8 @@ CURLMcode curl_multi_cleanup(CURLM *m) (void)multi_done(data, CURLE_OK, TRUE); data->multi = NULL; /* clear the association */ - Curl_uint_tbl_remove(&multi->xfers, mid); - data->mid = UINT_MAX; + Curl_uint32_tbl_remove(&multi->xfers, mid); + data->mid = UINT32_MAX; #ifdef USE_LIBPSL if(data->psl == &multi->psl) @@ -2863,16 +2910,18 @@ CURLMcode curl_multi_cleanup(CURLM *m) #endif if(data->state.internal) Curl_close(&data); - } - while(Curl_uint_tbl_next(&multi->xfers, mid, &mid, &entry)); + } while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry)); } +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_multi_destroy(multi, !multi->quick_exit); +#endif Curl_cpool_destroy(&multi->cpool); Curl_cshutdn_destroy(&multi->cshutdn, multi->admin); if(multi->admin) { CURL_TRC_M(multi->admin, "multi_cleanup, closing admin handle, done"); multi->admin->multi = NULL; - Curl_uint_tbl_remove(&multi->xfers, multi->admin->mid); + Curl_uint32_tbl_remove(&multi->xfers, multi->admin->mid); Curl_close(&multi->admin); } @@ -2888,28 +2937,25 @@ CURLMcode curl_multi_cleanup(CURLM *m) #ifdef USE_WINSOCK WSACloseEvent(multi->wsa_event); -#else +#endif #ifdef ENABLE_WAKEUP - wakeup_close(multi->wakeup_pair[0]); -#ifndef USE_EVENTFD - wakeup_close(multi->wakeup_pair[1]); -#endif -#endif + Curl_wakeup_destroy(multi->wakeup_pair); #endif multi_xfer_bufs_free(multi); + Curl_mntfy_cleanup(multi); #ifdef DEBUGBUILD - if(Curl_uint_tbl_count(&multi->xfers)) { + if(Curl_uint32_tbl_count(&multi->xfers)) { multi_xfer_tbl_dump(multi); DEBUGASSERT(0); } #endif - Curl_uint_bset_destroy(&multi->process); - Curl_uint_bset_destroy(&multi->dirty); - Curl_uint_bset_destroy(&multi->pending); - Curl_uint_bset_destroy(&multi->msgsent); - Curl_uint_tbl_destroy(&multi->xfers); - free(multi); + Curl_uint32_bset_destroy(&multi->process); + Curl_uint32_bset_destroy(&multi->dirty); + Curl_uint32_bset_destroy(&multi->pending); + Curl_uint32_bset_destroy(&multi->msgsent); + Curl_uint32_tbl_destroy(&multi->xfers); + curlx_free(multi); return CURLM_OK; } @@ -2954,7 +3000,6 @@ CURLMsg *curl_multi_info_read(CURLM *m, int *msgs_in_queue) return NULL; } - void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s) { if(data) { @@ -2970,7 +3015,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. @@ -2978,7 +3023,7 @@ void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s) * The splay tree only has each sessionhandle as a single node and the nearest * timeout is used to sort it on. */ -static CURLMcode add_next_timeout(struct curltime now, +static CURLMcode add_next_timeout(const struct curltime *pnow, struct Curl_multi *multi, struct Curl_easy *d) { @@ -2992,7 +3037,7 @@ static CURLMcode add_next_timeout(struct curltime now, for(e = Curl_llist_head(list); e;) { struct Curl_llist_node *n = Curl_node_next(e); struct time_node *node = Curl_node_elem(e); - timediff_t diff = curlx_timediff_us(node->time, now); + timediff_t diff = curlx_ptimediff_us(&node->time, pnow); if(diff <= 0) /* remove outdated entry */ Curl_node_remove(e); @@ -3015,92 +3060,87 @@ static CURLMcode add_next_timeout(struct curltime now, /* Insert this node again into the splay. Keep the timer in the list in case we need to recompute future timers. */ - multi->timetree = Curl_splayinsert(*tv, multi->timetree, + multi->timetree = Curl_splayinsert(tv, multi->timetree, &d->state.timenode); } return CURLM_OK; } -struct multi_run_ctx { - struct Curl_multi *multi; - struct curltime now; - size_t run_xfers; - SIGPIPE_MEMBER(pipe_st); - bool run_cpool; -}; - -static 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) { /* Check if there is one (more) expired timer to deal with! This function extracts a matching node if there is one */ - multi->timetree = Curl_splaygetbest(mrc->now, multi->timetree, &t); + multi->timetree = Curl_splaygetbest(ts, multi->timetree, &t); if(!t) return; data = Curl_splayget(t); /* assign this for next loop */ if(!data) continue; - - (void)add_next_timeout(mrc->now, multi, 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); + CURL_TRC_TIMER(data, n->eid, "has expired"); + } + } +#endif + (void)add_next_timeout(ts, multi, data); Curl_multi_mark_dirty(data); } } -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 result = CURLM_OK; - unsigned int mid; + CURLMcode mresult = CURLM_OK; + uint32_t mid; - if(Curl_uint_bset_first(&multi->dirty, &mid)) { + *pnum = 0; + if(Curl_uint32_bset_first(&multi->dirty, &mid)) { do { struct Curl_easy *data = Curl_multi_get_easy(multi, mid); if(data) { CURL_TRC_M(data, "multi_run_dirty"); - if(data == multi->admin) { - Curl_uint_bset_remove(&multi->dirty, mid); - mrc->run_cpool = TRUE; - continue; - } - else if(!Curl_uint_bset_contains(&multi->process, mid)) { + if(!Curl_uint32_bset_contains(&multi->process, mid)) { /* We are no longer processing this transfer */ - Curl_uint_bset_remove(&multi->dirty, mid); + Curl_uint32_bset_remove(&multi->dirty, mid); continue; } - mrc->run_xfers++; - sigpipe_apply(data, &mrc->pipe_st); + (*pnum)++; /* runsingle() clears the dirty mid */ - result = multi_runsingle(multi, &mrc->now, data); + mresult = multi_runsingle(multi, data, sigpipe_ctx); - if(CURLM_OK >= result) { + if(CURLM_OK >= mresult) { /* reassess event handling of data */ - result = Curl_multi_ev_assess_xfer(multi, data); - if(result) + mresult = Curl_multi_ev_assess_xfer(multi, data); + if(mresult) goto out; } } else { CURL_TRC_M(multi->admin, "multi_run_dirty, %u no longer found", mid); - Curl_uint_bset_remove(&multi->dirty, mid); + Curl_uint32_bset_remove(&multi->dirty, mid); } - } - while(Curl_uint_bset_next(&multi->dirty, mid, &mid)); + } while(Curl_uint32_bset_next(&multi->dirty, mid, &mid)); } out: - return result; + return mresult; } static CURLMcode multi_socket(struct Curl_multi *multi, @@ -3109,30 +3149,27 @@ static CURLMcode multi_socket(struct Curl_multi *multi, int ev_bitmask, int *running_handles) { - CURLMcode result = CURLM_OK; - struct multi_run_ctx mrc; + CURLMcode mresult = CURLM_OK; + struct Curl_sigpipe_ctx pipe_ctx; + uint32_t run_xfers; (void)ev_bitmask; - memset(&mrc, 0, sizeof(mrc)); - mrc.multi = multi; - mrc.now = curlx_now(); - sigpipe_init(&mrc.pipe_st); + sigpipe_init(&pipe_ctx); if(checkall) { /* *perform() deals with running_handles on its own */ - result = curl_multi_perform(multi, running_handles); + mresult = multi_perform(multi, running_handles); - if(result != CURLM_BAD_HANDLE) { + if(mresult != CURLM_BAD_HANDLE) { /* Reassess event status of all active transfers */ - result = Curl_multi_ev_assess_xfer_bset(multi, &multi->process); + mresult = Curl_multi_ev_assess_xfer_bset(multi, &multi->process); } - mrc.run_cpool = TRUE; goto out; } if(s != CURL_SOCKET_TIMEOUT) { /* Mark all transfers of that socket as dirty */ - Curl_multi_ev_dirty_xfers(multi, s, &mrc.run_cpool); + Curl_multi_ev_dirty_xfers(multi, s); } else { /* Asked to run due to time-out. Clear the 'last_expire_ts' variable to @@ -3141,50 +3178,54 @@ static CURLMcode multi_socket(struct Curl_multi *multi, handles the case when the application asks libcurl to run the timeout prematurely. */ memset(&multi->last_expire_ts, 0, sizeof(multi->last_expire_ts)); - mrc.run_cpool = TRUE; + + /* Applications may set `socket_cb` *after* having added transfers + * first. *Then* kick off processing with a + * curl_multi_socket_action(TIMEOUT) afterwards. Make sure our + * admin handle registers its pollset with the callbacks present. */ + mresult = multi_assess_wakeup(multi); + if(mresult) + goto out; } - multi_mark_expired_as_dirty(&mrc); - result = multi_run_dirty(&mrc); - if(result) + 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. */ - mrc.now = curlx_now(); - multi_mark_expired_as_dirty(&mrc); - result = multi_run_dirty(&mrc); + multi_mark_expired_as_dirty(multi, &multi->now); + mresult = multi_run_dirty(multi, &pipe_ctx, &run_xfers); } out: - if(mrc.run_cpool) { - sigpipe_apply(multi->admin, &mrc.pipe_st); - Curl_cshutdn_perform(&multi->cshutdn, multi->admin, s); - } - sigpipe_restore(&mrc.pipe_st); + sigpipe_restore(&pipe_ctx); if(multi_ischanged(multi, TRUE)) process_pending_handles(multi); + if(!mresult && CURL_MNTFY_HAS_ENTRIES(multi)) + mresult = Curl_mntfy_dispatch_all(multi); + if(running_handles) { unsigned int running = Curl_multi_xfers_running(multi); *running_handles = (running < INT_MAX) ? (int)running : INT_MAX; } - if(CURLM_OK >= result) - result = Curl_update_timer(multi); - return result; + if(CURLM_OK >= mresult) + mresult = Curl_update_timer(multi); + return mresult; } #undef curl_multi_setopt -CURLMcode curl_multi_setopt(CURLM *m, - CURLMoption option, ...) +CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...) { - CURLMcode res = CURLM_OK; + CURLMcode mresult = CURLM_OK; va_list param; unsigned long uarg; struct Curl_multi *multi = m; @@ -3225,10 +3266,12 @@ CURLMcode curl_multi_setopt(CURLM *m, multi->maxconnects = (unsigned int)uarg; break; case CURLMOPT_MAX_HOST_CONNECTIONS: - multi->max_host_connections = va_arg(param, long); + if(!curlx_sltouz(va_arg(param, long), &multi->max_host_connections)) + mresult = CURLM_BAD_FUNCTION_ARGUMENT; break; case CURLMOPT_MAX_TOTAL_CONNECTIONS: - multi->max_total_connections = va_arg(param, long); + if(!curlx_sltouz(va_arg(param, long), &multi->max_total_connections)) + mresult = CURLM_BAD_FUNCTION_ARGUMENT; break; /* options formerly used for pipelining */ case CURLMOPT_MAX_PIPELINE_LENGTH: @@ -3241,16 +3284,20 @@ CURLMcode curl_multi_setopt(CURLM *m, break; case CURLMOPT_PIPELINING_SERVER_BL: break; - case CURLMOPT_MAX_CONCURRENT_STREAMS: - { - long streams = va_arg(param, long); - if((streams < 1) || (streams > INT_MAX)) - streams = 100; - multi->max_concurrent_streams = (unsigned int)streams; - } + case CURLMOPT_MAX_CONCURRENT_STREAMS: { + long streams = va_arg(param, long); + if((streams < 1) || (streams > INT_MAX)) + streams = 100; + multi->max_concurrent_streams = (unsigned int)streams; break; + } case CURLMOPT_NETWORK_CHANGED: { long val = va_arg(param, long); + if(val & CURLMNWC_CLEAR_ALL) + /* In the beginning, all values available to set were 1 by mistake. We + converted this to mean "all", thus setting all the bits + automatically */ + val = CURLMNWC_CLEAR_DNS | CURLMNWC_CLEAR_CONNS; if(val & CURLMNWC_CLEAR_DNS) { Curl_dnscache_clear(multi->admin); } @@ -3259,12 +3306,46 @@ CURLMcode curl_multi_setopt(CURLM *m, } break; } + case CURLMOPT_NOTIFYFUNCTION: + multi->ntfy.ntfy_cb = va_arg(param, curl_notify_callback); + break; + case CURLMOPT_NOTIFYDATA: + multi->ntfy.ntfy_cb_data = va_arg(param, void *); + break; + case CURLMOPT_RESOLVE_THREADS_MAX: +#ifdef USE_RESOLV_THREADED + uarg = va_arg(param, long); + if((uarg <= 0) || (uarg > UINT32_MAX)) + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + else { + CURLcode result = Curl_async_thrdd_multi_set_props( + multi, 0, (uint32_t)uarg, 2000); + switch(result) { + case CURLE_OK: + mresult = CURLM_OK; + break; + case CURLE_BAD_FUNCTION_ARGUMENT: + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + break; + case CURLE_OUT_OF_MEMORY: + mresult = CURLM_OUT_OF_MEMORY; + break; + default: + mresult = CURLM_INTERNAL_ERROR; + break; + } + } +#endif + break; + case CURLMOPT_QUICK_EXIT: + multi->quick_exit = va_arg(param, long) ? 1 : 0; + break; default: - res = CURLM_UNKNOWN_OPTION; + mresult = CURLM_UNKNOWN_OPTION; break; } va_end(param); - return res; + return mresult; } /* we define curl_multi_socket() in the public multi.h header */ @@ -3275,6 +3356,8 @@ CURLMcode curl_multi_socket(CURLM *m, curl_socket_t s, int *running_handles) struct Curl_multi *multi = m; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; + if(multi->in_ntfy_callback) + return CURLM_RECURSIVE_API_CALL; return multi_socket(multi, FALSE, s, 0, running_handles); } @@ -3284,6 +3367,8 @@ CURLMcode curl_multi_socket_action(CURLM *m, curl_socket_t s, struct Curl_multi *multi = m; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; + if(multi->in_ntfy_callback) + return CURLM_RECURSIVE_API_CALL; return multi_socket(multi, FALSE, s, ev_bitmask, running_handles); } @@ -3292,54 +3377,53 @@ CURLMcode curl_multi_socket_all(CURLM *m, int *running_handles) struct Curl_multi *multi = m; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; + if(multi->in_ntfy_callback) + return CURLM_RECURSIVE_API_CALL; return multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); } - static bool multi_has_dirties(struct Curl_multi *multi) { - unsigned int mid; - if(Curl_uint_bset_first(&multi->dirty, &mid)) { + uint32_t mid; + if(Curl_uint32_bset_first(&multi->dirty, &mid)) { do { struct Curl_easy *data = Curl_multi_get_easy(multi, mid); if(data) { - if(Curl_uint_bset_contains(&multi->process, mid)) + if(Curl_uint32_bset_contains(&multi->process, mid)) return TRUE; /* We are no longer processing this transfer */ - Curl_uint_bset_remove(&multi->dirty, mid); + Curl_uint32_bset_remove(&multi->dirty, mid); } else { CURL_TRC_M(multi->admin, "dirty transfer %u no longer found", mid); - Curl_uint_bset_remove(&multi->dirty, mid); + Curl_uint32_bset_remove(&multi->dirty, mid); } - } - while(Curl_uint_bset_next(&multi->dirty, mid, &mid)); + } while(Curl_uint32_bset_next(&multi->dirty, mid, &mid)); } return FALSE; } -static CURLMcode multi_timeout(struct Curl_multi *multi, - struct curltime *expire_time, - long *timeout_ms) +static void multi_timeout(struct Curl_multi *multi, + struct curltime *expire_time, + long *timeout_ms) { - static const struct curltime tv_zero = {0, 0}; + static const struct curltime tv_zero = { 0, 0 }; + VERBOSE(struct Curl_easy *data = NULL); if(multi->dead) { *timeout_ms = 0; - return CURLM_OK; + return; } if(multi_has_dirties(multi)) { - *expire_time = curlx_now(); + *expire_time = *multi_now(multi); *timeout_ms = 0; - return CURLM_OK; + return; } else if(multi->timetree) { - /* we have a tree of expire times */ - struct curltime now = curlx_now(); - + const struct curltime *pnow = multi_now(multi); /* splay the lowest to the bottom */ - multi->timetree = Curl_splay(tv_zero, multi->timetree); + multi->timetree = Curl_splay(&tv_zero, multi->timetree); /* this will not return NULL from a non-empty tree, but some compilers * are not convinced of that. Analyzers are hard. */ *expire_time = multi->timetree ? multi->timetree->key : tv_zero; @@ -3347,18 +3431,18 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, /* 'multi->timetree' will be non-NULL here but the compilers sometimes yell at us if we assume so */ if(multi->timetree && - curlx_timediff_us(multi->timetree->key, now) > 0) { + curlx_ptimediff_us(&multi->timetree->key, pnow) > 0) { /* some time left before expiration */ - timediff_t diff = curlx_timediff_ceil(multi->timetree->key, now); + timediff_t diff_ms = + curlx_timediff_ceil_ms(multi->timetree->key, *pnow); + 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; + *timeout_ms = (long)diff_ms; } else { - if(multi->timetree) { - struct Curl_easy *data = Curl_splayget(multi->timetree); - CURL_TRC_M(data, "multi_timeout() says this has expired"); - } + if(multi->timetree) + VERBOSE(data = Curl_splayget(multi->timetree)); /* 0 means immediately */ *timeout_ms = 0; } @@ -3368,7 +3452,16 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, *timeout_ms = -1; } - return CURLM_OK; +#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); + CURL_TRC_TIMER(data, n->eid, "gives multi timeout in %ldms", + *timeout_ms); + } + } +#endif } CURLMcode curl_multi_timeout(CURLM *m, @@ -3384,11 +3477,10 @@ CURLMcode curl_multi_timeout(CURLM *m, if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - return multi_timeout(multi, &expire_time, timeout_ms); + multi_timeout(multi, &expire_time, timeout_ms); + return CURLM_OK; } -#define DEBUG_UPDATE_TIMER 0 - /* * Tell the application it should update its timers, if it subscribes to the * update timer callback. @@ -3402,52 +3494,36 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) if(!multi->timer_cb || multi->dead) return CURLM_OK; - if(multi_timeout(multi, &expire_ts, &timeout_ms)) { - return CURLM_OK; - } + multi_timeout(multi, &expire_ts, &timeout_ms); if(timeout_ms < 0 && multi->last_timeout_ms < 0) { -#if DEBUG_UPDATE_TIMER - fprintf(stderr, "Curl_update_timer(), still no timeout, no change\n"); -#endif + /* nothing to do */ } else if(timeout_ms < 0) { /* there is no timeout now but there was one previously */ -#if DEBUG_UPDATE_TIMER - fprintf(stderr, "Curl_update_timer(), remove timeout, " - " last_timeout=%ldms\n", multi->last_timeout_ms); -#endif + CURL_TRC_M(multi->admin, "[TIMER] clear"); timeout_ms = -1; /* normalize */ set_value = TRUE; } else if(multi->last_timeout_ms < 0) { -#if DEBUG_UPDATE_TIMER - fprintf(stderr, "Curl_update_timer(), had no timeout, set now\n"); -#endif + CURL_TRC_M(multi->admin, "[TIMER] set %ldms, none before", timeout_ms); set_value = TRUE; } - else if(curlx_timediff_us(multi->last_expire_ts, expire_ts)) { + else if(curlx_ptimediff_us(&multi->last_expire_ts, &expire_ts)) { /* We had a timeout before and have one now, the absolute timestamp * differs. The relative timeout_ms may be the same, but the starting * point differs. Let the application restart its timer. */ -#if DEBUG_UPDATE_TIMER - fprintf(stderr, "Curl_update_timer(), expire timestamp changed\n"); -#endif + CURL_TRC_M(multi->admin, "[TIMER] set %ldms, replace previous", + timeout_ms); set_value = TRUE; } else { /* We have same expire time as previously. Our relative 'timeout_ms' * may be different now, but the application has the timer running * and we do not to tell it to start this again. */ -#if DEBUG_UPDATE_TIMER - fprintf(stderr, "Curl_update_timer(), same expire timestamp, no change\n"); -#endif } if(set_value) { -#if DEBUG_UPDATE_TIMER - fprintf(stderr, "Curl_update_timer(), set timeout %ldms\n", timeout_ms); -#endif multi->last_expire_ts = expire_ts; multi->last_timeout_ms = timeout_ms; set_in_callback(multi, TRUE); @@ -3466,8 +3542,7 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) * * Remove a given timestamp from the list of timeouts. */ -static void -multi_deltimeout(struct Curl_easy *data, expire_id eid) +static void multi_deltimeout(struct Curl_easy *data, expire_id eid) { struct Curl_llist_node *e; struct Curl_llist *timeoutlist = &data->state.timeoutlist; @@ -3488,10 +3563,9 @@ multi_deltimeout(struct Curl_easy *data, expire_id eid) * of list is always the timeout nearest in time. * */ -static CURLMcode -multi_addtimeout(struct Curl_easy *data, - struct curltime *stamp, - expire_id eid) +static CURLMcode multi_addtimeout(struct Curl_easy *data, + struct curltime *stamp, + expire_id eid) { struct Curl_llist_node *e; struct time_node *node; @@ -3510,22 +3584,22 @@ multi_addtimeout(struct Curl_easy *data, /* find the correct spot in the list */ for(e = Curl_llist_head(timeoutlist); e; e = Curl_node_next(e)) { struct time_node *check = Curl_node_elem(e); - timediff_t diff = curlx_timediff(check->time, node->time); + timediff_t diff = curlx_ptimediff_ms(&check->time, &node->time); if(diff > 0) break; prev = e; } - } /* else this is the first timeout on the list */ Curl_llist_insert_next(timeoutlist, prev, node, &node->list); + CURL_TRC_TIMER(data, eid, "set for %" FMT_TIMEDIFF_T "ns", + curlx_ptimediff_us(&node->time, Curl_pgrs_now(data))); return CURLM_OK; } void Curl_expire_ex(struct Curl_easy *data, - const struct curltime *nowp, timediff_t milli, expire_id id) { struct Curl_multi *multi = data->multi; @@ -3539,16 +3613,16 @@ void Curl_expire_ex(struct Curl_easy *data, DEBUGASSERT(id < EXPIRE_LAST); - set = *nowp; - set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bits conversion */ - set.tv_usec += (int)(milli%1000)*1000; + set = *Curl_pgrs_now(data); + set.tv_sec += (time_t)(milli / 1000); /* may be a 64 to 32-bit conversion */ + set.tv_usec += (int)(milli % 1000) * 1000; if(set.tv_usec >= 1000000) { set.tv_sec++; 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 @@ -3559,7 +3633,7 @@ void Curl_expire_ex(struct Curl_easy *data, /* This means that the struct is added as a node in the splay tree. Compare if the new time is earlier, and only remove-old/add-new if it is. */ - timediff_t diff = curlx_timediff(set, *curr_expire); + timediff_t diff = curlx_ptimediff_ms(&set, curr_expire); int rc; if(diff > 0) { @@ -3580,11 +3654,8 @@ void Curl_expire_ex(struct Curl_easy *data, value since it is our local minimum. */ *curr_expire = set; Curl_splayset(&data->state.timenode, data); - multi->timetree = Curl_splayinsert(*curr_expire, multi->timetree, + multi->timetree = Curl_splayinsert(curr_expire, multi->timetree, &data->state.timenode); - if(data->id >= 0) - CURL_TRC_M(data, "[TIMEOUT] set %s to expire in %" FMT_TIMEDIFF_T "ns", - CURL_TIMER_NAME(id), curlx_timediff_us(set, *nowp)); } /* @@ -3600,8 +3671,7 @@ void Curl_expire_ex(struct Curl_easy *data, */ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) { - struct curltime now = curlx_now(); - Curl_expire_ex(data, &now, milli, id); + Curl_expire_ex(data, milli, id); } /* @@ -3614,8 +3684,7 @@ void Curl_expire_done(struct Curl_easy *data, expire_id id) { /* remove the timer, if there */ multi_deltimeout(data, id); - if(data->id >= 0) - CURL_TRC_M(data, "[TIMEOUT] cleared %s", CURL_TIMER_NAME(id)); + CURL_TRC_TIMER(data, id, "cleared"); } /* @@ -3623,7 +3692,7 @@ void Curl_expire_done(struct Curl_easy *data, expire_id id) * * 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; @@ -3631,7 +3700,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 @@ -3651,19 +3720,17 @@ 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, - void *hashp) +CURLMcode curl_multi_assign(CURLM *m, curl_socket_t sockfd, + void *sockp) { struct Curl_multi *multi = m; if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; - return Curl_multi_ev_assign(multi, s, hashp); + return Curl_multi_ev_assign(multi, sockfd, sockp); } static void move_pending_to_connect(struct Curl_multi *multi, @@ -3672,8 +3739,8 @@ static void move_pending_to_connect(struct Curl_multi *multi, DEBUGASSERT(data->mstate == MSTATE_PENDING); /* Remove this node from the pending set, add into process set */ - Curl_uint_bset_remove(&multi->pending, data->mid); - Curl_uint_bset_add(&multi->process, data->mid); + Curl_uint32_bset_remove(&multi->pending, data->mid); + Curl_uint32_bset_add(&multi->process, data->mid); multistate(data, MSTATE_CONNECT); Curl_multi_mark_dirty(data); /* make it run */ @@ -3695,8 +3762,8 @@ static void move_pending_to_connect(struct Curl_multi *multi, */ static void process_pending_handles(struct Curl_multi *multi) { - unsigned int mid; - if(Curl_uint_bset_first(&multi->pending, &mid)) { + uint32_t mid; + if(Curl_uint32_bset_first(&multi->pending, &mid)) { do { struct Curl_easy *data = Curl_multi_get_easy(multi, mid); if(data) { @@ -3704,10 +3771,9 @@ static void process_pending_handles(struct Curl_multi *multi) break; } /* transfer no longer known, should not happen */ - Curl_uint_bset_remove(&multi->pending, mid); + Curl_uint32_bset_remove(&multi->pending, mid); DEBUGASSERT(0); - } - while(Curl_uint_bset_next(&multi->pending, mid, &mid)); + } while(Curl_uint32_bset_next(&multi->pending, mid, &mid)); } } @@ -3732,19 +3798,19 @@ CURL **curl_multi_get_handles(CURLM *m) { struct Curl_multi *multi = m; void *entry; - unsigned int count = Curl_uint_tbl_count(&multi->xfers); - CURL **a = malloc(sizeof(struct Curl_easy *) * (count + 1)); + 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, mid; + unsigned int i = 0; + uint32_t mid; - if(Curl_uint_tbl_first(&multi->xfers, &mid, &entry)) { + if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) { do { struct Curl_easy *data = entry; DEBUGASSERT(i < count); if(!data->state.internal) a[i++] = data; - } - while(Curl_uint_tbl_next(&multi->xfers, mid, &mid, &entry)); + } while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry)); } a[i] = NULL; /* last entry is a NULL */ } @@ -3756,6 +3822,7 @@ CURLMcode curl_multi_get_offt(CURLM *m, curl_off_t *pvalue) { struct Curl_multi *multi = m; + uint32_t n; if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -3763,21 +3830,23 @@ CURLMcode curl_multi_get_offt(CURLM *m, return CURLM_BAD_FUNCTION_ARGUMENT; switch(info) { - case CURLMINFO_XFERS_CURRENT: { - unsigned int n = Curl_uint_tbl_count(&multi->xfers); + case CURLMINFO_XFERS_CURRENT: + n = Curl_uint32_tbl_count(&multi->xfers); if(n && multi->admin) --n; *pvalue = (curl_off_t)n; return CURLM_OK; - } case CURLMINFO_XFERS_RUNNING: - *pvalue = (curl_off_t)Curl_uint_bset_count(&multi->process); + n = Curl_uint32_bset_count(&multi->process); + if(n && Curl_uint32_bset_contains(&multi->process, multi->admin->mid)) + --n; + *pvalue = (curl_off_t)n; return CURLM_OK; case CURLMINFO_XFERS_PENDING: - *pvalue = (curl_off_t)Curl_uint_bset_count(&multi->pending); + *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->pending); return CURLM_OK; case CURLMINFO_XFERS_DONE: - *pvalue = (curl_off_t)Curl_uint_bset_count(&multi->msgsent); + *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->msgsent); return CURLM_OK; case CURLMINFO_XFERS_ADDED: *pvalue = multi->xfers_total_ever; @@ -3811,16 +3880,16 @@ CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data, if(data->multi->xfer_buf && data->set.buffer_size > data->multi->xfer_buf_len) { /* not large enough, get a new one */ - free(data->multi->xfer_buf); + curlx_free(data->multi->xfer_buf); data->multi->xfer_buf = NULL; data->multi->xfer_buf_len = 0; } if(!data->multi->xfer_buf) { - data->multi->xfer_buf = malloc((size_t)data->set.buffer_size); + data->multi->xfer_buf = curlx_malloc(curlx_uitouz(data->set.buffer_size)); if(!data->multi->xfer_buf) { - failf(data, "could not allocate xfer_buf of %zu bytes", - (size_t)data->set.buffer_size); + failf(data, "could not allocate xfer_buf of %u bytes", + data->set.buffer_size); return CURLE_OUT_OF_MEMORY; } data->multi->xfer_buf_len = data->set.buffer_size; @@ -3864,16 +3933,17 @@ CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data, if(data->multi->xfer_ulbuf && data->set.upload_buffer_size > data->multi->xfer_ulbuf_len) { /* not large enough, get a new one */ - free(data->multi->xfer_ulbuf); + curlx_free(data->multi->xfer_ulbuf); data->multi->xfer_ulbuf = NULL; data->multi->xfer_ulbuf_len = 0; } if(!data->multi->xfer_ulbuf) { - data->multi->xfer_ulbuf = malloc((size_t)data->set.upload_buffer_size); + data->multi->xfer_ulbuf = + curlx_malloc(curlx_uitouz(data->set.upload_buffer_size)); if(!data->multi->xfer_ulbuf) { - failf(data, "could not allocate xfer_ulbuf of %zu bytes", - (size_t)data->set.upload_buffer_size); + failf(data, "could not allocate xfer_ulbuf of %u bytes", + data->set.upload_buffer_size); return CURLE_OUT_OF_MEMORY; } data->multi->xfer_ulbuf_len = data->set.upload_buffer_size; @@ -3911,13 +3981,13 @@ CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data, if(data->multi->xfer_sockbuf && blen > data->multi->xfer_sockbuf_len) { /* not large enough, get a new one */ - free(data->multi->xfer_sockbuf); + curlx_free(data->multi->xfer_sockbuf); data->multi->xfer_sockbuf = NULL; data->multi->xfer_sockbuf_len = 0; } if(!data->multi->xfer_sockbuf) { - data->multi->xfer_sockbuf = malloc(blen); + data->multi->xfer_sockbuf = curlx_malloc(blen); if(!data->multi->xfer_sockbuf) { failf(data, "could not allocate xfer_sockbuf of %zu bytes", blen); return CURLE_OUT_OF_MEMORY; @@ -3942,26 +4012,26 @@ void Curl_multi_xfer_sockbuf_release(struct Curl_easy *data, char *buf) static void multi_xfer_bufs_free(struct Curl_multi *multi) { DEBUGASSERT(multi); - Curl_safefree(multi->xfer_buf); + curlx_safefree(multi->xfer_buf); multi->xfer_buf_len = 0; multi->xfer_buf_borrowed = FALSE; - Curl_safefree(multi->xfer_ulbuf); + curlx_safefree(multi->xfer_ulbuf); multi->xfer_ulbuf_len = 0; multi->xfer_ulbuf_borrowed = FALSE; - Curl_safefree(multi->xfer_sockbuf); + curlx_safefree(multi->xfer_sockbuf); multi->xfer_sockbuf_len = 0; multi->xfer_sockbuf_borrowed = FALSE; } struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi, - unsigned int mid) + uint32_t mid) { - struct Curl_easy *data = mid ? Curl_uint_tbl_get(&multi->xfers, mid) : NULL; - if(data && GOOD_EASY_HANDLE(data)) + struct Curl_easy *data = Curl_uint32_tbl_get(&multi->xfers, mid); + if(GOOD_EASY_HANDLE(data)) return data; CURL_TRC_M(multi->admin, "invalid easy handle in xfer table for mid=%u", mid); - Curl_uint_tbl_remove(&multi->xfers, mid); + Curl_uint32_tbl_remove(&multi->xfers, mid); return NULL; } @@ -3972,46 +4042,66 @@ unsigned int Curl_multi_xfers_running(struct Curl_multi *multi) void Curl_multi_mark_dirty(struct Curl_easy *data) { - if(data->multi && data->mid != UINT_MAX) - Curl_uint_bset_add(&data->multi->dirty, data->mid); + if(data->multi && data->mid != UINT32_MAX) + Curl_uint32_bset_add(&data->multi->dirty, data->mid); } void Curl_multi_clear_dirty(struct Curl_easy *data) { - if(data->multi && data->mid != UINT_MAX) - Curl_uint_bset_remove(&data->multi->dirty, data->mid); + if(data->multi && data->mid != UINT32_MAX) + Curl_uint32_bset_remove(&data->multi->dirty, data->mid); +} + +CURLMcode curl_multi_notify_enable(CURLM *m, unsigned int notification) +{ + struct Curl_multi *multi = m; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + return Curl_mntfy_enable(multi, notification); +} + +CURLMcode curl_multi_notify_disable(CURLM *m, unsigned int notification) +{ + struct Curl_multi *multi = m; + + if(!GOOD_MULTI_HANDLE(multi)) + return CURLM_BAD_HANDLE; + return Curl_mntfy_disable(multi, notification); } #ifdef DEBUGBUILD -static void multi_xfer_dump(struct Curl_multi *multi, unsigned int mid, +static void multi_xfer_dump(struct Curl_multi *multi, uint32_t mid, void *entry) { struct Curl_easy *data = entry; (void)multi; if(!data) { - fprintf(stderr, "mid=%u, entry=NULL, bug in xfer table?\n", mid); + curl_mfprintf(stderr, "mid=%u, entry=NULL, bug in xfer table?\n", mid); } else { - fprintf(stderr, "mid=%u, magic=%s, p=%p, id=%" FMT_OFF_T ", url=%s\n", - mid, (data->magic == CURLEASY_MAGIC_NUMBER) ? "GOOD" : "BAD!", - (void *)data, data->id, data->state.url); + curl_mfprintf(stderr, "mid=%u, magic=%s, p=%p, id=%" FMT_OFF_T + ", url=%s\n", + mid, + (data->magic == CURLEASY_MAGIC_NUMBER) ? "GOOD" : "BAD!", + (void *)data, data->id, Curl_bufref_ptr(&data->state.url)); } } static void multi_xfer_tbl_dump(struct Curl_multi *multi) { - unsigned int mid; + uint32_t mid; void *entry; - fprintf(stderr, "=== multi xfer table (count=%u, capacity=%u\n", - Curl_uint_tbl_count(&multi->xfers), - Curl_uint_tbl_capacity(&multi->xfers)); - if(Curl_uint_tbl_first(&multi->xfers, &mid, &entry)) { + curl_mfprintf(stderr, "=== multi xfer table (count=%u, capacity=%u\n", + Curl_uint32_tbl_count(&multi->xfers), + Curl_uint32_tbl_capacity(&multi->xfers)); + if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) { multi_xfer_dump(multi, mid, entry); - while(Curl_uint_tbl_next(&multi->xfers, mid, &mid, &entry)) + while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry)) multi_xfer_dump(multi, mid, entry); } - fprintf(stderr, "===\n"); + curl_mfprintf(stderr, "===\n"); fflush(stderr); } #endif /* DEBUGBUILD */ diff --git a/lib/multi_ev.c b/lib/multi_ev.c index 61c639d9e4..b98cda0c68 100644 --- a/lib/multi_ev.c +++ b/lib/multi_ev.c @@ -21,29 +21,18 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "urldata.h" #include "url.h" #include "cfilters.h" #include "curl_trc.h" #include "multiif.h" -#include "curlx/timeval.h" #include "multi_ev.h" #include "select.h" #include "uint-bset.h" #include "uint-spbset.h" -#include "uint-table.h" -#include "curlx/warnless.h" #include "multihandle.h" -#include "socks.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" static void mev_in_callback(struct Curl_multi *multi, bool value) @@ -51,13 +40,11 @@ static void mev_in_callback(struct Curl_multi *multi, bool value) multi->in_callback = value; } -#define CURL_MEV_CONN_HASH_SIZE 3 - /* Information about a socket for which we inform the libcurl application * what to supervise (CURL_POLL_IN/CURL_POLL_OUT/CURL_POLL_REMOVE) */ struct mev_sh_entry { - struct uint_spbset xfers; /* bitset of transfers `mid`s on this socket */ + struct uint32_spbset xfers; /* bitset of transfers `mid`s on this socket */ struct connectdata *conn; /* connection using this socket or NULL */ void *user_data; /* libcurl app data via curl_multi_assign() */ unsigned int action; /* CURL_POLL_IN/CURL_POLL_OUT we last told the @@ -70,7 +57,7 @@ struct mev_sh_entry { static size_t mev_sh_entry_hash(void *key, size_t key_length, size_t slots_num) { - curl_socket_t fd = *((curl_socket_t *) key); + curl_socket_t fd = *((curl_socket_t *)key); (void)key_length; return (fd % (curl_socket_t)slots_num); } @@ -78,21 +65,22 @@ static size_t mev_sh_entry_hash(void *key, size_t key_length, size_t slots_num) static size_t mev_sh_entry_compare(void *k1, size_t k1_len, void *k2, size_t k2_len) { - (void)k1_len; (void)k2_len; - return (*((curl_socket_t *) k1)) == (*((curl_socket_t *) k2)); + (void)k1_len; + (void)k2_len; + return (*((curl_socket_t *)k1)) == (*((curl_socket_t *)k2)); } /* sockhash entry destructor callback */ static void mev_sh_entry_dtor(void *freethis) { struct mev_sh_entry *entry = (struct mev_sh_entry *)freethis; - Curl_uint_spbset_destroy(&entry->xfers); - free(entry); + Curl_uint32_spbset_destroy(&entry->xfers); + curlx_free(entry); } /* look up a given socket in the socket hash, skip invalid sockets */ -static struct mev_sh_entry * -mev_sh_entry_get(struct Curl_hash *sh, curl_socket_t s) +static struct mev_sh_entry *mev_sh_entry_get(struct Curl_hash *sh, + curl_socket_t s) { if(s != CURL_SOCKET_BAD) { /* only look for proper sockets */ @@ -102,8 +90,8 @@ mev_sh_entry_get(struct Curl_hash *sh, curl_socket_t s) } /* make sure this socket is present in the hash for this handle */ -static struct mev_sh_entry * -mev_sh_entry_add(struct Curl_hash *sh, curl_socket_t s) +static struct mev_sh_entry *mev_sh_entry_add(struct Curl_hash *sh, + curl_socket_t s) { struct mev_sh_entry *there = mev_sh_entry_get(sh, s); struct mev_sh_entry *check; @@ -114,11 +102,11 @@ mev_sh_entry_add(struct Curl_hash *sh, curl_socket_t s) } /* not present, add it */ - check = calloc(1, sizeof(struct mev_sh_entry)); + check = curlx_calloc(1, sizeof(struct mev_sh_entry)); if(!check) return NULL; /* major failure */ - Curl_uint_spbset_init(&check->xfers); + Curl_uint32_spbset_init(&check->xfers); /* make/add new hash entry */ if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) { @@ -137,13 +125,13 @@ static void mev_sh_entry_kill(struct Curl_multi *multi, curl_socket_t s) static size_t mev_sh_entry_user_count(struct mev_sh_entry *e) { - return Curl_uint_spbset_count(&e->xfers) + (e->conn ? 1 : 0); + return Curl_uint32_spbset_count(&e->xfers) + (e->conn ? 1 : 0); } static bool mev_sh_entry_xfer_known(struct mev_sh_entry *e, struct Curl_easy *data) { - return Curl_uint_spbset_contains(&e->xfers, data->mid); + return Curl_uint32_spbset_contains(&e->xfers, data->mid); } static bool mev_sh_entry_conn_known(struct mev_sh_entry *e, @@ -157,7 +145,7 @@ static bool mev_sh_entry_xfer_add(struct mev_sh_entry *e, { /* detect weird values */ DEBUGASSERT(mev_sh_entry_user_count(e) < 100000); - return Curl_uint_spbset_add(&e->xfers, data->mid); + return Curl_uint32_spbset_add(&e->xfers, data->mid); } static bool mev_sh_entry_conn_add(struct mev_sh_entry *e, @@ -172,13 +160,12 @@ static bool mev_sh_entry_conn_add(struct mev_sh_entry *e, return TRUE; } - static bool mev_sh_entry_xfer_remove(struct mev_sh_entry *e, struct Curl_easy *data) { - bool present = Curl_uint_spbset_contains(&e->xfers, data->mid); + bool present = Curl_uint32_spbset_contains(&e->xfers, data->mid); if(present) - Curl_uint_spbset_remove(&e->xfers, data->mid); + Curl_uint32_spbset_remove(&e->xfers, data->mid); return present; } @@ -208,8 +195,8 @@ 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) { - CURL_TRC_M(data, "ev %s, call(fd=%" FMT_SOCKET_T ", ev=REMOVE)", - cause, s); + 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, multi->socket_userp, entry->user_data); @@ -342,7 +329,7 @@ static CURLMcode mev_pollset_diff(struct Curl_multi *multi, /* What was the previous action the transfer had regarding this socket? * If the transfer is new to the socket, disregard the information * in `last_poll`, because the socket might have been destroyed and - * reopened. We'd have cleared the sh_entry for that, but the socket + * reopened. We would have cleared the sh_entry for that, but the socket * might still be mentioned in the hashed pollsets. */ last_action = 0; if(first_time) { @@ -358,7 +345,7 @@ static CURLMcode mev_pollset_diff(struct Curl_multi *multi, ", total=%u/%d (xfer/conn)", s, conn ? "connection" : "transfer", conn ? conn->connection_id : data->mid, - Curl_uint_spbset_count(&entry->xfers), + Curl_uint32_spbset_count(&entry->xfers), entry->conn ? 1 : 0); } else { @@ -426,7 +413,7 @@ static CURLMcode mev_pollset_diff(struct Curl_multi *multi, return mresult; CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", removed transfer, " "total=%u/%d (xfer/conn)", s, - Curl_uint_spbset_count(&entry->xfers), + Curl_uint32_spbset_count(&entry->xfers), entry->conn ? 1 : 0); } else { @@ -448,12 +435,11 @@ static void mev_pollset_dtor(void *key, size_t klen, void *entry) (void)klen; if(ps) { Curl_pollset_cleanup(ps); - free(ps); + curlx_free(ps); } } -static struct easy_pollset* -mev_add_new_conn_pollset(struct connectdata *conn) +static struct easy_pollset *mev_add_new_conn_pollset(struct connectdata *conn) { struct easy_pollset *ps; @@ -465,8 +451,7 @@ mev_add_new_conn_pollset(struct connectdata *conn) return ps; } -static struct easy_pollset* -mev_add_new_xfer_pollset(struct Curl_easy *data) +static struct easy_pollset *mev_add_new_xfer_pollset(struct Curl_easy *data) { struct easy_pollset *ps; @@ -478,9 +463,8 @@ mev_add_new_xfer_pollset(struct Curl_easy *data) return ps; } -static struct easy_pollset * -mev_get_last_pollset(struct Curl_easy *data, - struct connectdata *conn) +static struct easy_pollset *mev_get_last_pollset(struct Curl_easy *data, + struct connectdata *conn) { if(data) { if(conn) @@ -495,7 +479,7 @@ static CURLMcode mev_assess(struct Curl_multi *multi, struct connectdata *conn) { struct easy_pollset ps, *last_ps; - CURLMcode res = CURLM_OK; + CURLMcode mresult = CURLM_OK; if(!multi || !multi->socket_cb) return CURLM_OK; @@ -504,13 +488,13 @@ static CURLMcode mev_assess(struct Curl_multi *multi, if(conn) { CURLcode r = Curl_conn_adjust_pollset(data, conn, &ps); if(r) { - res = (r == CURLE_OUT_OF_MEMORY) ? - CURLM_OUT_OF_MEMORY : CURLM_INTERNAL_ERROR; + mresult = (r == CURLE_OUT_OF_MEMORY) ? + CURLM_OUT_OF_MEMORY : CURLM_INTERNAL_ERROR; goto out; } } - else if(data) - Curl_multi_pollset(data, &ps, "ev assess"); + else + Curl_multi_pollset(data, &ps); last_ps = mev_get_last_pollset(data, conn); if(!last_ps && ps.n) { @@ -519,18 +503,18 @@ static CURLMcode mev_assess(struct Curl_multi *multi, else last_ps = mev_add_new_xfer_pollset(data); if(!last_ps) { - res = CURLM_OUT_OF_MEMORY; + mresult = CURLM_OUT_OF_MEMORY; goto out; } } if(last_ps) - res = mev_pollset_diff(multi, data, conn, &ps, last_ps); + mresult = mev_pollset_diff(multi, data, conn, &ps, last_ps); else DEBUGASSERT(!ps.n); out: Curl_pollset_cleanup(&ps); - return res; + return mresult; } CURLMcode Curl_multi_ev_assess_xfer(struct Curl_multi *multi, @@ -547,23 +531,22 @@ CURLMcode Curl_multi_ev_assess_conn(struct Curl_multi *multi, } CURLMcode Curl_multi_ev_assess_xfer_bset(struct Curl_multi *multi, - struct uint_bset *set) + struct uint32_bset *set) { - unsigned int mid; - CURLMcode result = CURLM_OK; + uint32_t mid; + CURLMcode mresult = CURLM_OK; - if(multi && multi->socket_cb && Curl_uint_bset_first(set, &mid)) { + if(multi && multi->socket_cb && Curl_uint32_bset_first(set, &mid)) { do { struct Curl_easy *data = Curl_multi_get_easy(multi, mid); - if(data) - result = Curl_multi_ev_assess_xfer(multi, data); - } - while(!result && Curl_uint_bset_next(set, mid, &mid)); + if(data) { + mresult = Curl_multi_ev_assess_xfer(multi, data); + } + } while(!mresult && Curl_uint32_bset_next(set, mid, &mid)); } - return result; + return mresult; } - CURLMcode Curl_multi_ev_assign(struct Curl_multi *multi, curl_socket_t s, void *user_data) @@ -576,8 +559,7 @@ CURLMcode Curl_multi_ev_assign(struct Curl_multi *multi, } void Curl_multi_ev_dirty_xfers(struct Curl_multi *multi, - curl_socket_t s, - bool *run_cpool) + curl_socket_t s) { struct mev_sh_entry *entry; @@ -586,14 +568,14 @@ 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; - unsigned int mid; + uint32_t mid; - if(Curl_uint_spbset_first(&entry->xfers, &mid)) { + if(Curl_uint32_spbset_first(&entry->xfers, &mid)) { do { data = Curl_multi_get_easy(multi, mid); if(data) { @@ -601,14 +583,13 @@ void Curl_multi_ev_dirty_xfers(struct Curl_multi *multi, } else { CURL_TRC_M(multi->admin, "socket transfer %u no longer found", mid); - Curl_uint_spbset_remove(&entry->xfers, mid); + Curl_uint32_spbset_remove(&entry->xfers, mid); } - } - while(Curl_uint_spbset_next(&entry->xfers, mid, &mid)); + } while(Curl_uint32_spbset_next(&entry->xfers, mid, &mid)); } if(entry->conn) - *run_cpool = TRUE; + Curl_multi_mark_dirty(multi->admin); } } @@ -622,10 +603,8 @@ void Curl_multi_ev_xfer_done(struct Curl_multi *multi, struct Curl_easy *data) { DEBUGASSERT(!data->conn); /* transfer should have been detached */ - if(data != multi->admin) { - (void)mev_assess(multi, data, NULL); - Curl_meta_remove(data, CURL_META_MEV_POLLSET); - } + (void)mev_assess(multi, data, NULL); + Curl_meta_remove(data, CURL_META_MEV_POLLSET); } void Curl_multi_ev_conn_done(struct Curl_multi *multi, @@ -636,8 +615,6 @@ void Curl_multi_ev_conn_done(struct Curl_multi *multi, Curl_conn_meta_remove(conn, CURL_META_MEV_POLLSET); } -#define CURL_MEV_PS_HASH_SLOTS (991) /* nice prime */ - void Curl_multi_ev_init(struct Curl_multi *multi, size_t hashsize) { Curl_hash_init(&multi->ev.sh_entries, hashsize, mev_sh_entry_hash, diff --git a/lib/multi_ev.h b/lib/multi_ev.h index 20c1aeac81..ddbb9331d3 100644 --- a/lib/multi_ev.h +++ b/lib/multi_ev.h @@ -23,13 +23,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "hash.h" struct Curl_easy; struct Curl_multi; struct easy_pollset; -struct uint_bset; +struct uint32_bset; /* meta key for event pollset at easy handle or connection */ #define CURL_META_MEV_POLLSET "meta:mev:ps" @@ -55,7 +54,7 @@ CURLMcode Curl_multi_ev_assess_xfer(struct Curl_multi *multi, struct Curl_easy *data); /* Assess all easy handles on the list */ CURLMcode Curl_multi_ev_assess_xfer_bset(struct Curl_multi *multi, - struct uint_bset *set); + struct uint32_bset *set); /* Assess the connection by getting its current pollset */ CURLMcode Curl_multi_ev_assess_conn(struct Curl_multi *multi, struct Curl_easy *data, @@ -63,8 +62,7 @@ CURLMcode Curl_multi_ev_assess_conn(struct Curl_multi *multi, /* Mark all transfers tied to the given socket as dirty */ void Curl_multi_ev_dirty_xfers(struct Curl_multi *multi, - curl_socket_t s, - bool *run_cpool); + curl_socket_t s); /* Socket will be closed, forget anything we know about it. */ void Curl_multi_ev_socket_done(struct Curl_multi *multi, diff --git a/lib/multi_ntfy.c b/lib/multi_ntfy.c new file mode 100644 index 0000000000..1319aaec07 --- /dev/null +++ b/lib/multi_ntfy.c @@ -0,0 +1,207 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "urldata.h" +#include "curl_trc.h" +#include "multihandle.h" +#include "multiif.h" +#include "multi_ntfy.h" + + +struct mntfy_entry { + uint32_t mid; + uint32_t type; +}; + +#define CURL_MNTFY_CHUNK_SIZE 128 + +struct mntfy_chunk { + struct mntfy_chunk *next; + size_t r_offset; + size_t w_offset; + struct mntfy_entry entries[CURL_MNTFY_CHUNK_SIZE]; +}; + +static struct mntfy_chunk *mnfty_chunk_create(void) +{ + return curlx_calloc(1, sizeof(struct mntfy_chunk)); +} + +static void mnfty_chunk_destroy(struct mntfy_chunk *chunk) +{ + curlx_free(chunk); +} + +static void mnfty_chunk_reset(struct mntfy_chunk *chunk) +{ + memset(chunk, 0, sizeof(*chunk)); +} + +static bool mntfy_chunk_append(struct mntfy_chunk *chunk, + struct Curl_easy *data, + uint32_t type) +{ + struct mntfy_entry *e; + + if(chunk->w_offset >= CURL_MNTFY_CHUNK_SIZE) + return FALSE; + e = &chunk->entries[chunk->w_offset++]; + e->mid = data->mid; + e->type = type; + return TRUE; +} + +static struct mntfy_chunk *mntfy_non_full_tail(struct curl_multi_ntfy *mntfy) +{ + struct mntfy_chunk *chunk; + if(!mntfy->tail) { + chunk = mnfty_chunk_create(); + if(!chunk) + return NULL; + DEBUGASSERT(!mntfy->head); + mntfy->head = mntfy->tail = chunk; + return chunk; + } + else if(mntfy->tail->w_offset < CURL_MNTFY_CHUNK_SIZE) + return mntfy->tail; + else { /* tail is full. */ + chunk = mnfty_chunk_create(); + if(!chunk) + return NULL; + DEBUGASSERT(mntfy->head); + mntfy->tail->next = chunk; + mntfy->tail = chunk; + return chunk; + } +} + +static void mntfy_chunk_dispatch_all(struct Curl_multi *multi, + struct mntfy_chunk *chunk) +{ + struct mntfy_entry *e; + struct Curl_easy *data; + + if(multi->ntfy.ntfy_cb) { + while((chunk->r_offset < chunk->w_offset) && !multi->ntfy.failure) { + e = &chunk->entries[chunk->r_offset]; + data = e->mid ? Curl_multi_get_easy(multi, e->mid) : multi->admin; + /* 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 %u to xfer %u", + e->type, e->mid); + multi->ntfy.ntfy_cb(multi, e->type, data, multi->ntfy.ntfy_cb_data); + } + /* once dispatched, safe to increment */ + chunk->r_offset++; + } + } + mnfty_chunk_reset(chunk); +} + +void Curl_mntfy_init(struct Curl_multi *multi) +{ + memset(&multi->ntfy, 0, sizeof(multi->ntfy)); + Curl_uint32_bset_init(&multi->ntfy.enabled); +} + +CURLMcode Curl_mntfy_resize(struct Curl_multi *multi) +{ + if(Curl_uint32_bset_resize(&multi->ntfy.enabled, CURLMNOTIFY_EASY_DONE + 1)) + return CURLM_OUT_OF_MEMORY; + return CURLM_OK; +} + +void Curl_mntfy_cleanup(struct Curl_multi *multi) +{ + while(multi->ntfy.head) { + struct mntfy_chunk *chunk = multi->ntfy.head; + multi->ntfy.head = chunk->next; + mnfty_chunk_destroy(chunk); + } + multi->ntfy.tail = NULL; + Curl_uint32_bset_destroy(&multi->ntfy.enabled); +} + +CURLMcode Curl_mntfy_enable(struct Curl_multi *multi, unsigned int type) +{ + if(type > CURLMNOTIFY_EASY_DONE) + return CURLM_UNKNOWN_OPTION; + Curl_uint32_bset_add(&multi->ntfy.enabled, type); + return CURLM_OK; +} + +CURLMcode Curl_mntfy_disable(struct Curl_multi *multi, unsigned int type) +{ + if(type > CURLMNOTIFY_EASY_DONE) + return CURLM_UNKNOWN_OPTION; + Curl_uint32_bset_remove(&multi->ntfy.enabled, (uint32_t)type); + return CURLM_OK; +} + +void Curl_mntfy_add(struct Curl_easy *data, unsigned int type) +{ + struct Curl_multi *multi = data ? data->multi : NULL; + if(multi && multi->ntfy.ntfy_cb && !multi->ntfy.failure && + 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 %u for xfer %u", type, data->mid); + if(tail) + mntfy_chunk_append(tail, data, (uint32_t)type); + else + multi->ntfy.failure = CURLM_OUT_OF_MEMORY; + multi->ntfy.has_entries = TRUE; + } +} + +CURLMcode Curl_mntfy_dispatch_all(struct Curl_multi *multi) +{ + DEBUGASSERT(!multi->in_ntfy_callback); + multi->in_ntfy_callback = TRUE; + while(multi->ntfy.head && !multi->ntfy.failure) { + struct mntfy_chunk *chunk = multi->ntfy.head; + /* this may cause new notifications to be added! */ + mntfy_chunk_dispatch_all(multi, chunk); + DEBUGASSERT(chunk->r_offset == chunk->w_offset); + + if(chunk == multi->ntfy.tail) /* last one, keep */ + break; + DEBUGASSERT(chunk->next); + DEBUGASSERT(multi->ntfy.head != multi->ntfy.tail); + multi->ntfy.head = chunk->next; + mnfty_chunk_destroy(chunk); + } + multi->in_ntfy_callback = FALSE; + + if(multi->ntfy.failure) { + CURLMcode mresult = multi->ntfy.failure; + multi->ntfy.failure = CURLM_OK; /* reset, once delivered */ + return mresult; + } + else + multi->ntfy.has_entries = FALSE; + return CURLM_OK; +} diff --git a/lib/multi_ntfy.h b/lib/multi_ntfy.h new file mode 100644 index 0000000000..05df2d6681 --- /dev/null +++ b/lib/multi_ntfy.h @@ -0,0 +1,60 @@ +#ifndef HEADER_CURL_MULTI_NTFY_H +#define HEADER_CURL_MULTI_NTFY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "uint-bset.h" + +struct Curl_easy; +struct Curl_multi; + +struct curl_multi_ntfy { + curl_notify_callback ntfy_cb; + void *ntfy_cb_data; + struct uint32_bset enabled; + struct mntfy_chunk *head; + struct mntfy_chunk *tail; + CURLMcode failure; + BIT(has_entries); +}; + +void Curl_mntfy_init(struct Curl_multi *multi); +CURLMcode Curl_mntfy_resize(struct Curl_multi *multi); +void Curl_mntfy_cleanup(struct Curl_multi *multi); + +CURLMcode Curl_mntfy_enable(struct Curl_multi *multi, unsigned int type); +CURLMcode Curl_mntfy_disable(struct Curl_multi *multi, unsigned int type); + +void Curl_mntfy_add(struct Curl_easy *data, unsigned int type); + +#define CURLM_NTFY(d, t) \ + do { \ + if((d) && (d)->multi && (d)->multi->ntfy.ntfy_cb) \ + Curl_mntfy_add((d), (t)); \ + } while(0) + +#define CURL_MNTFY_HAS_ENTRIES(m) ((m)->ntfy.has_entries) + +CURLMcode Curl_mntfy_dispatch_all(struct Curl_multi *multi); + +#endif /* HEADER_CURL_MULTI_NTFY_H */ diff --git a/lib/multihandle.h b/lib/multihandle.h index ae41044adc..a0519889ab 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -23,13 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "llist.h" #include "hash.h" #include "conncache.h" #include "cshutdn.h" -#include "hostip.h" +#include "dnscache.h" #include "multi_ev.h" +#include "multi_ntfy.h" #include "psl.h" #include "socketpair.h" #include "uint-bset.h" @@ -50,31 +50,27 @@ struct Curl_message { */ typedef enum { MSTATE_INIT, /* 0 - start in this state */ - MSTATE_PENDING, /* 1 - no connections, waiting for one */ - MSTATE_SETUP, /* 2 - start a new transfer */ - MSTATE_CONNECT, /* 3 - resolve/connect has been sent off */ - MSTATE_RESOLVING, /* 4 - awaiting the resolve to finalize */ - MSTATE_CONNECTING, /* 5 - awaiting the TCP connect to finalize */ - MSTATE_TUNNELING, /* 6 - awaiting HTTPS proxy SSL initialization to - complete and/or proxy CONNECT to finalize */ - MSTATE_PROTOCONNECT, /* 7 - initiate protocol connect procedure */ - MSTATE_PROTOCONNECTING, /* 8 - completing the protocol-specific connect - phase */ - MSTATE_DO, /* 9 - start send off the request (part 1) */ - MSTATE_DOING, /* 10 - sending off the request (part 1) */ - MSTATE_DOING_MORE, /* 11 - send off the request (part 2) */ - MSTATE_DID, /* 12 - done sending off request */ - MSTATE_PERFORMING, /* 13 - transfer data */ - MSTATE_RATELIMITING, /* 14 - wait because limit-rate exceeded */ - MSTATE_DONE, /* 15 - post data transfer operation */ - MSTATE_COMPLETED, /* 16 - operation complete */ - MSTATE_MSGSENT, /* 17 - the operation complete message is sent */ - MSTATE_LAST /* 18 - not a true state, never use this */ + MSTATE_PENDING, /* no connections, waiting for one */ + MSTATE_SETUP, /* start a new transfer */ + MSTATE_CONNECT, /* resolve/connect has been sent off */ + MSTATE_CONNECTING, /* awaiting the TCP connect to finalize */ + MSTATE_PROTOCONNECT, /* initiate protocol connect procedure */ + MSTATE_PROTOCONNECTING, /* completing the protocol-specific connect phase */ + MSTATE_DO, /* start send off the request (part 1) */ + MSTATE_DOING, /* sending off the request (part 1) */ + MSTATE_DOING_MORE, /* send off the request (part 2) */ + MSTATE_DID, /* done sending off request */ + MSTATE_PERFORMING, /* transfer data */ + MSTATE_RATELIMITING, /* wait because limit-rate exceeded */ + MSTATE_DONE, /* post data transfer operation */ + MSTATE_COMPLETED, /* operation complete */ + MSTATE_MSGSENT, /* the operation complete message is sent */ + MSTATE_LAST /* not a true state, never use this */ } CURLMstate; #define CURLPIPE_ANY (CURLPIPE_MULTIPLEX) -#ifndef CURL_DISABLE_SOCKETPAIR +#if !defined(CURL_DISABLE_SOCKETPAIR) && !defined(USE_WINSOCK) #define ENABLE_WAKEUP #endif @@ -90,12 +86,12 @@ struct Curl_multi { unsigned int xfers_alive; /* amount of added transfers that have not yet reached COMPLETE state */ curl_off_t xfers_total_ever; /* total of added transfers, ever. */ - struct uint_tbl xfers; /* transfers added to this multi */ + struct uint32_tbl xfers; /* transfers added to this multi */ /* Each transfer's mid may be present in at most one of these */ - struct uint_bset process; /* transfer being processed */ - struct uint_bset dirty; /* transfer to be run NOW, e.g. ASAP. */ - struct uint_bset pending; /* transfers in waiting (conn limit etc.) */ - struct uint_bset msgsent; /* transfers done with message for application */ + struct uint32_bset process; /* transfer being processed */ + struct uint32_bset dirty; /* transfer to be run NOW, e.g. ASAP. */ + struct uint32_bset pending; /* transfers in waiting (conn limit etc.) */ + struct uint32_bset msgsent; /* transfers done with message for application */ struct Curl_llist msglist; /* a list of messages from completed transfers */ @@ -112,12 +108,17 @@ struct Curl_multi { struct Curl_dnscache dnscache; /* DNS cache */ struct Curl_ssl_scache *ssl_scache; /* TLS session pool */ +#ifdef USE_RESOLV_THREADED + struct curl_thrdq *resolv_thrdq; +#endif #ifdef USE_LIBPSL /* PSL cache. */ struct PslCache psl; #endif + /* current time for transfers running in this multi handle */ + struct curltime now; /* timetree points to the splay-tree of time nodes to figure out expire times of all currently set timers */ struct Curl_tree *timetree; @@ -134,6 +135,8 @@ struct Curl_multi { /* multi event related things */ struct curl_multi_ev ev; + /* multi notification related things */ + struct curl_multi_ntfy ntfy; /* `proto_hash` is a general key-value store for protocol implementations * with the lifetime of the multi handle. The number of elements kept here @@ -146,11 +149,10 @@ struct Curl_multi { struct cshutdn cshutdn; /* connection shutdown handling */ struct cpool cpool; /* connection pool (bundles) */ - long max_host_connections; /* if >0, a fixed limit of the maximum number - of connections per host */ - - long max_total_connections; /* if >0, a fixed limit of the maximum number - of connections in total */ + size_t max_host_connections; /* if >0, a fixed limit of the maximum number + of connections per host */ + size_t max_total_connections; /* if >0, a fixed limit of the maximum number + of connections in total */ /* timer callback and user data pointer for the *socket() API */ curl_multi_timer_callback timer_cb; @@ -160,24 +162,25 @@ struct Curl_multi { #ifdef USE_WINSOCK WSAEVENT wsa_event; /* Winsock event used for waits */ -#else +#endif #ifdef ENABLE_WAKEUP curl_socket_t wakeup_pair[2]; /* eventfd()/pipe()/socketpair() used for wakeup 0 is used for read, 1 is used for write */ -#endif #endif unsigned int max_concurrent_streams; unsigned int maxconnects; /* if >0, a fixed limit of the maximum number of entries we are allowed to grow the connection cache to */ -#define IPV6_UNKNOWN 0 -#define IPV6_DEAD 1 -#define IPV6_WORKS 2 - unsigned char ipv6_up; /* IPV6_* defined */ +#ifdef DEBUGBUILD + unsigned int now_access_count; +#endif + uint32_t last_resolv_id; /* id of the last DNS resolve operation */ + BIT(ipv6_works); BIT(multiplexing); /* multiplexing wanted */ BIT(recheckstate); /* see Curl_multi_connchanged */ BIT(in_callback); /* true while executing a callback */ + BIT(in_ntfy_callback); /* true while dispatching notifications */ #ifdef USE_OPENSSL BIT(ssl_seeded); #endif @@ -186,6 +189,7 @@ struct Curl_multi { BIT(xfer_buf_borrowed); /* xfer_buf is currently being borrowed */ BIT(xfer_ulbuf_borrowed); /* xfer_ulbuf is currently being borrowed */ BIT(xfer_sockbuf_borrowed); /* xfer_sockbuf is currently being borrowed */ + BIT(quick_exit); /* do not join threads on cleanup */ #ifdef DEBUGBUILD BIT(warned); /* true after user warned of DEBUGBUILD */ #endif diff --git a/lib/multiif.h b/lib/multiif.h index 1423d5a03d..039db269e0 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -23,16 +23,14 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * 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, - const struct curltime *nowp, 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, @@ -42,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(unsigned int xfer_table_size, - size_t hashsize, +struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, + size_t ev_hashsize, size_t chashsize, size_t dnssize, size_t sesssize); @@ -68,13 +67,11 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, struct Curl_easy *data, struct connectdata *conn); - /* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option */ unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi); CURLMcode Curl_multi_pollset(struct Curl_easy *data, - struct easy_pollset *ps, - const char *caller); + struct easy_pollset *ps); /** * Borrow the transfer buffer from the multi, suitable @@ -154,7 +151,7 @@ void Curl_multi_xfer_sockbuf_release(struct Curl_easy *data, char *buf); * Returns NULL if not found. */ struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi, - unsigned int mid); + uint32_t mid); /* Get the # of transfers current in process/pending. */ unsigned int Curl_multi_xfers_running(struct Curl_multi *multi); @@ -165,4 +162,6 @@ void Curl_multi_mark_dirty(struct Curl_easy *data); /* Clear transfer from the dirty set. */ void Curl_multi_clear_dirty(struct Curl_easy *data); +void Curl_multi_set_now(struct Curl_multi *multi); + #endif /* HEADER_CURL_MULTIIF_H */ diff --git a/lib/netrc.c b/lib/netrc.c index 447ee09585..72d8feee7d 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -21,8 +21,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #ifndef CURL_DISABLE_NETRC #ifdef HAVE_PWD_H @@ -35,17 +35,12 @@ #endif #endif -#include #include "netrc.h" #include "strcase.h" #include "curl_get_line.h" +#include "curlx/fopen.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - /* Get user and password from .netrc when given a machine name */ enum host_lookup_state { @@ -64,8 +59,8 @@ enum found_state { #define FOUND_LOGIN 1 #define FOUND_PASSWORD 2 -#define MAX_NETRC_LINE 16384 -#define MAX_NETRC_FILE (128*1024) +#define MAX_NETRC_LINE 16384 +#define MAX_NETRC_FILE (128 * 1024) #define MAX_NETRC_TOKEN 4096 /* convert a dynbuf call CURLcode error to a NETRCcode error */ @@ -76,31 +71,330 @@ enum found_state { static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf) { NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */ - FILE *file = fopen(filename, FOPEN_READTEXT); - struct dynbuf linebuf; - curlx_dyn_init(&linebuf, MAX_NETRC_LINE); + FILE *file = curlx_fopen(filename, FOPEN_READTEXT); if(file) { - ret = NETRC_OK; - while(Curl_get_line(&linebuf, file)) { - CURLcode result; - const char *line = curlx_dyn_ptr(&linebuf); - /* skip comments on load */ - curlx_str_passblanks(&line); - if(*line == '#') - continue; - result = curlx_dyn_add(filebuf, line); - if(result) { - ret = curl2netrc(result); - goto done; + curlx_struct_stat stat; + if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) { + CURLcode result = CURLE_OK; + bool eof; + struct dynbuf linebuf; + curlx_dyn_init(&linebuf, MAX_NETRC_LINE); + ret = NETRC_OK; + do { + const char *line; + /* Curl_get_line always returns lines ending with a newline */ + result = Curl_get_line(&linebuf, file, &eof); + if(!result) { + line = curlx_dyn_ptr(&linebuf); + /* skip comments on load */ + curlx_str_passblanks(&line); + if(*line == '#') + continue; + result = curlx_dyn_add(filebuf, line); + } + if(result) { + curlx_dyn_free(filebuf); + ret = curl2netrc(result); + break; + } + } while(!eof); + curlx_dyn_free(&linebuf); + } + curlx_fclose(file); + } + return ret; +} + +/* bundled parser state to keep function signatures compact */ +struct netrc_state { + char *login; + char *password; + enum host_lookup_state state; + enum found_state keyword; + NETRCcode retcode; + unsigned char found; /* FOUND_LOGIN | FOUND_PASSWORD bits */ + bool our_login; + bool done; + bool specific_login; +}; + +/* + * Parse a quoted token starting after the opening '"'. Handles \n, \r, \t + * escape sequences. Advances *tok_endp past the closing '"'. + * + * Returns NETRC_OK or error. + */ +static NETRCcode netrc_quoted_token(const char **tok_endp, + struct dynbuf *token) +{ + bool escape = FALSE; + NETRCcode rc = NETRC_SYNTAX_ERROR; + const char *tok_end = *tok_endp; + tok_end++; /* pass the leading quote */ + while(*tok_end) { + CURLcode result; + char s = *tok_end; + if(escape) { + escape = FALSE; + switch(s) { + case 'n': + s = '\n'; + break; + case 'r': + s = '\r'; + break; + case 't': + s = '\t'; + break; } } + else if(s == '\\') { + escape = TRUE; + tok_end++; + continue; + } + else if(s == '\"') { + tok_end++; /* pass the ending quote */ + rc = NETRC_OK; + break; + } + result = curlx_dyn_addn(token, &s, 1); + if(result) { + *tok_endp = tok_end; + return curl2netrc(result); + } + tok_end++; } -done: - curlx_dyn_free(&linebuf); - if(file) - fclose(file); - return ret; + *tok_endp = tok_end; + return rc; +} + +/* + * Gets the next token from the netrc buffer at *tokp. Writes the token into + * the 'token' dynbuf. Advances *tok_endp past the consumed token in the input + * buffer. Updates *statep for MACDEF newline handling. Sets *lineend = TRUE + * when the line is exhausted. + * + * Returns NETRC_OK or an error code. + */ +static NETRCcode netrc_get_token(const char **tokp, + const char **tok_endp, + struct dynbuf *token, + enum host_lookup_state *statep, + bool *lineend) +{ + const char *tok = *tokp; + const char *tok_end; + + *lineend = FALSE; + curlx_dyn_reset(token); + curlx_str_passblanks(&tok); + + /* tok is first non-space letter */ + if(*statep == MACDEF) { + if((*tok == '\n') || (*tok == '\r')) + *statep = NOTHING; /* end of macro definition */ + *lineend = TRUE; + *tokp = tok; + return NETRC_OK; + } + + if(!*tok || (*tok == '\n')) { + /* end of line */ + *lineend = TRUE; + *tokp = tok; + return NETRC_OK; + } + + tok_end = tok; + if(*tok == '\"') { + /* quoted string */ + NETRCcode ret = netrc_quoted_token(&tok_end, token); + if(ret) + return ret; + } + else { + /* unquoted token */ + size_t len = 0; + CURLcode result; + while(*tok_end > ' ') { + tok_end++; + len++; + } + if(!len) + return NETRC_SYNTAX_ERROR; + result = curlx_dyn_addn(token, tok, len); + if(result) + return curl2netrc(result); + } + + *tok_endp = tok_end; + + if(curlx_dyn_len(token)) + *tokp = curlx_dyn_ptr(token); + else + /* set it to blank to avoid NULL */ + *tokp = ""; + + return NETRC_OK; +} + +/* + * Reset parser for a new machine entry. Frees password and optionally login + * if it was not user-specified. + */ +static void netrc_new_machine(struct netrc_state *ns) +{ + ns->keyword = NONE; + ns->found = 0; + ns->our_login = FALSE; + curlx_safefree(ns->password); + if(!ns->specific_login) + curlx_safefree(ns->login); +} + +/* + * Process a parsed token through the HOSTVALID state machine branch. This + * handles login/password values and keyword transitions for the matched host. + * + * Returns NETRC_OK or an error code. + */ +static NETRCcode netrc_hostvalid(struct netrc_state *ns, const char *tok) +{ + if(ns->keyword == LOGIN) { + if(ns->specific_login) + ns->our_login = !Curl_timestrcmp(ns->login, tok); + else { + ns->our_login = TRUE; + curlx_free(ns->login); + ns->login = curlx_strdup(tok); + if(!ns->login) + return NETRC_OUT_OF_MEMORY; + } + ns->found |= FOUND_LOGIN; + ns->keyword = NONE; + } + else if(ns->keyword == PASSWORD) { + curlx_free(ns->password); + ns->password = curlx_strdup(tok); + if(!ns->password) + return NETRC_OUT_OF_MEMORY; + ns->found |= FOUND_PASSWORD; + ns->keyword = NONE; + } + else if(curl_strequal("login", tok)) + ns->keyword = LOGIN; + else if(curl_strequal("password", tok)) + ns->keyword = PASSWORD; + else if(curl_strequal("machine", tok)) { + /* a new machine here */ + + if(ns->found & FOUND_PASSWORD && + /* a password was provided for this host */ + + ((!ns->specific_login || ns->our_login) || + /* either there was no specific login to search for, or this + is the specific one we wanted */ + (ns->specific_login && !(ns->found & FOUND_LOGIN)))) { + /* or we look for a specific login, but that was not specified */ + + ns->done = TRUE; + return NETRC_OK; + } + + ns->state = HOSTFOUND; + netrc_new_machine(ns); + } + else if(curl_strequal("default", tok)) { + ns->state = HOSTVALID; + ns->retcode = NETRC_OK; + netrc_new_machine(ns); + } + if((ns->found == (FOUND_PASSWORD | FOUND_LOGIN)) && ns->our_login) + ns->done = TRUE; + return NETRC_OK; +} + +/* + * Process one parsed token through the netrc state + * machine. Updates the parser state in *ns. + * Returns NETRC_OK or an error code. + */ +static NETRCcode netrc_handle_token(struct netrc_state *ns, + const char *tok, + const char *host) +{ + switch(ns->state) { + case NOTHING: + if(curl_strequal("macdef", tok)) + ns->state = MACDEF; + else if(curl_strequal("machine", tok)) { + ns->state = HOSTFOUND; + netrc_new_machine(ns); + } + else if(curl_strequal("default", tok)) { + ns->state = HOSTVALID; + ns->retcode = NETRC_OK; + } + break; + case MACDEF: + if(!*tok) + ns->state = NOTHING; + break; + case HOSTFOUND: + if(curl_strequal(host, tok)) { + ns->state = HOSTVALID; + ns->retcode = NETRC_OK; + } + else + ns->state = NOTHING; + break; + case HOSTVALID: + return netrc_hostvalid(ns, tok); + } + return NETRC_OK; +} + +/* + * Finalize the parse result: fill in defaults and free + * resources on error. + */ +static NETRCcode netrc_finalize(struct netrc_state *ns, + char **loginp, + char **passwordp, + struct store_netrc *store) +{ + NETRCcode retcode = ns->retcode; + if(!retcode) { + if(!ns->password && ns->our_login) { + /* success without a password, set a blank one */ + ns->password = curlx_strdup(""); + if(!ns->password) + retcode = NETRC_OUT_OF_MEMORY; + } + else if(!ns->login && !ns->password) + /* a default with no credentials */ + retcode = NETRC_NO_MATCH; + } + if(!retcode) { + /* success */ + if(!ns->specific_login) + *loginp = ns->login; + + /* netrc_finalize() can return a password even when specific_login is set + but our_login is false (e.g., host matched but the requested login + never matched). See test 685. */ + *passwordp = ns->password; + } + else { + curlx_dyn_free(&store->filebuf); + store->loaded = FALSE; + if(!ns->specific_login) + curlx_free(ns->login); + curlx_free(ns->password); + } + return retcode; } /* @@ -108,23 +402,20 @@ done: */ static NETRCcode parsenetrc(struct store_netrc *store, const char *host, - char **loginp, /* might point to a username */ + char **loginp, char **passwordp, const char *netrcfile) { - NETRCcode retcode = NETRC_NO_MATCH; - char *login = *loginp; - char *password = NULL; - bool specific_login = !!login; /* points to something */ - enum host_lookup_state state = NOTHING; - enum found_state keyword = NONE; - unsigned char found = 0; /* login + password found bits, as they can come in - 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; + struct netrc_state ns; + + memset(&ns, 0, sizeof(ns)); + ns.retcode = NETRC_NO_MATCH; + ns.login = *loginp; + ns.specific_login = !!ns.login; + DEBUGASSERT(!*passwordp); curlx_dyn_init(&token, MAX_NETRC_TOKEN); @@ -137,196 +428,33 @@ static NETRCcode parsenetrc(struct store_netrc *store, netrcbuffer = curlx_dyn_ptr(filebuf); - while(!done) { + while(!ns.done) { const char *tok = netrcbuffer; - while(tok && !done) { + while(tok && !ns.done) { const char *tok_end; - bool quoted; - curlx_dyn_reset(&token); - curlx_str_passblanks(&tok); - /* tok is first non-space letter */ - if(state == MACDEF) { - if((*tok == '\n') || (*tok == '\r')) - state = NOTHING; /* end of macro definition */ + bool lineend; + NETRCcode ret; + + ret = netrc_get_token(&tok, &tok_end, &token, &ns.state, &lineend); + if(ret) { + ns.retcode = ret; + goto out; } - - if(!*tok || (*tok == '\n')) - /* end of line */ + if(lineend) break; - /* leading double-quote means quoted string */ - quoted = (*tok == '\"'); - - tok_end = tok; - if(!quoted) { - size_t len = 0; - CURLcode result; - while(*tok_end > ' ') { - tok_end++; - len++; - } - if(!len) { - retcode = NETRC_SYNTAX_ERROR; - goto out; - } - result = curlx_dyn_addn(&token, tok, len); - if(result) { - retcode = curl2netrc(result); - goto out; - } + ret = netrc_handle_token(&ns, tok, host); + if(ret) { + ns.retcode = ret; + goto out; } - else { - bool escape = FALSE; - bool endquote = FALSE; - tok_end++; /* pass the leading quote */ - while(*tok_end) { - CURLcode result; - char s = *tok_end; - if(escape) { - escape = FALSE; - switch(s) { - case 'n': - s = '\n'; - break; - case 'r': - s = '\r'; - break; - case 't': - s = '\t'; - break; - } - } - else if(s == '\\') { - escape = TRUE; - tok_end++; - continue; - } - else if(s == '\"') { - tok_end++; /* pass the ending quote */ - endquote = TRUE; - break; - } - result = curlx_dyn_addn(&token, &s, 1); - if(result) { - retcode = curl2netrc(result); - goto out; - } - tok_end++; - } - if(escape || !endquote) { - /* bad syntax, get out */ - retcode = NETRC_SYNTAX_ERROR; - goto out; - } - } - - if(curlx_dyn_len(&token)) - tok = curlx_dyn_ptr(&token); - else - /* since tok might actually be NULL for no content, set it to blank - to avoid having to deal with it being NULL */ - tok = ""; - - switch(state) { - case NOTHING: - if(curl_strequal("macdef", tok)) - /* Define a macro. A macro is defined with the specified name; its - contents begin with the next .netrc line and continue until a - null line (consecutive new-line characters) is encountered. */ - state = MACDEF; - else if(curl_strequal("machine", tok)) { - /* the next tok is the machine name, this is in itself the delimiter - that starts the stuff entered for this machine, after this we - need to search for 'login' and 'password'. */ - state = HOSTFOUND; - keyword = NONE; - found = 0; - our_login = FALSE; - Curl_safefree(password); - if(!specific_login) - Curl_safefree(login); - } - else if(curl_strequal("default", tok)) { - state = HOSTVALID; - retcode = NETRC_OK; /* we did find our host */ - } - break; - case MACDEF: - if(!*tok) - state = NOTHING; - break; - case HOSTFOUND: - if(curl_strequal(host, tok)) { - /* and yes, this is our host! */ - state = HOSTVALID; - retcode = NETRC_OK; /* we did find our host */ - } - else - /* not our host */ - state = NOTHING; - break; - case HOSTVALID: - /* we are now parsing sub-keywords concerning "our" host */ - if(keyword == LOGIN) { - if(specific_login) - our_login = !Curl_timestrcmp(login, tok); - else { - our_login = TRUE; - free(login); - login = strdup(tok); - if(!login) { - retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */ - goto out; - } - } - found |= FOUND_LOGIN; - keyword = NONE; - } - else if(keyword == PASSWORD) { - free(password); - password = strdup(tok); - if(!password) { - retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */ - goto out; - } - if(!specific_login || our_login) - found |= FOUND_PASSWORD; - keyword = NONE; - } - else if(curl_strequal("login", tok)) - keyword = LOGIN; - else if(curl_strequal("password", tok)) - keyword = PASSWORD; - else if(curl_strequal("machine", tok)) { - /* a new machine here */ - if(found & FOUND_PASSWORD) { - done = TRUE; - break; - } - state = HOSTFOUND; - keyword = NONE; - found = 0; - Curl_safefree(password); - if(!specific_login) - Curl_safefree(login); - } - else if(curl_strequal("default", tok)) { - state = HOSTVALID; - retcode = NETRC_OK; /* we did find our host */ - Curl_safefree(password); - if(!specific_login) - Curl_safefree(login); - } - if((found == (FOUND_PASSWORD|FOUND_LOGIN)) && our_login) { - done = TRUE; - break; - } - break; - } /* switch (state) */ + /* tok_end cannot point to a null byte here since lines are always + newline terminated */ + DEBUGASSERT(*tok_end); tok = ++tok_end; } - if(!done) { - char *nl = NULL; + if(!ns.done) { + const char *nl = NULL; if(tok) nl = strchr(tok, '\n'); if(!nl) @@ -338,31 +466,7 @@ static NETRCcode parsenetrc(struct store_netrc *store, out: curlx_dyn_free(&token); - if(!retcode) { - if(!password && our_login) { - /* success without a password, set a blank one */ - password = strdup(""); - if(!password) - retcode = NETRC_OUT_OF_MEMORY; /* out of memory */ - } - else if(!login && !password) - /* a default with no credentials */ - retcode = NETRC_NO_MATCH; - } - if(!retcode) { - /* success */ - if(!specific_login) - *loginp = login; - *passwordp = password; - } - else { - curlx_dyn_free(filebuf); - if(!specific_login) - free(login); - free(password); - } - - return retcode; + return netrc_finalize(&ns, loginp, passwordp, store); } const char *Curl_netrc_strerror(NETRCcode ret) @@ -410,8 +514,8 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, } else { struct passwd pw, *pw_res; - if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) - && pw_res) { + if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) && + pw_res) { home = pw.pw_dir; } #elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID) @@ -436,41 +540,41 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, return NETRC_FILE_MISSING; /* no home directory found (or possibly out of memory) */ - filealloc = aprintf("%s%s.netrc", home, DIR_CHAR); + filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR); if(!filealloc) { - free(homea); + curlx_free(homea); return NETRC_OUT_OF_MEMORY; } } retcode = parsenetrc(store, host, loginp, passwordp, filealloc); - free(filealloc); + curlx_free(filealloc); #ifdef _WIN32 if(retcode == NETRC_FILE_MISSING) { /* fallback to the old-style "_netrc" file */ - filealloc = aprintf("%s%s_netrc", home, DIR_CHAR); + filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR); if(!filealloc) { - free(homea); + curlx_free(homea); return NETRC_OUT_OF_MEMORY; } retcode = parsenetrc(store, host, loginp, passwordp, filealloc); - free(filealloc); + curlx_free(filealloc); } #endif - free(homea); + curlx_free(homea); } else retcode = parsenetrc(store, host, loginp, passwordp, netrcfile); 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/lib/netrc.h b/lib/netrc.h index 0d6d081bfb..90318c2bd6 100644 --- a/lib/netrc.h +++ b/lib/netrc.h @@ -23,9 +23,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #ifndef CURL_DISABLE_NETRC + #include "curlx/dynbuf.h" struct store_netrc { @@ -44,20 +45,19 @@ 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); - /* Assume: (*passwordp)[0]=0, host[0] != 0. - * If (*loginp)[0] = 0, search for login and password within a machine - * section in the netrc. - * If (*loginp)[0] != 0, search for password within machine and login. - */ + 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. + * If (*loginp)[0] != 0, search for password within machine and login. + */ #else /* disabled */ -#define Curl_parsenetrc(a,b,c,d,e,f) 1 #define Curl_netrc_init(x) #define Curl_netrc_cleanup(x) #endif diff --git a/lib/noproxy.c b/lib/noproxy.c index 22b067ad1b..a80db15684 100644 --- a/lib/noproxy.c +++ b/lib/noproxy.c @@ -21,12 +21,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifndef CURL_DISABLE_PROXY -#include /* for curl_strnequal() */ #include "curlx/inet_pton.h" #include "noproxy.h" #include "curlx/strparse.h" @@ -43,6 +41,9 @@ * Curl_cidr4_match() returns TRUE if the given IPv4 address is within the * specified CIDR address range. */ +UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ + const char *network, /* 1.2.3.4 address */ + unsigned int bits); UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ const char *network, /* 1.2.3.4 address */ unsigned int bits) @@ -64,9 +65,10 @@ UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ unsigned int haddr = htonl(address); unsigned int hcheck = htonl(check); #if 0 - fprintf(stderr, "Host %s (%x) network %s (%x) bits %u mask %x => %x\n", - ipv4, haddr, network, hcheck, bits, mask, - (haddr ^ hcheck) & mask); + curl_mfprintf(stderr, "Host %s (%x) network %s (%x) " + "bits %u mask %x => %x\n", + ipv4, haddr, network, hcheck, bits, mask, + (haddr ^ hcheck) & mask); #endif if((haddr ^ hcheck) & mask) return FALSE; @@ -75,6 +77,9 @@ UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ return address == check; } +UNITTEST bool Curl_cidr6_match(const char *ipv6, + const char *network, + unsigned int bits); UNITTEST bool Curl_cidr6_match(const char *ipv6, const char *network, unsigned int bits) @@ -98,7 +103,7 @@ UNITTEST bool Curl_cidr6_match(const char *ipv6, return FALSE; if(bytes && memcmp(address, check, bytes)) return FALSE; - if(rest && !((address[bytes] ^ check[bytes]) & (0xff << (8 - rest)))) + if(rest && ((address[bytes] ^ check[bytes]) & (0xff << (8 - rest)))) return FALSE; return TRUE; @@ -116,14 +121,72 @@ enum nametype { TYPE_IPV6 }; +static bool match_host(const char *token, size_t tokenlen, + const char *name, size_t namelen) +{ + bool match = FALSE; + + /* ignore trailing dots in the token to check */ + if(token[tokenlen - 1] == '.') + tokenlen--; + + if(tokenlen && (*token == '.')) { + /* ignore leading token dot as well */ + token++; + tokenlen--; + } + /* A: example.com matches 'example.com' + B: www.example.com matches 'example.com' + C: nonexample.com DOES NOT match 'example.com' + */ + if(tokenlen == namelen) + /* case A, exact match */ + match = curl_strnequal(token, name, namelen); + else if(tokenlen < namelen) { + /* case B, tailmatch domain */ + match = (name[namelen - tokenlen - 1] == '.') && + curl_strnequal(token, name + (namelen - tokenlen), tokenlen); + } + /* case C passes through, not a match */ + return match; +} + +static bool match_ip(int type, const char *token, size_t tokenlen, + const char *name) +{ + char *slash; + unsigned int bits = 0; + char checkip[128]; + if(tokenlen >= sizeof(checkip)) + /* this cannot match */ + return FALSE; + /* copy the check name to a temp buffer */ + memcpy(checkip, token, tokenlen); + checkip[tokenlen] = 0; + + slash = strchr(checkip, '/'); + /* if the slash is part of this token, use it */ + if(slash) { + curl_off_t value; + const char *p = &slash[1]; + if(curlx_str_number(&p, &value, 128) || *p) + return FALSE; + /* a too large value is rejected in the cidr function below */ + bits = (unsigned int)value; + *slash = 0; /* null-terminate there */ + } + if(type == TYPE_IPV6) + return Curl_cidr6_match(name, checkip, bits); + else + return Curl_cidr4_match(name, checkip, bits); +} + /**************************************************************** -* Checks if the host is in the noproxy list. returns TRUE if it matches and -* therefore the proxy should NOT be used. -****************************************************************/ + * Checks if the host is in the noproxy list. returns TRUE if it matches and + * therefore the proxy should NOT be used. + ****************************************************************/ bool Curl_check_noproxy(const char *name, const char *no_proxy) { - char hostip[128]; - /* * If we do not have a hostname at all, like for example with a FILE * transfer, we have nothing to interrogate the noproxy list with. @@ -139,43 +202,30 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy) if(no_proxy && no_proxy[0]) { const char *p = no_proxy; size_t namelen; + char address[16]; enum nametype type = TYPE_HOST; 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 */ - if(name[0] == '[') { - char *endptr; - /* IPv6 numerical address */ - endptr = strchr(name, ']'); - if(!endptr) - return FALSE; - name++; - namelen = endptr - name; - if(namelen >= sizeof(hostip)) - return FALSE; - memcpy(hostip, name, namelen); - hostip[namelen] = 0; - name = hostip; + /* Check if name is an IP address; if not, assume it being a hostname. */ + namelen = strlen(name); + if(curlx_inet_pton(AF_INET, name, &address) == 1) + type = TYPE_IPV4; +#ifdef USE_IPV6 + else if(curlx_inet_pton(AF_INET6, name, &address) == 1) type = TYPE_IPV6; - } +#endif else { - unsigned int address; - namelen = strlen(name); - if(curlx_inet_pton(AF_INET, name, &address) == 1) - type = TYPE_IPV4; - else { - /* ignore trailing dots in the hostname */ - if(name[namelen - 1] == '.') - namelen--; - } + /* ignore trailing dots in the hostname */ + if(name[namelen - 1] == '.') + namelen--; } while(*p) { const char *token; size_t tokenlen = 0; - bool match = FALSE; /* pass blanks */ curlx_str_passblanks(&p); @@ -188,64 +238,16 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy) } if(tokenlen) { - switch(type) { - case TYPE_HOST: - /* ignore trailing dots in the token to check */ - if(token[tokenlen - 1] == '.') - tokenlen--; + bool match = FALSE; + if(type == TYPE_HOST) + match = match_host(token, tokenlen, name, namelen); + else + match = match_ip(type, token, tokenlen, name); - if(tokenlen && (*token == '.')) { - /* ignore leading token dot as well */ - token++; - tokenlen--; - } - /* A: example.com matches 'example.com' - B: www.example.com matches 'example.com' - C: nonexample.com DOES NOT match 'example.com' - */ - if(tokenlen == namelen) - /* case A, exact match */ - match = curl_strnequal(token, name, namelen); - else if(tokenlen < namelen) { - /* case B, tailmatch domain */ - match = (name[namelen - tokenlen - 1] == '.') && - curl_strnequal(token, name + (namelen - tokenlen), - tokenlen); - } - /* case C passes through, not a match */ - break; - case TYPE_IPV4: - case TYPE_IPV6: { - const char *check = token; - char *slash; - unsigned int bits = 0; - char checkip[128]; - if(tokenlen >= sizeof(checkip)) - /* this cannot match */ - break; - /* copy the check name to a temp buffer */ - memcpy(checkip, check, tokenlen); - checkip[tokenlen] = 0; - check = checkip; - - slash = strchr(check, '/'); - /* if the slash is part of this token, use it */ - if(slash) { - /* if the bits variable gets a crazy value here, that is fine as - the value will then be rejected in the cidr function */ - bits = (unsigned int)atoi(slash + 1); - *slash = 0; /* null-terminate there */ - } - if(type == TYPE_IPV6) - match = Curl_cidr6_match(name, check, bits); - else - match = Curl_cidr4_match(name, check, bits); - break; - } - } if(match) return TRUE; - } /* if(tokenlen) */ + } + /* pass blanks after pattern */ curlx_str_passblanks(&p); /* if not a comma, this ends the loop */ @@ -255,7 +257,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/lib/noproxy.h b/lib/noproxy.h index 71ae7eaafa..e16c139bb5 100644 --- a/lib/noproxy.h +++ b/lib/noproxy.h @@ -26,17 +26,6 @@ #include "curl_setup.h" #ifndef CURL_DISABLE_PROXY - -#ifdef UNITTESTS - -UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ - const char *network, /* 1.2.3.4 address */ - unsigned int bits); -UNITTEST bool Curl_cidr6_match(const char *ipv6, - const char *network, - unsigned int bits); -#endif - bool Curl_check_noproxy(const char *name, const char *no_proxy); #endif diff --git a/lib/openldap.c b/lib/openldap.c index da26d4a78d..5d6637543f 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -22,7 +22,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP) @@ -42,8 +41,8 @@ #include "urldata.h" #include "url.h" -#include #include "sendf.h" +#include "curl_trc.h" #include "vtls/vtls.h" #include "transfer.h" #include "curl_ldap.h" @@ -52,10 +51,7 @@ #include "connect.h" #include "curl_sasl.h" #include "strcase.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "bufref.h" /* * Uncommenting this will enable the built-in debug logging of the openldap @@ -95,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_NONE /* flags */ -}; - -#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 */ -}; -#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. */ @@ -208,7 +114,6 @@ struct ldapreqinfo { /* meta key for storing ldapconninfo at connection */ #define CURL_META_LDAP_CONN "meta:proto:ldap:conn" - /* * oldap_state() * @@ -217,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", @@ -234,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; } @@ -276,14 +182,14 @@ static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp) *ludp = NULL; if(!data->state.up.user && !data->state.up.password && !data->state.up.options) - rc = ldap_url_parse(data->state.url, ludp); + rc = ldap_url_parse(Curl_bufref_ptr(&data->state.url), ludp); if(rc != LDAP_URL_SUCCESS) { const char *msg = "url parsing problem"; result = rc == LDAP_URL_ERR_MEM ? CURLE_OUT_OF_MEMORY : CURLE_URL_MALFORMAT; rc -= LDAP_URL_SUCCESS; - if((size_t) rc < CURL_ARRAYSIZE(url_errs)) + if((size_t)rc < CURL_ARRAYSIZE(url_errs)) msg = url_errs[rc]; failf(data, "LDAP local: %s", msg); } @@ -430,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; @@ -468,6 +374,10 @@ static CURLcode oldap_perform_mechs(struct Curl_easy *data) if(!li) return CURLE_FAILED_INIT; + /* Casting away the const for the 3rd parameter that the LDAP API expects as + a non-const char ** is potentially unsafe but we believe the lack of + const in the API was an oversight and that no LDAP implementation + actually modifies the input. */ rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)", (char **)CURL_UNCONST(supportedSASLMechanisms), 0, NULL, NULL, NULL, 0, &li->msgid); @@ -496,17 +406,88 @@ 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 ber_slen_t ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, - ber_len_t len); -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); - -static Sockbuf_IO ldapsb_tls = +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; +} + +static Sockbuf_IO ldapsb_tls = { ldapsb_tls_setup, ldapsb_tls_remove, ldapsb_tls_ctrl, @@ -531,18 +512,19 @@ static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate) if(!li) return CURLE_FAILED_INIT; result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); - if(!result) { - oldap_state(data, li, newstate); + if(result) + return result; + oldap_state(data, li, newstate); - if(ssldone) { - Sockbuf *sb; + if(ssldone) { + Sockbuf *sb; - /* Install the libcurl SSL handlers into the sockbuf. */ - ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); - ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); - li->recv = conn->recv[FIRSTSOCKET]; - li->send = conn->send[FIRSTSOCKET]; - } + /* Install the libcurl SSL handlers into the sockbuf. */ + 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; + li->recv = conn->recv[FIRSTSOCKET]; + li->send = conn->send[FIRSTSOCKET]; } return result; @@ -570,7 +552,7 @@ static void oldap_easy_dtor(void *key, size_t klen, void *entry) struct ldapreqinfo *lr = entry; (void)key; (void)klen; - free(lr); + curlx_free(lr); } static void oldap_conn_dtor(void *key, size_t klen, void *entry) @@ -582,9 +564,23 @@ static void oldap_conn_dtor(void *key, size_t klen, void *entry) ldap_unbind_ext(li->ld, NULL, NULL); li->ld = NULL; } - free(li); + 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; @@ -599,7 +595,7 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) (void)done; - li = calloc(1, sizeof(struct ldapconninfo)); + li = curlx_calloc(1, sizeof(struct ldapconninfo)); if(!li) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -618,12 +614,11 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) if(result) goto out; - hosturl = aprintf("%s://%s%s%s:%d", - conn->handler->scheme, - conn->bits.ipv6_ip ? "[" : "", - conn->host.name, - conn->bits.ipv6_ip ? "]" : "", - conn->remote_port); + hosturl = curl_maprintf("%s://%s:%d", + conn->scheme->name, + (data->state.up.hostname[0] == '[') ? + data->state.up.hostname : conn->host.name, + conn->remote_port); if(!hosturl) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -654,6 +649,19 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) /* Do not chase referrals. */ ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); + { + ber_len_t max = 256 * 1024; + Sockbuf *sb; + if((ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb) != LDAP_OPT_SUCCESS) || + /* Set the maximum allowed size of an incoming message, which to + OpenLDAP means that it will malloc() memory up to this size. If not + set, there is no limit and we instead risk a malloc() failure. */ + !ber_sockbuf_ctrl(sb, LBER_SB_OPT_SET_MAX_INCOMING, &max)) { + result = CURLE_FAILED_INIT; + goto out; + } + } + #ifdef USE_SSL if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { result = oldap_ssl_connect(data, OLDAP_SSL); @@ -677,7 +685,7 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) result = oldap_perform_bind(data, OLDAP_BIND); out: - free(hosturl); + curlx_free(hosturl); return result; } @@ -685,13 +693,19 @@ out: static CURLcode oldap_state_mechs_resp(struct Curl_easy *data, LDAPMessage *msg, int code) { - struct connectdata *conn = data->conn; - struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); + struct connectdata *conn; + struct ldapconninfo *li; int rc; BerElement *ber = NULL; CURLcode result = CURLE_OK; struct berval bv, *bvals; + if(!data) + return CURLE_FAILED_INIT; + + conn = data->conn; + li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); + if(!li) return CURLE_FAILED_INIT; switch(ldap_msgtype(msg)) { @@ -714,8 +728,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; } @@ -728,7 +743,7 @@ static CURLcode oldap_state_mechs_resp(struct Curl_easy *data, case LDAP_RES_SEARCH_RESULT: switch(code) { case LDAP_SIZELIMIT_EXCEEDED: - infof(data, "Too many authentication mechanisms\n"); + infof(data, "Too many authentication mechanisms"); FALLTHROUGH(); case LDAP_SUCCESS: case LDAP_NO_RESULTS_RETURNED: @@ -813,7 +828,7 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); LDAPMessage *msg = NULL; - struct timeval tv = {0, 0}; + struct timeval tv = { 0, 0 }; int code = LDAP_SUCCESS; int rc; @@ -946,18 +961,17 @@ static CURLcode oldap_disconnect(struct Curl_easy *data, (void)data; #endif - if(li) { - if(li->ld) { + if(li && li->ld) { #ifdef USE_SSL - if(ssl_installed(conn)) { - Sockbuf *sb; - ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); - ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); - } -#endif - ldap_unbind_ext(li->ld, NULL, NULL); - li->ld = NULL; + if(ssl_installed(conn)) { + Sockbuf *sb; + 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; } +#endif + ldap_unbind_ext(li->ld, NULL, NULL); + li->ld = NULL; } return CURLE_OK; } @@ -974,9 +988,8 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done) if(!li) return CURLE_FAILED_INIT; - connkeep(conn, "OpenLDAP do"); - infof(data, "LDAP local: %s", data->state.url); + infof(data, "LDAP local: %s", Curl_bufref_ptr(&data->state.url)); result = oldap_url_parse(data, &lud); if(result) @@ -986,8 +999,11 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done) if(ssl_installed(conn)) { Sockbuf *sb; /* re-install the libcurl SSL handlers into the sockbuf. */ - ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb); - ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data); + 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)) { + ldap_free_urldesc(lud); + return CURLE_FAILED_INIT; + } } #endif @@ -1001,7 +1017,7 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done) goto out; } - lr = calloc(1, sizeof(struct ldapreqinfo)); + lr = curlx_calloc(1, sizeof(struct ldapreqinfo)); if(!lr || Curl_meta_set(data, CURL_META_LDAP_EASY, lr, oldap_easy_dtor)) { ldap_abandon_ext(li->ld, msgid, NULL, NULL); @@ -1073,9 +1089,8 @@ static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, int rc; LDAPMessage *msg = NULL; BerElement *ber = NULL; - struct timeval tv = {0, 0}; + struct timeval tv = { 0, 0 }; struct berval bv, *bvals; - bool binary = FALSE; CURLcode result = CURLE_AGAIN; int code; char *info = NULL; @@ -1146,6 +1161,7 @@ static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, rc == LDAP_SUCCESS; rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) { int i; + bool binary; if(!bv.bv_val) break; @@ -1159,7 +1175,7 @@ static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, } binary = bv.bv_len > 7 && - !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7); + curl_strnequal(bv.bv_val + bv.bv_len - 7, ";binary", 7); for(i = 0; bvals[i].bv_val != NULL; i++) { bool binval = FALSE; @@ -1171,8 +1187,9 @@ static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, if(!binary) { /* check for leading or trailing whitespace */ - if(ISBLANK(bvals[i].bv_val[0]) || - ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1])) + if(bvals[i].bv_len && + (ISBLANK(bvals[i].bv_val[0]) || + ISBLANK(bvals[i].bv_val[bvals[i].bv_len - 1]))) binval = TRUE; else { /* check for unprintable characters */ @@ -1190,12 +1207,13 @@ static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, /* Binary value, encode to base64. */ if(bvals[i].bv_len) - result = curlx_base64_encode(bvals[i].bv_val, bvals[i].bv_len, + result = curlx_base64_encode((uint8_t *)bvals[i].bv_val, + bvals[i].bv_len, &val_b64, &val_b64_sz); if(!result) result = client_write(data, STRCONST(": "), val_b64, val_b64_sz, STRCONST("\n")); - free(val_b64); + curlx_free(val_b64); } else result = client_write(data, STRCONST(" "), @@ -1213,8 +1231,6 @@ static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, break; } - ber_free(ber, 0); - if(!result) result = client_write(data, STRCONST("\n"), NULL, 0, NULL, 0); if(!result) @@ -1222,94 +1238,54 @@ static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, break; } + ber_free(ber, 0); ldap_msgfree(msg); return result; } -#ifdef USE_SSL -static int -ldapsb_tls_setup(Sockbuf_IO_Desc *sbiod, void *arg) +void Curl_ldap_version(char *buf, size_t bufsz) { - sbiod->sbiod_pvt = arg; - return 0; -} + LDAPAPIInfo api; + api.ldapai_info_version = LDAP_API_INFO_VERSION; -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); + if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) == LDAP_OPT_SUCCESS) { + 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)) + - patch) / 100; + curl_msnprintf(buf, bufsz, "%s/%u.%u.%u", + api.ldapai_vendor_name, major, minor, patch); + ldap_memfree(api.ldapai_vendor_name); + ber_memvfree((void **)api.ldapai_extensions); } - return 0; + else + curl_msnprintf(buf, bufsz, "OpenLDAP"); } -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 */ +/* + * 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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; #endif /* !CURL_DISABLE_LDAP && USE_OPENLDAP */ + +/* The LDAP scheme structs are in ldap.c */ diff --git a/lib/optiontable.pl b/lib/optiontable.pl index 43fbd39d3b..db0659cbb0 100755 --- a/lib/optiontable.pl +++ b/lib/optiontable.pl @@ -31,6 +31,7 @@ print <) { +my $file = shift @ARGV; +open(CURL_H, '<', $file) or die; +while() { my $l = $_; if($fl) { # continued deprecation @@ -121,7 +123,7 @@ while() { } } } - +close(CURL_H); for my $name (sort @names) { my $oname = $name; @@ -131,19 +133,19 @@ for my $name (sort @names) { $name = $alias{$name}; $flag = "CURLOT_FLAG_ALIAS"; } - my $o = sprintf(" {\"%s\", %s, %s, %s},\n", + my $o = sprintf(" { \"%s\", %s, %s, %s },\n", $oname, $opt{$name}, $type{$name}, $flag); if(length($o) < 80) { print $o; } else { - printf(" {\"%s\", %s,\n %s, %s},\n", - $oname, $opt{$name}, $type{$name}, $flag); + printf(" { \"%s\", %s,\n %s, %s },\n", + $oname, $opt{$name}, $type{$name}, $flag); } } print < - -#include -#include "curlx/warnless.h" -#include "parsedate.h" -#include "curlx/strparse.h" - -/* - * 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); +#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 */ +const char * const Curl_wkday[] = { + "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" +}; +const char * const Curl_month[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; +#endif #define PARSEDATE_OK 0 -#define PARSEDATE_FAIL -1 +#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 -#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 */ -const char * const Curl_wkday[] = -{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; -const char * const Curl_month[]= -{ "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; -#endif - -#ifndef CURL_DISABLE_PARSEDATE -static const char * const weekday[] = -{ "Monday", "Tuesday", "Wednesday", "Thursday", - "Friday", "Saturday", "Sunday" }; +static const char * const weekday[] = { + "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" +}; struct tzinfo { char name[5]; - int offset; /* +/- in minutes */ + int16_t offset; /* +/- in minutes */ }; -/* 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 */ -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 */ - {"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 */ - /* 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 */ - /* "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 */ +#define tDAYZONE (-60) /* offset for daylight savings time */ + +/* alpha-sorted list of time zones */ +static const struct tzinfo tz[] = { + { "A", -1 * 60 }, /* Alpha */ + { "ADT", 240 + tDAYZONE }, /* Atlantic Daylight */ + { "AHST", 600 }, /* Alaska-Hawaii Standard */ + { "AST", 240 }, /* Atlantic Standard */ + { "B", -2 * 60 }, /* Bravo */ + { "BST", 0 + tDAYZONE }, /* British Summer */ + { "C", -3 * 60 }, /* Charlie */ + { "CAT", 600 }, /* Central Alaska */ + { "CCT", -480 }, /* China Coast, USSR Zone 7 */ + { "CDT", 360 + tDAYZONE }, /* Central Daylight */ + { "CEST", -60 + tDAYZONE }, /* Central European Summer */ + { "CET", -60 }, /* Central European */ + { "CST", 360 }, /* Central Standard */ + { "D", -4 * 60 }, /* Delta */ + { "E", -5 * 60 }, /* Echo */ + { "EADT", -600 + tDAYZONE }, /* Eastern Australian Daylight */ + { "EAST", -600 }, /* Eastern Australian Standard */ + { "EDT", 300 + tDAYZONE }, /* Eastern Daylight */ + { "EET", -120 }, /* Eastern Europe, USSR Zone 1 */ + { "EST", 300 }, /* Eastern Standard */ + { "F", -6 * 60 }, /* Foxtrot */ + { "FST", -60 + tDAYZONE }, /* French Summer */ + { "FWT", -60 }, /* French Winter */ + { "G", -7 * 60 }, /* Golf */ + { "GMT", 0 }, /* Greenwich Mean */ + { "GST", -600 }, /* Guam Standard, USSR Zone 9 */ + { "H", -8 * 60 }, /* Hotel */ + { "HDT", 600 + tDAYZONE }, /* Hawaii Daylight */ + { "HST", 600 }, /* Hawaii Standard */ + { "I", -9 * 60 }, /* India */ + { "IDLE", -720 }, /* International Date Line East */ + { "IDLW", 720 }, /* International Date Line West */ + { "JST", -540 }, /* Japan Standard, USSR Zone 8 */ + { "K", -10 * 60 }, /* Kilo */ + { "L", -11 * 60 }, /* Lima */ + { "M", -12 * 60 }, /* Mike */ + { "MDT", 420 + tDAYZONE }, /* Mountain Daylight */ + { "MEST", -60 + tDAYZONE }, /* Middle European Summer */ + { "MESZ", -60 + tDAYZONE }, /* Middle European Summer */ + { "MET", -60 }, /* Middle European */ + { "MEWT", -60 }, /* Middle European Winter */ + { "MST", 420 }, /* Mountain Standard */ + { "N", 60 }, /* November */ + { "NT", 660 }, /* Nome */ /* spellchecker:disable-line */ + { "NZDT", -720 + tDAYZONE }, /* New Zealand Daylight */ + { "NZST", -720 }, /* New Zealand Standard */ + { "NZT", -720 }, /* New Zealand */ + { "O", 2 * 60 }, /* Oscar */ + { "P", 3 * 60 }, /* Papa */ + { "PDT", 480 + tDAYZONE }, /* Pacific Daylight */ + { "PST", 480 }, /* Pacific Standard */ + { "Q", 4 * 60 }, /* Quebec */ + { "R", 5 * 60 }, /* Romeo */ + { "S", 6 * 60 }, /* Sierra */ + { "T", 7 * 60 }, /* Tango */ + { "U", 8 * 60 }, /* Uniform */ + { "UT", 0 }, /* Universal Time */ + { "UTC", 0 }, /* Universal (Coordinated) */ + { "V", 9 * 60 }, /* Victor */ + { "W", 10 * 60 }, /* Whiskey */ + { "WADT", -420 + tDAYZONE }, /* West Australian Daylight */ + { "WAST", -420 }, /* spellchecker:disable-line */ + /* West Australian Standard */ + { "WAT", 60 }, /* West Africa */ + { "WET", 0 }, /* Western European */ + { "X", 11 * 60 }, /* X-ray */ + { "Y", 12 * 60 }, /* Yankee */ + { "YDT", 540 + tDAYZONE }, /* Yukon Daylight */ + { "YST", 540 }, /* Yukon Standard */ + { "Z", 0 }, /* Zulu, zero meridian, a.k.a. UTC */ }; /* returns: @@ -246,22 +228,24 @@ static int checkmonth(const char *check, size_t len) return -1; /* return the offset or -1, no real offset is -1 */ } -/* return the time zone offset between GMT and the input one, in number - of seconds or -1 if the timezone was not found/legal */ +static int tzcompare(const void *m1, const void *m2) +{ + const struct tzinfo *tz1 = m1; + const struct tzinfo *tz2 = m2; + return strcmp(tz1->name, tz2->name); +} +/* return the time zone offset between GMT and the input one, in number of + seconds or -1 if the timezone was not found/legal */ static int checktz(const char *check, size_t len) { - unsigned int i; - const struct tzinfo *what = tz; - if(len > 4) /* longer than any valid timezone */ - return -1; - - for(i = 0; i < CURL_ARRAYSIZE(tz); i++) { - size_t ilen = strlen(what->name); - if((ilen == len) && - curl_strnequal(check, what->name, len)) - return what->offset*60; - what++; + if(len <= 4) { + const struct tzinfo *what; + struct tzinfo find; + curlx_strcopy(find.name, sizeof(find.name), check, len); + what = bsearch(&find, tz, CURL_ARRAYSIZE(tz), sizeof(tz[0]), tzcompare); + if(what) + return what->offset * 60; } return -1; } @@ -279,6 +263,9 @@ enum assume { DATE_TIME }; +/* (1969 / 4) - (1969 / 100) + (1969 / 400) = 492 - 19 + 4 = 477 */ +#define LEAP_DAYS_BEFORE_1969 477 + /* * time2epoch: time stamp to seconds since epoch in GMT time zone. Similar to * mktime but for GMT only. @@ -286,14 +273,15 @@ enum assume { static time_t time2epoch(int sec, int min, int hour, int mday, int mon, int year) { - static const int month_days_cumulative [12] = - { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; - 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; + static const int cumulative_days[12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 + }; + int y = year - (mon <= 1); + int leap_days = (y / 4) - (y / 100) + (y / 400) - LEAP_DAYS_BEFORE_1969; + time_t days = (time_t)(year - 1970) * 365 + leap_days + + cumulative_days[mon] + mday - 1; + + return (((days * 24 + hour) * 60 + min) * 60) + sec; } /* Returns the value of a single-digit or two-digit decimal number, return @@ -304,13 +292,12 @@ 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; } - /* HH:MM:SS or HH:MM and accept single-digits too */ static bool match_time(const char *date, int *h, int *m, int *s, char **endp) @@ -362,7 +349,7 @@ static int parsedate(const char *date, time_t *output) time_t t = 0; int wdaynum = -1; /* day of the week number, 0-6 (mon-sun) */ int monnum = -1; /* month of the year number, 0-11 */ - int mdaynum = -1; /* day of month, 1 - 31 */ + int mdaynum = -1; /* day of month, 1 - 31 */ int hournum = -1; int minnum = -1; int secnum = -1; @@ -399,7 +386,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; @@ -434,7 +421,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 @@ -444,11 +431,11 @@ 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 */ - tzoff = date[-1]=='+' ? -tzoff : tzoff; + tzoff = date[-1] == '+' ? -tzoff : tzoff; } else if((num_digits == 8) && @@ -457,9 +444,9 @@ static int parsedate(const char *date, time_t *output) (mdaynum == -1)) { /* 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 */ - mdaynum = val%100; + yearnum = val / 10000; + monnum = ((val % 10000) / 100) - 1; /* month is 0 - 11 */ + mdaynum = val % 100; } if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) { @@ -493,12 +480,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; @@ -571,11 +558,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) @@ -596,27 +583,3 @@ int Curl_getdate_capped(const char *p, time_t *tp) int rc = parsedate(p, tp); return (rc == PARSEDATE_FAIL); } - -/* - * Curl_gmtime() is a gmtime() replacement for portability. Do not use the - * gmtime_r() or gmtime() functions anywhere else but here. - * - */ - -CURLcode Curl_gmtime(time_t intime, struct tm *store) -{ - const struct tm *tm; -#ifdef HAVE_GMTIME_R - /* thread-safe version */ - tm = (struct tm *)gmtime_r(&intime, store); -#else - /* !checksrc! disable BANNEDFUNC 1 */ - tm = gmtime(&intime); - if(tm) - *store = *tm; /* copy the pointed struct to the local copy */ -#endif - - if(!tm) - return CURLE_BAD_FUNCTION_ARGUMENT; - return CURLE_OK; -} diff --git a/lib/parsedate.h b/lib/parsedate.h index e5efb53f61..ea136bd301 100644 --- a/lib/parsedate.h +++ b/lib/parsedate.h @@ -23,16 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - extern const char * const Curl_wkday[7]; extern const char * const Curl_month[12]; -CURLcode Curl_gmtime(time_t intime, struct tm *store); - /* Curl_getdate_capped() differs from curl_getdate() in that this returns 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/lib/pingpong.c b/lib/pingpong.c index 003ad58441..44e424418d 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -24,60 +24,36 @@ * IMAP, POP3, SMTP and whatever more that likes them. * ***************************************************************************/ - #include "curl_setup.h" #include "urldata.h" -#include "cfilters.h" -#include "connect.h" -#include "sendf.h" -#include "select.h" -#include "progress.h" -#include "speedcheck.h" #include "pingpong.h" -#include "multiif.h" -#include "vtls/vtls.h" -#include "strdup.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" #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) +#include "cfilters.h" +#include "connect.h" +#include "multiif.h" +#include "sendf.h" +#include "curl_trc.h" +#include "select.h" +#include "progress.h" + +timediff_t Curl_pp_state_timeleft_ms(struct Curl_easy *data, + struct pingpong *pp) { - timediff_t timeout_ms; /* in milliseconds */ - timediff_t response_time = (data->set.server_response_timeout > 0) ? - data->set.server_response_timeout : pp->response_time; - struct curltime now = curlx_now(); + timediff_t xfer_remain_ms; + timediff_t remain_ms = data->set.server_response_timeout ? + data->set.server_response_timeout : PINGPONG_TIMEOUT_MS; - /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine - remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is - supposed to govern the response for any given server response, not for - the time from connect to the given server response. */ - - /* Without a requested timeout, we only wait 'response_time' seconds for the - full response to arrive before we bail out */ - timeout_ms = response_time - curlx_timediff(now, pp->response); - - if((data->set.timeout > 0) && !disconnecting) { - /* if timeout is requested, find out how much overall remains */ - timediff_t timeout2_ms = Curl_timeleft(data, &now, FALSE); - /* pick the lowest number */ - timeout_ms = CURLMIN(timeout_ms, timeout2_ms); - } - - if(disconnecting) { - timediff_t total_left_ms = Curl_timeleft(data, NULL, FALSE); - timeout_ms = CURLMIN(timeout_ms, CURLMAX(total_left_ms, 0)); - } - - return timeout_ms; + /* If the overall transfer has less time remaining than pingpong + * has otherwise for the state, return that. */ + remain_ms -= curlx_ptimediff_ms(Curl_pgrs_now(data), &pp->response); + /* transfer remaining time is 0, when it has no timeout. */ + xfer_remain_ms = Curl_timeleft_ms(data); + if(xfer_remain_ms) + return CURLMIN(remain_ms, xfer_remain_ms); + return remain_ms; } /* @@ -91,7 +67,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_timeleft_ms(data, pp); CURLcode result = CURLE_OK; if(timeout_ms <= 0) { @@ -99,7 +75,6 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, return CURLE_OPERATION_TIMEDOUT; /* already too little time */ } - DEBUGF(infof(data, "pp_statematch, timeout=%" FMT_TIMEDIFF_T, timeout_ms)); if(block) { interval_ms = 1000; /* use 1 second timeout intervals */ if(timeout_ms < interval_ms) @@ -111,15 +86,12 @@ 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 */ rc = 1; else { - DEBUGF(infof(data, "pp_statematch, select, timeout=%" FMT_TIMEDIFF_T - ", sendleft=%zu", - timeout_ms, pp->sendleft)); rc = Curl_socket_check(pp->sendleft ? CURL_SOCKET_BAD : sock, /* reading */ CURL_SOCKET_BAD, pp->sendleft ? sock : CURL_SOCKET_BAD, /* writing */ @@ -128,11 +100,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, if(block) { /* if we did not wait, we do not have to spend time on this now */ - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, curlx_now()); - + result = Curl_pgrsCheck(data); if(result) return result; } @@ -150,11 +118,11 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, } /* initialize stuff to prepare for reading a fresh new response */ -void Curl_pp_init(struct pingpong *pp) +void Curl_pp_init(struct pingpong *pp, const struct curltime *pnow) { DEBUGASSERT(!pp->initialised); pp->nread_resp = 0; - pp->response = curlx_now(); /* start response time-out now! */ + pp->response = *pnow; /* start response time-out */ pp->pending_resp = TRUE; curlx_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD); curlx_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD); @@ -182,10 +150,6 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, CURLcode result; struct connectdata *conn = data->conn; -#ifdef HAVE_GSSAPI - enum protection_level data_sec; -#endif - DEBUGASSERT(pp->sendleft == 0); DEBUGASSERT(pp->sendsize == 0); DEBUGASSERT(pp->sendthis == NULL); @@ -208,9 +172,6 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, write_len = curlx_dyn_len(&pp->sendbuf); s = curlx_dyn_ptr(&pp->sendbuf); -#ifdef HAVE_GSSAPI - conn->data_prot = PROT_CMD; -#endif result = Curl_conn_send(data, FIRSTSOCKET, s, write_len, FALSE, &bytes_written); if(result == CURLE_AGAIN) { @@ -218,11 +179,6 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, } else if(result) return result; -#ifdef HAVE_GSSAPI - data_sec = conn->data_prot; - DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST); - conn->data_prot = (unsigned char)data_sec; -#endif Curl_debug(data, CURLINFO_HEADER_OUT, s, bytes_written); @@ -235,13 +191,12 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, else { pp->sendthis = NULL; pp->sendleft = pp->sendsize = 0; - pp->response = curlx_now(); + pp->response = *Curl_pgrs_now(data); } return CURLE_OK; } - /*********************************************************************** * * Curl_pp_sendf() @@ -272,17 +227,7 @@ static CURLcode pingpong_read(struct Curl_easy *data, size_t buflen, size_t *nread) { - CURLcode result; -#ifdef HAVE_GSSAPI - enum protection_level prot = data->conn->data_prot; - data->conn->data_prot = PROT_CLEAR; -#endif - result = Curl_conn_recv(data, sockindex, buffer, buflen, nread); -#ifdef HAVE_GSSAPI - DEBUGASSERT(prot > PROT_NONE && prot < PROT_LAST); - data->conn->data_prot = (unsigned char)prot; -#endif - return result; + return Curl_conn_recv(data, sockindex, buffer, buflen, nread); } /* @@ -312,7 +257,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, size_t full = curlx_dyn_len(&pp->recvbuf); /* trim off the "final" leading part */ - curlx_dyn_tail(&pp->recvbuf, full - pp->nfinal); + curlx_dyn_tail(&pp->recvbuf, full - pp->nfinal); pp->nfinal = 0; /* now gone */ } @@ -340,18 +285,15 @@ 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 */ size_t length = nl - line + 1; /* output debug output if that is requested */ -#ifdef HAVE_GSSAPI - if(!conn->sec_complete) -#endif - Curl_debug(data, CURLINFO_HEADER_IN, line, length); + Curl_debug(data, CURLINFO_HEADER_IN, line, length); /* * Pass all response-lines to the callback function registered for @@ -440,7 +382,7 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data, else { pp->sendthis = NULL; pp->sendleft = pp->sendsize = 0; - pp->response = curlx_now(); + pp->response = *Curl_pgrs_now(data); } return CURLE_OK; } diff --git a/lib/pingpong.h b/lib/pingpong.h index 5db96c4345..864f2c6835 100644 --- a/lib/pingpong.h +++ b/lib/pingpong.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_FTP) || \ @@ -53,8 +52,6 @@ struct pingpong { size_t sendsize; /* total size of the sendthis buffer */ struct curltime response; /* set to Curl_now() when a command has been sent off, used to time-out response reading */ - timediff_t response_time; /* When no timeout is given, this is the amount of - milliseconds we await for a server response. */ struct dynbuf sendbuf; struct dynbuf recvbuf; size_t overflow; /* number of bytes left after a final response line */ @@ -73,11 +70,14 @@ struct pingpong { read */ }; -#define PINGPONG_SETUP(pp,s,e) \ - do { \ - (pp)->response_time = RESP_TIMEOUT; \ - (pp)->statemachine = s; \ - (pp)->endofresp = e; \ +/* Default pingpong response timeout in milliseconds, unless a transfer + * has CURLOPT_SERVER_RESPONSE_TIMEOUT(_MS) set. */ +#define PINGPONG_TIMEOUT_MS (60 * 1000) + +#define PINGPONG_SETUP(pp, s, e) \ + do { \ + (pp)->statemachine = s; \ + (pp)->endofresp = e; \ } while(0) /* @@ -90,13 +90,12 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, struct pingpong *pp, bool block, bool disconnecting); /* initialize stuff to prepare for reading a fresh new response */ -void Curl_pp_init(struct pingpong *pp); - -/* 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); +void Curl_pp_init(struct pingpong *pp, const struct curltime *pnow); +/* Returns time remaining in ms. 0 or negative number means the + timeout has already triggered */ +timediff_t Curl_pp_state_timeleft_ms(struct Curl_easy *data, + struct pingpong *pp); /*********************************************************************** * @@ -151,7 +150,6 @@ CURLcode Curl_pp_pollset(struct Curl_easy *data, struct pingpong *pp, struct easy_pollset *ps); - /*********************************************************************** * * Curl_pp_moredata() diff --git a/lib/pop3.c b/lib/pop3.c index f5ecfd178b..317c04bbe3 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -36,8 +36,9 @@ * Draft LOGIN SASL Mechanism * ***************************************************************************/ - #include "curl_setup.h" +#include "urldata.h" +#include "pop3.h" #ifndef CURL_DISABLE_POP3 @@ -55,32 +56,22 @@ #include #endif -#include -#include "urldata.h" #include "sendf.h" +#include "curl_trc.h" #include "hostip.h" #include "progress.h" #include "transfer.h" #include "escape.h" -#include "http.h" /* for HTTP proxy tunnel stuff */ -#include "socks.h" #include "pingpong.h" -#include "pop3.h" #include "vtls/vtls.h" #include "cfilters.h" #include "connect.h" #include "select.h" -#include "multiif.h" #include "url.h" #include "bufref.h" #include "curl_sasl.h" #include "curl_md5.h" -#include "curlx/warnless.h" -#include "strdup.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "curlx/strdup.h" /* Authentication type flags */ #define POP3_TYPE_CLEARTEXT (1 << 0) @@ -88,11 +79,11 @@ #define POP3_TYPE_SASL (1 << 2) /* Authentication type values */ -#define POP3_TYPE_NONE 0 -#define POP3_TYPE_ANY (POP3_TYPE_CLEARTEXT|POP3_TYPE_APOP|POP3_TYPE_SASL) +#define POP3_TYPE_NONE 0 +#define POP3_TYPE_ANY (POP3_TYPE_CLEARTEXT | POP3_TYPE_APOP | POP3_TYPE_SASL) /* This is the 5-bytes End-Of-Body marker for POP3 */ -#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a" +#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a" #define POP3_EOB_LEN 5 /* meta key for storing protocol meta at easy handle */ @@ -144,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 -}; - -#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 - | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */ -}; -#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; @@ -277,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 '.'. */ @@ -286,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 @@ -323,8 +308,10 @@ static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn, /* Are we processing CAPA command responses? */ if(pop3c->state == POP3_CAPA) { - /* Do we have the terminating line? */ - if(len >= 1 && line[0] == '.') + /* Do we have the terminating line? Per RFC 2449 this is a line + containing only a single dot */ + if((len == 3 && line[0] == '.' && line[1] == '\r') || + (len == 2 && line[0] == '.' && line[1] == '\n')) /* Treat the response as a success */ *resp = '+'; else @@ -371,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 */ @@ -402,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", @@ -503,16 +489,16 @@ 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 */ + /* perform CAPA now, changes pop3c->state out of POP3_UPGRADETLS */ result = pop3_perform_capa(data, conn); } out: @@ -571,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; @@ -589,10 +575,10 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, if(!ctxt) return CURLE_OUT_OF_MEMORY; - Curl_MD5_update(ctxt, (const unsigned char *) pop3c->apoptimestamp, + Curl_MD5_update(ctxt, (const unsigned char *)pop3c->apoptimestamp, curlx_uztoui(strlen(pop3c->apoptimestamp))); - Curl_MD5_update(ctxt, (const unsigned char *) conn->passwd, + Curl_MD5_update(ctxt, (const unsigned char *)conn->passwd, curlx_uztoui(strlen(conn->passwd))); /* Finalise the digest */ @@ -600,7 +586,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, /* Convert the calculated 16 octet digest into a 32 byte hex string */ for(i = 0; i < MD5_DIGEST_LEN; i++) - msnprintf(&secret[2 * i], 3, "%02x", digest[i]); + curl_msnprintf(&secret[2 * i], 3, "%02x", digest[i]); result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret); @@ -625,7 +611,7 @@ static CURLcode pop3_perform_auth(struct Curl_easy *data, struct pop3_conn *pop3c = Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN); CURLcode result = CURLE_OK; - const char *ir = (const char *) Curl_bufref_ptr(initresp); + const char *ir = Curl_bufref_ptr(initresp); if(!pop3c) return CURLE_FAILED_INIT; @@ -659,8 +645,7 @@ static CURLcode pop3_continue_auth(struct Curl_easy *data, if(!pop3c) return CURLE_FAILED_INIT; - return Curl_pp_sendf(data, &pop3c->pp, - "%s", (const char *) Curl_bufref_ptr(resp)); + return Curl_pp_sendf(data, &pop3c->pp, "%s", Curl_bufref_ptr(resp)); } /*********************************************************************** @@ -824,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); @@ -835,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 */ @@ -876,15 +861,15 @@ static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code, /* Do we have an untagged continuation response? */ if(pop3code == '*') { /* Does the server support the STLS capability? */ - if(len >= 4 && !memcmp(line, "STLS", 4)) + if(len >= 4 && curl_strnequal(line, "STLS", 4)) pop3c->tls_supported = TRUE; /* Does the server support clear text authentication? */ - else if(len >= 4 && !memcmp(line, "USER", 4)) + else if(len >= 4 && curl_strnequal(line, "USER", 4)) pop3c->authtypes |= POP3_TYPE_CLEARTEXT; /* Does the server support SASL based authentication? */ - else if(len >= 5 && !memcmp(line, "SASL ", 5)) { + else if(len >= 5 && curl_strnequal(line, "SASL ", 5)) { pop3c->authtypes |= POP3_TYPE_SASL; /* Advance past the SASL keyword */ @@ -894,13 +879,10 @@ static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code, /* Loop through the data line */ for(;;) { size_t llen; - size_t wordlen; + size_t wordlen = 0; unsigned short mechbit; - while(len && - (*line == ' ' || *line == '\t' || - *line == '\r' || *line == '\n')) { - + while(len && (ISBLANK(*line) || ISNEWLINE(*line))) { line++; len--; } @@ -909,9 +891,8 @@ static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code, break; /* Extract the word */ - for(wordlen = 0; wordlen < len && line[wordlen] != ' ' && - line[wordlen] != '\t' && line[wordlen] != '\r' && - line[wordlen] != '\n';) + while(wordlen < len && !ISBLANK(line[wordlen]) && + !ISNEWLINE(line[wordlen])) wordlen++; /* Test the word for a matching authentication mechanism */ @@ -1057,8 +1038,7 @@ static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code, } else /* Send the PASS command */ - result = Curl_pp_sendf(data, &pop3c->pp, "PASS %s", - conn->passwd ? conn->passwd : ""); + result = Curl_pp_sendf(data, &pop3c->pp, "PASS %s", conn->passwd); if(!result) pop3_state(data, POP3_PASS); @@ -1083,6 +1063,145 @@ static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code, return result; } +/*********************************************************************** + * + * 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 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); + + CURL_REQ_CLEAR_RECV(data); + 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, @@ -1110,7 +1229,7 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data, 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 + /* 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; @@ -1174,11 +1293,11 @@ upgrade_tls: 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; @@ -1277,6 +1396,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() @@ -1298,9 +1431,6 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done) if(!pop3c) return CURLE_FAILED_INIT; - /* We always support persistent connections in POP3 */ - connkeep(conn, "POP3 default"); - PINGPONG_SETUP(pp, pop3_statemachine, pop3_endofresp); /* Set the default preferred authentication type and mechanism */ @@ -1308,7 +1438,7 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done) Curl_sasl_init(&pop3c->sasl, data, &saslpop3); /* Initialise the pingpong layer */ - Curl_pp_init(pp); + Curl_pp_init(pp, Curl_pgrs_now(data)); /* Parse the URL options */ result = pop3_parse_url_options(conn); @@ -1349,8 +1479,8 @@ static CURLcode pop3_done(struct Curl_easy *data, CURLcode status, } /* Cleanup our per-request based variables */ - Curl_safefree(pop3->id); - Curl_safefree(pop3->custom); + curlx_safefree(pop3->id); + curlx_safefree(pop3->custom); /* Clear the transfer mode for the next request */ pop3->transfer = PPTRANSFER_BODY; @@ -1377,18 +1507,17 @@ static CURLcode pop3_perform(struct Curl_easy *data, bool *connected, DEBUGF(infof(data, "DO phase starts")); - if(data->req.no_body) { - /* Requested no body means no transfer */ - pop3->transfer = PPTRANSFER_INFO; - } - - *dophase_done = FALSE; /* not done yet */ - - /* Start the first command in the DO phase */ + /* Start the first command in the DO phase, may alter data->req.no_body */ result = pop3_perform_command(data); if(result) return result; + if(data->req.no_body) + /* Requested no body means no transfer */ + pop3->transfer = PPTRANSFER_INFO; + + *dophase_done = FALSE; /* not done yet */ + /* Run the state-machine */ result = pop3_multi_statemach(data, dophase_done); *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); @@ -1399,6 +1528,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() @@ -1458,16 +1627,7 @@ static CURLcode pop3_disconnect(struct Curl_easy *data, Curl_pp_disconnect(&pop3c->pp); /* Cleanup our connection based variables */ - Curl_safefree(pop3c->apoptimestamp); - - 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; + curlx_safefree(pop3c->apoptimestamp); return CURLE_OK; } @@ -1488,40 +1648,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_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - /* 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; @@ -1529,9 +1655,9 @@ static void pop3_easy_dtor(void *key, size_t klen, void *entry) (void)klen; DEBUGASSERT(pop3); /* Cleanup our per-request based variables */ - Curl_safefree(pop3->id); - Curl_safefree(pop3->custom); - free(pop3); + curlx_safefree(pop3->id); + curlx_safefree(pop3->custom); + curlx_free(pop3); } static void pop3_conn_dtor(void *key, size_t klen, void *entry) @@ -1541,20 +1667,20 @@ static void pop3_conn_dtor(void *key, size_t klen, void *entry) (void)klen; DEBUGASSERT(pop3c); Curl_pp_disconnect(&pop3c->pp); - Curl_safefree(pop3c->apoptimestamp); - free(pop3c); + curlx_safefree(pop3c->apoptimestamp); + curlx_free(pop3c); } static CURLcode pop3_setup_connection(struct Curl_easy *data, struct connectdata *conn) { struct pop3_conn *pop3c; - struct POP3 *pop3 = calloc(1, sizeof(*pop3)); + struct POP3 *pop3 = curlx_calloc(1, sizeof(*pop3)); if(!pop3 || Curl_meta_set(data, CURL_META_POP3_EASY, pop3, pop3_easy_dtor)) return CURLE_OUT_OF_MEMORY; - pop3c = calloc(1, sizeof(*pop3c)); + pop3c = curlx_calloc(1, sizeof(*pop3c)); if(!pop3c || Curl_conn_meta_set(conn, CURL_META_POP3_CONN, pop3c, pop3_conn_dtor)) return CURLE_OUT_OF_MEMORY; @@ -1562,244 +1688,27 @@ 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 */ - - 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; -} - -/*********************************************************************** - * - * 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_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 very 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; -} +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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; #endif /* CURL_DISABLE_POP3 */ diff --git a/lib/pop3.h b/lib/pop3.h index 485e7c2c49..75c3a67da7 100644 --- a/lib/pop3.h +++ b/lib/pop3.h @@ -23,8 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -extern const struct Curl_handler Curl_handler_pop3; -extern const struct Curl_handler Curl_handler_pop3s; +#ifndef CURL_DISABLE_POP3 +extern const struct Curl_protocol Curl_protocol_pop3; +#endif #endif /* HEADER_CURL_POP3_H */ diff --git a/lib/progress.c b/lib/progress.c index fdae3194c5..eecec2757b 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -21,101 +21,164 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #include "urldata.h" -#include "sendf.h" +#include "curl_trc.h" #include "multiif.h" #include "progress.h" -#include "curlx/timeval.h" -#include "curl_printf.h" - -/* check rate limits within this many recent milliseconds, at minimum. */ -#define MIN_RATE_LIMIT_PERIOD 3000 +#include "transfer.h" +#include "curlx/strcopy.h" #ifndef CURL_DISABLE_PROGRESS_METER -/* Provide a string that is 2 + 1 + 2 + 1 + 2 = 8 letters long (plus the zero - byte) */ -static void time2str(char *r, curl_off_t seconds) +/* Provide a string that is 7 letters long (plus the zero byte). + + Unit test 1636. +*/ +UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds); +UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds) { curl_off_t h; if(seconds <= 0) { - strcpy(r, "--:--:--"); + curlx_strcopy(r, rsize, STRCONST(" ")); return; } h = seconds / 3600; if(h <= 99) { curl_off_t m = (seconds - (h * 3600)) / 60; - curl_off_t s = (seconds - (h * 3600)) - (m * 60); - msnprintf(r, 9, "%2" FMT_OFF_T ":%02" FMT_OFF_T ":%02" FMT_OFF_T, h, m, s); + if(h <= 9) { + curl_off_t s = (seconds - (h * 3600)) - (m * 60); + if(h) + curl_msnprintf(r, rsize, "%" FMT_OFF_T ":%02" FMT_OFF_T ":" + "%02" FMT_OFF_T, h, m, s); + else + curl_msnprintf(r, rsize, " %02" FMT_OFF_T ":%02" FMT_OFF_T, m, s); + } + else + curl_msnprintf(r, rsize, "%" FMT_OFF_T "h %02" FMT_OFF_T "m", h, m); } else { - /* this equals to more than 99 hours, switch to a more suitable output - format to fit within the limits. */ curl_off_t d = seconds / 86400; h = (seconds - (d * 86400)) / 3600; - if(d <= 999) - msnprintf(r, 9, "%3" FMT_OFF_T "d %02" FMT_OFF_T "h", d, h); - else - msnprintf(r, 9, "%7" FMT_OFF_T "d", d); + if(d <= 99) + curl_msnprintf(r, rsize, "%2" FMT_OFF_T "d %02" FMT_OFF_T "h", d, h); + else if(d <= 999) + curl_msnprintf(r, rsize, "%6" FMT_OFF_T "d", d); + else { /* more than 999 days */ + curl_off_t m = d / 30; + if(m <= 999) + curl_msnprintf(r, rsize, "%6" FMT_OFF_T "m", m); + else { /* more than 999 months */ + curl_off_t y = d / 365; + if(y <= 99999) + curl_msnprintf(r, rsize, "%6" FMT_OFF_T "y", y); + else + curlx_strcopy(r, rsize, STRCONST(">99999y")); + } + } } } /* The point of this function would be to return a string of the input data, - but never longer than 5 columns (+ one zero byte). - Add suffix k, M, G when suitable... */ -static char *max5data(curl_off_t bytes, char *max5) + but never longer than 6 columns (+ one zero byte). + Add suffix k, M, G when suitable... + + Unit test 1636 +*/ +UNITTEST char *max6out(curl_off_t bytes, char *max6, size_t mlen); +UNITTEST char *max6out(curl_off_t bytes, char *max6, size_t mlen) { -#define ONE_KILOBYTE (curl_off_t)1024 -#define ONE_MEGABYTE (1024 * ONE_KILOBYTE) -#define ONE_GIGABYTE (1024 * ONE_MEGABYTE) -#define ONE_TERABYTE (1024 * ONE_GIGABYTE) -#define ONE_PETABYTE (1024 * ONE_TERABYTE) - + /* a signed 64-bit value is 8192 petabytes maximum, shown as + 8.0E (exabytes)*/ if(bytes < 100000) - msnprintf(max5, 6, "%5" FMT_OFF_T, bytes); - - else if(bytes < 10000 * ONE_KILOBYTE) - msnprintf(max5, 6, "%4" FMT_OFF_T "k", bytes/ONE_KILOBYTE); - - else if(bytes < 100 * ONE_MEGABYTE) - /* 'XX.XM' is good as long as we are less than 100 megs */ - msnprintf(max5, 6, "%2" FMT_OFF_T ".%0" - FMT_OFF_T "M", bytes/ONE_MEGABYTE, - (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/10) ); - - else if(bytes < 10000 * ONE_MEGABYTE) - /* 'XXXXM' is good until we are at 10000MB or above */ - msnprintf(max5, 6, "%4" FMT_OFF_T "M", bytes/ONE_MEGABYTE); - - else if(bytes < 100 * ONE_GIGABYTE) - /* 10000 MB - 100 GB, we show it as XX.XG */ - msnprintf(max5, 6, "%2" FMT_OFF_T ".%0" - FMT_OFF_T "G", bytes/ONE_GIGABYTE, - (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/10) ); - - else if(bytes < 10000 * ONE_GIGABYTE) - /* up to 10000GB, display without decimal: XXXXG */ - msnprintf(max5, 6, "%4" FMT_OFF_T "G", bytes/ONE_GIGABYTE); - - else if(bytes < 10000 * ONE_TERABYTE) - /* up to 10000TB, display without decimal: XXXXT */ - msnprintf(max5, 6, "%4" FMT_OFF_T "T", bytes/ONE_TERABYTE); - - else - /* up to 10000PB, display without decimal: XXXXP */ - msnprintf(max5, 6, "%4" FMT_OFF_T "P", bytes/ONE_PETABYTE); - - /* 16384 petabytes (16 exabytes) is the maximum a 64-bit unsigned number can - hold, but our data type is signed so 8192PB will be the maximum. */ - - return max5; + curl_msnprintf(max6, mlen, "%6" CURL_FORMAT_CURL_OFF_T, bytes); + else { + const char unit[] = { 'k', 'M', 'G', 'T', 'P', 'E', 0 }; + int k = 0; + curl_off_t nbytes; + curl_off_t rest; + do { + nbytes = bytes / 1024; + if(nbytes < 1000) + break; + bytes = nbytes; + k++; + DEBUGASSERT(unit[k]); + } while(unit[k]); + rest = bytes % 1024; + if(nbytes <= 99) + /* xx.yyU */ + curl_msnprintf(max6, mlen, "%2" CURL_FORMAT_CURL_OFF_T + ".%02" CURL_FORMAT_CURL_OFF_T "%c", nbytes, + rest * 100 / 1024, unit[k]); + else + /* xxx.yU */ + curl_msnprintf(max6, mlen, "%3" CURL_FORMAT_CURL_OFF_T + ".%" CURL_FORMAT_CURL_OFF_T "%c", nbytes, + rest * 10 / 1024, unit[k]); + } + return max6; } #endif -/* +static void pgrs_speedinit(struct Curl_easy *data) +{ + memset(&data->state.keeps_speed, 0, sizeof(struct curltime)); +} +/* + * @unittest: 1606 + */ +UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data, + const struct curltime *pnow); +UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data, + const struct curltime *pnow) +{ + if(!data->set.low_speed_time || !data->set.low_speed_limit || + Curl_xfer_recv_is_paused(data) || Curl_xfer_send_is_paused(data)) + /* A paused transfer is not qualified for speed checks */ + return CURLE_OK; + + if(data->progress.current_speed >= 0) { + if(data->progress.current_speed < data->set.low_speed_limit) { + if(!data->state.keeps_speed.tv_sec) + /* under the limit at this moment */ + data->state.keeps_speed = *pnow; + else { + /* how long has it been under the limit */ + timediff_t howlong = + curlx_ptimediff_ms(pnow, &data->state.keeps_speed); + + if(howlong >= data->set.low_speed_time * 1000) { + /* too long */ + 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; + } + } + } + else + /* faster right now */ + data->state.keeps_speed.tv_sec = 0; + } + + /* since low speed limit is enabled, set the expire timer to make this + connection's speed get checked again in a second */ + Curl_expire(data, 1000, EXPIRE_SPEEDCHECK); + + return CURLE_OK; +} + +const struct curltime *Curl_pgrs_now(struct Curl_easy *data) +{ + struct curltime *pnow = data->multi ? + &data->multi->now : &data->progress.now; + curlx_pnow(pnow); + return pnow; +} + +/* New proposed interface, 9th of February 2000: pgrsStartNow() - sets start time @@ -125,7 +188,6 @@ static char *max5data(curl_off_t bytes, char *max5) pgrsSetUploadCounter() - amount of data currently uploaded pgrsUpdate() - show progress pgrsDone() - transfer complete - */ int Curl_pgrsDone(struct Curl_easy *data) @@ -139,12 +201,22 @@ int Curl_pgrsDone(struct Curl_easy *data) if(!data->progress.hide && !data->progress.callback) /* only output if we do not use a progress callback and we are not * hidden */ - fprintf(data->set.err, "\n"); + curl_mfprintf(data->set.err, "\n"); - data->progress.speeder_c = 0; /* reset the progress meter display */ return 0; } +void Curl_pgrsReset(struct Curl_easy *data) +{ + Curl_pgrsSetUploadCounter(data, 0); + data->progress.dl.cur_size = 0; + Curl_pgrsSetUploadSize(data, -1); + Curl_pgrsSetDownloadSize(data, -1); + data->progress.speeder_c = 0; /* reset speed records */ + data->progress.deliver = 0; + pgrs_speedinit(data); +} + /* reset the known transfer sizes */ void Curl_pgrsResetTransferSizes(struct Curl_easy *data) { @@ -152,8 +224,23 @@ void Curl_pgrsResetTransferSizes(struct Curl_easy *data) Curl_pgrsSetUploadSize(data, -1); } +void Curl_pgrsRecvPause(struct Curl_easy *data, bool enable) +{ + if(!enable) { + data->progress.speeder_c = 0; /* reset speed records */ + pgrs_speedinit(data); /* reset low speed measurements */ + } +} + +void Curl_pgrsSendPause(struct Curl_easy *data, bool enable) +{ + if(!enable) { + data->progress.speeder_c = 0; /* reset speed records */ + pgrs_speedinit(data); /* reset low speed measurements */ + } +} + /* - * * Curl_pgrsTimeWas(). Store the timestamp time at the given label. */ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, @@ -180,7 +267,7 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, case TIMER_POSTQUEUE: /* Queue time is accumulative from all involved redirects */ data->progress.t_postqueue += - curlx_timediff_us(timestamp, data->progress.t_startqueue); + curlx_ptimediff_us(×tamp, &data->progress.t_startqueue); break; case TIMER_STARTACCEPT: data->progress.t_acceptdata = timestamp; @@ -200,8 +287,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. */ @@ -216,13 +303,14 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, delta = &data->progress.t_posttransfer; break; case TIMER_REDIRECT: - data->progress.t_redirect = curlx_timediff_us(timestamp, - data->progress.start); + data->progress.t_redirect = curlx_ptimediff_us(×tamp, + &data->progress.start); data->progress.t_startqueue = timestamp; break; } if(delta) { - timediff_t us = curlx_timediff_us(timestamp, data->progress.t_startsingle); + timediff_t us = curlx_ptimediff_us(×tamp, + &data->progress.t_startsingle); if(us < 1) us = 1; /* make sure at least one microsecond passed */ *delta += us; @@ -230,122 +318,62 @@ 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. * * @unittest: 1399 */ -struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer) +void Curl_pgrsTime(struct Curl_easy *data, timerid timer) { - struct curltime now = curlx_now(); - - Curl_pgrsTimeWas(data, timer, now); - return now; + Curl_pgrsTimeWas(data, timer, *Curl_pgrs_now(data)); } void Curl_pgrsStartNow(struct Curl_easy *data) { struct Progress *p = &data->progress; + p->speeder_c = 0; /* reset the progress meter display */ - p->start = curlx_now(); + p->start = *Curl_pgrs_now(data); p->is_t_startransfer_set = FALSE; - p->ul.limit.start = p->start; - p->dl.limit.start = p->start; - p->ul.limit.start_size = 0; - p->dl.limit.start_size = 0; p->dl.cur_size = 0; p->ul.cur_size = 0; /* the sizes are unknown at start */ p->dl_size_known = FALSE; p->ul_size_known = FALSE; - Curl_ratelimit(data, p->start); } -/* - * This is used to handle speed limits, calculating how many milliseconds to - * wait until we are back under the speed limit, if needed. - * - * The way it works is by having a "starting point" (time & amount of data - * transferred by then) used in the speed computation, to be used instead of - * the start of the transfer. This starting point is regularly moved as - * transfer goes on, to keep getting accurate values (instead of average over - * the entire transfer). - * - * This function takes the current amount of data transferred, the amount at - * the starting point, the limit (in bytes/s), the time of the starting point - * and the current time. - * - * Returns 0 if no waiting is needed or when no waiting is needed but the - * starting point should be reset (to current); or the number of milliseconds - * to wait to get back under the speed limit. - */ -timediff_t Curl_pgrsLimitWaitTime(struct pgrs_dir *d, - curl_off_t bytes_per_sec, - struct curltime now) +/* check that the 'delta' amount of bytes are okay to deliver to the + application, or return error if not. */ +CURLcode Curl_pgrs_deliver_check(struct Curl_easy *data, size_t delta) { - curl_off_t bytes = d->cur_size - d->limit.start_size; - timediff_t should_ms; - timediff_t took_ms; - - /* no limit or we did not get to any bytes yet */ - if(!bytes_per_sec || !bytes) - return 0; - - /* The time it took us to have `bytes` */ - took_ms = curlx_timediff_ceil(now, d->limit.start); - - /* The time it *should* have taken us to have `bytes` - * when obeying the bytes_per_sec speed_limit. */ - if(bytes < CURL_OFF_T_MAX/1000) { - /* (1000 * bytes / (bytes / sec)) = 1000 * sec = ms */ - should_ms = (timediff_t) (1000 * bytes / bytes_per_sec); + if(data->set.max_filesize && + ((curl_off_t)delta > data->set.max_filesize - data->progress.deliver)) { + failf(data, "Would have exceeded max file size"); + return CURLE_FILESIZE_EXCEEDED; } - else { - /* very large `bytes`, first calc the seconds it should have taken. - * if that is small enough, convert to milliseconds. */ - should_ms = (timediff_t) (bytes / bytes_per_sec); - if(should_ms < TIMEDIFF_T_MAX/1000) - should_ms *= 1000; - else - should_ms = TIMEDIFF_T_MAX; - } - - if(took_ms < should_ms) { - /* when gotten to `bytes` too fast, wait the difference */ - return should_ms - took_ms; - } - return 0; -} - -/* - * Set the number of downloaded bytes so far. - */ -CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size) -{ - data->progress.dl.cur_size = size; return CURLE_OK; } -/* - * Update the timestamp and sizestamp to use for rate limit calculations. - */ -void Curl_ratelimit(struct Curl_easy *data, struct curltime now) +/* this counts how much data is delivered to the application, which + in compressed cases may differ from downloaded amount */ +void Curl_pgrs_deliver_inc(struct Curl_easy *data, size_t delta) { - /* do not set a new stamp unless the time since last update is long enough */ - if(data->set.max_recv_speed) { - if(curlx_timediff(now, data->progress.dl.limit.start) >= - MIN_RATE_LIMIT_PERIOD) { - data->progress.dl.limit.start = now; - data->progress.dl.limit.start_size = data->progress.dl.cur_size; - } + data->progress.deliver += delta; +} + +void Curl_pgrs_download_inc(struct Curl_easy *data, size_t delta) +{ + if(delta) { + data->progress.dl.cur_size += delta; + Curl_rlimit_drain(&data->progress.dl.rlimit, delta, Curl_pgrs_now(data)); } - if(data->set.max_send_speed) { - if(curlx_timediff(now, data->progress.ul.limit.start) >= - MIN_RATE_LIMIT_PERIOD) { - data->progress.ul.limit.start = now; - data->progress.ul.limit.start_size = data->progress.ul.cur_size; - } +} + +void Curl_pgrs_upload_inc(struct Curl_easy *data, size_t delta) +{ + if(delta) { + data->progress.ul.cur_size += delta; + Curl_rlimit_drain(&data->progress.ul.rlimit, delta, Curl_pgrs_now(data)); } } @@ -383,7 +411,7 @@ void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size) void Curl_pgrsEarlyData(struct Curl_easy *data, curl_off_t sent) { - data->progress.earlydata_sent = sent; + data->progress.earlydata_sent = sent; } /* returns the average speed in bytes / second */ @@ -392,7 +420,7 @@ static curl_off_t trspeed(curl_off_t size, /* number of bytes */ { if(us < 1) return size * 1000000; - else if(size < CURL_OFF_T_MAX/1000000) + else if(size < CURL_OFF_T_MAX / 1000000) return (size * 1000000) / us; else if(us >= 1000000) return size / (us / 1000000); @@ -401,75 +429,81 @@ static curl_off_t trspeed(curl_off_t size, /* number of bytes */ } /* returns TRUE if it is time to show the progress meter */ -static bool progress_calc(struct Curl_easy *data, struct curltime now) +static bool progress_calc(struct Curl_easy *data, + const struct curltime *pnow) { - bool timetoshow = FALSE; struct Progress * const p = &data->progress; + int i_next, i_oldest, i_latest; + timediff_t duration_us; + curl_off_t amount; /* The time spent so far (from the start) in microseconds */ - p->timespent = curlx_timediff_us(now, p->start); + p->timespent = curlx_ptimediff_us(pnow, &p->start); p->dl.speed = trspeed(p->dl.cur_size, p->timespent); p->ul.speed = trspeed(p->ul.cur_size, p->timespent); - /* Calculations done at most once a second, unless end is reached */ - if(p->lastshow != now.tv_sec) { - int countindex; /* amount of seconds stored in the speeder array */ - int nowindex = p->speeder_c% CURR_TIME; - p->lastshow = now.tv_sec; - timetoshow = TRUE; - - /* Let's do the "current speed" thing, with the dl + ul speeds - combined. Store the speed at entry 'nowindex'. */ - p->speeder[ nowindex ] = p->dl.cur_size + p->ul.cur_size; - - /* remember the exact time for this moment */ - p->speeder_time [ nowindex ] = now; - - /* advance our speeder_c counter, which is increased every time we get - here and we expect it to never wrap as 2^32 is a lot of seconds! */ + if(!p->speeder_c) { /* no previous record exists */ + p->speed_amount[0] = p->dl.cur_size + p->ul.cur_size; + p->speed_time[0] = *pnow; p->speeder_c++; + /* use the overall average at the start */ + p->current_speed = p->ul.speed + p->dl.speed; + p->lastshow = pnow->tv_sec; + return TRUE; + } + /* We have at least one record now. Where to put the next and + * where is the latest one? */ + i_next = p->speeder_c % CURL_SPEED_RECORDS; + i_latest = (i_next > 0) ? (i_next - 1) : (CURL_SPEED_RECORDS - 1); - /* figure out how many index entries of data we have stored in our speeder - array. With N_ENTRIES filled in, we have about N_ENTRIES-1 seconds of - transfer. Imagine, after one second we have filled in two entries, - after two seconds we have filled in three entries etc. */ - countindex = ((p->speeder_c >= CURR_TIME) ? CURR_TIME : p->speeder_c) - 1; - - /* first of all, we do not do this if there is no counted seconds yet */ - if(countindex) { - int checkindex; - timediff_t span_ms; - curl_off_t amount; - - /* Get the index position to compare with the 'nowindex' position. - Get the oldest entry possible. While we have less than CURR_TIME - entries, the first entry will remain the oldest. */ - checkindex = (p->speeder_c >= CURR_TIME) ? p->speeder_c%CURR_TIME : 0; - - /* Figure out the exact time for the time span */ - span_ms = curlx_timediff(now, p->speeder_time[checkindex]); - if(span_ms == 0) - span_ms = 1; /* at least one millisecond MUST have passed */ - - /* Calculate the average speed the last 'span_ms' milliseconds */ - amount = p->speeder[nowindex]- p->speeder[checkindex]; - - if(amount > (0xffffffff/1000)) - /* the 'amount' value is bigger than would fit in 32 bits if - multiplied with 1000, so we use the double math for this */ - p->current_speed = (curl_off_t) - ((double)amount/((double)span_ms/1000.0)); - else - /* the 'amount' value is small enough to fit within 32 bits even - when multiplied with 1000 */ - p->current_speed = amount * 1000/span_ms; + /* Make a new record only when some time has passed. + * Too frequent calls otherwise ruin the history. */ + if(curlx_ptimediff_ms(pnow, &p->speed_time[i_latest]) >= 1000) { + p->speeder_c++; + i_latest = i_next; + p->speed_amount[i_latest] = p->dl.cur_size + p->ul.cur_size; + p->speed_time[i_latest] = *pnow; + } + else if(data->req.done) { + /* When a transfer is done, and we did not have a current speed + * already, update the last record. Otherwise, stay at the speed + * we have. The last chunk of data, when rate limiting, would increase + * reported speed since it no longer measures a full second. */ + if(!p->current_speed) { + p->speed_amount[i_latest] = p->dl.cur_size + p->ul.cur_size; + p->speed_time[i_latest] = *pnow; } - else - /* the first second we use the average */ - p->current_speed = p->ul.speed + p->dl.speed; + } + else { + /* transfer ongoing, wait for more time to pass. */ + return FALSE; + } - } /* Calculations end */ - return timetoshow; + i_oldest = (p->speeder_c < CURL_SPEED_RECORDS) ? 0 : + ((i_latest + 1) % CURL_SPEED_RECORDS); + + /* 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_us = curlx_ptimediff_us(&p->speed_time[i_latest], + &p->speed_time[i_oldest]); + if(duration_us <= 0) + duration_us = 1; + + if(amount > (CURL_OFF_T_MAX / 1000000)) { + /* the 'amount' value is bigger than would fit in 64 bits if + multiplied with 1000000, so we use the double math for this */ + p->current_speed = + (curl_off_t)(((double)amount * 1000000.0) / (double)duration_us); + } + else { + p->current_speed = amount * 1000000 / duration_us; + } + + if((p->lastshow == pnow->tv_sec) && !data->req.done) + return FALSE; + p->lastshow = pnow->tv_sec; + return TRUE; } #ifndef CURL_DISABLE_PROGRESS_METER @@ -484,7 +518,7 @@ static curl_off_t pgrs_est_percent(curl_off_t total, curl_off_t cur) if(total > 10000) return cur / (total / 100); else if(total > 0) - return (cur*100) / total; + return (cur * 100) / total; return 0; } @@ -503,29 +537,29 @@ static void pgrs_estimates(struct pgrs_dir *d, static void progress_meter(struct Curl_easy *data) { struct Progress *p = &data->progress; - char max5[6][10]; + char max6[6][7]; struct pgrs_estimate dl_estm; struct pgrs_estimate ul_estm; struct pgrs_estimate total_estm; curl_off_t total_cur_size; curl_off_t total_expected_size; curl_off_t dl_size; - char time_left[10]; - char time_total[10]; - char time_spent[10]; - curl_off_t cur_secs = (curl_off_t)p->timespent/1000000; /* seconds */ + char time_left[8]; + char time_total[8]; + char time_spent[8]; + curl_off_t cur_secs = (curl_off_t)p->timespent / 1000000; /* seconds */ if(!p->headers_out) { if(data->state.resume_from) { - fprintf(data->set.err, - "** Resuming transfer from byte position %" FMT_OFF_T "\n", - data->state.resume_from); + curl_mfprintf(data->set.err, + "** Resuming transfer from byte position %" FMT_OFF_T "\n", + data->state.resume_from); } - fprintf(data->set.err, - " %% Total %% Received %% Xferd Average Speed " - "Time Time Time Current\n" - " Dload Upload " - "Total Spent Left Speed\n"); + curl_mfprintf(data->set.err, + " %% Total %% Received %% Xferd Average Speed " + "Time Time Time Current\n" + " Dload Upload " + "Total Spent Left Speed\n"); p->headers_out = TRUE; /* headers are shown */ } @@ -536,16 +570,15 @@ static void progress_meter(struct Curl_easy *data) /* Since both happen at the same time, total expected duration is max. */ total_estm.secs = CURLMAX(ul_estm.secs, dl_estm.secs); /* create the three time strings */ - time2str(time_left, total_estm.secs > 0 ? (total_estm.secs - cur_secs) : 0); - time2str(time_total, total_estm.secs); - time2str(time_spent, cur_secs); + time2str(time_left, sizeof(time_left), + total_estm.secs > 0 ? (total_estm.secs - cur_secs) : 0); + time2str(time_total, sizeof(time_total), total_estm.secs); + time2str(time_spent, sizeof(time_spent), cur_secs); /* Get the total amount of data expected to get transferred */ - total_expected_size = - p->ul_size_known ? p->ul.total_size : p->ul.cur_size; + total_expected_size = p->ul_size_known ? p->ul.total_size : p->ul.cur_size; - dl_size = - p->dl_size_known ? p->dl.total_size : p->dl.cur_size; + dl_size = p->dl_size_known ? p->dl.total_size : p->dl.cur_size; /* integer overflow check */ if((CURL_OFF_T_MAX - total_expected_size) < dl_size) @@ -559,71 +592,78 @@ static void progress_meter(struct Curl_easy *data) /* Get the percentage of data transferred so far */ total_estm.percent = pgrs_est_percent(total_expected_size, total_cur_size); - fprintf(data->set.err, - "\r" - "%3" FMT_OFF_T " %s " - "%3" FMT_OFF_T " %s " - "%3" FMT_OFF_T " %s %s %s %s %s %s %s", - total_estm.percent, /* 3 letters */ /* total % */ - max5data(total_expected_size, max5[2]), /* total size */ - dl_estm.percent, /* 3 letters */ /* rcvd % */ - max5data(p->dl.cur_size, max5[0]), /* rcvd size */ - ul_estm.percent, /* 3 letters */ /* xfer % */ - max5data(p->ul.cur_size, max5[1]), /* xfer size */ - max5data(p->dl.speed, max5[3]), /* avrg dl speed */ - max5data(p->ul.speed, max5[4]), /* avrg ul speed */ - time_total, /* 8 letters */ /* total time */ - time_spent, /* 8 letters */ /* time spent */ - time_left, /* 8 letters */ /* time left */ - max5data(p->current_speed, max5[5]) + curl_mfprintf(data->set.err, + "\r" + "%3" FMT_OFF_T " %s " + "%3" FMT_OFF_T " %s " + "%3" FMT_OFF_T " %s %s %s %s %s %s %s", + total_estm.percent, /* 3 letters */ /* total % */ + max6out(total_expected_size, max6[2], + sizeof(max6[2])), /* total size */ + dl_estm.percent, /* 3 letters */ /* rcvd % */ + max6out(p->dl.cur_size, max6[0], + sizeof(max6[0])), /* rcvd size */ + ul_estm.percent, /* 3 letters */ /* xfer % */ + max6out(p->ul.cur_size, max6[1], + sizeof(max6[1])), /* xfer size */ + max6out(p->dl.speed, max6[3], + sizeof(max6[3])), /* avrg dl speed */ + max6out(p->ul.speed, max6[4], + sizeof(max6[4])), /* avrg ul speed */ + time_total, /* 7 letters */ /* total time */ + time_spent, /* 7 letters */ /* time spent */ + time_left, /* 7 letters */ /* time left */ + max6out(p->current_speed, max6[5], + sizeof(max6[5])) /* current speed */ ); /* 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 - /* * Curl_pgrsUpdate() returns 0 for success or the value returned by the * progress callback! */ -static int pgrsupdate(struct Curl_easy *data, bool showprogress) +static CURLcode pgrsupdate(struct Curl_easy *data, bool showprogress) { if(!data->progress.hide) { + int rc; if(data->set.fxferinfo) { - int result; /* There is a callback set, call that */ Curl_set_in_callback(data, TRUE); - result = data->set.fxferinfo(data->set.progress_client, - data->progress.dl.total_size, - data->progress.dl.cur_size, - data->progress.ul.total_size, - data->progress.ul.cur_size); + rc = data->set.fxferinfo(data->set.progress_client, + data->progress.dl.total_size, + data->progress.dl.cur_size, + data->progress.ul.total_size, + data->progress.ul.cur_size); Curl_set_in_callback(data, FALSE); - if(result != CURL_PROGRESSFUNC_CONTINUE) { - if(result) + if(rc != CURL_PROGRESSFUNC_CONTINUE) { + if(rc) { failf(data, "Callback aborted"); - return result; + return CURLE_ABORTED_BY_CALLBACK; + } + return CURLE_OK; } } else if(data->set.fprogress) { - int result; /* The older deprecated callback is set, call that */ Curl_set_in_callback(data, TRUE); - result = data->set.fprogress(data->set.progress_client, - (double)data->progress.dl.total_size, - (double)data->progress.dl.cur_size, - (double)data->progress.ul.total_size, - (double)data->progress.ul.cur_size); + rc = data->set.fprogress(data->set.progress_client, + (double)data->progress.dl.total_size, + (double)data->progress.dl.cur_size, + (double)data->progress.ul.total_size, + (double)data->progress.ul.cur_size); Curl_set_in_callback(data, FALSE); - if(result != CURL_PROGRESSFUNC_CONTINUE) { - if(result) + if(rc != CURL_PROGRESSFUNC_CONTINUE) { + if(rc) { failf(data, "Callback aborted"); - return result; + return CURLE_ABORTED_BY_CALLBACK; + } + return CURLE_OK; } } @@ -631,21 +671,35 @@ static int pgrsupdate(struct Curl_easy *data, bool showprogress) progress_meter(data); } - return 0; + return CURLE_OK; } -int Curl_pgrsUpdate(struct Curl_easy *data) +static CURLcode pgrs_update(struct Curl_easy *data, + const struct curltime *pnow) { - struct curltime now = curlx_now(); /* what time is it */ - bool showprogress = progress_calc(data, now); + bool showprogress = progress_calc(data, pnow); return pgrsupdate(data, showprogress); } +CURLcode Curl_pgrsUpdate(struct Curl_easy *data) +{ + return pgrs_update(data, Curl_pgrs_now(data)); +} + +CURLcode Curl_pgrsCheck(struct Curl_easy *data) +{ + CURLcode result; + + result = pgrs_update(data, Curl_pgrs_now(data)); + if(!result && !data->req.done) + result = pgrs_speedcheck(data, Curl_pgrs_now(data)); + return result; +} + /* * Update all progress, do not do progress meter/callbacks. */ void Curl_pgrsUpdate_nometer(struct Curl_easy *data) { - struct curltime now = curlx_now(); /* what time is it */ - (void)progress_calc(data, now); + (void)progress_calc(data, Curl_pgrs_now(data)); } diff --git a/lib/progress.h b/lib/progress.h index bbe135cdbc..7d419ecb8a 100644 --- a/lib/progress.h +++ b/lib/progress.h @@ -23,9 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curlx/timeval.h" +struct Curl_easy; typedef enum { TIMER_NONE, @@ -43,24 +43,36 @@ typedef enum { TIMER_LAST /* must be last */ } timerid; +/* Get the current timestamp of the transfer */ +const struct curltime *Curl_pgrs_now(struct Curl_easy *data); + int Curl_pgrsDone(struct Curl_easy *data); void Curl_pgrsStartNow(struct Curl_easy *data); void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size); void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size); - -/* It is fine to not check the return code if 'size' is set to 0 */ -CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size); - +CURLcode Curl_pgrs_deliver_check(struct Curl_easy *data, size_t delta); +void Curl_pgrs_deliver_inc(struct Curl_easy *data, size_t delta); +void Curl_pgrs_download_inc(struct Curl_easy *data, size_t delta); +void Curl_pgrs_upload_inc(struct Curl_easy *data, size_t delta); void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size); -void Curl_ratelimit(struct Curl_easy *data, struct curltime now); -int Curl_pgrsUpdate(struct Curl_easy *data); -void Curl_pgrsUpdate_nometer(struct Curl_easy *data); +/* perform progress update, invoking callbacks at intervals */ +CURLcode Curl_pgrsUpdate(struct Curl_easy *data); +/* perform progress update, no callbacks invoked */ +void Curl_pgrsUpdate_nometer(struct Curl_easy *data); +/* perform progress update with callbacks and speed checks */ +CURLcode Curl_pgrsCheck(struct Curl_easy *data); + +/* Inform progress/speedcheck about receive/send pausing */ +void Curl_pgrsRecvPause(struct Curl_easy *data, bool enable); +void Curl_pgrsSendPause(struct Curl_easy *data, bool enable); + +/* Reset sizes and counters for up- and download. */ +void Curl_pgrsReset(struct Curl_easy *data); +/* Reset sizes for up- and download. */ void Curl_pgrsResetTransferSizes(struct Curl_easy *data); -struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer); -timediff_t Curl_pgrsLimitWaitTime(struct pgrs_dir *d, - curl_off_t speed_limit, - struct curltime now); + +void Curl_pgrsTime(struct Curl_easy *data, timerid timer); /** * Update progress timer with the elapsed time from its start to `timestamp`. * This allows updating timers later and is used by happy eyeballing, where diff --git a/lib/protocol.c b/lib/protocol.c new file mode 100644 index 0000000000..c8d43251cf --- /dev/null +++ b/lib/protocol.c @@ -0,0 +1,485 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "protocol.h" +#include "strcase.h" + +#include "dict.h" +#include "file.h" +#include "ftp.h" +#include "gopher.h" +#include "http.h" +#include "imap.h" +#include "curl_ldap.h" +#include "mqtt.h" +#include "pop3.h" +#include "rtsp.h" +#include "smb.h" +#include "smtp.h" +#include "telnet.h" +#include "tftp.h" +#include "ws.h" +#include "vssh/ssh.h" + + +/* All URI schemes known to libcurl, but not necessarily implemented + * by protocol handlers. */ +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +const struct Curl_scheme Curl_scheme_smb = { + "smb", /* scheme */ +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) + &Curl_protocol_smb, +#else + ZERO_NULL, +#endif + CURLPROTO_SMB, /* protocol */ + CURLPROTO_SMB, /* family */ + PROTOPT_NONE, /* flags */ + PORT_SMB, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_smbs = { + "smbs", /* scheme */ +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) && defined(USE_SSL) + &Curl_protocol_smb, +#else + ZERO_NULL, +#endif + CURLPROTO_SMBS, /* protocol */ + CURLPROTO_SMB, /* family */ + PROTOPT_SSL, /* flags */ + PORT_SMBS, /* defport */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +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 */ +}; + +const struct Curl_scheme Curl_scheme_ws = { + "ws", /* scheme */ +#if defined(CURL_DISABLE_WEBSOCKETS) || defined(CURL_DISABLE_HTTP) + 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(CURL_DISABLE_HTTP) || \ + !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 */ +}; + +/* 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 + 2. check how small the table gets + 3. tweak the hash algorithm, then rerun from 1 + 4. when the table is good enough + 5. copy the table into this source code + 6. make sure this function uses the same hash function that worked for + schemetable.c + */ + static const struct Curl_scheme * const all_schemes[47] = { + &Curl_scheme_mqtt, + &Curl_scheme_smtp, + &Curl_scheme_tftp, + &Curl_scheme_imap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &Curl_scheme_ldaps, + &Curl_scheme_dict, NULL, + &Curl_scheme_file, NULL, + &Curl_scheme_pop3s, + &Curl_scheme_ftp, + &Curl_scheme_scp, + &Curl_scheme_mqtts, + &Curl_scheme_imaps, + &Curl_scheme_ldap, + &Curl_scheme_http, + &Curl_scheme_smb, NULL, NULL, + &Curl_scheme_telnet, + &Curl_scheme_https, + &Curl_scheme_gopher, + &Curl_scheme_rtsp, NULL, NULL, + &Curl_scheme_wss, NULL, + &Curl_scheme_gophers, + &Curl_scheme_smtps, + &Curl_scheme_pop3, + &Curl_scheme_ws, NULL, NULL, + &Curl_scheme_sftp, + &Curl_scheme_ftps, NULL, + &Curl_scheme_smbs, NULL, + }; + + if(len && (len <= 7)) { + const char *s = scheme; + size_t l = len; + const struct Curl_scheme *h; + unsigned int c = 792; + while(l) { + c <<= 4; + c += (unsigned int)Curl_raw_tolower(*s); + s++; + l--; + } + + h = all_schemes[c % 47]; + if(h && curl_strnequal(scheme, h->name, len) && !h->name[len]) + return h; + } + return NULL; +} + +const struct Curl_scheme *Curl_get_scheme(const char *scheme) +{ + return Curl_getn_scheme(scheme, strlen(scheme)); +} diff --git a/lib/protocol.h b/lib/protocol.h new file mode 100644 index 0000000000..f8254096e2 --- /dev/null +++ b/lib/protocol.h @@ -0,0 +1,276 @@ +#ifndef HEADER_CURL_PROTOCOL_H +#define HEADER_CURL_PROTOCOL_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 + * + ***************************************************************************/ +/* This file is for lib internal stuff */ +#include "curl_setup.h" + +struct Curl_easy; +struct connectdata; +struct easy_pollset; + +/* Known protocol default port numbers */ +#define PORT_FTP 21 +#define PORT_FTPS 990 +#define PORT_TELNET 23 +#define PORT_HTTP 80 +#define PORT_HTTPS 443 +#define PORT_DICT 2628 +#define PORT_LDAP 389 +#define PORT_LDAPS 636 +#define PORT_TFTP 69 +#define PORT_SSH 22 +#define PORT_IMAP 143 +#define PORT_IMAPS 993 +#define PORT_POP3 110 +#define PORT_POP3S 995 +#define PORT_SMB 445 +#define PORT_SMBS 445 +#define PORT_SMTP 25 +#define PORT_SMTPS 465 /* sometimes called SSMTP */ +#define PORT_RTSP 554 +#define PORT_GOPHER 70 +#define PORT_MQTT 1883 +#define PORT_MQTTS 8883 + +/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number, + * the rest are internal information. If we use higher bits we only do this on + * platforms that have a >= 64-bit type and then we use such a type for the + * protocol fields in the scheme definition. + */ +#define CURLPROTO_WS (1L << 30) +#define CURLPROTO_WSS ((curl_prot_t)1 << 31) +#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) + +typedef curl_off_t curl_prot_t; + +/* 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 0x3fffffff + +/* Convenience defines for checking protocols or their SSL based version. Each + protocol scheme should only ever have a single CURLPROTO_ in its protocol + field. */ +#define PROTO_FAMILY_HTTP (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_WS | \ + CURLPROTO_WSS) +#define PROTO_FAMILY_FTP (CURLPROTO_FTP | CURLPROTO_FTPS) +#define PROTO_FAMILY_POP3 (CURLPROTO_POP3 | CURLPROTO_POP3S) +#define PROTO_FAMILY_SMB (CURLPROTO_SMB | CURLPROTO_SMBS) +#define PROTO_FAMILY_SMTP (CURLPROTO_SMTP | CURLPROTO_SMTPS) +#define PROTO_FAMILY_SSH (CURLPROTO_SCP | CURLPROTO_SFTP) + +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) || \ + !defined(CURL_DISABLE_POP3) +/* these protocols support CURLOPT_DIRLISTONLY */ +#define CURL_LIST_ONLY_PROTOCOL 1 +#endif + +/* When redirecting transfers. */ +typedef enum { + 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 */ + FOLLOW_REDIR /* a full true redirect */ +} followtype; + +/* + * Specific protocol handler, an implementation of one or more URI schemes. + */ +struct Curl_protocol { + /* Complement to setup_connection_internals(). This is done before the + transfer "owns" the connection. */ + CURLcode (*setup_connection)(struct Curl_easy *data, + struct connectdata *conn); + + /* These two functions MUST be set to be protocol dependent */ + CURLcode (*do_it)(struct Curl_easy *data, bool *done); + CURLcode (*done)(struct Curl_easy *, CURLcode, bool); + + /* If the curl_do() function is better made in two halves, this + * curl_do_more() function will be called afterwards, if set. For example + * for doing the FTP stuff after the PASV/PORT command. + */ + CURLcode (*do_more)(struct Curl_easy *, int *); + + /* This function *MAY* be set to a protocol-dependent function that is run + * after the connect() and everything is done, as a step in the connection. + * The 'done' pointer points to a bool that should be set to TRUE if the + * function completes before return. If it does not complete, the caller + * should call the ->connecting() function until it is. + */ + CURLcode (*connect_it)(struct Curl_easy *data, bool *done); + + /* See above. */ + CURLcode (*connecting)(struct Curl_easy *data, bool *done); + CURLcode (*doing)(struct Curl_easy *data, bool *done); + + /* Called from the multi interface during the PROTOCONNECT phase, and it + should then return a proper fd set */ + CURLcode (*proto_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); + + /* Called from the multi interface during the DOING phase, and it should + then return a proper fd set */ + CURLcode (*doing_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); + + /* Called from the multi interface during the DO_MORE phase, and it should + then return a proper fd set */ + CURLcode (*domore_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); + + /* Called from the multi interface during the DO_DONE, PERFORM and + WAITPERFORM phases, and it should then return a proper fd set. Not setting + this will make libcurl use the generic default one. */ + CURLcode (*perform_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); + + /* This function *MAY* be set to a protocol-dependent function that is run + * by the curl_disconnect(), as a step in the disconnection. If the handler + * is called because the connection has been considered dead, + * dead_connection is set to TRUE. The connection is (again) associated with + * the transfer here. + */ + CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *, + bool dead_connection); + + /* If used, this function gets called from transfer.c to + allow the protocol to do extra handling in writing response to + the client. */ + CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen, + bool is_eos); + + /* If used, this function gets called from transfer.c to + allow the protocol to do extra handling in writing a single response + header line to the client. */ + CURLcode (*write_resp_hd)(struct Curl_easy *data, + const char *hd, size_t hdlen, bool is_eos); + + /* If used, this function checks for a connection managed by this + protocol and currently not in use, if it should be considered dead. */ + bool (*connection_is_dead)(struct Curl_easy *data, + struct connectdata *conn); + + /* attach() attaches this transfer to this connection */ + void (*attach)(struct Curl_easy *data, struct connectdata *conn); + + /* return CURLE_OK if a redirect to `newurl` should be followed, + CURLE_TOO_MANY_REDIRECTS otherwise. May alter `data` to change + the way the follow request is performed. */ + CURLcode (*follow)(struct Curl_easy *data, const char *newurl, + followtype type); +}; + +#define PROTOPT_NONE 0 /* nothing extra */ +#define PROTOPT_SSL (1 << 0) /* uses SSL */ +#define PROTOPT_DUAL (1 << 1) /* this protocol uses two connections */ +#define PROTOPT_CLOSEACTION (1 << 2) /* need action before socket close */ +/* some protocols will have to call the underlying functions without regard to + what exact state the socket signals. IE even if the socket says "readable", + the send function might need to be called while uploading, or vice versa. +*/ +#define PROTOPT_DIRLOCK (1 << 3) +#define PROTOPT_NONETWORK (1 << 4) /* protocol does not use the network! */ +#define PROTOPT_NEEDSPWD (1 << 5) /* needs a password, and if none is set it + gets a default */ +#define PROTOPT_NOURLQUERY (1 << 6) /* protocol cannot handle + URL query strings (?foo=bar) ! */ +#define PROTOPT_CREDSPERREQUEST (1 << 7) /* requires login credentials per + request instead of per + connection */ +#define PROTOPT_ALPN (1 << 8) /* set ALPN for this */ +/* (1 << 9) was PROTOPT_STREAM, now free */ +#define PROTOPT_URLOPTIONS (1 << 10) /* allow options part in the userinfo + field of the URL */ +#define PROTOPT_PROXY_AS_HTTP (1 << 11) /* allow this non-HTTP scheme over a + HTTP proxy as HTTP proxies may know + this protocol and act as + a gateway */ +#define PROTOPT_WILDCARD (1 << 12) /* protocol supports wildcard matching */ +#define PROTOPT_USERPWDCTRL (1 << 13) /* Allow "control bytes" (< 32 ASCII) in + username and password */ +#define PROTOPT_NOTCPPROXY (1 << 14) /* this protocol cannot proxy over TCP */ +#define PROTOPT_SSL_REUSE (1 << 15) /* this protocol may reuse an existing + SSL connection in the same family + without having PROTOPT_SSL. */ +#define PROTOPT_CONN_REUSE (1 << 16) /* this protocol can reuse connections */ + +/* Everything about a URI scheme. */ +struct Curl_scheme { + const char *name; /* URL scheme name in lowercase */ + const struct Curl_protocol *run; /* implementation, optional */ + curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single + specific protocol bit */ + 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. */ +}; + +/* Get scheme definition for a URI scheme name + * @param scheme URI scheme name, case-insensitive + * @return NULL if scheme is not known + */ +const struct Curl_scheme *Curl_get_scheme(const char *scheme); +const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len); + +/* For direct access to a URI scheme */ +extern const struct Curl_scheme Curl_scheme_dict; +extern const struct Curl_scheme Curl_scheme_file; +extern const struct Curl_scheme Curl_scheme_ftp; +extern const struct Curl_scheme Curl_scheme_ftps; +extern const struct Curl_scheme Curl_scheme_gopher; +extern const struct Curl_scheme Curl_scheme_gophers; +extern const struct Curl_scheme Curl_scheme_http; +extern const struct Curl_scheme Curl_scheme_https; +extern const struct Curl_scheme Curl_scheme_imap; +extern const struct Curl_scheme Curl_scheme_imaps; +extern const struct Curl_scheme Curl_scheme_ldap; +extern const struct Curl_scheme Curl_scheme_ldaps; +extern const struct Curl_scheme Curl_scheme_mqtt; +extern const struct Curl_scheme Curl_scheme_mqtts; +extern const struct Curl_scheme Curl_scheme_pop3; +extern const struct Curl_scheme Curl_scheme_pop3s; +extern const struct Curl_scheme Curl_scheme_rtsp; +extern const struct Curl_scheme Curl_scheme_scp; +extern const struct Curl_scheme Curl_scheme_sftp; +extern const struct Curl_scheme Curl_scheme_smb; +extern const struct Curl_scheme Curl_scheme_smbs; +extern const struct Curl_scheme Curl_scheme_smtp; +extern const struct Curl_scheme Curl_scheme_smtps; +extern const struct Curl_scheme Curl_scheme_telnet; +extern const struct Curl_scheme Curl_scheme_tftp; +extern const struct Curl_scheme Curl_scheme_ws; +extern const struct Curl_scheme Curl_scheme_wss; + +#endif /* HEADER_CURL_PROTOCOL_H */ diff --git a/lib/psl.c b/lib/psl.c index a488a46e93..e2488aea22 100644 --- a/lib/psl.c +++ b/lib/psl.c @@ -21,20 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #ifdef USE_LIBPSL #include "psl.h" -#include "share.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "progress.h" +#include "curl_share.h" void Curl_psl_destroy(struct PslCache *pslcache) { @@ -46,25 +39,18 @@ void Curl_psl_destroy(struct PslCache *pslcache) } } -static time_t now_seconds(void) -{ - struct curltime now = curlx_now(); - - return now.tv_sec; -} - const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy) { struct PslCache *pslcache = easy->psl; const psl_ctx_t *psl; - time_t now; + time_t now_sec; if(!pslcache) return NULL; Curl_share_lock(easy, CURL_LOCK_DATA_PSL, CURL_LOCK_ACCESS_SHARED); - now = now_seconds(); - if(!pslcache->psl || pslcache->expires <= now) { + now_sec = Curl_pgrs_now(easy)->tv_sec; + if(!pslcache->psl || pslcache->expires <= now_sec) { /* Let a chance to other threads to do the job: avoids deadlock. */ Curl_share_unlock(easy, CURL_LOCK_DATA_PSL); @@ -72,8 +58,10 @@ const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy) Curl_share_lock(easy, CURL_LOCK_DATA_PSL, CURL_LOCK_ACCESS_SINGLE); /* Recheck in case another thread did the job. */ - now = now_seconds(); - if(!pslcache->psl || pslcache->expires <= now) { + if(pslcache->expires <= now_sec) { + now_sec = Curl_pgrs_now(easy)->tv_sec; + } + if(!pslcache->psl || pslcache->expires <= now_sec) { bool dynamic = FALSE; time_t expires = TIME_T_MAX; @@ -81,7 +69,8 @@ const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy) psl = psl_latest(NULL); dynamic = psl != NULL; /* Take care of possible time computation overflow. */ - expires = now < TIME_T_MAX - PSL_TTL ? now + PSL_TTL : TIME_T_MAX; + expires = (now_sec < TIME_T_MAX - PSL_TTL) ? + (now_sec + PSL_TTL) : TIME_T_MAX; /* Only get the built-in PSL if we do not already have the "latest". */ if(!psl && !pslcache->dynamic) @@ -96,7 +85,7 @@ const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy) pslcache->expires = expires; } } - Curl_share_unlock(easy, CURL_LOCK_DATA_PSL); /* Release exclusive lock. */ + Curl_share_unlock(easy, CURL_LOCK_DATA_PSL); /* Release exclusive lock. */ Curl_share_lock(easy, CURL_LOCK_DATA_PSL, CURL_LOCK_ACCESS_SHARED); } psl = pslcache->psl; diff --git a/lib/psl.h b/lib/psl.h index dc11469a52..97871f9634 100644 --- a/lib/psl.h +++ b/lib/psl.h @@ -23,8 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #ifdef USE_LIBPSL + #include struct Curl_easy; diff --git a/lib/rand.c b/lib/rand.c index a1a5e42c2b..8defe9bd11 100644 --- a/lib/rand.c +++ b/lib/rand.c @@ -21,83 +21,36 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - -#ifdef HAVE_FCNTL_H -#include -#endif #ifdef HAVE_ARPA_INET_H #include #endif -#include #include "urldata.h" #include "vtls/vtls.h" -#include "sendf.h" -#include "curlx/timeval.h" +#include "curl_trc.h" #include "rand.h" #include "escape.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - #ifdef _WIN32 - -#if defined(_WIN32_WINNT) && _WIN32_WINNT >= _WIN32_WINNT_VISTA && \ - !defined(CURL_WINDOWS_UWP) -# define HAVE_WIN_BCRYPTGENRANDOM -# include -# ifdef _MSC_VER -# pragma comment(lib, "bcrypt.lib") -# endif - /* Offered by mingw-w64 v3+. MS SDK v7.0A+. */ -# ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG -# define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002 -# endif -# ifndef STATUS_SUCCESS -# define STATUS_SUCCESS ((NTSTATUS)0x00000000L) -# endif -#elif defined(USE_WIN32_CRYPTO) -# include -# ifdef _MSC_VER -# pragma comment(lib, "advapi32.lib") -# endif +#include +#ifdef _MSC_VER +# pragma comment(lib, "bcrypt.lib") +#endif +#ifndef STATUS_SUCCESS +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #endif CURLcode Curl_win32_random(unsigned char *entropy, size_t length) { memset(entropy, 0, length); -#ifdef HAVE_WIN_BCRYPTGENRANDOM 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; -#endif } #endif @@ -131,7 +84,8 @@ static CURLcode weak_random(struct Curl_easy *data, static bool seeded = FALSE; unsigned int rnd; if(!seeded) { - struct curltime now = curlx_now(); + struct curltime now; + curlx_pnow(&now); randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec; randseed = randseed * 1103515245 + 12345; randseed = randseed * 1103515245 + 12345; @@ -149,12 +103,6 @@ static CURLcode weak_random(struct Curl_easy *data, } #endif -#ifdef USE_SSL -#define _random(x,y,z) Curl_ssl_random(x,y,z) -#else -#define _random(x,y,z) weak_random(x,y,z) -#endif - static CURLcode randit(struct Curl_easy *data, unsigned int *rnd, bool env_override) { @@ -185,7 +133,11 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd, #endif /* data may be NULL! */ - return _random(data, (unsigned char *)rnd, sizeof(*rnd)); +#ifdef USE_SSL + return Curl_ssl_random(data, (unsigned char *)rnd, sizeof(*rnd)); +#else + return weak_random(data, (unsigned char *)rnd, sizeof(*rnd)); +#endif } /* @@ -241,20 +193,13 @@ CURLcode Curl_rand_bytes(struct Curl_easy *data, * size. */ -CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, - size_t num) +CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, size_t num) { CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; 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)) { + 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")); return CURLE_BAD_FUNCTION_ARGUMENT; @@ -262,11 +207,11 @@ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, num--; /* save one for null-termination */ - result = Curl_rand(data, buffer, num/2); + result = Curl_rand(data, buffer, num / 2); if(result) return result; - Curl_hexencode(buffer, num/2, rnd, num + 1); + Curl_hexencode(buffer, num / 2, rnd, num + 1); return result; } diff --git a/lib/rand.h b/lib/rand.h index 2ba60e7297..afccd0aac1 100644 --- a/lib/rand.h +++ b/lib/rand.h @@ -23,17 +23,16 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - CURLcode Curl_rand_bytes(struct Curl_easy *data, #ifdef DEBUGBUILD - bool allow_env_override, + bool env_override, #endif 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 /* @@ -41,8 +40,7 @@ CURLcode Curl_rand_bytes(struct Curl_easy *data, * hexadecimal digits PLUS a null-terminating byte. It must be an odd number * size. */ -CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, - size_t num); +CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd, size_t num); /* * Curl_rand_alnum() fills the 'rnd' buffer with a given 'num' size with random diff --git a/lib/ratelimit.c b/lib/ratelimit.c new file mode 100644 index 0000000000..dc013757e9 --- /dev/null +++ b/lib/ratelimit.c @@ -0,0 +1,293 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "urldata.h" +#include "ratelimit.h" + +#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 */ + +static void rlimit_update(struct Curl_rlimit *r, + const struct curltime *pts) +{ + timediff_t elapsed_us, elapsed_steps; + int64_t token_gain; + + DEBUGASSERT(r->rate_per_step); + if((r->ts.tv_sec == pts->tv_sec) && (r->ts.tv_usec == pts->tv_usec)) + return; + + elapsed_us = curlx_ptimediff_us(pts, &r->ts); + if(elapsed_us < 0) { /* not going back in time */ + DEBUGASSERT(0); + return; + } + + elapsed_us += r->spare_us; + if(elapsed_us < r->step_us) + return; + + /* we do the update */ + r->ts = *pts; + elapsed_steps = elapsed_us / r->step_us; + r->spare_us = elapsed_us % r->step_us; + + /* How many tokens did we gain since the last update? */ + if(r->rate_per_step > (INT64_MAX / elapsed_steps)) + token_gain = INT64_MAX; + else { + token_gain = r->rate_per_step * elapsed_steps; + } + + 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. */ + if(r->burst_per_step && (r->tokens > r->burst_per_step)) { + r->tokens = r->burst_per_step; + } +} + +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 = r->rate_per_sec = rate_per_sec; + r->burst_per_step = r->burst_per_sec = 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) +{ + /* A start always resets the values to initial defaults, then + * fine tunes the intervals for the total_tokens expected. */ + r->rate_per_step = r->rate_per_sec; + r->burst_per_step = r->burst_per_sec; + r->step_us = CURL_US_PER_SEC; + r->spare_us = 0; + r->tokens = r->rate_per_step; + 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) { + rlimit_update(r, pts); + return r->tokens; + } + else + return INT64_MAX; +} + +void Curl_rlimit_drain(struct Curl_rlimit *r, + size_t tokens, + const struct curltime *pts) +{ + if(r->blocked || !r->rate_per_step) + return; + + rlimit_update(r, pts); +#if 8 <= SIZEOF_SIZE_T + if(tokens > INT64_MAX) { + r->tokens = INT64_MAX; + } + else +#endif + { + int64_t val = (int64_t)tokens; + if((INT64_MIN + val) < r->tokens) + r->tokens -= val; + else + r->tokens = INT64_MIN; + } +} + +timediff_t Curl_rlimit_wait_ms(struct Curl_rlimit *r, + const struct curltime *pts) +{ + timediff_t wait_us, elapsed_us; + + if(r->blocked || !r->rate_per_step) + return 0; + 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 = 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) + return 0; + wait_us -= elapsed_us; + 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) +{ + if(!activate == !r->blocked) + return; + + r->ts = *pts; + r->blocked = activate; + if(!r->blocked) { + /* Start rate limiting fresh. The amount of time this was blocked + * does not generate extra tokens. */ + Curl_rlimit_start(r, pts, -1); + } + else { + r->tokens = 0; + } +} diff --git a/lib/ratelimit.h b/lib/ratelimit.h new file mode 100644 index 0000000000..3c3e38b895 --- /dev/null +++ b/lib/ratelimit.h @@ -0,0 +1,108 @@ +#ifndef HEADER_CURL_RLIMIT_H +#define HEADER_CURL_RLIMIT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curlx/timeval.h" + +struct Curl_easy; + +/* This is a rate limiter that provides "tokens" to be consumed + * 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. + * - at/after the 2nd second, 1 million tokens are available again. + * - nothing happens for a second, the 1 million tokens would grow + * to 2 million, however the burst limit caps those at 1.5 million. + * Thus: + * - setting "burst" to CURL_OFF_T_MAX would average tokens over the + * complete lifetime. E.g. for a download, at the *end* of it, the + * average rate from start to finish would be the rate limit. + * - 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 { + int64_t rate_per_sec; /* rate tokens generated per second */ + int64_t burst_per_sec; /* burst rate of tokens per second */ + 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 */ + 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, + int64_t rate_per_sec, + int64_t burst_per_sec, + 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 */ +int64_t Curl_rlimit_avail(struct Curl_rlimit *r, + const struct curltime *pts); + +/* 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); + +/* Block/unblock ratelimiting. A blocked ratelimit has 0 tokens available. */ +void Curl_rlimit_block(struct Curl_rlimit *r, + bool activate, + const struct curltime *pts); + +#endif /* HEADER_CURL_RLIMIT_H */ diff --git a/lib/rename.c b/lib/rename.c deleted file mode 100644 index d3a46e0e6a..0000000000 --- a/lib/rename.c +++ /dev/null @@ -1,73 +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 "rename.h" - -#include "curl_setup.h" - -#if (!defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_COOKIES)) || \ - !defined(CURL_DISABLE_ALTSVC) - -#include "curlx/multibyte.h" -#include "curlx/timeval.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* return 0 on success, 1 on error */ -int Curl_rename(const char *oldpath, const char *newpath) -{ -#if defined(_WIN32) && !defined(UNDER_CE) - /* 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. */ - const int max_wait_ms = 1000; - struct curltime start = curlx_now(); - TCHAR *tchar_oldpath = curlx_convert_UTF8_to_tchar(oldpath); - TCHAR *tchar_newpath = curlx_convert_UTF8_to_tchar(newpath); - for(;;) { - timediff_t diff; - if(MoveFileEx(tchar_oldpath, tchar_newpath, MOVEFILE_REPLACE_EXISTING)) { - curlx_unicodefree(tchar_oldpath); - curlx_unicodefree(tchar_newpath); - break; - } - diff = curlx_timediff(curlx_now(), start); - if(diff < 0 || diff > max_wait_ms) { - curlx_unicodefree(tchar_oldpath); - curlx_unicodefree(tchar_newpath); - return 1; - } - Sleep(1); - } -#else - if(rename(oldpath, newpath)) - return 1; -#endif - return 0; -} - -#endif diff --git a/lib/request.c b/lib/request.c index 8751ada559..c414383dc0 100644 --- a/lib/request.c +++ b/lib/request.c @@ -21,26 +21,20 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #include "urldata.h" #include "cfilters.h" #include "curlx/dynbuf.h" #include "doh.h" -#include "multiif.h" #include "progress.h" #include "request.h" #include "sendf.h" +#include "curl_trc.h" #include "transfer.h" #include "url.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - void Curl_req_init(struct SingleRequest *req) { memset(req, 0, sizeof(*req)); @@ -65,12 +59,17 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req, req->header = FALSE; req->headerline = 0; req->headerbytecount = 0; - req->allheadercount = 0; + req->allheadercount = 0; req->deductheadercount = 0; req->httpversion_sent = 0; req->httpversion = 0; req->sendbuf_hds_len = 0; + curlx_safefree(req->userpwd); +#ifndef CURL_DISABLE_PROXY + curlx_safefree(req->proxyuserpwd); +#endif + result = Curl_client_start(data); if(result) return result; @@ -95,7 +94,7 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req, CURLcode Curl_req_start(struct SingleRequest *req, struct Curl_easy *data) { - req->start = curlx_now(); + req->start = *Curl_pgrs_now(data); return Curl_req_soft_reset(req, data); } @@ -108,24 +107,27 @@ CURLcode Curl_req_done(struct SingleRequest *req, if(!aborted) (void)req_flush(data); Curl_client_reset(data); -#ifndef CURL_DISABLE_DOH - Curl_doh_close(data); -#endif return CURLE_OK; } void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) { - struct curltime t0 = {0, 0}; + struct curltime t0 = { 0, 0 }; - Curl_safefree(req->newurl); + curlx_safefree(req->newurl); + curlx_safefree(req->userpwd); +#ifndef CURL_DISABLE_PROXY + curlx_safefree(req->proxyuserpwd); +#endif +#ifndef CURL_DISABLE_COOKIES + curlx_safefree(req->cookiehost); +#endif Curl_client_reset(data); if(req->sendbuf_init) Curl_bufq_reset(&req->sendbuf); -#ifndef CURL_DISABLE_DOH - Curl_doh_close(data); -#endif + /* clear any resolve data */ + Curl_resolv_destroy_all(data); /* Can no longer memset() this struct as we need to keep some state */ req->size = -1; req->maxdownload = -1; @@ -133,13 +135,13 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) req->writebytecount = 0; req->start = t0; req->headerbytecount = 0; - req->allheadercount = 0; + req->allheadercount = 0; req->deductheadercount = 0; req->headerline = 0; req->offset = 0; req->httpcode = 0; - req->keepon = 0; - req->upgr101 = UPGR101_INIT; + req->io_flags = 0; + req->upgr101 = UPGR101_NONE; req->sendbuf_hds_len = 0; req->timeofdoc = 0; req->location = NULL; @@ -159,16 +161,24 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) req->ignorebody = FALSE; req->http_bodyless = FALSE; req->chunk = FALSE; + req->resp_trailer = FALSE; req->ignore_cl = FALSE; req->upload_chunky = FALSE; 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) { - Curl_safefree(req->newurl); + curlx_safefree(req->newurl); + curlx_safefree(req->userpwd); +#ifndef CURL_DISABLE_PROXY + curlx_safefree(req->proxyuserpwd); +#endif if(req->sendbuf_init) Curl_bufq_free(&req->sendbuf); Curl_client_cleanup(data); @@ -224,7 +234,7 @@ static CURLcode xfer_send(struct Curl_easy *data, size_t body_len = *pnwritten - hds_len; Curl_debug(data, CURLINFO_DATA_OUT, buf + hds_len, body_len); data->req.writebytecount += body_len; - Curl_pgrsSetUploadCounter(data, data->req.writebytecount); + Curl_pgrs_upload_inc(data, body_len); } } } @@ -259,7 +269,7 @@ static CURLcode req_set_upload_done(struct Curl_easy *data) { DEBUGASSERT(!data->req.upload_done); data->req.upload_done = TRUE; - data->req.keepon &= ~(KEEP_SEND|KEEP_SEND_TIMED); /* we are done sending */ + CURL_REQ_CLEAR_SEND(data); Curl_pgrsTime(data, TIMER_POSTRANSFER); Curl_creader_done(data, data->req.upload_aborted); @@ -298,7 +308,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; } } @@ -394,6 +404,11 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req, return result; buf += nwritten; blen -= nwritten; + if(!blen) { + result = req_set_upload_done(data); + if(result) + return result; + } } if(blen) { @@ -416,14 +431,23 @@ bool Curl_req_sendbuf_empty(struct Curl_easy *data) bool Curl_req_want_send(struct Curl_easy *data) { - /* Not done and - * - KEEP_SEND and not PAUSEd. - * - or request has buffered data to send - * - or transfer connection has pending data to send */ + /* Not done and upload not blocked and either one of + * - REQ_IO_SEND + * - request has buffered data to send + * - connection has pending data to send */ return !data->req.done && - (((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) || - !Curl_req_sendbuf_empty(data) || - Curl_xfer_needs_flush(data)); + !Curl_rlimit_is_blocked(&data->progress.ul.rlimit) && + (CURL_REQ_WANT_SEND(data) || + !Curl_req_sendbuf_empty(data) || + Curl_xfer_needs_flush(data)); +} + +bool Curl_req_want_recv(struct Curl_easy *data) +{ + /* Not done and download not blocked and want RECV */ + return !data->req.done && + !Curl_rlimit_is_blocked(&data->progress.dl.rlimit) && + CURL_REQ_WANT_RECV(data); } bool Curl_req_done_sending(struct Curl_easy *data) @@ -459,8 +483,7 @@ CURLcode Curl_req_abort_sending(struct Curl_easy *data) if(!data->req.upload_done) { Curl_bufq_reset(&data->req.sendbuf); data->req.upload_aborted = TRUE; - /* no longer KEEP_SEND and KEEP_SEND_PAUSE */ - data->req.keepon &= ~KEEP_SENDBITS; + CURL_REQ_CLEAR_SEND(data); return req_set_upload_done(data); } return CURLE_OK; @@ -471,6 +494,9 @@ CURLcode Curl_req_stop_send_recv(struct Curl_easy *data) /* stop receiving and ALL sending as well, including PAUSE and HOLD. * We might still be paused on receive client writes though, so * keep those bits around. */ - data->req.keepon &= ~(KEEP_RECV|KEEP_SENDBITS); - return Curl_req_abort_sending(data); + CURLcode result = CURLE_OK; + if(CURL_REQ_WANT_SEND(data)) + result = Curl_req_abort_sending(data); + CURL_REQ_CLEAR_IO(data); + return result; } diff --git a/lib/request.h b/lib/request.h index bce34de8ba..6948d79be7 100644 --- a/lib/request.h +++ b/lib/request.h @@ -23,9 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* This file is for lib internal stuff */ - #include "curl_setup.h" #include "bufq.h" @@ -33,8 +31,27 @@ /* forward declarations */ struct UserDefined; +/* Bits on the io_flags member of SingleRequest */ +#define REQ_IO_RECV (1 << 0) /* there is or may be data to read */ +#define REQ_IO_SEND (1 << 1) /* there is or may be data to write */ + +/* Low level request receive/send io_flags checks. */ +#define CURL_REQ_WANT_SEND(d) ((d)->req.io_flags & REQ_IO_SEND) +#define CURL_REQ_WANT_RECV(d) ((d)->req.io_flags & REQ_IO_RECV) +#define CURL_REQ_WANT_IO(d) \ + ((d)->req.io_flags & (REQ_IO_RECV | REQ_IO_SEND)) +/* Low level request receive/send io_flags manipulations. */ +#define CURL_REQ_SET_SEND(d) ((d)->req.io_flags |= REQ_IO_SEND) +#define CURL_REQ_SET_RECV(d) ((d)->req.io_flags |= REQ_IO_RECV) +#define CURL_REQ_CLEAR_SEND(d) \ + ((d)->req.io_flags &= (uint8_t)~REQ_IO_SEND) +#define CURL_REQ_CLEAR_RECV(d) \ + ((d)->req.io_flags &= (uint8_t)~REQ_IO_RECV) +#define CURL_REQ_CLEAR_IO(d) \ + ((d)->req.io_flags &= (uint8_t)~(REQ_IO_RECV | REQ_IO_SEND)) + 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 */ @@ -42,14 +59,12 @@ enum expect100 { }; enum upgrade101 { - UPGR101_INIT, /* default state */ - UPGR101_WS, /* upgrade to WebSockets requested */ + UPGR101_NONE, /* default state */ + UPGR101_WS, /* upgrade to WebSocket requested */ UPGR101_H2, /* upgrade to HTTP/2 requested */ - UPGR101_RECEIVED, /* 101 response received */ - UPGR101_WORKING /* talking upgraded protocol */ + UPGR101_RECEIVED /* 101 response received */ }; - /* * Request specific data in the easy handle (Curl_easy). Previously, * these members were on the connectdata struct but since a conn struct may @@ -63,6 +78,8 @@ struct SingleRequest { -1 means unlimited */ curl_off_t bytecount; /* total number of bytes read */ curl_off_t writebytecount; /* number of bytes written */ + curl_off_t offset; /* possible resume offset read from the + Content-Range: header */ struct curltime start; /* transfer started at this time */ unsigned int headerbytecount; /* received server headers (not CONNECT @@ -76,11 +93,8 @@ struct SingleRequest { in a CURLE_GOT_NOTHING error code */ int headerline; /* counts header lines to better track the first one */ - curl_off_t offset; /* possible resume offset read from the - Content-Range: header */ int httpcode; /* error code from the 'HTTP/1.? XXX' or 'RTSP/1.? XXX' line */ - int keepon; unsigned char httpversion_sent; /* Version in request (09, 10, 11, etc.) */ unsigned char httpversion; /* Version in response (09, 10, 11, etc.) */ enum upgrade101 upgr101; /* 101 upgrade state */ @@ -98,7 +112,15 @@ struct SingleRequest { header data */ char *newurl; /* Set to the new URL to use when a redirect or a retry is wanted */ + uint8_t io_flags; /* REQ_IO_RECV | REQ_IO_SEND */ + char *userpwd; /* auth header */ +#ifndef CURL_DISABLE_PROXY + char *proxyuserpwd; /* proxy auth header */ +#endif +#ifndef CURL_DISABLE_COOKIES + char *cookiehost; +#endif #ifndef CURL_DISABLE_COOKIES unsigned char setcookies; #endif @@ -113,8 +135,8 @@ struct SingleRequest { BIT(eos_sent); /* iff EOS has been sent to the server */ BIT(rewind_read); /* iff reader needs rewind at next start */ BIT(upload_done); /* set to TRUE when all request data has been sent */ - BIT(upload_aborted); /* set to TRUE when upload was aborted. Will also - * show `upload_done` as TRUE. */ + BIT(upload_aborted); /* set to TRUE when upload was aborted. Also + * shows `upload_done` as TRUE. */ BIT(ignorebody); /* we read a response-body but we ignore it! */ BIT(http_bodyless); /* HTTP response status code is between 100 and 199, 204 or 304 */ @@ -131,6 +153,7 @@ struct SingleRequest { BIT(sendbuf_init); /* sendbuf is initialized */ BIT(shutdown); /* request end will shutdown connection */ BIT(shutdown_err_ignore); /* errors in shutdown will not fail request */ + BIT(reader_started); /* client reads have started */ }; /** @@ -177,11 +200,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); /** @@ -196,11 +219,13 @@ bool Curl_req_done_sending(struct Curl_easy *data); */ CURLcode Curl_req_send_more(struct Curl_easy *data); -/** - * TRUE iff the request wants to send, e.g. has buffered bytes. - */ +/* TRUE if the request wants to send, e.g. is not done sending + * and is not blocked. */ bool Curl_req_want_send(struct Curl_easy *data); +/* TRUE if the request wants to receive and is not blocked. */ +bool Curl_req_want_recv(struct Curl_easy *data); + /** * TRUE iff the request has no buffered bytes yet to send. */ @@ -208,13 +233,13 @@ bool Curl_req_sendbuf_empty(struct Curl_easy *data); /** * Stop sending any more request data to the server. - * Will clear the send buffer and mark request sending as done. + * Clear the send buffer and mark request sending as done. */ CURLcode Curl_req_abort_sending(struct Curl_easy *data); /** * Stop sending and receiving any more request data. - * Will abort sending if not done. + * Abort sending if not done. */ CURLcode Curl_req_stop_send_recv(struct Curl_easy *data); diff --git a/lib/rtsp.c b/lib/rtsp.c index 1d5f44f91b..285126371c 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -21,31 +21,26 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" +#include "urldata.h" +#include "rtsp.h" #ifndef CURL_DISABLE_RTSP -#include "urldata.h" -#include #include "transfer.h" #include "sendf.h" +#include "curl_trc.h" #include "multiif.h" #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" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - /* meta key for storing protocol meta at easy handle */ #define CURL_META_RTSP_EASY "meta:proto:rtsp:easy" @@ -71,46 +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 */ @@ -121,40 +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 */ - ZERO_NULL, /* 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_NONE /* flags */ -}; - #define MAX_RTP_BUFFERSIZE 1000000 /* arbitrary */ static void rtsp_easy_dtor(void *key, size_t klen, void *entry) @@ -162,7 +90,7 @@ static void rtsp_easy_dtor(void *key, size_t klen, void *entry) struct RTSP *rtsp = entry; (void)key; (void)klen; - free(rtsp); + curlx_free(rtsp); } static void rtsp_conn_dtor(void *key, size_t klen, void *entry) @@ -171,7 +99,7 @@ static void rtsp_conn_dtor(void *key, size_t klen, void *entry) (void)key; (void)klen; curlx_dyn_free(&rtspc->buf); - free(rtspc); + curlx_free(rtspc); } static CURLcode rtsp_setup_connection(struct Curl_easy *data, @@ -180,14 +108,14 @@ static CURLcode rtsp_setup_connection(struct Curl_easy *data, struct rtsp_conn *rtspc; struct RTSP *rtsp; - rtspc = calloc(1, sizeof(*rtspc)); + rtspc = curlx_calloc(1, sizeof(*rtspc)); if(!rtspc) return CURLE_OUT_OF_MEMORY; curlx_dyn_init(&rtspc->buf, MAX_RTP_BUFFERSIZE); if(Curl_conn_meta_set(conn, CURL_META_RTSP_CONN, rtspc, rtsp_conn_dtor)) return CURLE_OUT_OF_MEMORY; - rtsp = calloc(1, sizeof(struct RTSP)); + rtsp = curlx_calloc(1, sizeof(struct RTSP)); if(!rtsp || Curl_meta_set(data, CURL_META_RTSP_EASY, rtsp, rtsp_easy_dtor)) return CURLE_OUT_OF_MEMORY; @@ -195,38 +123,26 @@ static CURLcode rtsp_setup_connection(struct Curl_easy *data, return CURLE_OK; } - /* * 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 bool rtsp_conn_is_dead(struct Curl_easy *data, + struct connectdata *conn) { - unsigned int ret_val = CONNRESULT_NONE; - (void)data; - - if(checks_to_perform & CONNCHECK_ISDEAD) { - bool input_pending; - if(!Curl_conn_is_alive(data, conn, &input_pending)) - ret_val |= CONNRESULT_DEAD; - } - - return ret_val; + bool input_pending; + /* Contrary to default handling, this protocol allows pending + * input on an unused connection. */ + return !Curl_conn_is_alive(data, conn, &input_pending); } - static CURLcode rtsp_connect(struct Curl_easy *data, bool *done) { struct rtsp_conn *rtspc = Curl_conn_meta_get(data->conn, CURL_META_RTSP_CONN); - CURLcode httpStatus; if(!rtspc) return CURLE_FAILED_INIT; - httpStatus = Curl_http_connect(data, done); - /* Initialize the CSeq if not already done */ if(data->state.rtsp_next_client_CSeq == 0) data->state.rtsp_next_client_CSeq = 1; @@ -234,8 +150,8 @@ static CURLcode rtsp_connect(struct Curl_easy *data, bool *done) data->state.rtsp_next_server_CSeq = 1; rtspc->rtp_channel = -1; - - return httpStatus; + *done = TRUE; + return CURLE_OK; } static CURLcode rtsp_done(struct Curl_easy *data, @@ -257,16 +173,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) { @@ -278,7 +194,6 @@ static CURLcode rtsp_done(struct Curl_easy *data, return httpStatus; } - static CURLcode rtsp_setup_body(struct Curl_easy *data, Curl_RtspReq rtspreq, struct dynbuf *reqp) @@ -298,7 +213,8 @@ static CURLcode rtsp_setup_body(struct Curl_easy *data, } else { if(data->set.postfields) { - size_t plen = strlen(data->set.postfields); + size_t plen = (data->set.postfieldsize >= 0) ? + (size_t)data->set.postfieldsize : strlen(data->set.postfields); req_clen = (curl_off_t)plen; result = Curl_creader_set_buf(data, data->set.postfields, plen); } @@ -318,7 +234,7 @@ static CURLcode rtsp_setup_body(struct Curl_easy *data, /* As stated in the http comments, it is probably not wise to * actually set a custom Content-Length in the headers */ if(!Curl_checkheaders(data, STRCONST("Content-Length"))) { - result = curlx_dyn_addf(reqp, "Content-Length: %" FMT_OFF_T"\r\n", + result = curlx_dyn_addf(reqp, "Content-Length: %" FMT_OFF_T "\r\n", req_clen); if(result) return result; @@ -327,9 +243,8 @@ static CURLcode rtsp_setup_body(struct Curl_easy *data, if(rtspreq == RTSPREQ_SET_PARAMETER || rtspreq == RTSPREQ_GET_PARAMETER) { if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { - result = curlx_dyn_addn(reqp, - STRCONST("Content-Type: " - "text/parameters\r\n")); + result = curlx_dyn_addn(reqp, STRCONST("Content-Type: " + "text/parameters\r\n")); if(result) return result; } @@ -337,9 +252,8 @@ static CURLcode rtsp_setup_body(struct Curl_easy *data, if(rtspreq == RTSPREQ_ANNOUNCE) { if(!Curl_checkheaders(data, STRCONST("Content-Type"))) { - result = curlx_dyn_addn(reqp, - STRCONST("Content-Type: " - "application/sdp\r\n")); + result = curlx_dyn_addn(reqp, STRCONST("Content-Type: " + "application/sdp\r\n")); if(result) return result; } @@ -360,11 +274,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; @@ -391,12 +305,12 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) to this origin */ if(!data->state.first_host) { - data->state.first_host = strdup(conn->host.name); + data->state.first_host = curlx_strdup(conn->host.name); if(!data->state.first_host) 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 @@ -480,10 +394,10 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) if(rtspreq == RTSPREQ_SETUP && !p_transport) { /* New Transport: setting? */ if(data->set.str[STRING_RTSP_TRANSPORT]) { - free(data->state.aptr.rtsp_transport); + curlx_free(data->state.aptr.rtsp_transport); data->state.aptr.rtsp_transport = - aprintf("Transport: %s\r\n", - data->set.str[STRING_RTSP_TRANSPORT]); + curl_maprintf("Transport: %s\r\n", + data->set.str[STRING_RTSP_TRANSPORT]); if(!data->state.aptr.rtsp_transport) return CURLE_OUT_OF_MEMORY; } @@ -506,9 +420,10 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) /* Accept-Encoding header */ if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && data->set.str[STRING_ENCODING]) { - free(data->state.aptr.accept_encoding); + curlx_free(data->state.aptr.accept_encoding); data->state.aptr.accept_encoding = - aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); + curl_maprintf("Accept-Encoding: %s\r\n", + data->set.str[STRING_ENCODING]); if(!data->state.aptr.accept_encoding) { result = CURLE_OUT_OF_MEMORY; @@ -518,13 +433,13 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) } } - /* The User-Agent string might have been allocated in url.c already, because + /* The User-Agent string might have been allocated already, because it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ if(Curl_checkheaders(data, STRCONST("User-Agent")) && data->state.aptr.uagent) { - Curl_safefree(data->state.aptr.uagent); + curlx_safefree(data->state.aptr.uagent); } else if(!Curl_checkheaders(data, STRCONST("User-Agent")) && data->set.str[STRING_USERAGENT]) { @@ -538,14 +453,16 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) goto out; #ifndef CURL_DISABLE_PROXY - p_proxyuserpwd = data->state.aptr.proxyuserpwd; + p_proxyuserpwd = data->req.proxyuserpwd; #endif - p_userpwd = data->state.aptr.userpwd; + p_userpwd = data->req.userpwd; /* Referrer */ - Curl_safefree(data->state.aptr.ref); - if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) - data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); + curlx_safefree(data->state.aptr.ref); + if(Curl_bufref_ptr(&data->state.referer) && + !Curl_checkheaders(data, STRCONST("Referer"))) + data->state.aptr.ref = + curl_maprintf("Referer: %s\r\n", Curl_bufref_ptr(&data->state.referer)); p_referrer = data->state.aptr.ref; @@ -556,12 +473,13 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) * Go ahead and use the Range stuff supplied for HTTP */ if(data->state.use_range && - (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) { + (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) { /* Check to see if there is a range set in the custom headers */ if(!Curl_checkheaders(data, STRCONST("Range")) && data->state.range) { - free(data->state.aptr.rangeline); - data->state.aptr.rangeline = aprintf("Range: %s\r\n", data->state.range); + curlx_free(data->state.aptr.rangeline); + data->state.aptr.rangeline = curl_maprintf("Range: %s\r\n", + data->state.range); p_range = data->state.aptr.rangeline; } } @@ -583,7 +501,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; @@ -620,12 +538,6 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) p_proxyuserpwd ? p_proxyuserpwd : "", p_userpwd ? p_userpwd : ""); - /* - * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM - * with basic and digest, it will be freed anyway by the next request - */ - Curl_safefree(data->state.aptr.userpwd); - if(result) goto out; @@ -664,8 +576,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) /* if a request-body has been sent off, we make sure this progress is noted properly */ Curl_pgrsSetUploadCounter(data, data->req.writebytecount); - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; + result = Curl_pgrsUpdate(data); } out: curlx_dyn_free(&req_buffer); @@ -696,6 +607,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, @@ -809,6 +762,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; } @@ -859,6 +824,16 @@ out: 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, @@ -927,19 +902,19 @@ 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)) { - result = Curl_client_write(data, CLIENTWRITE_BODY| + result = Curl_client_write(data, CLIENTWRITE_BODY | (is_eos ? CLIENTWRITE_EOS : 0), buf, blen); } 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; @@ -955,122 +930,10 @@ 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; - } - - return CURLE_OK; -} - -CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header) -{ - if(checkprefix("CSeq:", header)) { - curl_off_t CSeq = 0; - struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY); - const char *p = &header[5]; - if(!rtsp) - return CURLE_FAILED_INIT; - curlx_str_passblanks(&p); - if(curlx_str_number(&p, &CSeq, LONG_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 */ - } - else if(checkprefix("Session:", header)) { - const char *start, *end; - size_t idlen; - - /* Find the first non-space letter */ - start = header + 8; - curlx_str_passblanks(&start); - - if(!*start) { - failf(data, "Got a blank Session ID"); - return CURLE_RTSP_SESSION_ERROR; - } - - /* Find the end of Session ID - * - * Allow any non whitespace content, up to the field separator or end of - * line. RFC 2326 is not 100% clear on the session ID and for example - * gstreamer does url-encoded session ID's not covered by the standard. - */ - end = start; - while((*end > ' ') && (*end != ';')) - end++; - idlen = end - start; - - if(data->set.str[STRING_RTSP_SESSION_ID]) { - - /* If the Session ID is set, then compare */ - if(strlen(data->set.str[STRING_RTSP_SESSION_ID]) != idlen || - strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen)) { - failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]", - start, data->set.str[STRING_RTSP_SESSION_ID]); - return CURLE_RTSP_SESSION_ERROR; - } - } - else { - /* If the Session ID is not set, and we find it in a response, then set - * it. - */ - - /* Copy the id substring into a new buffer */ - data->set.str[STRING_RTSP_SESSION_ID] = Curl_memdup0(start, idlen); - if(!data->set.str[STRING_RTSP_SESSION_ID]) - return CURLE_OUT_OF_MEMORY; - } - } - else if(checkprefix("Transport:", header)) { - CURLcode result; - result = rtsp_parse_transport(data, header + 10); - if(result) - return result; - } - return CURLE_OK; -} - -static -CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport) -{ - /* If we receive multiple Transport response-headers, the linterleaved + /* 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' */ @@ -1110,5 +973,96 @@ CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport) return CURLE_OK; } +CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header) +{ + if(checkprefix("CSeq:", header)) { + curl_off_t CSeq = 0; + struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY); + const char *p = &header[5]; + if(!rtsp) + return CURLE_FAILED_INIT; + curlx_str_passblanks(&p); + if(curlx_str_number(&p, &CSeq, UINT_MAX)) { + failf(data, "Unable to read the CSeq header: [%s]", header); + return CURLE_RTSP_CSEQ_ERROR; + } + data->state.rtsp_CSeq_recv = rtsp->CSeq_recv = (uint32_t)CSeq; + } + else if(checkprefix("Session:", header)) { + const char *start, *end; + size_t idlen; + + /* Find the first non-space letter */ + start = header + 8; + curlx_str_passblanks(&start); + + if(!*start) { + failf(data, "Got a blank Session ID"); + return CURLE_RTSP_SESSION_ERROR; + } + + /* Find the end of Session ID + * + * Allow any non whitespace content, up to the field separator or end of + * line. RFC 2326 is not 100% clear on the session ID and for example + * gstreamer does URL-encoded session ID's not covered by the standard. + */ + end = start; + while((*end > ' ') && (*end != ';')) + end++; + idlen = end - start; + + if(data->set.str[STRING_RTSP_SESSION_ID]) { + + /* If the Session ID is set, then compare */ + if(strlen(data->set.str[STRING_RTSP_SESSION_ID]) != idlen || + strncmp(start, data->set.str[STRING_RTSP_SESSION_ID], idlen)) { + failf(data, "Got RTSP Session ID Line [%s], but wanted ID [%s]", + start, data->set.str[STRING_RTSP_SESSION_ID]); + return CURLE_RTSP_SESSION_ERROR; + } + } + else { + /* If the Session ID is not set, and we find it in a response, then set + * it. + */ + + /* Copy the id substring into a new buffer */ + data->set.str[STRING_RTSP_SESSION_ID] = curlx_memdup0(start, idlen); + if(!data->set.str[STRING_RTSP_SESSION_ID]) + return CURLE_OUT_OF_MEMORY; + } + } + else if(checkprefix("Transport:", header)) { + CURLcode result; + result = rtsp_parse_transport(data, header + 10); + if(result) + return result; + } + return CURLE_OK; +} + +/* + * RTSP handler interface. + */ +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_conn_is_dead, /* connection_is_dead */ + ZERO_NULL, /* attach connection */ + Curl_http_follow, /* follow */ +}; #endif /* CURL_DISABLE_RTSP */ diff --git a/lib/rtsp.h b/lib/rtsp.h index 59f20a9f16..dd5df3ff73 100644 --- a/lib/rtsp.h +++ b/lib/rtsp.h @@ -23,17 +23,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #ifndef CURL_DISABLE_RTSP - -extern const struct Curl_handler Curl_handler_rtsp; - CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header); - +extern const struct Curl_protocol Curl_protocol_rtsp; #else -/* disabled */ -#define Curl_rtsp_parseheader(x,y) CURLE_NOT_BUILT_IN - -#endif /* CURL_DISABLE_RTSP */ +#define Curl_rtsp_parseheader(x, y) CURLE_NOT_BUILT_IN +#endif #endif /* HEADER_CURL_RTSP_H */ diff --git a/lib/select.c b/lib/select.c index af78cb836b..888490f2c9 100644 --- a/lib/select.c +++ b/lib/select.c @@ -21,34 +21,24 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(HAVE_SELECT) && !defined(HAVE_POLL) #error "We cannot compile without select() or poll() support." #endif -#include - #ifdef HAVE_SYS_SELECT_H #include #elif defined(HAVE_UNISTD_H) #include #endif -#include - #include "urldata.h" #include "connect.h" #include "select.h" #include "curl_trc.h" #include "curlx/timediff.h" #include "curlx/wait.h" -#include "curlx/warnless.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" #ifndef HAVE_POLL /* @@ -76,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 @@ -85,17 +75,17 @@ static int our_select(curl_socket_t maxfd, /* highest socket number */ #ifdef USE_WINSOCK /* Winsock select() must not be called with an fd_set that contains zero - fd flags, or it will return WSAEINVAL. But, it also cannot be called - with no fd_sets at all! From the documentation: + fd flags, or it will return WSAEINVAL. It also cannot be called with + no fd_sets at all! From the documentation: - Any two of the parameters, readfds, writefds, or exceptfds, can be - 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. + Any two of the parameters, readfds, writefds, or exceptfds, can be + given as null. At least one must be non-null, and any non-null + descriptor set must contain at least one handle to a socket. - It is unclear why Winsock does not just handle this for us instead of - calling this an error. Luckily, with Winsock, we can _also_ ask how - many bits are set on an fd_set. So, let's just check it beforehand. - */ + 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. Therefore, let's check it beforehand. + */ return select((int)maxfd + 1, fds_read && fds_read->fd_count ? fds_read : NULL, fds_write && fds_write->fd_count ? fds_write : NULL, @@ -138,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); } @@ -150,19 +140,19 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ num = 0; if(readfd0 != CURL_SOCKET_BAD) { pfd[num].fd = readfd0; - pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI; + pfd[num].events = POLLRDNORM | POLLIN | POLLRDBAND | POLLPRI; pfd[num].revents = 0; num++; } if(readfd1 != CURL_SOCKET_BAD) { pfd[num].fd = readfd1; - pfd[num].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI; + pfd[num].events = POLLRDNORM | POLLIN | POLLRDBAND | POLLPRI; pfd[num].revents = 0; num++; } if(writefd != CURL_SOCKET_BAD) { pfd[num].fd = writefd; - pfd[num].events = POLLWRNORM|POLLOUT|POLLPRI; + pfd[num].events = POLLWRNORM | POLLOUT | POLLPRI; pfd[num].revents = 0; num++; } @@ -174,23 +164,23 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ r = 0; num = 0; if(readfd0 != CURL_SOCKET_BAD) { - if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP)) + if(pfd[num].revents & (POLLRDNORM | POLLIN | POLLERR | POLLHUP)) r |= CURL_CSELECT_IN; - if(pfd[num].revents & (POLLPRI|POLLNVAL)) + if(pfd[num].revents & (POLLPRI | POLLNVAL)) r |= CURL_CSELECT_ERR; num++; } if(readfd1 != CURL_SOCKET_BAD) { - if(pfd[num].revents & (POLLRDNORM|POLLIN|POLLERR|POLLHUP)) + if(pfd[num].revents & (POLLRDNORM | POLLIN | POLLERR | POLLHUP)) r |= CURL_CSELECT_IN2; - if(pfd[num].revents & (POLLPRI|POLLNVAL)) + if(pfd[num].revents & (POLLPRI | POLLNVAL)) r |= CURL_CSELECT_ERR; num++; } if(writefd != CURL_SOCKET_BAD) { - if(pfd[num].revents & (POLLWRNORM|POLLOUT)) + if(pfd[num].revents & (POLLWRNORM | POLLOUT)) r |= CURL_CSELECT_OUT; - if(pfd[num].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) + if(pfd[num].revents & (POLLERR | POLLHUP | POLLPRI | POLLNVAL)) r |= CURL_CSELECT_ERR; } @@ -233,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); } @@ -269,10 +259,10 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms) if(ufds[i].revents & POLLHUP) ufds[i].revents |= POLLIN; if(ufds[i].revents & POLLERR) - ufds[i].revents |= POLLIN|POLLOUT; + ufds[i].revents |= POLLIN | POLLOUT; } -#else /* HAVE_POLL */ +#else /* !HAVE_POLL */ FD_ZERO(&fds_read); FD_ZERO(&fds_write); @@ -284,15 +274,15 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms) if(ufds[i].fd == CURL_SOCKET_BAD) continue; VERIFY_SOCK(ufds[i].fd); - if(ufds[i].events & (POLLIN|POLLOUT|POLLPRI| - POLLRDNORM|POLLWRNORM|POLLRDBAND)) { + if(ufds[i].events & (POLLIN | POLLOUT | POLLPRI | + POLLRDNORM | POLLWRNORM | POLLRDBAND)) { if(ufds[i].fd > maxfd) maxfd = ufds[i].fd; - if(ufds[i].events & (POLLRDNORM|POLLIN)) + if(ufds[i].events & (POLLRDNORM | POLLIN)) FD_SET(ufds[i].fd, &fds_read); - if(ufds[i].events & (POLLWRNORM|POLLOUT)) + if(ufds[i].events & (POLLWRNORM | POLLOUT)) FD_SET(ufds[i].fd, &fds_write); - if(ufds[i].events & (POLLRDBAND|POLLPRI)) + if(ufds[i].events & (POLLRDBAND | POLLPRI)) FD_SET(ufds[i].fd, &fds_err); } } @@ -338,7 +328,7 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms) r++; } -#endif /* HAVE_POLL */ +#endif /* HAVE_POLL */ return r; } @@ -364,7 +354,7 @@ void Curl_pollfds_cleanup(struct curl_pollfds *cpfds) { DEBUGASSERT(cpfds); if(cpfds->allocated_pfds) { - free(cpfds->pfds); + curlx_free(cpfds->pfds); } memset(cpfds, 0, sizeof(*cpfds)); } @@ -374,13 +364,13 @@ static CURLcode cpfds_increase(struct curl_pollfds *cpfds, unsigned int inc) struct pollfd *new_fds; unsigned int new_count = cpfds->count + inc; - new_fds = calloc(new_count, sizeof(struct pollfd)); + new_fds = curlx_calloc(new_count, sizeof(struct pollfd)); if(!new_fds) return CURLE_OUT_OF_MEMORY; memcpy(new_fds, cpfds->pfds, cpfds->count * sizeof(struct pollfd)); if(cpfds->allocated_pfds) - free(cpfds->pfds); + curlx_free(cpfds->pfds); cpfds->pfds = new_fds; cpfds->count = new_count; cpfds->allocated_pfds = TRUE; @@ -521,7 +511,7 @@ void Curl_pollset_init(struct easy_pollset *ps) struct easy_pollset *Curl_pollset_create(void) { - struct easy_pollset *ps = calloc(1, sizeof(*ps)); + struct easy_pollset *ps = curlx_calloc(1, sizeof(*ps)); if(ps) Curl_pollset_init(ps); return ps; @@ -533,11 +523,11 @@ void Curl_pollset_cleanup(struct easy_pollset *ps) DEBUGASSERT(ps->init == CURL_EASY_POLLSET_MAGIC); #endif if(ps->sockets != ps->def_sockets) { - free(ps->sockets); + curlx_free(ps->sockets); ps->sockets = ps->def_sockets; } if(ps->actions != ps->def_actions) { - free(ps->actions); + curlx_free(ps->actions); ps->actions = ps->def_actions; } ps->count = CURL_ARRAYSIZE(ps->def_sockets); @@ -578,14 +568,13 @@ 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; - DEBUGASSERT(add_flags <= (CURL_POLL_IN|CURL_POLL_OUT)); - DEBUGASSERT(remove_flags <= (CURL_POLL_IN|CURL_POLL_OUT)); - DEBUGASSERT((add_flags&remove_flags) == 0); /* no overlap */ + DEBUGASSERT(add_flags <= (CURL_POLL_IN | CURL_POLL_OUT)); + DEBUGASSERT(remove_flags <= (CURL_POLL_IN | CURL_POLL_OUT)); + DEBUGASSERT((add_flags & remove_flags) == 0); /* no overlap */ for(i = 0; i < ps->n; ++i) { if(ps->sockets[i] == sock) { ps->actions[i] &= (unsigned char)(~remove_flags); @@ -614,21 +603,21 @@ CURLcode Curl_pollset_change(struct Curl_easy *data, ps->count, new_count); if(new_count <= ps->count) return CURLE_OUT_OF_MEMORY; - nsockets = calloc(new_count, sizeof(nsockets[0])); + nsockets = curlx_calloc(new_count, sizeof(nsockets[0])); if(!nsockets) return CURLE_OUT_OF_MEMORY; - nactions = calloc(new_count, sizeof(nactions[0])); + nactions = curlx_calloc(new_count, sizeof(nactions[0])); if(!nactions) { - free(nsockets); + curlx_free(nsockets); return CURLE_OUT_OF_MEMORY; } memcpy(nsockets, ps->sockets, ps->count * sizeof(ps->sockets[0])); memcpy(nactions, ps->actions, ps->count * sizeof(ps->actions[0])); if(ps->sockets != ps->def_sockets) - free(ps->sockets); + curlx_free(ps->sockets); ps->sockets = nsockets; if(ps->actions != ps->def_actions) - free(ps->actions); + curlx_free(ps->actions); ps->actions = nactions; ps->count = new_count; } @@ -647,19 +636,25 @@ CURLcode Curl_pollset_set(struct Curl_easy *data, bool do_in, bool do_out) { return Curl_pollset_change(data, ps, sock, - (do_in ? CURL_POLL_IN : 0)| + (do_in ? CURL_POLL_IN : 0) | (do_out ? CURL_POLL_OUT : 0), - (!do_in ? CURL_POLL_IN : 0)| + (!do_in ? CURL_POLL_IN : 0) | (!do_out ? CURL_POLL_OUT : 0)); } +/* + * Return values: + * -1 = error + * 0 = timeout + * N = number of structures with non zero revent fields + */ int Curl_pollset_poll(struct Curl_easy *data, struct easy_pollset *ps, timediff_t timeout_ms) { struct pollfd *pfds; unsigned int i, npfds; - int result; + int rc; (void)data; DEBUGASSERT(data); @@ -668,7 +663,7 @@ int Curl_pollset_poll(struct Curl_easy *data, if(!ps->n) return curlx_wait_ms(timeout_ms); - pfds = calloc(ps->n, sizeof(*pfds)); + pfds = curlx_calloc(ps->n, sizeof(*pfds)); if(!pfds) return -1; @@ -688,9 +683,9 @@ int Curl_pollset_poll(struct Curl_easy *data, } } - result = Curl_poll(pfds, npfds, timeout_ms); - free(pfds); - return result; + rc = Curl_poll(pfds, npfds, timeout_ms); + curlx_free(pfds); + return rc; } void Curl_pollset_check(struct Curl_easy *data, @@ -711,7 +706,7 @@ void Curl_pollset_check(struct Curl_easy *data, *pwant_read = *pwant_write = FALSE; } -bool Curl_pollset_want_read(struct Curl_easy *data, +bool Curl_pollset_want_recv(struct Curl_easy *data, struct easy_pollset *ps, curl_socket_t sock) { @@ -723,3 +718,16 @@ bool Curl_pollset_want_read(struct Curl_easy *data, } return FALSE; } + +bool Curl_pollset_want_send(struct Curl_easy *data, + struct easy_pollset *ps, + curl_socket_t sock) +{ + unsigned int i; + (void)data; + for(i = 0; i < ps->n; ++i) { + if((ps->sockets[i] == sock) && (ps->actions[i] & CURL_POLL_OUT)) + return TRUE; + } + return FALSE; +} diff --git a/lib/select.h b/lib/select.h index 47cdd31267..87b695463f 100644 --- a/lib/select.h +++ b/lib/select.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_POLL_H @@ -36,9 +35,7 @@ * Definition of pollfd struct and constants for platforms lacking them. */ -#if !defined(HAVE_SYS_POLL_H) && \ - !defined(HAVE_POLL_H) && \ - !defined(POLLIN) +#if !defined(HAVE_SYS_POLL_H) && !defined(HAVE_POLL_H) && !defined(POLLIN) #define POLLIN 0x01 #define POLLPRI 0x02 @@ -47,11 +44,10 @@ #define POLLHUP 0x10 #define POLLNVAL 0x20 -struct pollfd -{ - curl_socket_t fd; - short events; - short revents; +struct pollfd { + curl_socket_t fd; + short events; + short revents; }; #endif @@ -73,49 +69,51 @@ 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) \ +#define SOCKET_READABLE(x, z) \ Curl_socket_check(x, CURL_SOCKET_BAD, CURL_SOCKET_BAD, z) -#define SOCKET_WRITABLE(x,z) \ +#define SOCKET_WRITABLE(x, z) \ Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, x, z) int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms); /* With Winsock the valid range is [0..INVALID_SOCKET-1] according to - https://docs.microsoft.com/en-us/windows/win32/winsock/socket-data-type-2 + https://learn.microsoft.com/windows/win32/winsock/socket-data-type-2 */ #ifdef USE_WINSOCK #define VALID_SOCK(s) ((s) < INVALID_SOCKET) #define FDSET_SOCK(x) 1 -#define VERIFY_SOCK(x) do { \ - if(!VALID_SOCK(x)) { \ - SET_SOCKERRNO(SOCKEINVAL); \ - return -1; \ - } \ -} while(0) +#define VERIFY_SOCK(x) \ + do { \ + if(!VALID_SOCK(x)) { \ + SET_SOCKERRNO(SOCKEINVAL); \ + return -1; \ + } \ + } while(0) #else #define VALID_SOCK(s) ((s) >= 0) /* If the socket is small enough to get set or read from an fdset */ #define FDSET_SOCK(s) ((s) < FD_SETSIZE) -#define VERIFY_SOCK(x) do { \ - if(!VALID_SOCK(x) || !FDSET_SOCK(x)) { \ - SET_SOCKERRNO(SOCKEINVAL); \ - return -1; \ - } \ +#define VERIFY_SOCK(x) \ + do { \ + if(!VALID_SOCK(x) || !FDSET_SOCK(x)) { \ + SET_SOCKERRNO(SOCKEINVAL); \ + return -1; \ + } \ } while(0) #endif - /* Keep the sockets to poll for an easy handle. * `actions` are bitmaps of CURL_POLL_IN and CURL_POLL_OUT. * Starts with small capacity, grows on demand. */ -#define EZ_POLLSET_DEF_COUNT 2 +#define EZ_POLLSET_DEF_COUNT 2 struct easy_pollset { curl_socket_t *sockets; @@ -133,7 +131,6 @@ struct easy_pollset { #define CURL_EASY_POLLSET_MAGIC 0x7a657370 #endif - /* allocate and initialise */ struct easy_pollset *Curl_pollset_create(void); @@ -154,25 +151,27 @@ void Curl_pollset_move(struct easy_pollset *to, struct easy_pollset *from); */ CURLcode Curl_pollset_change(struct Curl_easy *data, struct easy_pollset *ps, curl_socket_t sock, - int add_flags, int remove_flags); + int add_flags, + int remove_flags) WARN_UNUSED_RESULT; CURLcode Curl_pollset_set(struct Curl_easy *data, struct easy_pollset *ps, curl_socket_t sock, - bool do_in, bool do_out); + 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) #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) #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, @@ -187,10 +186,12 @@ void Curl_pollset_check(struct Curl_easy *data, struct easy_pollset *ps, curl_socket_t sock, bool *pwant_read, bool *pwant_write); -/** - * Return TRUE if the pollset contains socket with CURL_POLL_IN. - */ -bool Curl_pollset_want_read(struct Curl_easy *data, +/* TRUE if the pollset contains socket with CURL_POLL_IN. */ +bool Curl_pollset_want_recv(struct Curl_easy *data, + struct easy_pollset *ps, + curl_socket_t sock); +/* TRUE if the pollset contains socket with CURL_POLL_OUT. */ +bool Curl_pollset_want_send(struct Curl_easy *data, struct easy_pollset *ps, curl_socket_t sock); diff --git a/lib/sendf.c b/lib/sendf.c index 551bb5ca81..7c977d3b9e 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_NETINET_IN_H @@ -34,67 +33,16 @@ #include #endif -#include - #include "urldata.h" #include "sendf.h" +#include "curl_trc.h" #include "transfer.h" #include "cfilters.h" #include "connect.h" -#include "content_encoding.h" #include "cw-out.h" #include "cw-pause.h" -#include "vtls/vtls.h" -#include "vssh/ssh.h" -#include "easyif.h" #include "multiif.h" -#include "strerror.h" -#include "select.h" -#include "strdup.h" -#include "http2.h" #include "progress.h" -#include "curlx/warnless.h" -#include "ws.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - -static CURLcode 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) { @@ -102,7 +50,7 @@ static void cl_reset_writer(struct Curl_easy *data) while(writer) { data->req.writer_stack = writer->next; writer->cwt->do_close(data, writer); - free(writer); + curlx_free(writer); writer = data->req.writer_stack; } } @@ -110,10 +58,11 @@ static void cl_reset_writer(struct Curl_easy *data) static void cl_reset_reader(struct Curl_easy *data) { struct Curl_creader *reader = data->req.reader_stack; + data->req.reader_started = FALSE; while(reader) { data->req.reader_stack = reader->next; reader->crt->do_close(data, reader); - free(reader); + curlx_free(reader); reader = data->req.reader_stack; } } @@ -167,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) @@ -211,20 +160,7 @@ static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit) { if(limit != -1) { /* How much more are we allowed to write? */ - curl_off_t remain_diff; - remain_diff = limit - data->req.bytecount; - if(remain_diff < 0) { - /* already written too much! */ - return 0; - } -#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T - else if(remain_diff > SSIZE_MAX) { - return SIZE_MAX; - } -#endif - else { - return (size_t)remain_diff; - } + return curlx_sotouz_range(limit - data->req.bytecount, 0, SIZE_MAX); } return SIZE_MAX; } @@ -232,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, @@ -244,7 +182,8 @@ static CURLcode cw_download_write(struct Curl_easy *data, size_t nwrite, excess_len = 0; bool is_connect = !!(type & CLIENTWRITE_CONNECT); - if(!is_connect && !ctx->started_response) { + if(!ctx->started_response && + !(type & (CLIENTWRITE_INFO | CLIENTWRITE_CONNECT))) { Curl_pgrsTime(data, TIMER_STARTTRANSFER); ctx->started_response = TRUE; } @@ -258,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. @@ -316,11 +262,12 @@ static CURLcode cw_download_write(struct Curl_easy *data, if(result) return result; } + /* Update stats, write and report progress */ - data->req.bytecount += nwrite; - result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount); - if(result) - return result; + if(nwrite) { + data->req.bytecount += nwrite; + Curl_pgrs_download_inc(data, nwrite); + } if(excess_len) { if(!data->req.ignorebody) { @@ -375,55 +322,6 @@ static const struct Curl_cwtype cw_raw = { sizeof(struct Curl_cwriter) }; -/* Create an unencoding writer stage using the given handler. */ -CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter, - struct Curl_easy *data, - const struct Curl_cwtype *cwt, - Curl_cwriter_phase phase) -{ - struct Curl_cwriter *writer = NULL; - CURLcode result = CURLE_OUT_OF_MEMORY; - void *p; - - DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter)); - p = calloc(1, cwt->cwriter_size); - if(!p) - goto out; - - writer = (struct Curl_cwriter *)p; - writer->cwt = cwt; - writer->ctx = p; - writer->phase = phase; - result = cwt->do_init(data, writer); - -out: - *pwriter = result ? NULL : writer; - if(result) - free(writer); - return result; -} - -void Curl_cwriter_free(struct Curl_easy *data, - struct Curl_cwriter *writer) -{ - if(writer) { - writer->cwt->do_close(data, writer); - free(writer); - } -} - -size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase) -{ - struct Curl_cwriter *w; - size_t n = 0; - - for(w = data->req.writer_stack; w; w = w->next) { - if(w->phase == phase) - ++n; - } - return n; -} - static CURLcode do_init_writer_stack(struct Curl_easy *data) { struct Curl_cwriter *writer; @@ -469,6 +367,88 @@ static CURLcode do_init_writer_stack(struct Curl_easy *data) 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, + const struct Curl_cwtype *cwt, + Curl_cwriter_phase phase) +{ + struct Curl_cwriter *writer = NULL; + CURLcode result = CURLE_OUT_OF_MEMORY; + void *p; + + DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter)); + p = curlx_calloc(1, cwt->cwriter_size); + if(!p) + goto out; + + writer = (struct Curl_cwriter *)p; + writer->cwt = cwt; + writer->ctx = p; + writer->phase = phase; + result = cwt->do_init(data, writer); + +out: + *pwriter = result ? NULL : writer; + if(result) + curlx_free(writer); + return result; +} + +void Curl_cwriter_free(struct Curl_easy *data, + struct Curl_cwriter *writer) +{ + if(writer) { + writer->cwt->do_close(data, writer); + curlx_free(writer); + } +} + +size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase) +{ + struct Curl_cwriter *w; + size_t n = 0; + + for(w = data->req.writer_stack; w; w = w->next) { + if(w->phase == phase) + ++n; + } + return n; +} + CURLcode Curl_cwriter_add(struct Curl_easy *data, struct Curl_cwriter *writer) { @@ -649,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; @@ -682,11 +661,7 @@ static CURLcode cr_in_read(struct Curl_easy *data, } /* respect length limitations */ if(ctx->total_len >= 0) { - curl_off_t remain = ctx->total_len - ctx->read_len; - if(remain <= 0) - blen = 0; - else if(remain < (curl_off_t)blen) - blen = (size_t)remain; + blen = curlx_sotouz_range(ctx->total_len - ctx->read_len, 0, blen); } nread = 0; if(ctx->read_cb && blen) { @@ -700,7 +675,7 @@ static CURLcode cr_in_read(struct Curl_easy *data, case 0: if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) { failf(data, "client read function EOF fail, " - "only %"FMT_OFF_T"/%"FMT_OFF_T " of needed bytes read", + "only %" FMT_OFF_T "/%" FMT_OFF_T " of needed bytes read", ctx->read_len, ctx->total_len); result = CURLE_READ_ERROR; break; @@ -720,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; @@ -751,11 +726,11 @@ 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 - ", read=%"FMT_OFF_T") -> %d, nread=%zu, eos=%d", + CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%" FMT_OFF_T + ", read=%" FMT_OFF_T ") -> %d, nread=%zu, eos=%d", blen, ctx->total_len, ctx->read_len, result, *pnread, *peos); return result; @@ -766,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, @@ -804,7 +779,7 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data, } /* when seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ do { - char scratch[4*1024]; + char scratch[4 * 1024]; size_t readthisamountnow = (offset - passed > (curl_off_t)sizeof(scratch)) ? sizeof(scratch) : @@ -857,7 +832,7 @@ static CURLcode cr_in_rewind(struct Curl_easy *data, Curl_set_in_callback(data, FALSE); CURL_TRC_READ(data, "cr_in, rewind via set.seek_func -> %d", err); if(err) { - failf(data, "seek callback returned error %d", (int)err); + failf(data, "seek callback returned error %d", err); return CURLE_SEND_FAIL_REWIND; } } @@ -878,7 +853,14 @@ static CURLcode cr_in_rewind(struct Curl_easy *data, /* If no CURLOPT_READFUNCTION is used, we know that we operate on a given FILE * stream and we can actually attempt to rewind that ourselves with fseek() */ +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +#endif if(data->state.fread_func == (curl_read_callback)fread) { +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic pop +#endif int err = fseek(data->state.in, 0, SEEK_SET); CURL_TRC_READ(data, "cr_in, rewind via fseek -> %d(%d)", (int)err, (int)errno); @@ -920,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 = { @@ -947,7 +929,7 @@ CURLcode Curl_creader_create(struct Curl_creader **preader, void *p; DEBUGASSERT(crt->creader_size >= sizeof(struct Curl_creader)); - p = calloc(1, crt->creader_size); + p = curlx_calloc(1, crt->creader_size); if(!p) goto out; @@ -960,7 +942,7 @@ CURLcode Curl_creader_create(struct Curl_creader **preader, out: *preader = result ? NULL : reader; if(result) - free(reader); + curlx_free(reader); return result; } @@ -968,7 +950,7 @@ void Curl_creader_free(struct Curl_easy *data, struct Curl_creader *reader) { if(reader) { reader->crt->do_close(data, reader); - free(reader); + curlx_free(reader); } } @@ -1027,10 +1009,12 @@ static CURLcode cr_lc_read(struct Curl_easy *data, if(!nread || !memchr(buf, '\n', nread)) { /* nothing to convert, return this right away */ + if(nread) + ctx->prev_cr = (buf[nread - 1] == '\r'); if(ctx->read_eos) ctx->eos = TRUE; *pnread = nread; - *peos = ctx->eos; + *peos = (bool)ctx->eos; goto out; } @@ -1102,8 +1086,7 @@ static CURLcode cr_lc_add(struct Curl_easy *data) struct Curl_creader *reader = NULL; CURLcode result; - result = Curl_creader_create(&reader, data, &cr_lc, - CURL_CR_CONTENT_ENCODE); + result = Curl_creader_create(&reader, data, &cr_lc, CURL_CR_CONTENT_ENCODE); if(!result) result = Curl_creader_add(data, reader); @@ -1147,7 +1130,7 @@ CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len) struct cr_in_ctx *ctx; result = Curl_creader_create(&r, data, &cr_in, CURL_CR_CLIENT); - if(result) + if(result || !r) goto out; ctx = r->ctx; ctx->total_len = len; @@ -1155,7 +1138,7 @@ CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len) cl_reset_reader(data); result = do_init_reader_stack(data, r); out: - CURL_TRC_READ(data, "add fread reader, len=%"FMT_OFF_T " -> %d", + CURL_TRC_READ(data, "add fread reader, len=%" FMT_OFF_T " -> %d", len, result); return result; } @@ -1205,6 +1188,7 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, DEBUGASSERT(blen); DEBUGASSERT(nread); DEBUGASSERT(eos); + *nread = 0; if(!data->req.reader_stack) { result = Curl_creader_set_fread(data, data->state.infilesize); @@ -1212,9 +1196,26 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, return result; DEBUGASSERT(data->req.reader_stack); } + if(!data->req.reader_started) { + Curl_rlimit_start(&data->progress.ul.rlimit, Curl_pgrs_now(data), -1); + data->req.reader_started = TRUE; + } + if(Curl_rlimit_active(&data->progress.ul.rlimit)) { + curl_off_t ul_avail = Curl_rlimit_avail(&data->progress.ul.rlimit, + Curl_pgrs_now(data)); + if(ul_avail <= 0) { + result = CURLE_OK; + *eos = FALSE; + goto out; + } + if(ul_avail < (curl_off_t)blen) + blen = (size_t)ul_avail; + } result = Curl_creader_read(data, data->req.reader_stack, buf, blen, nread, eos); + +out: CURL_TRC_READ(data, "client_read(len=%zu) -> %d, nread=%zu, eos=%d", blen, result, *nread, *eos); return result; @@ -1298,7 +1299,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; @@ -1360,9 +1360,9 @@ static CURLcode cr_buf_resume_from(struct Curl_easy *data, /* already started reading? */ if(ctx->index) return CURLE_READ_ERROR; - if(offset <= 0) + boffset = curlx_sotouz_range(offset, 0, SIZE_MAX); + if(!boffset) return CURLE_OK; - boffset = (size_t)offset; if(boffset > ctx->blen) return CURLE_READ_ERROR; @@ -1436,6 +1436,7 @@ CURLcode Curl_creader_unpause(struct Curl_easy *data) while(reader) { result = reader->crt->cntrl(data, reader, CURL_CRCNTRL_UNPAUSE); + CURL_TRC_READ(data, "unpausing %s -> %d", reader->crt->name, result); if(result) break; reader = reader->next; @@ -1473,5 +1474,4 @@ struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data, return r; } return NULL; - } diff --git a/lib/sendf.h b/lib/sendf.h index 6867443901..75c6e248ea 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -23,11 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include "curl_trc.h" - /** * Type of data that is being written to the client (application) * - data written can be either BODY or META data @@ -42,21 +39,26 @@ * BODY, INFO and HEADER should not be mixed, as this would lead to * confusion on how to interpret/format/convert the data. */ -#define CLIENTWRITE_BODY (1<<0) /* non-meta information, BODY */ -#define CLIENTWRITE_INFO (1<<1) /* meta information, not a HEADER */ -#define CLIENTWRITE_HEADER (1<<2) /* meta information, HEADER */ -#define CLIENTWRITE_STATUS (1<<3) /* a special status HEADER */ -#define CLIENTWRITE_CONNECT (1<<4) /* a CONNECT related HEADER */ -#define CLIENTWRITE_1XX (1<<5) /* a 1xx response related HEADER */ -#define CLIENTWRITE_TRAILER (1<<6) /* a trailer HEADER */ -#define CLIENTWRITE_EOS (1<<7) /* End Of transfer download Stream */ -#define CLIENTWRITE_0LEN (1<<8) /* write even 0-length buffers */ +#define CLIENTWRITE_BODY (1 << 0) /* non-meta information, BODY */ +#define CLIENTWRITE_INFO (1 << 1) /* meta information, not a HEADER */ +#define CLIENTWRITE_HEADER (1 << 2) /* meta information, HEADER */ +#define CLIENTWRITE_STATUS (1 << 3) /* a special status HEADER */ +#define CLIENTWRITE_CONNECT (1 << 4) /* a CONNECT related HEADER */ +#define CLIENTWRITE_1XX (1 << 5) /* a 1xx response related HEADER */ +#define CLIENTWRITE_TRAILER (1 << 6) /* a trailer HEADER */ +#define CLIENTWRITE_EOS (1 << 7) /* End Of transfer download Stream */ +#define CLIENTWRITE_0LEN (1 << 8) /* write even 0-length buffers */ + +/* Forward declarations */ +struct Curl_creader; +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; /** @@ -138,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); /** @@ -202,7 +204,6 @@ CURLcode Curl_cwriter_def_write(struct Curl_easy *data, void Curl_cwriter_def_close(struct Curl_easy *data, struct Curl_cwriter *writer); - typedef enum { CURL_CRCNTRL_REWIND, CURL_CRCNTRL_UNPAUSE, @@ -297,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); /** @@ -403,7 +404,6 @@ void Curl_creader_done(struct Curl_easy *data, int premature); struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data, const struct Curl_crtype *crt); - /** * Set the client reader to provide 0 bytes, immediate EOS. */ diff --git a/lib/setopt.c b/lib/setopt.c index 5adfe4dbeb..dae4218b70 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -21,11 +21,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #ifdef HAVE_NETINET_IN_H #include #endif @@ -41,31 +38,24 @@ #include "progress.h" #include "content_encoding.h" #include "strcase.h" -#include "share.h" +#include "curl_share.h" #include "vtls/vtls.h" -#include "curlx/warnless.h" -#include "sendf.h" +#include "curl_trc.h" #include "hostip.h" -#include "http2.h" #include "setopt.h" -#include "multiif.h" #include "altsvc.h" #include "hsts.h" #include "tftp.h" -#include "strdup.h" +#include "curlx/strdup.h" #include "escape.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "bufref.h" static CURLcode setopt_set_timeout_sec(timediff_t *ptimeout_ms, long secs) { if(secs < 0) return CURLE_BAD_FUNCTION_ARGUMENT; -#if LONG_MAX > (TIMEDIFF_T_MAX/1000) - if(secs > (TIMEDIFF_T_MAX/1000)) { +#if LONG_MAX > (TIMEDIFF_T_MAX / 1000) + if(secs > (TIMEDIFF_T_MAX / 1000)) { *ptimeout_ms = TIMEDIFF_T_MAX; return CURLE_OK; } @@ -93,13 +83,13 @@ CURLcode Curl_setstropt(char **charp, const char *s) /* Release the previous storage at `charp' and replace by a dynamic storage copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */ - Curl_safefree(*charp); + curlx_safefree(*charp); if(s) { if(strlen(s) > CURL_MAX_INPUT_LENGTH) return CURLE_BAD_FUNCTION_ARGUMENT; - *charp = strdup(s); + *charp = curlx_strdup(s); if(!*charp) return CURLE_OUT_OF_MEMORY; } @@ -113,15 +103,15 @@ CURLcode Curl_setblobopt(struct curl_blob **blobp, /* free the previous storage at `blobp' and replace by a dynamic storage copy of blob. If CURL_BLOB_COPY is set, the data is copied. */ - Curl_safefree(*blobp); + curlx_safefree(*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 *) - malloc(sizeof(struct curl_blob) + - ((blob->flags & CURL_BLOB_COPY) ? blob->len : 0)); + curlx_malloc(sizeof(struct curl_blob) + + ((blob->flags & CURL_BLOB_COPY) ? blob->len : 0)); if(!nblob) return CURLE_OUT_OF_MEMORY; *nblob = *blob; @@ -138,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; @@ -146,8 +137,8 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) DEBUGASSERT(userp); DEBUGASSERT(passwdp); - /* Parse the login details if specified. It not then we treat NULL as a hint - to clear the existing data */ + /* Parse the login details if specified. If not, then we treat NULL as a + hint to clear the existing data */ if(option) { size_t len = strlen(option); CURLcode result; @@ -159,10 +150,10 @@ static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp) return result; } - free(*userp); + curlx_free(*userp); *userp = user; - free(*passwdp); + curlx_free(*passwdp); *passwdp = passwd; return CURLE_OK; @@ -186,20 +177,22 @@ static CURLcode setstropt_interface(char *option, char **devp, if(result) return result; } - free(*devp); + curlx_free(*devp); *devp = dev; - free(*ifacep); + curlx_free(*ifacep); *ifacep = iface; - free(*hostp); + curlx_free(*hostp); *hostp = host; 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) { @@ -213,7 +206,7 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val) return CURLE_BAD_FUNCTION_ARGUMENT; if(curl_strequal(str, "all")) { - *val = ~(curl_prot_t) 0; + *val = ~(curl_prot_t)0; return CURLE_OK; } @@ -222,11 +215,11 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val) size_t tlen; str = strchr(str, ','); - tlen = str ? (size_t) (str - token) : strlen(token); + 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; @@ -261,7 +254,7 @@ static CURLcode httpauth(struct Curl_easy *data, bool proxy, /* switch off bits we cannot support */ #ifndef USE_NTLM - auth &= ~CURLAUTH_NTLM; /* no NTLM support */ + auth &= ~CURLAUTH_NTLM; /* no NTLM support */ #endif #ifndef USE_SPNEGO auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without GSS-API @@ -279,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 */ @@ -323,7 +316,7 @@ static CURLcode setopt_HTTP_VERSION(struct Curl_easy *data, long arg) data->set.httpwant = (unsigned char)arg; return CURLE_OK; } -#endif /* ! CURL_DISABLE_HTTP */ +#endif /* !CURL_DISABLE_HTTP */ #ifdef USE_SSL CURLcode Curl_setopt_SSLVERSION(struct Curl_easy *data, CURLoption option, @@ -360,57 +353,47 @@ CURLcode Curl_setopt_SSLVERSION(struct Curl_easy *data, CURLoption option, } return CURLE_OK; } -#endif /* ! USE_SSL */ +#endif /* !USE_SSL */ #ifndef CURL_DISABLE_RTSP 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; @@ -421,7 +404,7 @@ static CURLcode setopt_RTSP_REQUEST(struct Curl_easy *data, long arg) data->set.rtspreq = rtspreq; return CURLE_OK; } -#endif /* ! CURL_DISABLE_RTSP */ +#endif /* !CURL_DISABLE_RTSP */ #ifdef USE_SSL static void set_ssl_options(struct ssl_config_data *ssl, @@ -439,10 +422,11 @@ 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; struct UserDefined *s = &data->set; switch(option) { case CURLOPT_FORBID_REUSE: @@ -504,7 +488,7 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, case CURLOPT_UPLOAD: case CURLOPT_PUT: /* - * We want to sent data to the remote host. If this is HTTP, that equals + * We want to send data to the remote host. If this is HTTP, that equals * using the PUT request. */ if(enabled) { @@ -528,7 +512,7 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, case CURLOPT_HTTP09_ALLOWED: s->http09_allowed = enabled; break; -#if !defined(CURL_DISABLE_COOKIES) +#ifndef CURL_DISABLE_COOKIES case CURLOPT_COOKIESESSION: /* * Set this option to TRUE to start a new "cookie session". It will @@ -545,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; @@ -556,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 @@ -591,7 +571,7 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, else s->method = HTTPREQ_GET; break; -#endif /* ! CURL_DISABLE_HTTP */ +#endif /* !CURL_DISABLE_HTTP */ #ifndef CURL_DISABLE_PROXY case CURLOPT_HTTPPROXYTUNNEL: /* @@ -619,7 +599,7 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, * Enable verification of the hostname in the peer certificate for proxy */ s->proxy_ssl.primary.verifyhost = enabled; - + ok = 2; /* Update the current connection proxy_ssl_config. */ Curl_ssl_conn_config_update(data, TRUE); break; @@ -629,11 +609,11 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, */ s->proxy_transfer_mode = enabled; break; -#endif /* ! CURL_DISABLE_PROXY */ +#endif /* !CURL_DISABLE_PROXY */ #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) case CURLOPT_SOCKS5_GSSAPI_NEC: /* - * Set flag for NEC SOCK5 support + * Set flag for NEC SOCKS5 support */ s->socks5_gssapi_nec = enabled; break; @@ -658,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; @@ -683,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: /* @@ -692,7 +669,7 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, */ s->tftp_no_options = enabled; break; -#endif /* ! CURL_DISABLE_TFTP */ +#endif /* !CURL_DISABLE_TFTP */ case CURLOPT_TRANSFERTEXT: /* * This option was previously named 'FTPASCII'. Renamed to work with @@ -723,6 +700,7 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, * Enable verification of the hostname in the peer certificate for DoH */ s->doh_verifyhost = enabled; + ok = 2; break; case CURLOPT_DOH_SSL_VERIFYSTATUS: /* @@ -732,8 +710,9 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, return CURLE_NOT_BUILT_IN; s->doh_verifystatus = enabled; + ok = 2; break; -#endif /* ! CURL_DISABLE_DOH */ +#endif /* !CURL_DISABLE_DOH */ case CURLOPT_SSL_VERIFYHOST: /* * Enable verification of the hostname in the peer certificate @@ -743,6 +722,7 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, this argument took a boolean when it was not and misused it. Treat 1 and 2 the same */ s->ssl.primary.verifyhost = enabled; + ok = 2; /* Update the current connection ssl_config. */ Curl_ssl_conn_config_update(data, FALSE); @@ -781,22 +761,20 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, */ s->tcp_nodelay = enabled; break; - case CURLOPT_IGNORE_CONTENT_LENGTH: s->ignorecl = enabled; break; case CURLOPT_SSL_SESSIONID_CACHE: s->ssl.primary.cache_session = enabled; #ifndef CURL_DISABLE_PROXY - s->proxy_ssl.primary.cache_session = - s->ssl.primary.cache_session; + s->proxy_ssl.primary.cache_session = s->ssl.primary.cache_session; #endif break; #ifdef USE_SSH case CURLOPT_SSH_COMPRESSION: s->ssh_compression = enabled; break; -#endif /* ! USE_SSH */ +#endif /* !USE_SSH */ #ifndef CURL_DISABLE_SMTP case CURLOPT_MAIL_RCPT_ALLOWFAILS: /* allow RCPT TO command to fail for some recipients */ @@ -811,13 +789,13 @@ static CURLcode setopt_bool(struct Curl_easy *data, CURLoption option, s->tcp_keepalive = enabled; break; case CURLOPT_TCP_FASTOPEN: -#if defined(CONNECT_DATA_IDEMPOTENT) || defined(MSG_FASTOPEN) || \ +#if defined(CONNECT_DATA_IDEMPOTENT) || defined(MSG_FASTOPEN) || \ defined(TCP_FASTOPEN_CONNECT) s->tcp_fastopen = enabled; + break; #else return CURLE_NOT_BUILT_IN; #endif - break; case CURLOPT_SSL_ENABLE_ALPN: s->ssl_enable_alpn = enabled; break; @@ -842,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 > 1) || (arg < 0)) + 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; } @@ -864,194 +841,37 @@ 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) { - unsigned long uarg = (unsigned long)arg; - 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: - return setopt_set_timeout_sec(&s->dns_cache_timeout_ms, arg); - - 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; + if(arg != -1) + return setopt_set_timeout_sec(&s->dns_cache_timeout_ms, arg); + s->dns_cache_timeout_ms = -1; 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, 0, 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 > 65535)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->proxyport = (unsigned short)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)) @@ -1060,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)) @@ -1088,159 +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; + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->max_filesize = arg; break; + case CURLOPT_IPRESOLVE: + if((arg < CURL_IPRESOLVE_WHATEVER) || (arg > CURL_IPRESOLVE_V6)) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else + s->ipver = (unsigned char)arg; + break; + case CURLOPT_CONNECT_ONLY: + if(arg < 0 || arg > 2) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else { + s->connect_only = !!arg; + s->connect_only_ws = (arg == 2); + } + break; +#ifdef USE_IPV6 + case CURLOPT_ADDRESS_SCOPE: +#if SIZEOF_LONG > 4 + if((unsigned long)arg > UINT_MAX) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else +#endif + s->scope_id = (unsigned int)arg; + break; +#endif + case CURLOPT_TCP_KEEPIDLE: + result = value_range(&arg, 0, 0, INT_MAX); + if(!result) + s->tcp_keepidle = (int)arg; + break; + case CURLOPT_TCP_KEEPINTVL: + result = value_range(&arg, 0, 0, INT_MAX); + if(!result) + s->tcp_keepintvl = (int)arg; + break; + case CURLOPT_TCP_KEEPCNT: + result = value_range(&arg, 0, 0, INT_MAX); + if(!result) + s->tcp_keepcnt = (int)arg; + break; + 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; +} +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_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_USE_SSL: if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->use_ssl = (unsigned char)arg; + 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 - -#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; - 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; - break; -#endif -#ifdef USE_IPV6 - case CURLOPT_ADDRESS_SCOPE: -#if SIZEOF_LONG > 4 - if(uarg > UINT_MAX) - return CURLE_BAD_FUNCTION_ARGUMENT; -#endif - s->scope_id = (unsigned int)uarg; - 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_RTSP - case CURLOPT_RTSP_REQUEST: - return setopt_RTSP_REQUEST(data, arg); - case CURLOPT_RTSP_CLIENT_CSEQ: - data->state.rtsp_next_client_CSeq = arg; - break; - - case CURLOPT_RTSP_SERVER_CSEQ: - data->state.rtsp_next_server_CSeq = arg; - break; - -#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; - break; - case CURLOPT_TCP_KEEPINTVL: - result = value_range(&arg, 0, 0, INT_MAX); - if(result) - return result; - s->tcp_keepintvl = (int)arg; - break; - case CURLOPT_TCP_KEEPCNT: - result = value_range(&arg, 0, 0, INT_MAX); - if(result) - return result; - s->tcp_keepcnt = (int)arg; - break; case CURLOPT_SSL_ENABLE_NPN: break; + case CURLOPT_SSLENGINE_DEFAULT: + curlx_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; -#endif - case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: - return setopt_set_timeout_ms(&s->happy_eyeballs_timeout, arg); - - case CURLOPT_UPKEEP_INTERVAL_MS: - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - s->upkeep_interval_ms = arg; + result = CURLE_NOT_BUILT_IN; break; - case CURLOPT_MAXAGE_CONN: - return setopt_set_timeout_sec(&s->conn_max_idle_ms, arg); +#endif + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +#else + (void)data; + (void)option; + (void)arg; + return CURLE_UNKNOWN_OPTION; +#endif +} - case CURLOPT_MAXLIFETIME_CONN: - return setopt_set_timeout_sec(&s->conn_max_age_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; +#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; +} + +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]) { + curlx_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); + return Curl_altsvc_ctrl(data, arg); +#endif +#ifdef HAVE_GSSAPI + case CURLOPT_GSSAPI_DELEGATION: + s->gssapi_delegation = (unsigned char)arg & + (CURLGSSAPI_DELEGATION_POLICY_FLAG | CURLGSSAPI_DELEGATION_FLAG); break; #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; - break; 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); + curlx_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) @@ -1392,6 +1455,14 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, CURLcode result = CURLE_OK; struct UserDefined *s = &data->set; switch(option) { + case CURLOPT_CURLU: + /* + * pass CURLU to set URL + */ + Curl_bufref_free(&data->state.url); + curlx_safefree(s->str[STRING_SET_URL]); + s->uh = va_arg(param, CURLU *); + break; #ifndef CURL_DISABLE_HTTP #ifndef CURL_DISABLE_FORM_API case CURLOPT_HTTPPOST: @@ -1402,33 +1473,19 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, s->method = HTTPREQ_POST_FORM; s->opt_no_body = FALSE; /* this is implied */ Curl_mime_cleanpart(data->state.formp); - Curl_safefree(data->state.formp); + curlx_safefree(data->state.formp); data->state.mimepost = NULL; break; -#endif /* ! CURL_DISABLE_FORM_API */ -#endif /* ! CURL_DISABLE_HTTP */ +#endif /* !CURL_DISABLE_FORM_API */ +#endif /* !CURL_DISABLE_HTTP */ #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_IMAP) -# ifndef CURL_DISABLE_MIME + !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 - } + result = setopt_mimepost(data, va_arg(param, curl_mime *)); break; -#endif /* ! CURL_DISABLE_MIME */ -#endif /* ! disabled HTTP, SMTP or IMAP */ +#endif /* !CURL_DISABLE_MIME */ +#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */ case CURLOPT_STDERR: /* * Set to a FILE * that should receive all error writes. This @@ -1438,73 +1495,22 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, if(!s->err) s->err = stderr; break; - case CURLOPT_SHARE: - { + case CURLOPT_SHARE: { struct Curl_share *set = va_arg(param, struct Curl_share *); - /* disconnect from old share, if any */ - if(data->share) { - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + /* disconnect from old share, if any and possible */ + result = Curl_share_easy_unlink(data); + if(result) + return result; -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies == data->cookies) - data->cookies = NULL; -#endif - -#ifndef CURL_DISABLE_HSTS - if(data->share->hsts == data->hsts) - data->hsts = NULL; -#endif -#ifdef USE_LIBPSL - if(data->psl == &data->share->psl) - data->psl = data->multi ? &data->multi->psl : NULL; -#endif - if(data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) { - Curl_resolv_unlink(data, &data->state.dns[0]); - Curl_resolv_unlink(data, &data->state.dns[1]); - } - - data->share->dirty--; - - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - data->share = NULL; + /* use new share if it set */ + if(GOOD_SHARE_HANDLE(set)) { + result = Curl_share_easy_link(data, set); + if(result) + return result; } - - if(GOOD_SHARE_HANDLE(set)) - /* use new share if it set */ - data->share = set; - if(data->share) { - - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - - data->share->dirty++; - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies) { - /* use shared cookie list, first free own one if any */ - Curl_cookie_cleanup(data->cookies); - /* enable cookies since we now use a share that uses cookies! */ - data->cookies = data->share->cookies; - } -#endif /* CURL_DISABLE_HTTP */ -#ifndef CURL_DISABLE_HSTS - if(data->share->hsts) { - /* first free the private one if any */ - Curl_hsts_cleanup(&data->hsts); - data->hsts = data->share->hsts; - } -#endif -#ifdef USE_LIBPSL - if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL)) - data->psl = &data->share->psl; -#endif - - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - } - /* check for host cache not needed, - * it will be done by curl_easy_perform */ + break; } - break; #ifdef USE_HTTP2 case CURLOPT_STREAM_DEPENDS: @@ -1524,9 +1530,9 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, } #ifndef CURL_DISABLE_COOKIES -static CURLcode cookielist(struct Curl_easy *data, - const char *ptr) +static CURLcode cookielist(struct Curl_easy *data, const char *ptr) { + CURLcode result = CURLE_OK; if(!ptr) return CURLE_OK; @@ -1548,14 +1554,15 @@ static CURLcode cookielist(struct Curl_easy *data, } else if(curl_strequal(ptr, "RELOAD")) { /* reload cookies from file */ - Curl_cookie_loadfiles(data); + return Curl_cookie_loadfiles(data); } else { if(!data->cookies) { /* if cookie engine was not running, activate it */ - data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE); + data->cookies = Curl_cookie_init(); if(!data->cookies) return CURLE_OUT_OF_MEMORY; + data->state.cookie_engine = TRUE; } /* general protection against mistakes and abuse */ @@ -1565,19 +1572,18 @@ static CURLcode cookielist(struct Curl_easy *data, Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); if(checkprefix("Set-Cookie:", ptr)) /* HTTP Header format line */ - Curl_cookie_add(data, data->cookies, TRUE, FALSE, ptr + 11, NULL, - NULL, TRUE); + result = Curl_cookie_add(data, data->cookies, TRUE, FALSE, ptr + 11, + NULL, NULL, TRUE); else /* Netscape format line */ - Curl_cookie_add(data, data->cookies, FALSE, FALSE, ptr, NULL, - NULL, TRUE); + result = Curl_cookie_add(data, data->cookies, FALSE, FALSE, ptr, NULL, + NULL, TRUE); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } - return CURLE_OK; + return result; } -static CURLcode cookiefile(struct Curl_easy *data, - const char *ptr) +static CURLcode cookiefile(struct Curl_easy *data, const char *ptr) { /* * Set cookie file to read and parse. Can be used multiple times. @@ -1615,28 +1621,262 @@ static CURLcode cookiefile(struct Curl_easy *data, } #endif -static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, - char *ptr) +#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) { + curlx_safefree(s->str[STRING_PROXYUSERNAME]); + result = Curl_urldecode(u, 0, &s->str[STRING_PROXYUSERNAME], NULL, + REJECT_ZERO); + } + if(!result && p) { + curlx_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_PRE_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_PRE_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 + +#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 || 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 = curlx_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; + return result; +} +#endif + +static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + 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 */ @@ -1644,15 +1884,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: @@ -1669,42 +1900,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 { - if(s->postfieldsize < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; -#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T - /* - * Check that requested length does not overflow the size_t type. - */ - else if(s->postfieldsize > SIZE_MAX) - return CURLE_OUT_OF_MEMORY; -#endif - 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, (size_t)s->postfieldsize); - if(!p) - return CURLE_OUT_OF_MEMORY; - else { - 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: /* @@ -1712,12 +1908,15 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, */ s->postfields = ptr; /* Release old copied data. */ - Curl_safefree(s->str[STRING_COPYPOSTFIELDS]); + curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); s->method = HTTPREQ_POST; break; -#endif /* ! CURL_DISABLE_HTTP || ! CURL_DISABLE_MQTT */ +#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. @@ -1729,9 +1928,14 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, * */ if(ptr && !*ptr) { - char all[256]; - Curl_all_content_encodings(all, sizeof(all)); - return Curl_setstropt(&s->str[STRING_ENCODING], all); + ptr = Curl_get_content_encodings(); + if(ptr) { + curlx_free(s->str[STRING_ENCODING]); + s->str[STRING_ENCODING] = ptr; + } + else + result = CURLE_OUT_OF_MEMORY; + return result; } return Curl_setstropt(&s->str[STRING_ENCODING], ptr); @@ -1753,12 +1957,8 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, /* * String to set in the HTTP Referer: field. */ - if(data->state.referer_alloc) { - Curl_safefree(data->state.referer); - data->state.referer_alloc = FALSE; - } result = Curl_setstropt(&s->str[STRING_SET_REFERER], ptr); - data->state.referer = s->str[STRING_SET_REFERER]; + Curl_bufref_set(&data->state.referer, s->str[STRING_SET_REFERER], 0, NULL); break; case CURLOPT_USERAGENT: @@ -1787,11 +1987,12 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, * Activate the cookie parser. This may or may not already * have been made. */ - struct CookieInfo *newcookies = - Curl_cookie_init(data, NULL, data->cookies, s->cookiesession); - if(!newcookies) + if(!data->cookies) + data->cookies = Curl_cookie_init(); + if(!data->cookies) result = CURLE_OUT_OF_MEMORY; - data->cookies = newcookies; + else + data->state.cookie_engine = TRUE; } break; @@ -1799,7 +2000,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, return cookielist(data, ptr); #endif /* !CURL_DISABLE_COOKIES */ -#endif /* ! CURL_DISABLE_HTTP */ +#endif /* !CURL_DISABLE_HTTP */ case CURLOPT_CUSTOMREQUEST: /* @@ -1807,43 +2008,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 @@ -1931,15 +2098,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 @@ -1963,26 +2124,21 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, case CURLOPT_FTP_ALTERNATIVE_TO_USER: return Curl_setstropt(&s->str[STRING_FTP_ALTERNATIVE_TO_USER], ptr); -#ifdef HAVE_GSSAPI case CURLOPT_KRBLEVEL: - /* - * A string that defines the kerberos security level. - */ - result = Curl_setstropt(&s->str[STRING_KRB_LEVEL], ptr); - s->krb = !!(s->str[STRING_KRB_LEVEL]); + 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 #endif case CURLOPT_URL: /* * The URL to fetch. */ - if(data->state.url_alloc) { - Curl_safefree(data->state.url); - data->state.url_alloc = FALSE; - } result = Curl_setstropt(&s->str[STRING_SET_URL], ptr); - data->state.url = s->str[STRING_SET_URL]; + Curl_bufref_set(&data->state.url, s->str[STRING_SET_URL], 0, NULL); break; case CURLOPT_USERPWD: @@ -2015,139 +2171,36 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, * 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); - } - free(u); - 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); -#endif /* ! CURL_DISABLE_PROXY */ - 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 - */ - if(data->state.url_alloc) { - Curl_safefree(data->state.url); - data->state.url_alloc = FALSE; - } - else - data->state.url = NULL; - Curl_safefree(s->str[STRING_SET_URL]); - s->uh = (CURLU *)ptr; - break; case CURLOPT_SSLCERT: /* * 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. @@ -2159,18 +2212,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 */ - s->haproxyprotocol = TRUE; - break; - -#endif case CURLOPT_INTERFACE: /* * Set what interface or address/hostname to bind the socket to when @@ -2180,125 +2221,44 @@ 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 - */ - 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 - */ - 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. */ - 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. */ - 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. * Specify colon-delimited list of signature scheme names. */ if(Curl_ssl_supports(data, SSLSUPP_SIGNATURE_ALGORITHMS)) - return Curl_setstropt(&s->str[STRING_SSL_SIGNATURE_ALGORITHMS], - ptr); + 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 @@ -2307,13 +2267,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: /* @@ -2321,28 +2285,19 @@ 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: /* * Option to allow for the SHA256 of the host public key to be checked * for validation purposes. */ - return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], - ptr); - + 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 @@ -2352,28 +2307,34 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, #endif /* USE_LIBSSH2 */ #endif /* USE_SSH */ case CURLOPT_PROTOCOLS_STR: - if(ptr) - return protocol2num(ptr, &s->allowed_protocols); - /* make a NULL argument reset to default */ - s->allowed_protocols = (curl_prot_t) CURLPROTO_ALL; + if(ptr) { + curl_prot_t protos; + result = protocol2num(ptr, &protos); + if(!result) + s->allowed_protocols = protos; + } + else + /* make a NULL argument reset to default */ + s->allowed_protocols = (curl_prot_t)CURLPROTO_64ALL; break; - case CURLOPT_REDIR_PROTOCOLS_STR: - if(ptr) - return protocol2num(ptr, &s->redir_protocols); - /* make a NULL argument reset to default */ - s->redir_protocols = (curl_prot_t) CURLPROTO_REDIR; + if(ptr) { + curl_prot_t protos; + result = protocol2num(ptr, &protos); + if(!result) + s->redir_protocols = protos; + } + else + /* 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); @@ -2381,7 +2342,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: /* @@ -2389,83 +2349,53 @@ 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 +#endif /* !CURL_DISABLE_RTSP */ #ifdef USE_TLS_SRP case CURLOPT_TLSAUTH_USERNAME: return Curl_setstropt(&s->str[STRING_TLSAUTH_USERNAME], ptr); - + case CURLOPT_TLSAUTH_PASSWORD: + return Curl_setstropt(&s->str[STRING_TLSAUTH_PASSWORD], ptr); + case CURLOPT_TLSAUTH_TYPE: + if(ptr && !curl_strequal(ptr, "SRP")) + 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); - -#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; - break; -#ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_TYPE: if(ptr && !curl_strequal(ptr, "SRP")) - return CURLE_BAD_FUNCTION_ARGUMENT; + result = CURLE_BAD_FUNCTION_ARGUMENT; break; #endif #endif -#ifdef CURLRES_ARES +#ifdef USE_RESOLV_ARES case CURLOPT_DNS_SERVERS: - result = Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); - if(result) - return result; - return Curl_async_ares_set_dns_servers(data); + return Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); case CURLOPT_DNS_INTERFACE: - result = Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); - if(result) - return result; - return Curl_async_ares_set_dns_interface(data); + return Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); case CURLOPT_DNS_LOCAL_IP4: - result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); - if(result) - return result; - return Curl_async_ares_set_dns_local_ip4(data); + return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); case CURLOPT_DNS_LOCAL_IP6: - result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); - if(result) - return result; - return Curl_async_ares_set_dns_local_ip6(data); + return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); #endif #ifdef USE_UNIX_SOCKETS @@ -2524,7 +2454,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, } break; } -#endif /* ! CURL_DISABLE_HSTS */ +#endif /* !CURL_DISABLE_HSTS */ #ifndef CURL_DISABLE_ALTSVC case CURLOPT_ALTSVC: if(!data->asi) { @@ -2534,18 +2464,18 @@ 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) - (void)Curl_altsvc_load(data->asi, ptr); + return Curl_altsvc_load(data->asi, ptr); break; -#endif /* ! CURL_DISABLE_ALTSVC */ +#endif /* !CURL_DISABLE_ALTSVC */ #ifdef USE_ECH case CURLOPT_ECH: { size_t plen = 0; if(!ptr) { s->tls_ech = CURLECH_DISABLE; - return CURLE_OK; + break; } plen = strlen(ptr); if(plen > CURL_MAX_INPUT_LENGTH) { @@ -2554,28 +2484,20 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, } /* set tls_ech flag value, preserving CLA_CFG bit */ if(!strcmp(ptr, "false")) - s->tls_ech = CURLECH_DISABLE | - (s->tls_ech & CURLECH_CLA_CFG); + s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_DISABLE; else if(!strcmp(ptr, "grease")) - s->tls_ech = CURLECH_GREASE | - (s->tls_ech & CURLECH_CLA_CFG); + s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_GREASE; else if(!strcmp(ptr, "true")) - s->tls_ech = CURLECH_ENABLE | - (s->tls_ech & CURLECH_CLA_CFG); + s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_ENABLE; else if(!strcmp(ptr, "hard")) - s->tls_ech = CURLECH_HARD | - (s->tls_ech & CURLECH_CLA_CFG); + 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 @@ -2633,8 +2555,15 @@ static CURLcode setopt_func(struct Curl_easy *data, CURLoption option, */ s->fwrite_func = va_arg(param, curl_write_callback); if(!s->fwrite_func) +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +#endif /* When set to NULL, reset to our internal default function */ s->fwrite_func = (curl_write_callback)fwrite; +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic pop +#endif break; case CURLOPT_READFUNCTION: /* @@ -2643,8 +2572,15 @@ static CURLcode setopt_func(struct Curl_easy *data, CURLoption option, s->fread_func_set = va_arg(param, curl_read_callback); if(!s->fread_func_set) { s->is_fread_set = 0; +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +#endif /* When set to NULL, reset to our internal default function */ s->fread_func_set = (curl_read_callback)fread; +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic pop +#endif } else s->is_fread_set = 1; @@ -2785,7 +2721,7 @@ static CURLcode setopt_offt(struct Curl_easy *data, CURLoption option, if(s->postfieldsize < offt && s->postfields == s->str[STRING_COPYPOSTFIELDS]) { /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - Curl_safefree(s->str[STRING_COPYPOSTFIELDS]); + curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); s->postfields = NULL; } s->postfieldsize = offt; @@ -2807,6 +2743,8 @@ static CURLcode setopt_offt(struct Curl_easy *data, CURLoption option, if(offt < 0) return CURLE_BAD_FUNCTION_ARGUMENT; s->max_send_speed = offt; + Curl_rlimit_init(&data->progress.ul.rlimit, offt, offt, + Curl_pgrs_now(data)); break; case CURLOPT_MAX_RECV_SPEED_LARGE: /* @@ -2816,6 +2754,8 @@ static CURLcode setopt_offt(struct Curl_easy *data, CURLoption option, if(offt < 0) return CURLE_BAD_FUNCTION_ARGUMENT; s->max_recv_speed = offt; + Curl_rlimit_init(&data->progress.dl.rlimit, offt, offt, + Curl_pgrs_now(data)); break; case CURLOPT_RESUME_FROM_LARGE: /* @@ -2875,8 +2815,7 @@ 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_PROXY], - blob); + return Curl_setblobopt(&s->blobs[BLOB_SSL_ISSUERCERT_PROXY], blob); #endif case CURLOPT_SSLKEY_BLOB: /* @@ -2889,15 +2828,19 @@ static CURLcode setopt_blob(struct Curl_easy *data, CURLoption option, * Specify entire PEM of the CA certificate */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) + if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { + s->ssl.custom_cablob = TRUE; return Curl_setblobopt(&s->blobs[BLOB_CAINFO], blob); + } #endif return CURLE_NOT_BUILT_IN; case CURLOPT_ISSUERCERT_BLOB: /* * 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; @@ -2907,7 +2850,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) { @@ -2934,6 +2877,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) case CURLOPT_SHARE: /* CURLSH * */ case CURLOPT_STREAM_DEPENDS: /* CURL * */ case CURLOPT_STREAM_DEPENDS_E: /* CURL * */ + case CURLOPT_CURLU: /* CURLU * */ return setopt_pointers(data, option, param); default: break; @@ -2957,21 +2901,21 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ #undef curl_easy_setopt -CURLcode curl_easy_setopt(CURL *d, CURLoption tag, ...) +CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...) { va_list arg; CURLcode result; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; - va_start(arg, tag); + va_start(arg, option); - result = Curl_vsetopt(data, tag, arg); + result = Curl_vsetopt(data, option, arg); va_end(arg); if(result == CURLE_BAD_FUNCTION_ARGUMENT) - failf(data, "setopt 0x%x got bad argument", tag); + failf(data, "setopt 0x%x got bad argument", option); return result; } diff --git a/lib/setopt.h b/lib/setopt.h index c323dd74a3..c421f5c5e5 100644 --- a/lib/setopt.h +++ b/lib/setopt.h @@ -28,13 +28,13 @@ CURLcode Curl_setopt_SSLVERSION(struct Curl_easy *data, CURLoption option, long arg); #else -#define Curl_setopt_SSLVERSION(a,b,c) CURLE_NOT_BUILT_IN +#define Curl_setopt_SSLVERSION(a, b, c) CURLE_NOT_BUILT_IN #endif 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/lib/setup-os400.h b/lib/setup-os400.h index ef7baca67e..b5bd733672 100644 --- a/lib/setup-os400.h +++ b/lib/setup-os400.h @@ -24,28 +24,22 @@ * ***************************************************************************/ - /* OS/400 netdb.h does not define NI_MAXHOST. */ #define NI_MAXHOST 1025 /* 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 - /* Workaround bug in IBM QADRT runtime library: * function puts() does not output the implicit trailing newline. */ #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. */ @@ -73,24 +67,22 @@ extern int Curl_getnameinfo_a(const struct sockaddr *sa, /* GSSAPI wrappers. */ -extern OM_uint32 Curl_gss_import_name_a(OM_uint32 * minor_status, +extern OM_uint32 Curl_gss_import_name_a(OM_uint32 *minor_status, gss_buffer_t in_name, gss_OID in_name_type, - gss_name_t * out_name); + gss_name_t *out_name); #define gss_import_name Curl_gss_import_name_a - -extern OM_uint32 Curl_gss_display_status_a(OM_uint32 * minor_status, +extern OM_uint32 Curl_gss_display_status_a(OM_uint32 *minor_status, OM_uint32 status_value, int status_type, gss_OID mech_type, - gss_msg_ctx_t * message_context, + gss_msg_ctx_t *message_context, gss_buffer_t status_string); #define gss_display_status Curl_gss_display_status_a - -extern OM_uint32 Curl_gss_init_sec_context_a(OM_uint32 * minor_status, +extern OM_uint32 Curl_gss_init_sec_context_a(OM_uint32 *minor_status, gss_cred_id_t cred_handle, - gss_ctx_id_t * context_handle, + gss_ctx_id_t *context_handle, gss_name_t target_name, gss_OID mech_type, gss_flags_t req_flags, @@ -98,19 +90,17 @@ extern OM_uint32 Curl_gss_init_sec_context_a(OM_uint32 * minor_status, gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, - gss_OID * actual_mech_type, + gss_OID *actual_mech_type, gss_buffer_t output_token, - gss_flags_t * ret_flags, - OM_uint32 * time_rec); + gss_flags_t *ret_flags, + OM_uint32 *time_rec); #define gss_init_sec_context Curl_gss_init_sec_context_a - -extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 * minor_status, - gss_ctx_id_t * context_handle, +extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, gss_buffer_t output_token); #define gss_delete_sec_context Curl_gss_delete_sec_context_a - /* LDAP wrappers. */ #define BerValue struct berval diff --git a/lib/setup-vms.h b/lib/setup-vms.h index 0fd5e542b7..35d12f0b42 100644 --- a/lib/setup-vms.h +++ b/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 @@ -38,9 +37,9 @@ /* Hide the stuff we are overriding */ #define getenv decc_getenv #ifdef __DECC -# if __INITIAL_POINTER_SIZE != 64 -# define getpwuid decc_getpwuid -# endif +# if __INITIAL_POINTER_SIZE != 64 +# define getpwuid decc_getpwuid +# endif #endif #include char *decc$getenv(const char *__name); @@ -51,34 +50,34 @@ char *decc$getenv(const char *__name); #undef getenv #undef getpwuid -#define getenv vms_getenv +#define getenv vms_getenv #define getpwuid vms_getpwuid /* VAX needs these in upper case when compiling exact case */ #define sys$assign SYS$ASSIGN #define sys$dassgn SYS$DASSGN -#define sys$qiow SYS$QIOW +#define sys$qiow SYS$QIOW #ifdef __DECC -# if __INITIAL_POINTER_SIZE -# pragma __pointer_size __save -# endif +# if __INITIAL_POINTER_SIZE +# pragma __pointer_size __save +# endif #endif #if __USE_LONG_GID_T -# define decc_getpwuid DECC$__LONG_GID_GETPWUID +# define decc_getpwuid DECC$__LONG_GID_GETPWUID #else -# if __INITIAL_POINTER_SIZE -# define decc_getpwuid decc$__32_getpwuid -# else -# define decc_getpwuid decc$getpwuid -# endif +# if __INITIAL_POINTER_SIZE +# define decc_getpwuid decc$__32_getpwuid +# else +# define decc_getpwuid decc$getpwuid +# endif #endif - struct passwd *decc_getpwuid(uid_t uid); +struct passwd *decc_getpwuid(uid_t uid); #ifdef __DECC -# if __INITIAL_POINTER_SIZE == 32 +# if __INITIAL_POINTER_SIZE == 32 /* Translate the path, but only if the path is a VMS file specification */ /* The translation is usually only needed for older versions of VMS */ static char *vms_translate_path(const char *path) @@ -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; @@ -100,18 +99,18 @@ static char *vms_translate_path(const char *path) return (char *)path; } } -# else - /* VMS translate path is actually not needed on the current 64-bit */ - /* VMS platforms, so instead of figuring out the pointer settings */ - /* Change it to a noop */ -# define vms_translate_path(__path) __path -# endif +# else + /* VMS translate path is actually not needed on the current 64-bit */ + /* VMS platforms, so instead of figuring out the pointer settings */ + /* Change it to a noop */ +# define vms_translate_path(__path) __path +# endif #endif #ifdef __DECC -# if __INITIAL_POINTER_SIZE -# pragma __pointer_size __restore -# endif +# if __INITIAL_POINTER_SIZE +# pragma __pointer_size __restore +# endif #endif static char *vms_getenv(const char *envvar) @@ -137,7 +136,6 @@ static char *vms_getenv(const char *envvar) return result; } - static struct passwd vms_passwd_cache; static struct passwd *vms_getpwuid(uid_t uid) @@ -146,11 +144,11 @@ static struct passwd *vms_getpwuid(uid_t uid) /* Hack needed to support 64-bit builds, decc_getpwnam is 32-bit only */ #ifdef __DECC -# if __INITIAL_POINTER_SIZE +# if __INITIAL_POINTER_SIZE __char_ptr32 unix_path; -# else +# else char *unix_path; -# endif +# endif #else char *unix_path; #endif @@ -167,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; } @@ -191,182 +189,178 @@ static struct passwd *vms_getpwuid(uid_t uid) /* Bug - VMS OpenSSL and Kerberos universal symbols are in uppercase only */ /* VMS libraries should have universal symbols in exact and uppercase */ -#define ASN1_INTEGER_get ASN1_INTEGER_GET -#define ASN1_STRING_data ASN1_STRING_DATA -#define ASN1_STRING_length ASN1_STRING_LENGTH -#define ASN1_STRING_print ASN1_STRING_PRINT -#define ASN1_STRING_to_UTF8 ASN1_STRING_TO_UTF8 -#define ASN1_STRING_type ASN1_STRING_TYPE -#define BIO_ctrl BIO_CTRL -#define BIO_free BIO_FREE -#define BIO_new BIO_NEW -#define BIO_s_mem BIO_S_MEM -#define BN_bn2bin BN_BN2BIN -#define BN_num_bits BN_NUM_BITS +#define ASN1_INTEGER_get ASN1_INTEGER_GET +#define ASN1_STRING_data ASN1_STRING_DATA +#define ASN1_STRING_length ASN1_STRING_LENGTH +#define ASN1_STRING_print ASN1_STRING_PRINT +#define ASN1_STRING_to_UTF8 ASN1_STRING_TO_UTF8 +#define ASN1_STRING_type ASN1_STRING_TYPE +#define BIO_ctrl BIO_CTRL +#define BIO_free BIO_FREE +#define BIO_new BIO_NEW +#define BIO_s_mem BIO_S_MEM +#define BN_bn2bin BN_BN2BIN +#define BN_num_bits BN_NUM_BITS #define CRYPTO_cleanup_all_ex_data CRYPTO_CLEANUP_ALL_EX_DATA -#define CRYPTO_free CRYPTO_FREE -#define CRYPTO_malloc CRYPTO_MALLOC -#define CONF_modules_load_file CONF_MODULES_LOAD_FILE +#define CRYPTO_free CRYPTO_FREE +#define CRYPTO_malloc CRYPTO_MALLOC +#define CONF_modules_load_file CONF_MODULES_LOAD_FILE #ifdef __VAX # ifdef VMS_OLD_SSL /* Ancient OpenSSL on VAX/VMS missing this constant */ # define CONF_MFLAGS_IGNORE_MISSING_FILE 0x10 # undef CONF_modules_load_file - static int CONF_modules_load_file(const char *filename, - const char *appname, - unsigned long flags) { - return 1; - } +static int CONF_modules_load_file(const char *filename, + const char *appname, + unsigned long flags) { + return 1; +} # endif #endif -#define DES_ecb_encrypt DES_ECB_ENCRYPT -#define DES_set_key DES_SET_KEY -#define DES_set_odd_parity DES_SET_ODD_PARITY -#define ENGINE_ctrl ENGINE_CTRL -#define ENGINE_ctrl_cmd ENGINE_CTRL_CMD -#define ENGINE_finish ENGINE_FINISH -#define ENGINE_free ENGINE_FREE -#define ENGINE_get_first ENGINE_GET_FIRST -#define ENGINE_get_id ENGINE_GET_ID -#define ENGINE_get_next ENGINE_GET_NEXT -#define ENGINE_init ENGINE_INIT +#define DES_ecb_encrypt DES_ECB_ENCRYPT +#define DES_set_key DES_SET_KEY +#define DES_set_odd_parity DES_SET_ODD_PARITY +#define ENGINE_ctrl ENGINE_CTRL +#define ENGINE_ctrl_cmd ENGINE_CTRL_CMD +#define ENGINE_finish ENGINE_FINISH +#define ENGINE_free ENGINE_FREE +#define ENGINE_get_first ENGINE_GET_FIRST +#define ENGINE_get_id ENGINE_GET_ID +#define ENGINE_get_next ENGINE_GET_NEXT +#define ENGINE_init ENGINE_INIT #define ENGINE_load_builtin_engines ENGINE_LOAD_BUILTIN_ENGINES -#define ENGINE_load_private_key ENGINE_LOAD_PRIVATE_KEY -#define ENGINE_set_default ENGINE_SET_DEFAULT -#define ERR_clear_error ERR_CLEAR_ERROR -#define ERR_error_string ERR_ERROR_STRING -#define ERR_error_string_n ERR_ERROR_STRING_N -#define ERR_free_strings ERR_FREE_STRINGS -#define ERR_get_error ERR_GET_ERROR -#define ERR_peek_error ERR_PEEK_ERROR -#define ERR_remove_state ERR_REMOVE_STATE -#define EVP_PKEY_copy_parameters EVP_PKEY_COPY_PARAMETERS -#define EVP_PKEY_free EVP_PKEY_FREE -#define EVP_cleanup EVP_CLEANUP -#define GENERAL_NAMES_free GENERAL_NAMES_FREE -#define i2d_X509_PUBKEY I2D_X509_PUBKEY -#define MD4_Final MD4_FINAL -#define MD4_Init MD4_INIT -#define MD4_Update MD4_UPDATE -#define MD5_Final MD5_FINAL -#define MD5_Init MD5_INIT -#define MD5_Update MD5_UPDATE +#define ENGINE_load_private_key ENGINE_LOAD_PRIVATE_KEY +#define ENGINE_set_default ENGINE_SET_DEFAULT +#define ERR_clear_error ERR_CLEAR_ERROR +#define ERR_error_string ERR_ERROR_STRING +#define ERR_error_string_n ERR_ERROR_STRING_N +#define ERR_free_strings ERR_FREE_STRINGS +#define ERR_get_error ERR_GET_ERROR +#define ERR_peek_error ERR_PEEK_ERROR +#define ERR_remove_state ERR_REMOVE_STATE +#define EVP_PKEY_copy_parameters EVP_PKEY_COPY_PARAMETERS +#define EVP_PKEY_free EVP_PKEY_FREE +#define EVP_cleanup EVP_CLEANUP +#define GENERAL_NAMES_free GENERAL_NAMES_FREE +#define i2d_X509_PUBKEY I2D_X509_PUBKEY +#define MD4_Final MD4_FINAL +#define MD4_Init MD4_INIT +#define MD4_Update MD4_UPDATE +#define MD5_Final MD5_FINAL +#define MD5_Init MD5_INIT +#define MD5_Update MD5_UPDATE #define OPENSSL_add_all_algo_noconf OPENSSL_ADD_ALL_ALGO_NOCONF #ifndef __VAX #define OPENSSL_load_builtin_modules OPENSSL_LOAD_BUILTIN_MODULES #endif -#define PEM_read_X509 PEM_READ_X509 -#define PEM_write_bio_X509 PEM_WRITE_BIO_X509 -#define PKCS12_PBE_add PKCS12_PBE_ADD -#define PKCS12_free PKCS12_FREE -#define PKCS12_parse PKCS12_PARSE -#define RAND_add RAND_ADD -#define RAND_bytes RAND_BYTES -#define RAND_file_name RAND_FILE_NAME -#define RAND_load_file RAND_LOAD_FILE -#define RAND_status RAND_STATUS -#define SSL_CIPHER_get_name SSL_CIPHER_GET_NAME -#define SSL_CTX_add_client_CA SSL_CTX_ADD_CLIENT_CA -#define SSL_CTX_callback_ctrl SSL_CTX_CALLBACK_CTRL -#define SSL_CTX_check_private_key SSL_CTX_CHECK_PRIVATE_KEY -#define SSL_CTX_ctrl SSL_CTX_CTRL -#define SSL_CTX_free SSL_CTX_FREE -#define SSL_CTX_get_cert_store SSL_CTX_GET_CERT_STORE -#define SSL_CTX_load_verify_locations SSL_CTX_LOAD_VERIFY_LOCATIONS -#define SSL_CTX_new SSL_CTX_NEW -#define SSL_CTX_set_cipher_list SSL_CTX_SET_CIPHER_LIST -#define SSL_CTX_set_def_passwd_cb_ud SSL_CTX_SET_DEF_PASSWD_CB_UD -#define SSL_CTX_set_default_passwd_cb SSL_CTX_SET_DEFAULT_PASSWD_CB -#define SSL_CTX_set_msg_callback SSL_CTX_SET_MSG_CALLBACK -#define SSL_CTX_set_verify SSL_CTX_SET_VERIFY -#define SSL_CTX_use_PrivateKey SSL_CTX_USE_PRIVATEKEY -#define SSL_CTX_use_PrivateKey_file SSL_CTX_USE_PRIVATEKEY_FILE -#define SSL_CTX_use_cert_chain_file SSL_CTX_USE_CERT_CHAIN_FILE -#define SSL_CTX_use_certificate SSL_CTX_USE_CERTIFICATE -#define SSL_CTX_use_certificate_file SSL_CTX_USE_CERTIFICATE_FILE -#define SSL_SESSION_free SSL_SESSION_FREE -#define SSL_connect SSL_CONNECT -#define SSL_free SSL_FREE -#define SSL_get1_session SSL_GET1_SESSION -#define SSL_get_certificate SSL_GET_CERTIFICATE -#define SSL_get_current_cipher SSL_GET_CURRENT_CIPHER -#define SSL_get_error SSL_GET_ERROR -#define SSL_get_peer_cert_chain SSL_GET_PEER_CERT_CHAIN -#define SSL_get_peer_certificate SSL_GET_PEER_CERTIFICATE -#define SSL_get_privatekey SSL_GET_PRIVATEKEY -#define SSL_get_session SSL_GET_SESSION -#define SSL_get_shutdown SSL_GET_SHUTDOWN -#define SSL_get_verify_result SSL_GET_VERIFY_RESULT -#define SSL_library_init SSL_LIBRARY_INIT -#define SSL_load_error_strings SSL_LOAD_ERROR_STRINGS -#define SSL_new SSL_NEW -#define SSL_peek SSL_PEEK -#define SSL_pending SSL_PENDING -#define SSL_read SSL_READ -#define SSL_set_connect_state SSL_SET_CONNECT_STATE -#define SSL_set_fd SSL_SET_FD -#define SSL_set_session SSL_SET_SESSION -#define SSL_shutdown SSL_SHUTDOWN -#define SSL_version SSL_VERSION -#define SSL_write SSL_WRITE -#define SSLeay SSLEAY -#define SSLv23_client_method SSLV23_CLIENT_METHOD -#define SSLv3_client_method SSLV3_CLIENT_METHOD -#define TLSv1_client_method TLSV1_CLIENT_METHOD -#define UI_create_method UI_CREATE_METHOD -#define UI_destroy_method UI_DESTROY_METHOD -#define UI_get0_user_data UI_GET0_USER_DATA -#define UI_get_input_flags UI_GET_INPUT_FLAGS -#define UI_get_string_type UI_GET_STRING_TYPE -#define UI_create_method UI_CREATE_METHOD -#define UI_destroy_method UI_DESTROY_METHOD -#define UI_method_get_closer UI_METHOD_GET_CLOSER -#define UI_method_get_opener UI_METHOD_GET_OPENER -#define UI_method_get_reader UI_METHOD_GET_READER -#define UI_method_get_writer UI_METHOD_GET_WRITER -#define UI_method_set_closer UI_METHOD_SET_CLOSER -#define UI_method_set_opener UI_METHOD_SET_OPENER -#define UI_method_set_reader UI_METHOD_SET_READER -#define UI_method_set_writer UI_METHOD_SET_WRITER -#define UI_OpenSSL UI_OPENSSL -#define UI_set_result UI_SET_RESULT -#define X509V3_EXT_print X509V3_EXT_PRINT -#define X509_EXTENSION_get_critical X509_EXTENSION_GET_CRITICAL -#define X509_EXTENSION_get_data X509_EXTENSION_GET_DATA -#define X509_EXTENSION_get_object X509_EXTENSION_GET_OBJECT -#define X509_LOOKUP_file X509_LOOKUP_FILE -#define X509_NAME_ENTRY_get_data X509_NAME_ENTRY_GET_DATA -#define X509_NAME_get_entry X509_NAME_GET_ENTRY -#define X509_NAME_get_index_by_NID X509_NAME_GET_INDEX_BY_NID -#define X509_NAME_print_ex X509_NAME_PRINT_EX +#define PEM_read_X509 PEM_READ_X509 +#define PEM_write_bio_X509 PEM_WRITE_BIO_X509 +#define PKCS12_free PKCS12_FREE +#define PKCS12_parse PKCS12_PARSE +#define RAND_add RAND_ADD +#define RAND_bytes RAND_BYTES +#define RAND_file_name RAND_FILE_NAME +#define RAND_load_file RAND_LOAD_FILE +#define RAND_status RAND_STATUS +#define SSL_CIPHER_get_name SSL_CIPHER_GET_NAME +#define SSL_CTX_add_client_CA SSL_CTX_ADD_CLIENT_CA +#define SSL_CTX_callback_ctrl SSL_CTX_CALLBACK_CTRL +#define SSL_CTX_check_private_key SSL_CTX_CHECK_PRIVATE_KEY +#define SSL_CTX_ctrl SSL_CTX_CTRL +#define SSL_CTX_free SSL_CTX_FREE +#define SSL_CTX_get_cert_store SSL_CTX_GET_CERT_STORE +#define SSL_CTX_load_verify_locations SSL_CTX_LOAD_VERIFY_LOCATIONS +#define SSL_CTX_new SSL_CTX_NEW +#define SSL_CTX_set_cipher_list SSL_CTX_SET_CIPHER_LIST +#define SSL_CTX_set_def_passwd_cb_ud SSL_CTX_SET_DEF_PASSWD_CB_UD +#define SSL_CTX_set_default_passwd_cb SSL_CTX_SET_DEFAULT_PASSWD_CB +#define SSL_CTX_set_msg_callback SSL_CTX_SET_MSG_CALLBACK +#define SSL_CTX_set_verify SSL_CTX_SET_VERIFY +#define SSL_CTX_use_PrivateKey SSL_CTX_USE_PRIVATEKEY +#define SSL_CTX_use_PrivateKey_file SSL_CTX_USE_PRIVATEKEY_FILE +#define SSL_CTX_use_cert_chain_file SSL_CTX_USE_CERT_CHAIN_FILE +#define SSL_CTX_use_certificate SSL_CTX_USE_CERTIFICATE +#define SSL_CTX_use_certificate_file SSL_CTX_USE_CERTIFICATE_FILE +#define SSL_SESSION_free SSL_SESSION_FREE +#define SSL_connect SSL_CONNECT +#define SSL_free SSL_FREE +#define SSL_get1_session SSL_GET1_SESSION +#define SSL_get_certificate SSL_GET_CERTIFICATE +#define SSL_get_current_cipher SSL_GET_CURRENT_CIPHER +#define SSL_get_error SSL_GET_ERROR +#define SSL_get_peer_cert_chain SSL_GET_PEER_CERT_CHAIN +#define SSL_get_peer_certificate SSL_GET_PEER_CERTIFICATE +#define SSL_get_privatekey SSL_GET_PRIVATEKEY +#define SSL_get_session SSL_GET_SESSION +#define SSL_get_shutdown SSL_GET_SHUTDOWN +#define SSL_get_verify_result SSL_GET_VERIFY_RESULT +#define SSL_library_init SSL_LIBRARY_INIT +#define SSL_load_error_strings SSL_LOAD_ERROR_STRINGS +#define SSL_new SSL_NEW +#define SSL_peek SSL_PEEK +#define SSL_pending SSL_PENDING +#define SSL_read SSL_READ +#define SSL_set_connect_state SSL_SET_CONNECT_STATE +#define SSL_set_fd SSL_SET_FD +#define SSL_set_session SSL_SET_SESSION +#define SSL_shutdown SSL_SHUTDOWN +#define SSL_version SSL_VERSION +#define SSL_write SSL_WRITE +#define SSLeay SSLEAY +#define SSLv23_client_method SSLV23_CLIENT_METHOD +#define SSLv3_client_method SSLV3_CLIENT_METHOD +#define TLSv1_client_method TLSV1_CLIENT_METHOD +#define UI_create_method UI_CREATE_METHOD +#define UI_destroy_method UI_DESTROY_METHOD +#define UI_get0_user_data UI_GET0_USER_DATA +#define UI_get_input_flags UI_GET_INPUT_FLAGS +#define UI_get_string_type UI_GET_STRING_TYPE +#define UI_create_method UI_CREATE_METHOD +#define UI_destroy_method UI_DESTROY_METHOD +#define UI_method_get_closer UI_METHOD_GET_CLOSER +#define UI_method_get_opener UI_METHOD_GET_OPENER +#define UI_method_get_reader UI_METHOD_GET_READER +#define UI_method_get_writer UI_METHOD_GET_WRITER +#define UI_method_set_closer UI_METHOD_SET_CLOSER +#define UI_method_set_opener UI_METHOD_SET_OPENER +#define UI_method_set_reader UI_METHOD_SET_READER +#define UI_method_set_writer UI_METHOD_SET_WRITER +#define UI_OpenSSL UI_OPENSSL +#define UI_set_result UI_SET_RESULT +#define X509V3_EXT_print X509V3_EXT_PRINT +#define X509_EXTENSION_get_critical X509_EXTENSION_GET_CRITICAL +#define X509_EXTENSION_get_data X509_EXTENSION_GET_DATA +#define X509_EXTENSION_get_object X509_EXTENSION_GET_OBJECT +#define X509_LOOKUP_file X509_LOOKUP_FILE +#define X509_NAME_ENTRY_get_data X509_NAME_ENTRY_GET_DATA +#define X509_NAME_get_entry X509_NAME_GET_ENTRY +#define X509_NAME_get_index_by_NID X509_NAME_GET_INDEX_BY_NID +#define X509_NAME_print_ex X509_NAME_PRINT_EX #define X509_STORE_CTX_get_current_cert X509_STORE_CTX_GET_CURRENT_CERT -#define X509_STORE_add_lookup X509_STORE_ADD_LOOKUP -#define X509_STORE_set_flags X509_STORE_SET_FLAGS -#define X509_check_issued X509_CHECK_ISSUED -#define X509_free X509_FREE -#define X509_get_ext_d2i X509_GET_EXT_D2I -#define X509_get_issuer_name X509_GET_ISSUER_NAME -#define X509_get_pubkey X509_GET_PUBKEY -#define X509_get_serialNumber X509_GET_SERIALNUMBER -#define X509_get_subject_name X509_GET_SUBJECT_NAME -#define X509_load_crl_file X509_LOAD_CRL_FILE -#define X509_verify_cert_error_string X509_VERIFY_CERT_ERROR_STRING -#define d2i_PKCS12_fp D2I_PKCS12_FP -#define i2t_ASN1_OBJECT I2T_ASN1_OBJECT -#define sk_num SK_NUM -#define sk_pop SK_POP -#define sk_pop_free SK_POP_FREE -#define sk_value SK_VALUE -#ifdef __VAX -#define OPENSSL_NO_SHA256 -#endif -#define SHA256_Final SHA256_FINAL -#define SHA256_Init SHA256_INIT +#define X509_STORE_add_lookup X509_STORE_ADD_LOOKUP +#define X509_STORE_set_flags X509_STORE_SET_FLAGS +#define X509_check_issued X509_CHECK_ISSUED +#define X509_free X509_FREE +#define X509_get_ext_d2i X509_GET_EXT_D2I +#define X509_get_issuer_name X509_GET_ISSUER_NAME +#define X509_get_pubkey X509_GET_PUBKEY +#define X509_get_serialNumber X509_GET_SERIALNUMBER +#define X509_get_subject_name X509_GET_SUBJECT_NAME +#define X509_load_crl_file X509_LOAD_CRL_FILE +#define X509_verify_cert_error_string X509_VERIFY_CERT_ERROR_STRING +#define d2i_PKCS12_fp D2I_PKCS12_FP +#define i2t_ASN1_OBJECT I2T_ASN1_OBJECT +#define sk_num SK_NUM +#define sk_pop SK_POP +#define sk_pop_free SK_POP_FREE +#define sk_value SK_VALUE +#define SHA256_Final SHA256_FINAL +#define SHA256_Init SHA256_INIT #define SHA256_Update SHA256_UPDATE #define USE_UPPERCASE_GSSAPI 1 -#define gss_seal GSS_SEAL -#define gss_unseal GSS_UNSEAL +#define gss_seal GSS_SEAL +#define gss_unseal GSS_UNSEAL #define USE_UPPERCASE_KRBAPI 1 @@ -380,11 +374,11 @@ static struct passwd *vms_getpwuid(uid_t uid) /* VAX symbols are always in uppercase */ #ifdef __VAX -#define inflate INFLATE -#define inflateEnd INFLATEEND +#define inflate INFLATE +#define inflateEnd INFLATEEND #define inflateInit2_ INFLATEINIT2_ -#define inflateInit_ INFLATEINIT_ -#define zlibVersion ZLIBVERSION +#define inflateInit_ INFLATEINIT_ +#define zlibVersion ZLIBVERSION #endif /* Older VAX OpenSSL port defines these as Macros */ @@ -392,11 +386,11 @@ static struct passwd *vms_getpwuid(uid_t uid) /* that way a newer port will also work if some one has one */ #ifdef __VAX -# include -# ifndef OpenSSL_add_all_algorithms -# define OpenSSL_add_all_algorithms OPENSSL_ADD_ALL_ALGORITHMS - void OPENSSL_ADD_ALL_ALGORITHMS(void); -# endif +# include +# ifndef OpenSSL_add_all_algorithms +# define OpenSSL_add_all_algorithms OPENSSL_ADD_ALL_ALGORITHMS + void OPENSSL_ADD_ALL_ALGORITHMS(void); +# endif #endif #endif /* HEADER_CURL_SETUP_VMS_H */ diff --git a/lib/setup-win32.h b/lib/setup-win32.h index 02177a7a3b..6a89b966de 100644 --- a/lib/setup-win32.h +++ b/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/lib/sha256.c b/lib/sha256.c index 6f519add58..d97f45f05f 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -22,54 +22,36 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ defined(USE_LIBSSH2) || defined(USE_SSL) -#include "curlx/warnless.h" #include "curl_sha256.h" -#include "curl_hmac.h" -#ifdef USE_OPENSSL -#include -#elif defined(USE_GNUTLS) -#include -#elif defined(USE_MBEDTLS) +#ifdef USE_MBEDTLS #include #if MBEDTLS_VERSION_NUMBER < 0x03020000 - #error "mbedTLS 3.2.0 or later required" +#error "mbedTLS 3.2.0 or later required" #endif -#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 -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - /* Please keep the SSL backend-specific #if branches in this order: * * 1. USE_OPENSSL - * 2. USE_GNUTLS - * 3. USE_MBEDTLS - * 4. USE_COMMON_CRYPTO - * 5. USE_WIN32_CRYPTO + * 2. USE_WOLFSSL + * 3. USE_GNUTLS + * 4. USE_MBEDTLS + * 5. USE_COMMON_CRYPTO + * 6. USE_WIN32_CRYPTO * * This ensures that the same SSL branch gets activated throughout this source * file even if multiple backends are enabled at the same time. */ #ifdef USE_OPENSSL +#include struct ossl_sha256_ctx { EVP_MD_CTX *openssl_ctx; @@ -105,7 +87,33 @@ static void my_sha256_final(unsigned char *digest, void *in) EVP_MD_CTX_destroy(ctx->openssl_ctx); } +#elif defined(USE_WOLFSSL) +#include +#include + +typedef struct wc_Sha256 my_sha256_ctx; + +static CURLcode my_sha256_init(void *in) +{ + if(wc_InitSha256(in)) + return CURLE_FAILED_INIT; + return CURLE_OK; +} + +static void my_sha256_update(void *in, + const unsigned char *data, + unsigned int length) +{ + (void)wc_Sha256Update(in, data, (word32)length); +} + +static void my_sha256_final(unsigned char *digest, void *in) +{ + (void)wc_Sha256Final(in, digest); +} + #elif defined(USE_GNUTLS) +#include typedef struct sha256_ctx my_sha256_ctx; @@ -127,13 +135,17 @@ static void my_sha256_final(unsigned char *digest, void *ctx) sha256_digest(ctx, SHA256_DIGEST_SIZE, digest); } -#elif defined(USE_MBEDTLS) +#elif defined(USE_MBEDTLS) && \ + defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 /* mbedTLS 4+ */ +#include -typedef mbedtls_sha256_context my_sha256_ctx; +typedef psa_hash_operation_t my_sha256_ctx; static CURLcode my_sha256_init(void *ctx) { - (void)mbedtls_sha256_starts(ctx, 0); + memset(ctx, 0, sizeof(my_sha256_ctx)); + if(psa_hash_setup(ctx, PSA_ALG_SHA_256) != PSA_SUCCESS) + return CURLE_OUT_OF_MEMORY; return CURLE_OK; } @@ -141,15 +153,22 @@ static void my_sha256_update(void *ctx, const unsigned char *data, unsigned int length) { - (void)mbedtls_sha256_update(ctx, data, length); + (void)psa_hash_update(ctx, data, length); } static void my_sha256_final(unsigned char *digest, void *ctx) { - (void)mbedtls_sha256_finish(ctx, digest); + size_t actual_length; + (void)psa_hash_finish(ctx, digest, CURL_SHA256_DIGEST_LENGTH, + &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) @@ -171,6 +190,7 @@ static void my_sha256_final(unsigned char *digest, void *ctx) } #elif defined(USE_WIN32_CRYPTO) +#include struct sha256_ctx { HCRYPTPROV hCryptProv; @@ -178,16 +198,11 @@ 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; if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) return CURLE_OUT_OF_MEMORY; if(!CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash)) { @@ -204,11 +219,7 @@ static void my_sha256_update(void *in, unsigned int length) { my_sha256_ctx *ctx = (my_sha256_ctx *)in; -#ifdef __MINGW32CE__ - CryptHashData(ctx->hHash, (BYTE *)CURL_UNCONST(data), length, 0); -#else CryptHashData(ctx->hHash, (const BYTE *)data, length, 0); -#endif } static void my_sha256_final(unsigned char *digest, void *in) @@ -231,53 +242,36 @@ static void my_sha256_final(unsigned char *digest, void *in) /* When no other crypto library is available we use this code segment */ -/* This is based on SHA256 implementation in LibTomCrypt that was released into - * public domain by Tom St Denis. */ +/* This is based on the SHA256 implementation in LibTomCrypt that was released + * into public domain. */ -#define WPA_GET_BE32(a) ((((unsigned long)(a)[0]) << 24) | \ - (((unsigned long)(a)[1]) << 16) | \ - (((unsigned long)(a)[2]) << 8) | \ - ((unsigned long)(a)[3])) -#define WPA_PUT_BE32(a, val) \ -do { \ - (a)[0] = (unsigned char)((((unsigned long) (val)) >> 24) & 0xff); \ - (a)[1] = (unsigned char)((((unsigned long) (val)) >> 16) & 0xff); \ - (a)[2] = (unsigned char)((((unsigned long) (val)) >> 8) & 0xff); \ - (a)[3] = (unsigned char)(((unsigned long) (val)) & 0xff); \ -} while(0) +#define WPA_GET_BE32(a) \ + ((((unsigned long)(a)[0]) << 24) | \ + (((unsigned long)(a)[1]) << 16) | \ + (((unsigned long)(a)[2]) << 8) | \ + ((unsigned long)(a)[3])) +#define WPA_PUT_BE32(a, val) \ + do { \ + (a)[0] = (unsigned char)((((unsigned long)(val)) >> 24) & 0xff); \ + (a)[1] = (unsigned char)((((unsigned long)(val)) >> 16) & 0xff); \ + (a)[2] = (unsigned char)((((unsigned long)(val)) >> 8) & 0xff); \ + (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); \ -} while(0) -#endif +#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) 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]; }; @@ -302,20 +296,21 @@ static const unsigned long K[64] = { /* Various logical functions */ #define RORc(x, y) \ -(((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | \ - ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) -#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_R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) + (((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | \ + ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) + +#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_R(x, n) (((x) & 0xFFFFFFFFUL) >> (n)) + #define Sigma0(x) (Sha256_S(x, 2) ^ Sha256_S(x, 13) ^ Sha256_S(x, 22)) #define Sigma1(x) (Sha256_S(x, 6) ^ Sha256_S(x, 11) ^ Sha256_S(x, 25)) #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 */ -static int sha256_compress(struct sha256_state *md, - const unsigned char *buf) +/* Compress 512 bits */ +static int sha256_compress(struct sha256_state *md, const unsigned char *buf) { unsigned long S[8], W[64]; int i; @@ -324,17 +319,16 @@ static int sha256_compress(struct sha256_state *md, 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] */ for(i = 16; i < 64; i++) { - W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + - W[i - 16]; + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; } /* Compress */ -#define RND(a,b,c,d,e,f,g,h,i) \ +#define RND(a, b, c, d, e, f, g, h, i) \ do { \ unsigned long t0 = h + Sigma1(e) + Sha256_Ch(e, f, g) + K[i] + W[i]; \ unsigned long t1 = Sigma0(a) + Sha256_Maj(a, b, c); \ @@ -345,8 +339,15 @@ static int sha256_compress(struct sha256_state *md, for(i = 0; i < 64; ++i) { unsigned long t; RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); - t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; - S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + t = S[7]; + S[7] = S[6]; + S[6] = S[5]; + S[5] = S[4]; + S[4] = S[3]; + S[3] = S[2]; + S[2] = S[1]; + S[1] = S[0]; + S[0] = t; } /* Feedback */ @@ -441,7 +442,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; @@ -449,7 +450,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 */ @@ -477,20 +478,24 @@ 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) + size_t len) { CURLcode result; my_sha256_ctx ctx; result = my_sha256_init(&ctx); if(!result) { - my_sha256_update(&ctx, input, curlx_uztoui(length)); + do { + unsigned int ilen = (unsigned int)CURLMIN(len, UINT_MAX); + my_sha256_update(&ctx, input, ilen); + len -= ilen; + input += ilen; + } while(len); my_sha256_final(output, &ctx); } return result; } - const struct HMAC_params Curl_HMAC_SHA256 = { my_sha256_init, /* Hash initialization function. */ my_sha256_update, /* Hash update function. */ diff --git a/lib/sigpipe.h b/lib/sigpipe.h index 1be3a111e9..b489796953 100644 --- a/lib/sigpipe.h +++ b/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/lib/slist.c b/lib/slist.c index 366b247609..83fd2918c6 100644 --- a/lib/slist.c +++ b/lib/slist.c @@ -21,21 +21,14 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "slist.h" -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - /* returns last node in linked list */ static struct curl_slist *slist_get_last(struct curl_slist *list) { - struct curl_slist *item; + struct curl_slist *item; /* if caller passed us a NULL, return now */ if(!list) @@ -58,19 +51,20 @@ static struct curl_slist *slist_get_last(struct curl_slist *list) * If an error occurs, NULL is returned and the string argument is NOT * released. */ -struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, char *data) +struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, + const char *data) { - struct curl_slist *last; - struct curl_slist *new_item; + struct curl_slist *last; + struct curl_slist *new_item; DEBUGASSERT(data); - new_item = malloc(sizeof(struct curl_slist)); + new_item = curlx_malloc(sizeof(struct curl_slist)); if(!new_item) return NULL; new_item->next = NULL; - new_item->data = data; + new_item->data = CURL_UNCONST(data); /* if this is the first item, then new_item *is* the list */ if(!list) @@ -85,20 +79,19 @@ struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, char *data) * 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) +struct curl_slist *curl_slist_append(struct curl_slist *list, const char *data) { - char *dupdata = strdup(data); + char *dupdata = curlx_strdup(data); if(!dupdata) return NULL; list = Curl_slist_append_nodup(list, dupdata); if(!list) - free(dupdata); + curlx_free(dupdata); return list; } @@ -130,8 +123,8 @@ struct curl_slist *Curl_slist_duplicate(struct curl_slist *inlist) /* be nice and clean up resources */ void curl_slist_free_all(struct curl_slist *list) { - struct curl_slist *next; - struct curl_slist *item; + struct curl_slist *next; + struct curl_slist *item; if(!list) return; @@ -139,8 +132,8 @@ void curl_slist_free_all(struct curl_slist *list) item = list; do { next = item->next; - Curl_safefree(item->data); - free(item); + curlx_safefree(item->data); + curlx_free(item); item = next; } while(next); } diff --git a/lib/slist.h b/lib/slist.h index 9561fd0226..47a30824db 100644 --- a/lib/slist.h +++ b/lib/slist.h @@ -36,6 +36,6 @@ struct curl_slist *Curl_slist_duplicate(struct curl_slist *inlist); * it to the list. */ struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list, - char *data); + const char *data); #endif /* HEADER_CURL_SLIST_H */ diff --git a/lib/smb.c b/lib/smb.c index 81cf6e7cc1..6a97d2e006 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -22,31 +22,28 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" +#include "urldata.h" -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) +#if defined(CURL_ENABLE_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 "multiif.h" +#include "curl_trc.h" #include "cfilters.h" #include "connect.h" #include "progress.h" #include "transfer.h" #include "select.h" -#include "vtls/vtls.h" #include "curl_ntlm_core.h" #include "escape.h" #include "curl_endian.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - +#include "curlx/strcopy.h" /* meta key for storing protocol meta at easy handle */ #define CURL_META_SMB_EASY "meta:proto:smb:easy" @@ -292,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_NONE /* 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 /* flags */ -}; -#endif - #define MAX_PAYLOAD_SIZE 0x8000 #define MAX_MESSAGE_SIZE (MAX_PAYLOAD_SIZE + 0x1000) #define CLIENTNAME "curl" @@ -373,7 +299,7 @@ const struct Curl_handler Curl_handler_smbs = { defined(__OS400__) static unsigned short smb_swap16(unsigned short x) { - return (unsigned short) ((x << 8) | ((x >> 8) & 0xff)); + return (unsigned short)((x << 8) | ((x >> 8) & 0xff)); } static unsigned int smb_swap32(unsigned int x) @@ -384,8 +310,8 @@ static unsigned int smb_swap32(unsigned int x) static curl_off_t smb_swap64(curl_off_t x) { - return ((curl_off_t) smb_swap32((unsigned int) x) << 32) | - smb_swap32((unsigned int) (x >> 32)); + return ((curl_off_t)smb_swap32((unsigned int)x) << 32) | + smb_swap32((unsigned int)(x >> 32)); } #else @@ -397,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", @@ -411,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; } @@ -421,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", @@ -449,10 +376,8 @@ 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. */ - free(req); + curlx_safefree(req->path); + curlx_free(req); } static void smb_conn_dtor(void *key, size_t klen, void *entry) @@ -460,11 +385,58 @@ static void smb_conn_dtor(void *key, size_t klen, void *entry) struct smb_conn *smbc = entry; (void)key; (void)klen; - Curl_safefree(smbc->share); - Curl_safefree(smbc->domain); - Curl_safefree(smbc->recv_buf); - Curl_safefree(smbc->send_buf); - free(smbc); + curlx_safefree(smbc->share); + curlx_safefree(smbc->domain); + curlx_safefree(smbc->recv_buf); + curlx_safefree(smbc->send_buf); + 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 */ + curlx_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) { + curlx_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 @@ -476,13 +448,13 @@ static CURLcode smb_setup_connection(struct Curl_easy *data, struct smb_request *req; /* Initialize the connection state */ - smbc = calloc(1, sizeof(*smbc)); + smbc = curlx_calloc(1, sizeof(*smbc)); if(!smbc || Curl_conn_meta_set(conn, CURL_META_SMB_CONN, smbc, smb_conn_dtor)) return CURLE_OUT_OF_MEMORY; /* Initialize the request state */ - req = calloc(1, sizeof(*req)); + req = curlx_calloc(1, sizeof(*req)); if(!req || Curl_meta_set(data, CURL_META_SMB_EASY, req, smb_easy_dtor)) return CURLE_OUT_OF_MEMORY; @@ -507,16 +479,13 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) /* Initialize the connection state */ smbc->state = SMB_CONNECTING; - smbc->recv_buf = malloc(MAX_MESSAGE_SIZE); + smbc->recv_buf = curlx_malloc(MAX_MESSAGE_SIZE); if(!smbc->recv_buf) return CURLE_OUT_OF_MEMORY; - smbc->send_buf = malloc(MAX_MESSAGE_SIZE); + smbc->send_buf = curlx_malloc(MAX_MESSAGE_SIZE); if(!smbc->send_buf) return CURLE_OUT_OF_MEMORY; - /* Multiple requests are allowed with this connection */ - connkeep(conn, "SMB default"); - /* Parse the username, domain, and password */ slash = strchr(conn->user, '/'); if(!slash) @@ -524,14 +493,14 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) if(slash) { smbc->user = slash + 1; - smbc->domain = strdup(conn->user); + smbc->domain = curlx_strdup(conn->user); if(!smbc->domain) return CURLE_OUT_OF_MEMORY; smbc->domain[slash - conn->user] = 0; } else { smbc->user = conn->user; - smbc->domain = strdup(conn->host.name); + smbc->domain = curlx_strdup(conn->host.name); if(!smbc->domain) return CURLE_OUT_OF_MEMORY; } @@ -546,7 +515,7 @@ static CURLcode smb_recv_message(struct Curl_easy *data, char *buf = smbc->recv_buf; size_t bytes_read; size_t nbt_size; - size_t msg_size; + size_t msg_size = sizeof(struct smb_header); size_t len = MAX_MESSAGE_SIZE - smbc->got; CURLcode result; @@ -566,19 +535,28 @@ static CURLcode smb_recv_message(struct Curl_easy *data, nbt_size = Curl_read16_be((const unsigned char *) (buf + sizeof(unsigned short))) + sizeof(unsigned int); + if(nbt_size > MAX_MESSAGE_SIZE) { + failf(data, "too large NetBIOS frame size %zu", nbt_size); + return CURLE_RECV_ERROR; + } + else if(nbt_size < msg_size) { + /* Each SMB message must be at least this large, e.g. 32 bytes */ + failf(data, "too small NetBIOS frame size %zu", nbt_size); + return CURLE_RECV_ERROR; + } + if(smbc->got < nbt_size) return CURLE_OK; - msg_size = sizeof(struct smb_header); 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) + Curl_read16_le((const unsigned char *)&buf[msg_size]); if(nbt_size < msg_size) - return CURLE_READ_ERROR; + return CURLE_RECV_ERROR; } } @@ -600,8 +578,8 @@ static void smb_format_message(struct smb_conn *smbc, const unsigned int pid = 0xbad71d; /* made up */ memset(h, 0, sizeof(*h)); - h->nbt_length = htons((unsigned short) (sizeof(*h) - sizeof(unsigned int) + - len)); + h->nbt_length = htons((unsigned short)(sizeof(*h) - sizeof(unsigned int) + + len)); memcpy((char *)h->magic, "\xffSMB", 4); h->command = cmd; h->flags = SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES; @@ -609,7 +587,7 @@ static void smb_format_message(struct smb_conn *smbc, h->uid = smb_swap16(smbc->uid); h->tid = smb_swap16(req->tid); h->pid_high = smb_swap16((unsigned short)(pid >> 16)); - h->pid = smb_swap16((unsigned short) pid); + h->pid = smb_swap16((unsigned short)pid); } static CURLcode smb_send(struct Curl_easy *data, struct smb_conn *smbc, @@ -660,9 +638,12 @@ static CURLcode smb_send_message(struct Curl_easy *data, unsigned char cmd, const void *msg, size_t msg_len) { + if((MAX_MESSAGE_SIZE - sizeof(struct smb_header)) < msg_len) { + DEBUGASSERT(0); + return CURLE_SEND_ERROR; + } smb_format_message(smbc, req, (struct smb_header *)smbc->send_buf, cmd, msg_len); - DEBUGASSERT((sizeof(struct smb_header) + msg_len) <= MAX_MESSAGE_SIZE); memcpy(smbc->send_buf + sizeof(struct smb_header), msg, msg_len); return smb_send(data, smbc, sizeof(struct smb_header) + msg_len, 0); @@ -718,12 +699,12 @@ static CURLcode smb_send_setup(struct Curl_easy *data) p += sizeof(lm); memcpy(p, nt, sizeof(nt)); p += sizeof(nt); - p += msnprintf(p, byte_count - sizeof(nt) - sizeof(lm), - "%s%c" /* user */ - "%s%c" /* domain */ - "%s%c" /* OS */ - "%s", /* client name */ - smbc->user, 0, smbc->domain, 0, CURL_OS, 0, CLIENTNAME); + p += curl_msnprintf(p, byte_count - sizeof(nt) - sizeof(lm), + "%s%c" /* user */ + "%s%c" /* domain */ + "%s%c" /* OS */ + "%s", /* client name */ + smbc->user, 0, smbc->domain, 0, CURL_OS, 0, CLIENTNAME); p++; /* count the final null-termination */ DEBUGASSERT(byte_count == (size_t)(p - msg.bytes)); msg.byte_count = smb_swap16((unsigned short)byte_count); @@ -750,11 +731,11 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data, msg.andx.command = SMB_COM_NO_ANDX_COMMAND; msg.pw_len = 0; - p += msnprintf(p, byte_count, - "\\\\%s\\" /* hostname */ - "%s%c" /* share */ - "%s", /* service */ - conn->host.name, smbc->share, 0, SERVICENAME); + p += curl_msnprintf(p, byte_count, + "\\\\%s\\" /* hostname */ + "%s%c" /* share */ + "%s", /* service */ + conn->host.name, smbc->share, 0, SERVICENAME); p++; /* count the final null-termination */ DEBUGASSERT(byte_count == (size_t)(p - msg.bytes)); msg.byte_count = smb_swap16((unsigned short)byte_count); @@ -786,8 +767,8 @@ static CURLcode smb_send_open(struct Curl_easy *data, msg.access = smb_swap32(SMB_GENERIC_READ); msg.create_disposition = smb_swap32(SMB_FILE_OPEN); } - msg.byte_count = smb_swap16((unsigned short) byte_count); - strcpy(msg.bytes, req->path); + msg.byte_count = smb_swap16((unsigned short)byte_count); + curlx_strcopy(msg.bytes, sizeof(msg.bytes), req->path, byte_count - 1); return smb_send_message(data, smbc, req, SMB_COM_NT_CREATE_ANDX, &msg, sizeof(msg) - sizeof(msg.bytes) + byte_count); @@ -827,8 +808,8 @@ static CURLcode smb_send_read(struct Curl_easy *data, msg.word_count = SMB_WC_READ_ANDX; msg.andx.command = SMB_COM_NO_ANDX_COMMAND; msg.fid = smb_swap16(req->fid); - msg.offset = smb_swap32((unsigned int) offset); - msg.offset_high = smb_swap32((unsigned int) (offset >> 32)); + msg.offset = smb_swap32((unsigned int)offset); + msg.offset_high = smb_swap32((unsigned int)(offset >> 32)); msg.min_bytes = smb_swap16(MAX_PAYLOAD_SIZE); msg.max_bytes = smb_swap16(MAX_PAYLOAD_SIZE); @@ -852,16 +833,16 @@ static CURLcode smb_send_write(struct Curl_easy *data, msg->word_count = SMB_WC_WRITE_ANDX; msg->andx.command = SMB_COM_NO_ANDX_COMMAND; msg->fid = smb_swap16(req->fid); - msg->offset = smb_swap32((unsigned int) offset); - msg->offset_high = smb_swap32((unsigned int) (offset >> 32)); - msg->data_length = smb_swap16((unsigned short) upload_size); + msg->offset = smb_swap32((unsigned int)offset); + msg->offset_high = smb_swap32((unsigned int)(offset >> 32)); + msg->data_length = smb_swap16((unsigned short)upload_size); msg->data_offset = smb_swap16(sizeof(*msg) - sizeof(unsigned int)); - msg->byte_count = smb_swap16((unsigned short) (upload_size + 1)); + msg->byte_count = smb_swap16((unsigned short)(upload_size + 1)); smb_format_message(smbc, req, &msg->h, SMB_COM_WRITE_ANDX, - sizeof(*msg) - sizeof(msg->h) + (size_t) upload_size); + sizeof(*msg) - sizeof(msg->h) + (size_t)upload_size); - return smb_send(data, smbc, sizeof(*msg), (size_t) upload_size); + return smb_send(data, smbc, sizeof(*msg), (size_t)upload_size); } static CURLcode smb_send_and_recv(struct Curl_easy *data, @@ -1010,7 +991,7 @@ static void get_posix_time(time_t *out, curl_off_t timestamp) *out = TIME_T_MIN; else #endif - *out = (time_t) timestamp; + *out = (time_t)timestamp; } else *out = 0; @@ -1080,7 +1061,7 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) next_state = SMB_TREE_DISCONNECT; break; } - smb_m = (const struct smb_nt_create_response*) msg; + smb_m = (const struct smb_nt_create_response *)msg; req->fid = smb_swap16(smb_m->fid); data->req.offset = 0; if(data->state.upload) { @@ -1104,14 +1085,14 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) break; case SMB_DOWNLOAD: - if(h->status || smbc->got < sizeof(struct smb_header) + 14) { + if(h->status || smbc->got < sizeof(struct smb_header) + 15) { req->result = CURLE_RECV_ERROR; next_state = SMB_CLOSE; break; } - len = Curl_read16_le(((const unsigned char *) msg) + + len = Curl_read16_le((const unsigned char *)msg + sizeof(struct smb_header) + 11); - off = Curl_read16_le(((const unsigned char *) msg) + + off = Curl_read16_le((const unsigned char *)msg + sizeof(struct smb_header) + 13); if(len > 0) { if(off + sizeof(unsigned int) + len > smbc->got) { @@ -1133,16 +1114,16 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) break; case SMB_UPLOAD: - if(h->status || smbc->got < sizeof(struct smb_header) + 6) { + if(h->status || smbc->got < sizeof(struct smb_header) + 7) { req->result = CURLE_UPLOAD_FAILED; next_state = SMB_CLOSE; break; } - len = Curl_read16_le(((const unsigned char *) msg) + + len = Curl_read16_le((const unsigned char *)msg + sizeof(struct smb_header) + 5); data->req.bytecount += len; data->req.offset += len; - Curl_pgrsSetUploadCounter(data, data->req.bytecount); + Curl_pgrs_upload_inc(data, len); if(data->req.bytecount >= data->req.size) next_state = SMB_CLOSE; else @@ -1224,46 +1205,27 @@ 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; +/* + * SMB handler interface + */ +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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; - /* 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 = strdup((*path == '/' || *path == '\\') ? path + 1 : path); - 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; - req->path = slash; - - for(; *slash; slash++) { - if(*slash == '/') - *slash = '\\'; - } - return CURLE_OK; -} - -#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */ +#endif /* CURL_ENABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */ diff --git a/lib/smb.h b/lib/smb.h index aa0be881c1..5a92ad13db 100644 --- a/lib/smb.h +++ b/lib/smb.h @@ -24,13 +24,8 @@ * 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 */ +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) +extern const struct Curl_protocol Curl_protocol_smb; +#endif #endif /* HEADER_CURL_SMB_H */ diff --git a/lib/smtp.c b/lib/smtp.c index 5dba1f9ea8..86fc883140 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -38,8 +38,9 @@ * Draft LOGIN SASL Mechanism * ***************************************************************************/ - #include "curl_setup.h" +#include "urldata.h" +#include "smtp.h" #ifndef CURL_DISABLE_SMTP @@ -57,35 +58,25 @@ #include #endif -#include -#include "urldata.h" #include "sendf.h" +#include "curl_trc.h" #include "hostip.h" #include "progress.h" #include "transfer.h" #include "escape.h" -#include "http.h" /* for HTTP proxy tunnel stuff */ +#include "pingpong.h" #include "mime.h" -#include "socks.h" -#include "smtp.h" #include "vtls/vtls.h" #include "cfilters.h" #include "connect.h" #include "select.h" -#include "multiif.h" #include "url.h" #include "curl_gethostname.h" #include "bufref.h" #include "curl_sasl.h" -#include "curlx/warnless.h" #include "idn.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - /* meta key for storing protocol meta at easy handle */ #define CURL_META_SMTP_EASY "meta:proto:smtp:easy" /* meta key for storing protocol meta at connection */ @@ -145,116 +136,343 @@ 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); +/*********************************************************************** + * + * smtp_parse_url_options() + * + * Parse the URL login options. + */ static CURLcode smtp_parse_url_options(struct connectdata *conn, - struct smtp_conn *smtpc); + 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); + 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); -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); + struct SMTP *smtp) +{ + CURLcode result = CURLE_OK; + const char *custom = data->set.str[STRING_CUSTOMREQUEST]; -/* - * SMTP protocol handler. + /* 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: + * + * 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; -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 + /* 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 */ }; -#ifdef USE_SSL -/* - * SMTPS protocol handler. - */ +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; +} -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 - | PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS /* flags */ -}; -#endif +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); +} -/* 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 */ +/* 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() @@ -267,7 +485,7 @@ 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; + bool end = FALSE; (void)data; DEBUGASSERT(smtpc); @@ -286,12 +504,12 @@ static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn, char tmpline[6]; curl_off_t code; const char *p = tmpline; - result = TRUE; + end = TRUE; memcpy(tmpline, line, (len == 5 ? 5 : 3)); - tmpline[len == 5 ? 5 : 3 ] = 0; + tmpline[len == 5 ? 5 : 3] = 0; if(curlx_str_number(&p, &code, len == 5 ? 99999 : 999)) return FALSE; - *resp = (int) code; + *resp = (int)code; /* Make sure real server never sends internal value */ if(*resp == 1) @@ -300,11 +518,11 @@ static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn, /* Do we have a multiline (continuation) response? */ else if(line[3] == '-' && (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) { - result = TRUE; + end = TRUE; *resp = 1; /* Internal response code */ } - return result; + return end; } /*********************************************************************** @@ -328,13 +546,12 @@ static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out) if(len > 4) { /* Find the start of the message */ len -= 4; - for(message += 4; *message == ' ' || *message == '\t'; message++, len--) + for(message += 4; 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 */ @@ -358,7 +575,7 @@ static void smtp_state(struct Curl_easy *data, struct smtp_conn *smtpc, smtpstate newstate) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE /* for debug purposes */ static const char * const names[] = { "STOP", @@ -476,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 */ @@ -511,7 +728,7 @@ static CURLcode smtp_perform_auth(struct Curl_easy *data, CURLcode result = CURLE_OK; struct smtp_conn *smtpc = Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN); - const char *ir = (const char *) Curl_bufref_ptr(initresp); + const char *ir = Curl_bufref_ptr(initresp); if(!smtpc) return CURLE_FAILED_INIT; @@ -544,8 +761,7 @@ static CURLcode smtp_continue_auth(struct Curl_easy *data, (void)mech; if(!smtpc) return CURLE_FAILED_INIT; - return Curl_pp_sendf(data, &smtpc->pp, - "%s", (const char *) Curl_bufref_ptr(resp)); + return Curl_pp_sendf(data, &smtpc->pp, "%s", Curl_bufref_ptr(resp)); } /*********************************************************************** @@ -618,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 = ""; @@ -632,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) */ @@ -645,7 +862,7 @@ static CURLcode smtp_perform_command(struct Curl_easy *data, utf8 ? " SMTPUTF8" : ""); Curl_free_idnconverted_hostname(&host); - free(address); + curlx_free(address); } else { /* Establish whether we should report that we support SMTPUTF8 for EXPN @@ -707,25 +924,26 @@ 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 = aprintf("<%s@%s>%s", address, host.name, suffix); + from = curl_maprintf("<%s@%s>%s", address, host.name, suffix); 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 */ - from = aprintf("<%s>%s", address, suffix); + /* 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); - free(address); + curlx_free(address); } else /* Null reverse-path, RFC-5321, sect. 3.6.3 */ - from = strdup("<>"); + from = curlx_strdup("<>"); if(!from) { result = CURLE_OUT_OF_MEMORY; @@ -748,25 +966,26 @@ 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) { - auth = aprintf("<%s@%s>%s", address, host.name, suffix); + auth = curl_maprintf("<%s@%s>%s", address, host.name, suffix); Curl_free_idnconverted_hostname(&host); } else - /* An invalid mailbox was provided but we will simply let the server - worry about it */ - auth = aprintf("<%s>%s", address, suffix); - free(address); + /* An invalid mailbox was provided but we let the server worry + about it */ + auth = curl_maprintf("<%s>%s", address, suffix); + curlx_free(address); } else /* Empty AUTH, RFC-2554, sect. 5 */ - auth = strdup("<>"); + auth = curlx_strdup("<>"); if(!auth) { result = CURLE_OUT_OF_MEMORY; @@ -776,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); @@ -806,7 +1027,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data, /* Calculate the optional SIZE parameter */ if(smtpc->size_supported && data->state.infilesize > 0) { - size = aprintf("%" FMT_OFF_T, data->state.infilesize); + size = curl_maprintf("%" FMT_OFF_T, data->state.infilesize); if(!size) { result = CURLE_OUT_OF_MEMORY; @@ -840,16 +1061,16 @@ 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 */ out: - free(from); - free(auth); - free(size); + curlx_free(from); + curlx_free(auth); + curlx_free(size); if(!result) smtp_state(data, smtpc, SMTP_MAIL); @@ -885,13 +1106,13 @@ 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); Curl_free_idnconverted_hostname(&host); - free(address); + curlx_free(address); if(!result) smtp_state(data, smtpc, SMTP_RCPT); @@ -926,7 +1147,7 @@ static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data, CURLcode result = CURLE_OK; (void)instate; - if(smtpcode/100 != 2) { + if(smtpcode / 100 != 2) { failf(data, "Got unexpected smtp-server response: %d", smtpcode); result = CURLE_WEIRD_SERVER_REPLY; } @@ -975,9 +1196,9 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, (void)instate; - if(smtpcode/100 != 2 && smtpcode != 1) { - if(data->set.use_ssl <= CURLUSESSL_TRY - || Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) + if(smtpcode / 100 != 2 && smtpcode != 1) { + if(data->set.use_ssl <= CURLUSESSL_TRY || + Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) result = smtp_perform_helo(data, smtpc); else { failf(data, "Remote access denied: %d", smtpcode); @@ -989,19 +1210,19 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data, len -= 4; /* Does the server support the STARTTLS capability? */ - if(len >= 8 && !memcmp(line, "STARTTLS", 8)) + if(len >= 8 && curl_strnequal(line, "STARTTLS", 8)) smtpc->tls_supported = TRUE; /* Does the server support the SIZE capability? */ - else if(len >= 4 && !memcmp(line, "SIZE", 4)) + else if(len >= 4 && curl_strnequal(line, "SIZE", 4)) smtpc->size_supported = TRUE; /* Does the server support the UTF-8 capability? */ - else if(len >= 8 && !memcmp(line, "SMTPUTF8", 8)) + else if(len >= 8 && curl_strnequal(line, "SMTPUTF8", 8)) smtpc->utf8_supported = TRUE; /* Does the server support authentication? */ - else if(len >= 5 && !memcmp(line, "AUTH ", 5)) { + else if(len >= 5 && curl_strnequal(line, "AUTH ", 5)) { smtpc->auth_supported = TRUE; /* Advance past the AUTH keyword */ @@ -1014,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--; } @@ -1026,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 */ @@ -1076,7 +1293,7 @@ static CURLcode smtp_state_helo_resp(struct Curl_easy *data, CURLcode result = CURLE_OK; (void)instate; - if(smtpcode/100 != 2) { + if(smtpcode / 100 != 2) { failf(data, "Remote access denied: %d", smtpcode); result = CURLE_REMOTE_ACCESS_DENIED; } @@ -1123,13 +1340,13 @@ 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; - if((smtp->rcpt && smtpcode/100 != 2 && smtpcode != 553 && smtpcode != 1) || - (!smtp->rcpt && smtpcode/100 != 2 && smtpcode != 1)) { + if((smtp->rcpt && smtpcode / 100 != 2 && smtpcode != 553 && smtpcode != 1) || + (!smtp->rcpt && smtpcode / 100 != 2 && smtpcode != 1)) { failf(data, "Command failed: %d", smtpcode); result = CURLE_WEIRD_SERVER_REPLY; } @@ -1137,7 +1354,7 @@ static CURLcode smtp_state_command_resp(struct Curl_easy *data, if(!data->req.no_body) result = Curl_client_write(data, CLIENTWRITE_BODY, line, len); - if(smtpcode != 1) { + if(!result && (smtpcode != 1)) { if(smtp->rcpt) { smtp->rcpt = smtp->rcpt->next; @@ -1168,7 +1385,7 @@ static CURLcode smtp_state_mail_resp(struct Curl_easy *data, CURLcode result = CURLE_OK; (void)instate; - if(smtpcode/100 != 2) { + if(smtpcode / 100 != 2) { failf(data, "MAIL failed: %d", smtpcode); result = CURLE_SEND_ERROR; } @@ -1192,7 +1409,7 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data, (void)instate; - is_smtp_err = (smtpcode/100 != 2); + is_smtp_err = (smtpcode / 100 != 2); /* If there is multiple RCPT TO to be issued, it is possible to ignore errors and proceed with only the valid addresses. */ @@ -1421,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() @@ -1441,16 +1672,13 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done) if(!smtpc) return CURLE_FAILED_INIT; - /* We always support persistent connections in SMTP */ - connkeep(data->conn, "SMTP default"); - PINGPONG_SETUP(&smtpc->pp, smtp_pp_statemachine, smtp_endofresp); /* Initialize the SASL storage */ Curl_sasl_init(&smtpc->sasl, data, &saslsmtp); /* Initialise the pingpong layer */ - Curl_pp_init(&smtpc->pp); + Curl_pp_init(&smtpc->pp, Curl_pgrs_now(data)); /* Parse the URL options */ result = smtp_parse_url_options(data->conn, smtpc); @@ -1496,7 +1724,7 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, return CURLE_OK; /* Cleanup our per-request based variables */ - Curl_safefree(smtp->custom); + curlx_safefree(smtp->custom); if(status) { connclose(conn, "SMTP done with bad status"); /* marked for closure */ @@ -1579,6 +1807,55 @@ out: return result; } +/* Call this when the DO phase has completed */ +static CURLcode smtp_dophase_done(struct Curl_easy *data, + struct SMTP *smtp, + bool connected) +{ + (void)connected; + + if(smtp->transfer != PPTRANSFER_BODY) + /* no data to transfer */ + Curl_xfer_setup_nop(data); + + return CURLE_OK; +} + +/*********************************************************************** + * + * smtp_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 smtp_regular_transfer(struct Curl_easy *data, + struct smtp_conn *smtpc, + struct SMTP *smtp, + 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 = smtp_perform(data, smtpc, smtp, &connected, dophase_done); + + /* Perform post DO phase operations if necessary */ + if(!result && *dophase_done) + result = smtp_dophase_done(data, smtp, connected); + + CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d", + result, *dophase_done); + return result; +} + /*********************************************************************** * * smtp_do() @@ -1624,7 +1901,6 @@ static CURLcode smtp_disconnect(struct Curl_easy *data, { struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN); - (void)data; if(!smtpc) return CURLE_FAILED_INIT; @@ -1642,20 +1918,6 @@ static CURLcode smtp_disconnect(struct Curl_easy *data, return CURLE_OK; } -/* Call this when the DO phase has completed */ -static CURLcode smtp_dophase_done(struct Curl_easy *data, - struct SMTP *smtp, - bool connected) -{ - (void)connected; - - if(smtp->transfer != PPTRANSFER_BODY) - /* no data to transfer */ - Curl_xfer_setup_nop(data); - - return CURLE_OK; -} - /* Called from multi.c while DOing */ static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done) { @@ -1677,51 +1939,12 @@ static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done) return result; } -/*********************************************************************** - * - * smtp_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 smtp_regular_transfer(struct Curl_easy *data, - struct smtp_conn *smtpc, - struct SMTP *smtp, - 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_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - /* Carry out the perform */ - result = smtp_perform(data, smtpc, smtp, &connected, dophase_done); - - /* Perform post DO phase operations if necessary */ - if(!result && *dophase_done) - result = smtp_dophase_done(data, smtp, connected); - - CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d", - result, *dophase_done); - return result; -} - - static void smtp_easy_dtor(void *key, size_t klen, void *entry) { struct SMTP *smtp = entry; (void)key; (void)klen; - free(smtp); + curlx_free(smtp); } static void smtp_conn_dtor(void *key, size_t klen, void *entry) @@ -1730,8 +1953,8 @@ static void smtp_conn_dtor(void *key, size_t klen, void *entry) (void)key; (void)klen; Curl_pp_disconnect(&smtpc->pp); - Curl_safefree(smtpc->domain); - free(smtpc); + curlx_safefree(smtpc->domain); + curlx_free(smtpc); } static CURLcode smtp_setup_connection(struct Curl_easy *data, @@ -1741,14 +1964,14 @@ static CURLcode smtp_setup_connection(struct Curl_easy *data, struct SMTP *smtp; CURLcode result = CURLE_OK; - smtpc = calloc(1, sizeof(*smtpc)); + 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; + result = CURLE_OUT_OF_MEMORY; + goto out; } - smtp = calloc(1, sizeof(*smtp)); + 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; @@ -1758,336 +1981,27 @@ out: return result; } -/*********************************************************************** - * - * smtp_parse_url_options() - * - * Parse the URL login options. +/* + * SMTP protocol handler. */ -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 = 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(eos); /* we have returned an EOS */ +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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ }; -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) -{ - 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); - 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, 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++; - } - - 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; - } - } - - if(ctx->read_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; - 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; - } - } - - *peos = FALSE; - 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. */ - ctx->eos = TRUE; - } - *peos = 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; -} - #endif /* CURL_DISABLE_SMTP */ diff --git a/lib/smtp.h b/lib/smtp.h index ed9824b14c..75b81979c2 100644 --- a/lib/smtp.h +++ b/lib/smtp.h @@ -23,11 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "pingpong.h" -#include "curl_sasl.h" - -extern const struct Curl_handler Curl_handler_smtp; -extern const struct Curl_handler Curl_handler_smtps; +#ifndef CURL_DISABLE_SMTP +extern const struct Curl_protocol Curl_protocol_smtp; +#endif #endif /* HEADER_CURL_SMTP_H */ diff --git a/lib/sockaddr.h b/lib/sockaddr.h index 2e2d375e06..2b03335087 100644 --- a/lib/sockaddr.h +++ b/lib/sockaddr.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" struct Curl_sockaddr_storage { diff --git a/lib/socketpair.c b/lib/socketpair.c index 4151a9bb97..76b959dd4a 100644 --- a/lib/socketpair.c +++ b/lib/socketpair.c @@ -21,17 +21,21 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #include "socketpair.h" #include "urldata.h" #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) { @@ -48,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; @@ -60,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; } @@ -69,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; } @@ -80,67 +84,67 @@ 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) -{ -#ifdef SOCK_NONBLOCK - type = nonblocking ? type | SOCK_NONBLOCK : type; +#ifndef USE_UNIX_SOCKETS +#error "unsupported Unix domain and socketpair build combo" #endif - if(socketpair(domain, type, protocol, socks)) + +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 + if(nonblocking) + type |= SOCK_NONBLOCK; +#endif + + 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 */ -#ifdef _WIN32 -/* - * This is a socketpair() implementation for Windows. - */ -#include -#ifdef HAVE_IO_H -#include -#endif -#else + +#else /* !USE_EVENTFD && !HAVE_PIPE && !HAVE_SOCKETPAIR */ + #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_NETINET_IN_H -#include /* IPPROTO_TCP */ +#include /* for IPPROTO_TCP */ #endif #ifdef HAVE_ARPA_INET_H #include #endif + #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 -#endif /* !INADDR_LOOPBACK */ -#endif /* !_WIN32 */ +#endif -#include "curlx/nonblock.h" /* for curlx_nonblock */ -#include "curlx/timeval.h" /* needed before select.h */ #include "select.h" /* for Curl_poll */ -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -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; @@ -150,11 +154,8 @@ 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 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + listener = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(listener == CURL_SOCKET_BAD) return -1; @@ -188,7 +189,7 @@ int Curl_socketpair(int domain, int type, int protocol, goto error; if(listen(listener, 1) == -1) goto error; - socks[0] = socket(AF_INET, SOCK_STREAM, 0); + socks[0] = CURL_SOCKET(AF_INET, SOCK_STREAM, 0); if(socks[0] == CURL_SOCKET_BAD) goto error; if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1) @@ -229,18 +230,18 @@ int Curl_socketpair(int domain, int type, int protocol, if(nread == -1) { int sockerr = SOCKERRNO; /* Do not block forever */ - if(curlx_timediff(curlx_now(), start) > (60 * 1000)) + if(curlx_timediff_ms(curlx_now(), start) > (60 * 1000)) goto error; if( #ifdef USE_WINSOCK - /* This is how Windows does it */ - (SOCKEWOULDBLOCK == sockerr) + /* This is how Windows does it */ + (SOCKEWOULDBLOCK == sockerr) #else - /* errno may be EWOULDBLOCK or on some systems EAGAIN when it - returned due to its inability to send off data without - blocking. We therefore treat both error codes the same here */ - (SOCKEWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || - (SOCKEINTR == sockerr) || (SOCKEINPROGRESS == sockerr) + /* errno may be EWOULDBLOCK or on some systems EAGAIN when it + returned due to its inability to send off data without + blocking. We therefore treat both error codes the same here */ + (SOCKEWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || + (SOCKEINTR == sockerr) || (SOCKEINPROGRESS == sockerr) #endif ) { continue; @@ -262,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; @@ -269,7 +274,103 @@ error: 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/lib/socketpair.h b/lib/socketpair.h index e601f5f71b..0427e72fc5 100644 --- a/lib/socketpair.h +++ b/lib/socketpair.h @@ -23,60 +23,21 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #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) - -#include -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) - -#include -int Curl_pipe(curl_socket_t socks[2], bool nonblocking); - -#else /* !USE_EVENTFD && !HAVE_PIPE */ - -#define wakeup_write swrite -#define wakeup_read sread -#define wakeup_close sclose - -#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 -#include -int Curl_socketpair(int domain, int type, int protocol, - 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]); + +/* return 0 on success or errno on failure */ +int Curl_wakeup_signal(curl_socket_t socks[2]); + +CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all); + +#else +#define Curl_wakeup_destroy(x) Curl_nop_stmt #endif #endif /* HEADER_CURL_SOCKETPAIR_H */ diff --git a/lib/socks.c b/lib/socks.c index fc6c9730f8..8d1a4e9540 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifndef CURL_DISABLE_PROXY @@ -34,61 +33,81 @@ #endif #include "urldata.h" -#include "sendf.h" +#include "bufq.h" +#include "curl_addrinfo.h" +#include "curl_trc.h" #include "select.h" #include "cfilters.h" +#include "cf-dns.h" #include "connect.h" -#include "curlx/timeval.h" #include "socks.h" -#include "multiif.h" /* for getsock macros */ #include "curlx/inet_pton.h" -#include "url.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" /* for the (SOCKS) connect state machine */ -enum connect_t { - CONNECT_INIT, - CONNECT_SOCKS_INIT, /* 1 */ - CONNECT_SOCKS_SEND, /* 2 waiting to send more first data */ - CONNECT_SOCKS_READ_INIT, /* 3 set up read */ - CONNECT_SOCKS_READ, /* 4 read server response */ - CONNECT_GSSAPI_INIT, /* 5 */ - CONNECT_AUTH_INIT, /* 6 setup outgoing auth buffer */ - CONNECT_AUTH_SEND, /* 7 send auth */ - CONNECT_AUTH_READ, /* 8 read auth response */ - CONNECT_REQ_INIT, /* 9 init SOCKS "request" */ - CONNECT_RESOLVING, /* 10 */ - CONNECT_RESOLVED, /* 11 */ - CONNECT_RESOLVE_REMOTE, /* 12 */ - CONNECT_REQ_SEND, /* 13 */ - CONNECT_REQ_SENDING, /* 14 */ - CONNECT_REQ_READ, /* 15 */ - CONNECT_REQ_READ_MORE, /* 16 */ - CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */ +enum socks_state_t { + SOCKS_ST_INIT, + /* SOCKS Version 4 states */ + SOCKS4_ST_START, + SOCKS4_ST_RESOLVING, + SOCKS4_ST_SEND, + SOCKS4_ST_RECV, + /* SOCKS Version 5 states */ + SOCKS5_ST_START, + SOCKS5_ST_REQ0_SEND, + SOCKS5_ST_RESP0_RECV, /* set up read */ + SOCKS5_ST_GSSAPI_INIT, + SOCKS5_ST_AUTH_INIT, /* setup outgoing auth buffer */ + SOCKS5_ST_AUTH_SEND, /* send auth */ + SOCKS5_ST_AUTH_RECV, /* read auth response */ + SOCKS5_ST_REQ1_INIT, /* init SOCKS "request" */ + SOCKS5_ST_RESOLVING, + SOCKS5_ST_REQ1_SEND, + SOCKS5_ST_RESP1_RECV, + /* Terminal states, all SOCKS versions */ + SOCKS_ST_SUCCESS, + SOCKS_ST_FAILED }; -#define CURL_SOCKS_BUF_SIZE 600 - -/* make sure we configure it not too low */ -#if CURL_SOCKS_BUF_SIZE < 600 -#error CURL_SOCKS_BUF_SIZE must be at least 600 +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) +static const char * const cf_socks_statename[] = { + "SOCKS_INIT", + "SOCKS4_START", + "SOCKS4_RESOLVING", + "SOCKS4_SEND", + "SOCKS4_RECV", + "SOCKS5_START", + "SOCKS5_REQ0_SEND", + "SOCKS5_RESP0_RECV", + "SOCKS5_GSSAPI_INIT", + "SOCKS5_AUTH_INIT", + "SOCKS5_AUTH_SEND", + "SOCKS5_AUTH_RECV", + "SOCKS5_REQ1_INIT", + "SOCKS5_RESOLVING", + "SOCKS5_REQ1_SEND", + "SOCKS5_RESP1_RECV", + "SOCKS_SUCCESS", + "SOCKS_FAILED" +}; #endif +#define SOCKS_CHUNK_SIZE 1024 +#define SOCKS_CHUNKS 1 + struct socks_state { - enum connect_t state; - size_t outstanding; /* send this many bytes more */ - unsigned char buffer[CURL_SOCKS_BUF_SIZE]; - unsigned char *outp; /* send from this pointer */ - + enum socks_state_t state; + struct bufq iobuf; const char *hostname; - int remote_port; + uint16_t remote_port; const char *proxy_user; const char *proxy_password; + CURLproxycode presult; + uint32_t resolv_id; + unsigned char version; + BIT(resolve_local); + BIT(start_resolving); + BIT(socks4a); }; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -98,39 +117,38 @@ struct socks_state { * * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions. */ -int Curl_blockread_all(struct Curl_cfilter *cf, - struct Curl_easy *data, /* transfer */ - char *buf, /* store read data here */ - size_t blen, /* space in buf */ - size_t *pnread) /* amount bytes read */ +CURLcode Curl_blockread_all(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, /* store read data here */ + size_t blen, /* space in buf */ + size_t *pnread) /* amount bytes read */ { size_t nread = 0; - CURLcode err; + CURLcode result; *pnread = 0; for(;;) { - timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); + timediff_t timeout_ms = Curl_timeleft_ms(data); if(timeout_ms < 0) { /* we already got the timeout */ return CURLE_OPERATION_TIMEDOUT; } if(!timeout_ms) timeout_ms = TIMEDIFF_T_MAX; - if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) { - return ~CURLE_OK; - } - err = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); - if(CURLE_AGAIN == err) + 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(result == CURLE_AGAIN) continue; - else if(err) - return (int)err; + else if(result) + return result; if(blen == nread) { *pnread += nread; return CURLE_OK; } if(!nread) /* EOF */ - return ~CURLE_OK; + return CURLE_RECV_ERROR; buf += nread; blen -= nread; @@ -139,326 +157,232 @@ int Curl_blockread_all(struct Curl_cfilter *cf, } #endif -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) -#define DEBUG_AND_VERBOSE -#define sxstate(x,c,d,y) socksstate(x,c,d,y, __LINE__) +#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) +#define sxstate(x, c, d, y) socksstate(x, c, d, y) #endif /* always use this function to change state, to make debugging easier */ static void socksstate(struct socks_state *sx, struct Curl_cfilter *cf, struct Curl_easy *data, - enum connect_t state -#ifdef DEBUG_AND_VERBOSE + enum socks_state_t state +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) , int lineno #endif ) { - enum connect_t oldstate = sx->state; -#ifdef DEBUG_AND_VERBOSE - /* synced with the state list in urldata.h */ - static const char * const socks_statename[] = { - "INIT", - "SOCKS_INIT", - "SOCKS_SEND", - "SOCKS_READ_INIT", - "SOCKS_READ", - "GSSAPI_INIT", - "AUTH_INIT", - "AUTH_SEND", - "AUTH_READ", - "REQ_INIT", - "RESOLVING", - "RESOLVED", - "RESOLVE_REMOTE", - "REQ_SEND", - "REQ_SENDING", - "REQ_READ", - "REQ_READ_MORE", - "DONE" - }; -#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)", - socks_statename[oldstate], socks_statename[sx->state], lineno); + cf_socks_statename[oldstate], + cf_socks_statename[sx->state], lineno); +#else + (void)cf; + (void)data; #endif } -static CURLproxycode socks_state_send(struct Curl_cfilter *cf, - struct socks_state *sx, - struct Curl_easy *data, - CURLproxycode failcode, - const char *description) +static CURLproxycode socks_failed(struct socks_state *sx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + CURLproxycode presult) { + sxstate(sx, cf, data, SOCKS_ST_FAILED); + sx->presult = presult; + return presult; +} + +static CURLproxycode socks_flush(struct socks_state *sx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + CURLcode result; + size_t nwritten; + + *done = FALSE; + while(!Curl_bufq_is_empty(&sx->iobuf)) { + result = Curl_cf_send_bufq(cf->next, data, &sx->iobuf, NULL, 0, + &nwritten); + if(result == CURLE_AGAIN) + return CURLPX_OK; + else if(result) { + failf(data, "Failed to send SOCKS request: %s", + curl_easy_strerror(result)); + return socks_failed(sx, cf, data, CURLPX_SEND_CONNECT); + } + } + *done = TRUE; + return CURLPX_OK; +} + +static CURLproxycode socks_recv(struct socks_state *sx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + size_t min_bytes, + bool *done) +{ + CURLcode result; + size_t nread; + + *done = FALSE; + while(Curl_bufq_len(&sx->iobuf) < min_bytes) { + result = Curl_cf_recv_bufq(cf->next, data, &sx->iobuf, + min_bytes - Curl_bufq_len(&sx->iobuf), + &nread); + if(result == CURLE_AGAIN) + return CURLPX_OK; + else if(result) { + failf(data, "Failed to receive SOCKS response: %s", + curl_easy_strerror(result)); + return CURLPX_RECV_CONNECT; + } + else if(!nread) { /* EOF */ + if(Curl_bufq_len(&sx->iobuf) < min_bytes) { + failf(data, "Failed to receive SOCKS response, " + "proxy closed connection"); + return CURLPX_RECV_CONNECT; + } + break; + } + } + *done = TRUE; + return CURLPX_OK; +} + +static CURLproxycode socks4_req_add_hd(struct socks_state *sx, + struct Curl_easy *data) +{ + unsigned char buf[4]; size_t nwritten; CURLcode result; - result = Curl_conn_cf_send(cf->next, data, (char *)sx->outp, - sx->outstanding, FALSE, &nwritten); - if(result) { - if(CURLE_AGAIN == result) - return CURLPX_OK; + (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 */ - failf(data, "Failed to send %s: %s", description, - curl_easy_strerror(result)); - return failcode; - } - else if(!nwritten) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } - - DEBUGASSERT(sx->outstanding >= nwritten); - /* not done, remain in state */ - sx->outstanding -= nwritten; - sx->outp += nwritten; + result = Curl_bufq_write(&sx->iobuf, buf, 4, &nwritten); + if(result || (nwritten != 4)) + return CURLPX_SEND_REQUEST; return CURLPX_OK; } -static CURLproxycode socks_state_recv(struct Curl_cfilter *cf, - struct socks_state *sx, +static CURLproxycode socks4_req_add_user(struct socks_state *sx, + struct Curl_easy *data) +{ + CURLcode result; + size_t nwritten; + + if(sx->proxy_user) { + size_t plen = strlen(sx->proxy_user); + if(plen > 255) { + /* there is no real size limit to this field in the protocol, but + SOCKS5 limits the proxy user field to 255 bytes and it seems likely + that a longer field is either a mistake or malicious input */ + failf(data, "Too long SOCKS proxy username"); + return CURLPX_LONG_USER; + } + /* add proxy name WITH trailing zero */ + result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, plen + 1, + &nwritten); + if(result || (nwritten != (plen + 1))) + return CURLPX_SEND_REQUEST; + } + else { + /* empty username */ + unsigned char b = 0; + result = Curl_bufq_write(&sx->iobuf, &b, 1, &nwritten); + if(result || (nwritten != 1)) + return CURLPX_SEND_REQUEST; + } + return CURLPX_OK; +} + +static CURLproxycode socks4_resolving(struct socks_state *sx, + struct Curl_cfilter *cf, struct Curl_easy *data, - CURLproxycode failcode, - const char *description) + bool *done) { - size_t nread; + const struct Curl_addrinfo *ai = NULL; CURLcode result; + size_t nwritten; + bool dns_done; - result = Curl_conn_cf_recv(cf->next, data, (char *)sx->outp, - sx->outstanding, &nread); + *done = FALSE; + if(sx->start_resolving) { + /* need to resolve hostname to add destination address */ + sx->start_resolving = FALSE; + DEBUGASSERT(sx->hostname && *sx->hostname); + result = Curl_cf_dns_insert_after( + cf, data, Curl_resolv_dns_queries(data, cf->conn->ip_version), + sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + if(result) { + failf(data, "unable to create DNS filter for socks"); + return CURLPX_UNKNOWN_FAIL; + } + } + + /* resolve the hostname by connecting the DNS filter */ + result = Curl_conn_cf_connect(cf->next, data, &dns_done); if(result) { - if(CURLE_AGAIN == result) - return CURLPX_OK; + failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", + sx->hostname); + return CURLPX_RESOLVE_HOST; + } + else if(!dns_done) + return CURLPX_OK; - failf(data, "SOCKS: Failed receiving %s: %s", description, - curl_easy_strerror(result)); - return failcode; + ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + if(ai) { + struct sockaddr_in *saddr_in; + char ipbuf[64]; + + Curl_printable_address(ai, ipbuf, sizeof(ipbuf)); + CURL_TRC_CF(data, cf, "SOCKS4 connect to IPv4 %s (locally resolved)", + ipbuf); + + saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr; + result = Curl_bufq_write(&sx->iobuf, + (unsigned char *)&saddr_in->sin_addr.s_addr, 4, + &nwritten); + + if(result || (nwritten != 4)) + return CURLPX_SEND_REQUEST; } - else if(!nread) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; + else { + /* No ipv4 address resolved */ + failf(data, "SOCKS4 connection to %s not supported", sx->hostname); + return CURLPX_RESOLVE_HOST; } - /* remain in reading state */ - DEBUGASSERT(sx->outstanding >= nread); - sx->outstanding -= nread; - sx->outp += nread; + + *done = TRUE; return CURLPX_OK; } -/* -* This function logs in to a SOCKS4 proxy and sends the specifics to the final -* destination server. -* -* Reference : -* https://www.openssh.com/txt/socks4.protocol -* -* Note : -* Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" -* Nonsupport "Identification Protocol (RFC1413)" -*/ -static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, - struct socks_state *sx, - struct Curl_easy *data) +static CURLproxycode socks4_check_resp(struct socks_state *sx, + struct Curl_cfilter *cf, + struct Curl_easy *data) { - struct connectdata *conn = cf->conn; - const bool protocol4a = - (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A); - unsigned char *socksreq = sx->buffer; - CURLcode result; - CURLproxycode presult; - struct Curl_dns_entry *dns = NULL; + const unsigned char *resp; + size_t rlen; - switch(sx->state) { - case CONNECT_SOCKS_INIT: - /* SOCKS4 can only do IPv4, insist! */ - conn->ip_version = CURL_IPRESOLVE_V4; - CURL_TRC_CF(data, cf, "SOCKS4%s communication to%s %s:%d", - protocol4a ? "a" : "", - conn->bits.httpproxy ? " HTTP proxy" : "", - sx->hostname, sx->remote_port); - - /* - * Compose socks4 request - * - * Request format - * - * +----+----+----+----+----+----+----+----+----+----+....+----+ - * | VN | CD | DSTPORT | DSTIP | USERID |NULL| - * +----+----+----+----+----+----+----+----+----+----+....+----+ - * # of bytes: 1 1 2 4 variable 1 - */ - - socksreq[0] = 4; /* version (SOCKS4) */ - socksreq[1] = 1; /* connect */ - socksreq[2] = (unsigned char)((sx->remote_port >> 8) & 0xff); /* MSB */ - socksreq[3] = (unsigned char)(sx->remote_port & 0xff); /* LSB */ - - /* DNS resolve only for SOCKS4, not SOCKS4a */ - if(!protocol4a) { - result = Curl_resolv(data, sx->hostname, sx->remote_port, - cf->conn->ip_version, TRUE, &dns); - - if(result == CURLE_AGAIN) { - sxstate(sx, cf, data, CONNECT_RESOLVING); - CURL_TRC_CF(data, cf, "SOCKS4 non-blocking resolve of %s", - sx->hostname); - return CURLPX_OK; - } - else if(result) - return CURLPX_RESOLVE_HOST; - sxstate(sx, cf, data, CONNECT_RESOLVED); - goto CONNECT_RESOLVED; - } - - /* socks4a does not resolve anything locally */ - sxstate(sx, cf, data, CONNECT_REQ_INIT); - goto CONNECT_REQ_INIT; - - case CONNECT_RESOLVING: - /* check if we have the name resolved by now */ - result = Curl_resolv_check(data, &dns); - if(!dns) { - if(result) - return CURLPX_RESOLVE_HOST; - return CURLPX_OK; - } - FALLTHROUGH(); - case CONNECT_RESOLVED: -CONNECT_RESOLVED: - { - struct Curl_addrinfo *hp = NULL; - /* - * We cannot use 'hostent' as a struct that Curl_resolv() returns. It - * returns a Curl_addrinfo pointer that may not always look the same. - */ - if(dns) { - hp = dns->addr; - - /* scan for the first IPv4 address */ - while(hp && (hp->ai_family != AF_INET)) - hp = hp->ai_next; - - if(hp) { - struct sockaddr_in *saddr_in; - char buf[64]; - Curl_printable_address(hp, buf, sizeof(buf)); - - saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; - socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0]; - socksreq[5] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[1]; - socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2]; - socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3]; - - CURL_TRC_CF(data, cf, "SOCKS4 connect to IPv4 %s (locally resolved)", - buf); - Curl_resolv_unlink(data, &dns); /* not used anymore from now on */ - } - else - failf(data, "SOCKS4 connection to %s not supported", sx->hostname); - } - else - failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", - sx->hostname); - - if(!hp) - return CURLPX_RESOLVE_HOST; - } - FALLTHROUGH(); - case CONNECT_REQ_INIT: -CONNECT_REQ_INIT: - /* - * This is currently not supporting "Identification Protocol (RFC1413)". - */ - socksreq[8] = 0; /* ensure empty userid is null-terminated */ - if(sx->proxy_user) { - size_t plen = strlen(sx->proxy_user); - if(plen > 255) { - /* there is no real size limit to this field in the protocol, but - SOCKS5 limits the proxy user field to 255 bytes and it seems likely - that a longer field is either a mistake or malicious input */ - failf(data, "Too long SOCKS proxy username"); - return CURLPX_LONG_USER; - } - /* copy the proxy name WITH trailing zero */ - memcpy(socksreq + 8, sx->proxy_user, plen + 1); - } - - /* - * Make connection - */ - { - size_t packetsize = 9 + - strlen((char *)socksreq + 8); /* size including NUL */ - - /* If SOCKS4a, set special invalid IP address 0.0.0.x */ - if(protocol4a) { - size_t hostnamelen = 0; - socksreq[4] = 0; - socksreq[5] = 0; - socksreq[6] = 0; - socksreq[7] = 1; - /* append hostname */ - hostnamelen = strlen(sx->hostname) + 1; /* length including NUL */ - if((hostnamelen <= 255) && - (packetsize + hostnamelen < sizeof(sx->buffer))) - strcpy((char *)socksreq + packetsize, sx->hostname); - else { - failf(data, "SOCKS4: too long hostname"); - return CURLPX_LONG_HOSTNAME; - } - packetsize += hostnamelen; - } - sx->outp = socksreq; - DEBUGASSERT(packetsize <= sizeof(sx->buffer)); - sx->outstanding = packetsize; - sxstate(sx, cf, data, CONNECT_REQ_SENDING); - } - FALLTHROUGH(); - case CONNECT_REQ_SENDING: - /* Send request */ - presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, - "SOCKS4 connect request"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in sending state */ - return CURLPX_OK; - } - /* done sending! */ - sx->outstanding = 8; /* receive data size */ - sx->outp = socksreq; - sxstate(sx, cf, data, CONNECT_SOCKS_READ); - - FALLTHROUGH(); - case CONNECT_SOCKS_READ: - /* Receive response */ - presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT, - "connect request ack"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in reading state */ - return CURLPX_OK; - } - sxstate(sx, cf, data, CONNECT_DONE); - break; - default: /* lots of unused states in SOCKS4 */ - break; + if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < 8) { + failf(data, "SOCKS4 reply is incomplete."); + return CURLPX_RECV_CONNECT; } + DEBUGASSERT(rlen == 8); /* * Response format * @@ -479,72 +403,194 @@ CONNECT_REQ_INIT: */ /* wrong version ? */ - if(socksreq[0]) { - failf(data, - "SOCKS4 reply has wrong version, version should be 0."); + if(resp[0]) { + failf(data, "SOCKS4 reply has wrong version, version should be 0."); return CURLPX_BAD_VERSION; } /* Result */ - switch(socksreq[1]) { + switch(resp[1]) { case 90: - CURL_TRC_CF(data, cf, "SOCKS4%s request granted.", protocol4a ? "a" : ""); - break; + CURL_TRC_CF(data, cf, "SOCKS4%s request granted.", sx->socks4a ? "a" : ""); + Curl_bufq_skip(&sx->iobuf, 8); + return CURLPX_OK; case 91: failf(data, - "[SOCKS] cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)" ", request rejected or failed.", - socksreq[4], socksreq[5], socksreq[6], socksreq[7], - (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), - (unsigned char)socksreq[1]); + resp[4], resp[5], resp[6], resp[7], + ((resp[2] << 8) | resp[3]), resp[1]); return CURLPX_REQUEST_FAILED; case 92: failf(data, - "[SOCKS] cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)" ", request rejected because SOCKS server cannot connect to " "identd on the client.", - socksreq[4], socksreq[5], socksreq[6], socksreq[7], - (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), - (unsigned char)socksreq[1]); + resp[4], resp[5], resp[6], resp[7], + ((resp[2] << 8) | resp[3]), resp[1]); return CURLPX_IDENTD; case 93: failf(data, - "[SOCKS] cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)" ", request rejected because the client program and identd " "report different user-ids.", - socksreq[4], socksreq[5], socksreq[6], socksreq[7], - (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), - (unsigned char)socksreq[1]); + resp[4], resp[5], resp[6], resp[7], + ((resp[2] << 8) | resp[3]), resp[1]); return CURLPX_IDENTD_DIFFER; default: failf(data, - "[SOCKS] cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" + "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)" ", Unknown.", - socksreq[4], socksreq[5], socksreq[6], socksreq[7], - (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), - (unsigned char)socksreq[1]); + resp[4], resp[5], resp[6], resp[7], + ((resp[2] << 8) | resp[3]), resp[1]); return CURLPX_UNKNOWN_FAIL; } - - 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) +/* + * This function logs in to a SOCKS4 proxy and sends the specifics to the final + * destination server. + * + * Reference : + * https://www.openssh.com/txt/socks4.protocol + * + * Note : + * Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" + * Nonsupport "Identification Protocol (RFC1413)" + */ +static CURLproxycode socks4_connect(struct Curl_cfilter *cf, + struct socks_state *sx, + struct Curl_easy *data) { - struct connectdata *conn = cf->conn; - const unsigned char auth = data->set.socks5auth; - unsigned char *socksreq = sx->buffer; + size_t nwritten; + CURLproxycode presult; + CURLcode result; + bool done; - if(conn->bits.httpproxy) - CURL_TRC_CF(data, cf, "SOCKS5: connecting to HTTP proxy %s port %d", +process_state: + switch(sx->state) { + case SOCKS_ST_INIT: + sx->version = 4; + sxstate(sx, cf, data, SOCKS4_ST_START); + FALLTHROUGH(); + + case SOCKS4_ST_START: + Curl_bufq_reset(&sx->iobuf); + sx->start_resolving = FALSE; + sx->socks4a = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A); + sx->resolve_local = !sx->socks4a; + sx->presult = CURLPX_OK; + + /* SOCKS4 can only do IPv4, insist! */ + cf->conn->ip_version = CURL_IPRESOLVE_V4; + CURL_TRC_CF(data, cf, "SOCKS4%s communication to%s %s:%u", + sx->socks4a ? "a" : "", + cf->conn->bits.httpproxy ? " HTTP proxy" : "", sx->hostname, sx->remote_port); + /* + * Compose socks4 request + * + * Request format + * + * +----+----+----+----+----+----+----+----+----+----+....+----+ + * | VN | CD | DSTPORT | DSTIP | USERID |NULL| + * +----+----+----+----+----+----+----+----+----+----+....+----+ + * # of bytes: 1 1 2 4 variable 1 + */ + presult = socks4_req_add_hd(sx, data); + if(presult) + return socks_failed(sx, cf, data, presult); + + /* DNS resolve only for SOCKS4, not SOCKS4a */ + if(!sx->resolve_local) { + /* socks4a, not resolving locally, sends the hostname. + * add an invalid address + user + hostname */ + unsigned char buf[4] = { 0, 0, 0, 1 }; + size_t hlen = strlen(sx->hostname) + 1; /* including NUL */ + + if(hlen > 255) { + failf(data, "SOCKS4: too long hostname"); + return socks_failed(sx, cf, data, CURLPX_LONG_HOSTNAME); + } + result = Curl_bufq_write(&sx->iobuf, buf, 4, &nwritten); + if(result || (nwritten != 4)) + return socks_failed(sx, cf, data, CURLPX_SEND_REQUEST); + presult = socks4_req_add_user(sx, data); + if(presult) + return socks_failed(sx, cf, data, presult); + result = Curl_bufq_cwrite(&sx->iobuf, sx->hostname, hlen, &nwritten); + if(result || (nwritten != hlen)) + return socks_failed(sx, cf, data, CURLPX_SEND_REQUEST); + /* request complete */ + sxstate(sx, cf, data, SOCKS4_ST_SEND); + goto process_state; + } + sx->start_resolving = TRUE; + sxstate(sx, cf, data, SOCKS4_ST_RESOLVING); + FALLTHROUGH(); + + case SOCKS4_ST_RESOLVING: + presult = socks4_resolving(sx, cf, data, &done); + if(presult) + return socks_failed(sx, cf, data, presult); + if(!done) + return CURLPX_OK; + /* append user */ + presult = socks4_req_add_user(sx, data); + if(presult) + return socks_failed(sx, cf, data, presult); + sxstate(sx, cf, data, SOCKS4_ST_SEND); + FALLTHROUGH(); + + case SOCKS4_ST_SEND: + presult = socks_flush(sx, cf, data, &done); + if(presult) + return socks_failed(sx, cf, data, presult); + else if(!done) + return CURLPX_OK; + sxstate(sx, cf, data, SOCKS4_ST_RECV); + FALLTHROUGH(); + + case SOCKS4_ST_RECV: + /* Receive 8 byte response */ + presult = socks_recv(sx, cf, data, 8, &done); + if(presult) + return socks_failed(sx, cf, data, presult); + else if(!done) + return CURLPX_OK; + presult = socks4_check_resp(sx, cf, data); + if(presult) + return socks_failed(sx, cf, data, presult); + sxstate(sx, cf, data, SOCKS_ST_SUCCESS); + FALLTHROUGH(); + + case SOCKS_ST_SUCCESS: + return CURLPX_OK; + + case SOCKS_ST_FAILED: + DEBUGASSERT(sx->presult); + return sx->presult; + + default: + DEBUGASSERT(0); + return socks_failed(sx, cf, data, CURLPX_SEND_REQUEST); + } +} + +static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, + struct socks_state *sx, + struct Curl_easy *data) +{ + const unsigned char auth = data->set.socks5auth; + unsigned char req[5]; /* version + len + 3 possible auth methods */ + unsigned char nauths; + size_t req_len, nwritten; + CURLcode result; + + (void)cf; /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ - if(!socks5_resolve_local && hostname_len > 255) { + if(!sx->resolve_local && strlen(sx->hostname) > 255) { failf(data, "SOCKS5: the destination hostname is too long to be " "resolved remotely by the proxy."); return CURLPX_LONG_HOSTNAME; @@ -557,27 +603,77 @@ static CURLproxycode socks5_init(struct Curl_cfilter *cf, /* 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 */ + req[0] = 5; /* version */ + nauths = 1; + req[1 + nauths] = 0; /* 1. no authentication */ #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - if(auth & CURLAUTH_GSSAPI) - socksreq[idx++] = 1; /* GSS-API */ + if(auth & CURLAUTH_GSSAPI) { + ++nauths; + req[1 + nauths] = 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); + if(sx->proxy_user) { + ++nauths; + req[1 + nauths] = 2; /* username/password */ + } + req[1] = nauths; + req_len = 2 + nauths; - sx->outp = socksreq; - DEBUGASSERT(idx <= sizeof(sx->buffer)); - sx->outstanding = idx; + result = Curl_bufq_write(&sx->iobuf, req, req_len, &nwritten); + if(result || (nwritten != req_len)) + return CURLPX_SEND_REQUEST; + return CURLPX_OK; +} + +static CURLproxycode socks5_check_resp0(struct socks_state *sx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + const unsigned char *resp; + unsigned char auth_mode; + size_t rlen; + + if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < 2) { + failf(data, "SOCKS5 initial reply is incomplete."); + return CURLPX_RECV_CONNECT; } - return socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, - "initial SOCKS5 request"); + if(resp[0] != 5) { + failf(data, "Received invalid version in initial SOCKS5 response."); + return CURLPX_BAD_VERSION; + } + + auth_mode = resp[1]; + Curl_bufq_skip(&sx->iobuf, 2); + + switch(auth_mode) { + case 0: + /* DONE! No authentication needed. Send request. */ + sxstate(sx, cf, data, SOCKS5_ST_REQ1_INIT); + return CURLPX_OK; + case 1: + if(data->set.socks5auth & CURLAUTH_GSSAPI) { + sxstate(sx, cf, data, SOCKS5_ST_GSSAPI_INIT); + return CURLPX_OK; + } + failf(data, + "SOCKS5 GSSAPI per-message authentication is not enabled."); + return CURLPX_GSSAPI_PERMSG; + case 2: + /* regular name + password authentication */ + if(data->set.socks5auth & CURLAUTH_BASIC) { + sxstate(sx, cf, data, SOCKS5_ST_AUTH_INIT); + return CURLPX_OK; + } + failf(data, "BASIC authentication proposed but not enabled."); + return CURLPX_NO_AUTH; + case 255: + failf(data, "No authentication method was acceptable."); + return CURLPX_NO_AUTH; + default: + failf(data, "Unknown SOCKS5 mode attempted to be used by server."); + return CURLPX_UNKNOWN_MODE; + } } static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, @@ -585,17 +681,22 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, 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; + size_t ulen = 0, plen = 0, nwritten; + unsigned char buf[2]; + CURLcode result; 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; + ulen = strlen(sx->proxy_user); + plen = strlen(sx->proxy_password); + /* the lengths must fit in a single byte */ + if(ulen > 255) { + failf(data, "Excessive username length for proxy auth"); + return CURLPX_LONG_USER; + } + if(plen > 255) { + failf(data, "Excessive password length for proxy auth"); + return CURLPX_LONG_PASSWD; + } } /* username/password request looks like @@ -605,31 +706,319 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, * | 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); + buf[0] = 1; /* username/pw subnegotiation version */ + buf[1] = (unsigned char)ulen; + result = Curl_bufq_write(&sx->iobuf, buf, 2, &nwritten); + if(result || (nwritten != 2)) + return CURLPX_SEND_REQUEST; + if(ulen) { + result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, ulen, &nwritten); + if(result || (nwritten != ulen)) + return CURLPX_SEND_REQUEST; } - 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); + buf[0] = (unsigned char)plen; + result = Curl_bufq_write(&sx->iobuf, buf, 1, &nwritten); + if(result || (nwritten != 1)) + return CURLPX_SEND_REQUEST; + if(plen) { + result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_password, plen, &nwritten); + if(result || (nwritten != plen)) + return CURLPX_SEND_REQUEST; } - len += proxy_password_len; - sxstate(sx, cf, data, CONNECT_AUTH_SEND); - DEBUGASSERT(len <= sizeof(sx->buffer)); - sx->outstanding = len; - sx->outp = socksreq; + sxstate(sx, cf, data, SOCKS5_ST_AUTH_SEND); + return CURLPX_OK; +} + +static CURLproxycode socks5_check_auth_resp(struct socks_state *sx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + const unsigned char *resp; + unsigned char auth_status; + size_t rlen; + + (void)cf; + if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < 2) { + failf(data, "SOCKS5 sub-negotiation response incomplete."); + return CURLPX_RECV_CONNECT; + } + + /* ignore the first (VER) byte */ + auth_status = resp[1]; + if(auth_status) { + failf(data, "User was rejected by the SOCKS5 server (%d %d).", + resp[0], resp[1]); + return CURLPX_USER_REJECTED; + } + Curl_bufq_skip(&sx->iobuf, 2); + return CURLPX_OK; +} + +static CURLproxycode socks5_req1_init(struct socks_state *sx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + unsigned char req[5]; + unsigned char ipbuf[16]; + const unsigned char *destination; + unsigned char desttype, destlen, hdlen; + size_t nwritten; + CURLcode result; + + req[0] = 5; /* version (SOCKS5) */ + req[1] = 1; /* connect */ + req[2] = 0; /* must be zero */ + if(sx->resolve_local) { + /* rest of request is added after resolving */ + result = Curl_bufq_write(&sx->iobuf, req, 3, &nwritten); + if(result || (nwritten != 3)) + return CURLPX_SEND_REQUEST; + return CURLPX_OK; + } + + /* remote resolving, send what type+addr/string to resolve */ +#ifdef USE_IPV6 + if(strchr(sx->hostname, ':')) { + desttype = 4; + destination = ipbuf; + destlen = 16; + if(curlx_inet_pton(AF_INET6, sx->hostname, ipbuf) != 1) + return CURLPX_BAD_ADDRESS_TYPE; + } + else +#endif + if(curlx_inet_pton(AF_INET, sx->hostname, ipbuf) == 1) { + desttype = 1; + destination = ipbuf; + destlen = 4; + } + else { + const size_t hostname_len = strlen(sx->hostname); + /* socks5_req0_init() already rejects hostnames longer than 255 bytes, so + this cast to unsigned char is safe. Assert to guard against future + refactoring that might remove or reorder that earlier check. */ + DEBUGASSERT(hostname_len <= 255); + desttype = 3; + destination = (const unsigned char *)sx->hostname; + destlen = (unsigned char)hostname_len; /* one byte length */ + } + + req[3] = desttype; + req[4] = destlen; + hdlen = (desttype == 3) ? 5 : 4; /* no length byte for ip addresses */ + result = Curl_bufq_write(&sx->iobuf, req, hdlen, &nwritten); + if(result || (nwritten != hdlen)) + return CURLPX_SEND_REQUEST; + result = Curl_bufq_write(&sx->iobuf, destination, destlen, &nwritten); + if(result || (nwritten != destlen)) + return CURLPX_SEND_REQUEST; + /* PORT MSB+LSB */ + req[0] = (unsigned char)((sx->remote_port >> 8) & 0xff); + req[1] = (unsigned char)(sx->remote_port & 0xff); + result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten); + if(result || (nwritten != 2)) + return CURLPX_SEND_REQUEST; + CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (remotely resolved)", + sx->hostname, sx->remote_port); + return CURLPX_OK; +} + +static CURLproxycode socks5_resolving(struct socks_state *sx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + const struct Curl_addrinfo *ai = NULL; + char dest[MAX_IPADR_LEN]; /* printable address */ + const unsigned char *destination = NULL; + unsigned char desttype = 1, destlen = 4; + unsigned char req[2]; + CURLcode result; + CURLproxycode presult = CURLPX_OK; + size_t nwritten; + bool dns_done; + + *done = FALSE; + if(sx->start_resolving) { + /* need to resolve hostname to add destination address */ + sx->start_resolving = FALSE; + DEBUGASSERT(sx->hostname && *sx->hostname); + result = Curl_cf_dns_insert_after( + cf, data, Curl_resolv_dns_queries(data, cf->conn->ip_version), + sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + if(result) { + failf(data, "unable to create DNS filter for socks"); + return CURLPX_UNKNOWN_FAIL; + } + } + + /* resolve the hostname by connecting the DNS filter */ + result = Curl_conn_cf_connect(cf->next, data, &dns_done); + if(result) { + failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); + return CURLPX_RESOLVE_HOST; + } + else if(!dns_done) + return CURLPX_OK; + +#ifdef USE_IPV6 + if(data->set.ipver != CURL_IPRESOLVE_V4) + ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET6, 0); +#endif + if(!ai) + ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + + if(!ai) { + failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); + presult = CURLPX_RESOLVE_HOST; + goto out; + } + + Curl_printable_address(ai, dest, sizeof(dest)); + + if(ai->ai_family == AF_INET) { + struct sockaddr_in *saddr_in; + desttype = 1; /* ATYP: IPv4 = 1 */ + destlen = 4; + saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr; + destination = (const unsigned char *)&saddr_in->sin_addr.s_addr; + CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (locally resolved)", + dest, sx->remote_port); + } +#ifdef USE_IPV6 + else if(ai->ai_family == AF_INET6) { + struct sockaddr_in6 *saddr_in6; + desttype = 4; /* ATYP: IPv6 = 4 */ + destlen = 16; + saddr_in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr; + destination = (const unsigned char *)&saddr_in6->sin6_addr.s6_addr; + CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%u (locally resolved)", + dest, sx->remote_port); + } +#endif + + if(!destination) { + failf(data, "SOCKS5 connection to %s not supported", dest); + presult = CURLPX_RESOLVE_HOST; + goto out; + } + + req[0] = desttype; + result = Curl_bufq_write(&sx->iobuf, req, 1, &nwritten); + if(result || (nwritten != 1)) { + presult = CURLPX_SEND_REQUEST; + goto out; + } + result = Curl_bufq_write(&sx->iobuf, destination, destlen, &nwritten); + if(result || (nwritten != destlen)) { + presult = CURLPX_SEND_REQUEST; + goto out; + } + /* PORT MSB+LSB */ + 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; + goto out; + } + +out: + *done = (presult == CURLPX_OK); + return presult; +} + +static CURLproxycode socks5_recv_resp1(struct socks_state *sx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + const unsigned char *resp; + size_t rlen, resp_len = 8; /* minimum response length */ + CURLproxycode presult; + + presult = socks_recv(sx, cf, data, resp_len, done); + if(presult) + return presult; + else if(!*done) + return CURLPX_OK; + + if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < resp_len) { + failf(data, "SOCKS5 response is incomplete."); + return CURLPX_RECV_CONNECT; + } + + /* Response packet includes BND.ADDR is variable length parameter by RFC + 1928, so the response packet MUST be read until the end to avoid errors + at subsequent protocol level. + + +----+-----+-------+------+----------+----------+ + |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | + +----+-----+-------+------+----------+----------+ + | 1 | 1 | X'00' | 1 | Variable | 2 | + +----+-----+-------+------+----------+----------+ + + ATYP: + o IP v4 address: X'01', BND.ADDR = 4 byte + o domain name: X'03', BND.ADDR = [ 1 byte length, string ] + o IP v6 address: X'04', BND.ADDR = 16 byte + */ + if(resp[0] != 5) { /* version */ + failf(data, "SOCKS5 reply has wrong version, version should be 5."); + return CURLPX_BAD_VERSION; + } + else if(resp[1]) { /* Anything besides 0 is an error */ + CURLproxycode rc = CURLPX_REPLY_UNASSIGNED; + int code = resp[1]; + failf(data, "cannot complete SOCKS5 connection to %s. (%d)", + sx->hostname, code); + if(code < 9) { + /* RFC 1928 section 6 lists: */ + static const CURLproxycode lookup[] = { + CURLPX_OK, + CURLPX_REPLY_GENERAL_SERVER_FAILURE, + CURLPX_REPLY_NOT_ALLOWED, + CURLPX_REPLY_NETWORK_UNREACHABLE, + CURLPX_REPLY_HOST_UNREACHABLE, + CURLPX_REPLY_CONNECTION_REFUSED, + CURLPX_REPLY_TTL_EXPIRED, + CURLPX_REPLY_COMMAND_NOT_SUPPORTED, + CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED, + }; + rc = lookup[code]; + } + return rc; + } + + /* Calculate real packet size */ + switch(resp[3]) { + case 1: /* IPv4 */ + resp_len = 4 + 4 + 2; + break; + case 3: /* domain name */ + resp_len = 4 + 1 + resp[4] + 2; /* header, var length, var bytes, port */ + break; + case 4: /* IPv6 */ + resp_len = 4 + 16 + 2; + break; + default: + failf(data, "SOCKS5 reply has wrong address type."); + return CURLPX_BAD_ADDRESS_TYPE; + } + + /* receive the rest of the response */ + presult = socks_recv(sx, cf, data, resp_len, done); + if(presult) + return presult; + else if(!*done) + return CURLPX_OK; + + if(!Curl_bufq_peek(&sx->iobuf, &resp, &rlen) || rlen < resp_len) { + failf(data, "SOCKS5 response is incomplete."); + return CURLPX_RECV_CONNECT; + } + /* got it all */ + *done = TRUE; return CURLPX_OK; } @@ -637,468 +1026,165 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, * This function logs in to a SOCKS5 proxy and sends the specifics to the final * destination server. */ -static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, - struct socks_state *sx, - struct Curl_easy *data) +static CURLproxycode socks5_connect(struct Curl_cfilter *cf, + struct socks_state *sx, + struct Curl_easy *data) { - /* - According to the RFC1928, section "6. Replies". This is what a SOCK5 - replies: - - +----+-----+-------+------+----------+----------+ - |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | - +----+-----+-------+------+----------+----------+ - | 1 | 1 | X'00' | 1 | Variable | 2 | - +----+-----+-------+------+----------+----------+ - - Where: - - o VER protocol version: X'05' - o REP Reply field: - o X'00' succeeded - */ - struct connectdata *conn = cf->conn; - unsigned char *socksreq = sx->buffer; - 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; - bool allow_gssapi = FALSE; - struct Curl_dns_entry *dns = NULL; + bool done; +process_state: switch(sx->state) { - case CONNECT_SOCKS_INIT: - presult = socks5_init(cf, sx, data, socks5_resolve_local, hostname_len); - if(presult || sx->outstanding) - return presult; - sxstate(sx, cf, data, CONNECT_SOCKS_READ); - goto CONNECT_SOCKS_READ_INIT; - case CONNECT_SOCKS_SEND: - presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT, - "initial SOCKS5 request"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in sending state */ - return CURLPX_OK; - } + case SOCKS_ST_INIT: + sx->version = 5; + sx->resolve_local = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS5); + sxstate(sx, cf, data, SOCKS5_ST_START); FALLTHROUGH(); - case CONNECT_SOCKS_READ_INIT: -CONNECT_SOCKS_READ_INIT: - sx->outstanding = 2; /* expect two bytes */ - sx->outp = socksreq; /* store it here */ + + case SOCKS5_ST_START: + if(cf->conn->bits.httpproxy) + CURL_TRC_CF(data, cf, "SOCKS5: connecting to HTTP proxy %s port %u", + sx->hostname, sx->remote_port); + presult = socks5_req0_init(cf, sx, data); + if(presult) + return socks_failed(sx, cf, data, presult); + sxstate(sx, cf, data, SOCKS5_ST_REQ0_SEND); FALLTHROUGH(); - 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) { - /* remain in reading state */ + + case SOCKS5_ST_REQ0_SEND: + presult = socks_flush(sx, cf, data, &done); + if(presult) + return socks_failed(sx, cf, data, presult); + else if(!done) return CURLPX_OK; - } - else if(socksreq[0] != 5) { - failf(data, "Received invalid version in initial SOCKS5 response."); - return CURLPX_BAD_VERSION; - } - else if(socksreq[1] == 0) { - /* DONE! No authentication needed. Send request. */ - sxstate(sx, cf, data, CONNECT_REQ_INIT); - goto CONNECT_REQ_INIT; - } - else if(socksreq[1] == 2) { - /* regular name + password authentication */ - sxstate(sx, cf, data, CONNECT_AUTH_INIT); - goto CONNECT_AUTH_INIT; - } + /* done sending! */ + sxstate(sx, cf, data, SOCKS5_ST_RESP0_RECV); + FALLTHROUGH(); + + case SOCKS5_ST_RESP0_RECV: + presult = socks_recv(sx, cf, data, 2, &done); + if(presult) + return socks_failed(sx, cf, data, presult); + else if(!done) + return CURLPX_OK; + presult = socks5_check_resp0(sx, cf, data); + if(presult) + return socks_failed(sx, cf, data, presult); + /* socks5_check_resp0() sets next socks state */ + goto process_state; + + case SOCKS5_ST_GSSAPI_INIT: { #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - else if(allow_gssapi && (socksreq[1] == 1)) { - sxstate(sx, cf, data, CONNECT_GSSAPI_INIT); - result = Curl_SOCKS5_gssapi_negotiate(cf, data); - if(result) { - failf(data, "Unable to negotiate SOCKS5 GSS-API context."); - return CURLPX_GSSAPI; - } - } -#endif - else { - /* error */ - if(!allow_gssapi && (socksreq[1] == 1)) { - failf(data, - "SOCKS5 GSSAPI per-message authentication is not supported."); - return CURLPX_GSSAPI_PERMSG; - } - else if(socksreq[1] == 255) { - failf(data, "No authentication method was acceptable."); - return CURLPX_NO_AUTH; - } - } - failf(data, - "Undocumented SOCKS5 mode attempted to be used by server."); - return CURLPX_UNKNOWN_MODE; -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - case CONNECT_GSSAPI_INIT: /* GSSAPI stuff done non-blocking */ - break; + CURLcode result = Curl_SOCKS5_gssapi_negotiate(cf, data); + if(result) { + failf(data, "Unable to negotiate SOCKS5 GSS-API context."); + return CURLPX_GSSAPI; + } + sxstate(sx, cf, data, SOCKS5_ST_REQ1_INIT); + goto process_state; +#else + failf(data, + "SOCKS5 GSSAPI per-message authentication is not supported."); + return socks_failed(sx, cf, data, CURLPX_GSSAPI_PERMSG); #endif + } - default: /* do nothing! */ - break; - -CONNECT_AUTH_INIT: - case CONNECT_AUTH_INIT: + case SOCKS5_ST_AUTH_INIT: presult = socks5_auth_init(cf, sx, data); if(presult) - return presult; + return socks_failed(sx, cf, data, presult); + sxstate(sx, cf, data, SOCKS5_ST_AUTH_SEND); FALLTHROUGH(); - case CONNECT_AUTH_SEND: - presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH, - "SOCKS5 sub-negotiation request"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in sending state */ - return CURLPX_OK; - } - sx->outp = socksreq; - sx->outstanding = 2; - sxstate(sx, cf, data, CONNECT_AUTH_READ); - FALLTHROUGH(); - case CONNECT_AUTH_READ: - presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH, - "SOCKS5 sub-negotiation response"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in reading state */ - return CURLPX_OK; - } - /* ignore the first (VER) byte */ - else if(socksreq[1]) { /* status */ - failf(data, "User was rejected by the SOCKS5 server (%d %d).", - socksreq[0], socksreq[1]); - return CURLPX_USER_REJECTED; - } + case SOCKS5_ST_AUTH_SEND: + presult = socks_flush(sx, cf, data, &done); + if(presult) + return socks_failed(sx, cf, data, presult); + else if(!done) + return CURLPX_OK; + sxstate(sx, cf, data, SOCKS5_ST_AUTH_RECV); + FALLTHROUGH(); + + case SOCKS5_ST_AUTH_RECV: + presult = socks_recv(sx, cf, data, 2, &done); + if(presult) + return socks_failed(sx, cf, data, presult); + else if(!done) + return CURLPX_OK; + presult = socks5_check_auth_resp(sx, cf, data); + if(presult) + return socks_failed(sx, cf, data, presult); /* Everything is good so far, user was authenticated! */ - sxstate(sx, cf, data, CONNECT_REQ_INIT); + sxstate(sx, cf, data, SOCKS5_ST_REQ1_INIT); FALLTHROUGH(); - case CONNECT_REQ_INIT: -CONNECT_REQ_INIT: - if(socks5_resolve_local) { - result = Curl_resolv(data, sx->hostname, sx->remote_port, - cf->conn->ip_version, TRUE, &dns); - if(result == CURLE_AGAIN) { - sxstate(sx, cf, data, CONNECT_RESOLVING); - return CURLPX_OK; - } - else if(result) - return CURLPX_RESOLVE_HOST; - sxstate(sx, cf, data, CONNECT_RESOLVED); - goto CONNECT_RESOLVED; + case SOCKS5_ST_REQ1_INIT: + presult = socks5_req1_init(sx, cf, data); + if(presult) + return socks_failed(sx, cf, data, presult); + if(!sx->resolve_local) { + /* we do not resolve, request is complete */ + sxstate(sx, cf, data, SOCKS5_ST_REQ1_SEND); + goto process_state; } - goto CONNECT_RESOLVE_REMOTE; + sx->start_resolving = TRUE; + sxstate(sx, cf, data, SOCKS5_ST_RESOLVING); + FALLTHROUGH(); - case CONNECT_RESOLVING: - /* check if we have the name resolved by now */ - result = Curl_resolv_check(data, &dns); - if(!dns) { - if(result) - return CURLPX_RESOLVE_HOST; + case SOCKS5_ST_RESOLVING: + presult = socks5_resolving(sx, cf, data, &done); + if(presult) + return socks_failed(sx, cf, data, presult); + if(!done) return CURLPX_OK; - } - FALLTHROUGH(); - case CONNECT_RESOLVED: -CONNECT_RESOLVED: - { - char dest[MAX_IPADR_LEN]; /* printable address */ - struct Curl_addrinfo *hp = NULL; - if(dns) - hp = dns->addr; -#ifdef USE_IPV6 - if(data->set.ipver != CURL_IPRESOLVE_WHATEVER) { - int wanted_family = data->set.ipver == CURL_IPRESOLVE_V4 ? - AF_INET : AF_INET6; - /* scan for the first proper address */ - while(hp && (hp->ai_family != wanted_family)) - hp = hp->ai_next; - } -#endif - if(!hp) { - failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", - sx->hostname); - return CURLPX_RESOLVE_HOST; - } - - Curl_printable_address(hp, dest, sizeof(dest)); - - len = 0; - socksreq[len++] = 5; /* version (SOCKS5) */ - socksreq[len++] = 1; /* connect */ - socksreq[len++] = 0; /* must be zero */ - if(hp->ai_family == AF_INET) { - int i; - struct sockaddr_in *saddr_in; - socksreq[len++] = 1; /* ATYP: IPv4 = 1 */ - - saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; - for(i = 0; i < 4; i++) { - socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i]; - } - - CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%d (locally resolved)", - dest, sx->remote_port); - } -#ifdef USE_IPV6 - else if(hp->ai_family == AF_INET6) { - int i; - struct sockaddr_in6 *saddr_in6; - socksreq[len++] = 4; /* ATYP: IPv6 = 4 */ - - saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr; - for(i = 0; i < 16; i++) { - socksreq[len++] = - ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i]; - } - - CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%d (locally resolved)", - dest, sx->remote_port); - } -#endif - else { - hp = NULL; /* fail! */ - failf(data, "SOCKS5 connection to %s not supported", dest); - } - - Curl_resolv_unlink(data, &dns); /* not used anymore from now on */ - goto CONNECT_REQ_SEND; - } -CONNECT_RESOLVE_REMOTE: - case CONNECT_RESOLVE_REMOTE: - /* Authentication is complete, now specify destination to the proxy */ - len = 0; - socksreq[len++] = 5; /* version (SOCKS5) */ - socksreq[len++] = 1; /* connect */ - socksreq[len++] = 0; /* must be zero */ - - if(!socks5_resolve_local) { - /* ATYP: domain name = 3, - IPv6 == 4, - IPv4 == 1 */ - unsigned char ip4[4]; -#ifdef USE_IPV6 - if(conn->bits.ipv6_ip) { - char ip6[16]; - if(curlx_inet_pton(AF_INET6, sx->hostname, ip6) != 1) - return CURLPX_BAD_ADDRESS_TYPE; - socksreq[len++] = 4; - memcpy(&socksreq[len], ip6, sizeof(ip6)); - len += sizeof(ip6); - } - else -#endif - if(curlx_inet_pton(AF_INET, sx->hostname, ip4) == 1) { - socksreq[len++] = 1; - memcpy(&socksreq[len], ip4, sizeof(ip4)); - len += sizeof(ip4); - } - else { - socksreq[len++] = 3; - socksreq[len++] = (unsigned char) hostname_len; /* one byte length */ - memcpy(&socksreq[len], sx->hostname, hostname_len); /* w/o NULL */ - len += hostname_len; - } - CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%d (remotely resolved)", - sx->hostname, sx->remote_port); - } + sxstate(sx, cf, data, SOCKS5_ST_REQ1_SEND); FALLTHROUGH(); - case CONNECT_REQ_SEND: -CONNECT_REQ_SEND: - /* PORT MSB */ - socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff); - /* PORT LSB */ - socksreq[len++] = (unsigned char)(sx->remote_port & 0xff); - + case SOCKS5_ST_REQ1_SEND: + presult = socks_flush(sx, cf, data, &done); + if(presult) + return socks_failed(sx, cf, data, presult); + else if(!done) + return CURLPX_OK; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - if(conn->socks5_gssapi_enctype) { + if(cf->conn->socks5_gssapi_enctype) { failf(data, "SOCKS5 GSS-API protection not yet implemented."); return CURLPX_GSSAPI_PROTECTION; } #endif - sx->outp = socksreq; - DEBUGASSERT(len <= sizeof(sx->buffer)); - sx->outstanding = len; - sxstate(sx, cf, data, CONNECT_REQ_SENDING); + sxstate(sx, cf, data, SOCKS5_ST_RESP1_RECV); FALLTHROUGH(); - case CONNECT_REQ_SENDING: - presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST, - "SOCKS5 connect request"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in send state */ + + case SOCKS5_ST_RESP1_RECV: + presult = socks5_recv_resp1(sx, cf, data, &done); + if(presult) + return socks_failed(sx, cf, data, presult); + if(!done) return CURLPX_OK; - } -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - if(conn->socks5_gssapi_enctype) { - failf(data, "SOCKS5 GSS-API protection not yet implemented."); - return CURLPX_GSSAPI_PROTECTION; - } -#endif - sx->outstanding = 10; /* minimum packet size is 10 */ - sx->outp = socksreq; - sxstate(sx, cf, data, CONNECT_REQ_READ); + CURL_TRC_CF(data, cf, "SOCKS5 request granted."); + sxstate(sx, cf, data, SOCKS_ST_SUCCESS); FALLTHROUGH(); - case CONNECT_REQ_READ: - presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK, - "SOCKS5 connect request ack"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in reading state */ - return CURLPX_OK; - } - else if(socksreq[0] != 5) { /* version */ - failf(data, - "SOCKS5 reply has wrong version, version should be 5."); - return CURLPX_BAD_VERSION; - } - else if(socksreq[1]) { /* Anything besides 0 is an error */ - CURLproxycode rc = CURLPX_REPLY_UNASSIGNED; - int code = socksreq[1]; - failf(data, "cannot complete SOCKS5 connection to %s. (%d)", - sx->hostname, (unsigned char)socksreq[1]); - if(code < 9) { - /* RFC 1928 section 6 lists: */ - static const CURLproxycode lookup[] = { - CURLPX_OK, - CURLPX_REPLY_GENERAL_SERVER_FAILURE, - CURLPX_REPLY_NOT_ALLOWED, - CURLPX_REPLY_NETWORK_UNREACHABLE, - CURLPX_REPLY_HOST_UNREACHABLE, - CURLPX_REPLY_CONNECTION_REFUSED, - CURLPX_REPLY_TTL_EXPIRED, - CURLPX_REPLY_COMMAND_NOT_SUPPORTED, - CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED, - }; - rc = lookup[code]; - } - return rc; - } - /* Fix: in general, returned BND.ADDR is variable length parameter by RFC - 1928, so the reply packet should be read until the end to avoid errors - at subsequent protocol level. + case SOCKS_ST_SUCCESS: + return CURLPX_OK; - +----+-----+-------+------+----------+----------+ - |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | - +----+-----+-------+------+----------+----------+ - | 1 | 1 | X'00' | 1 | Variable | 2 | - +----+-----+-------+------+----------+----------+ - - ATYP: - o IP v4 address: X'01', BND.ADDR = 4 byte - o domain name: X'03', BND.ADDR = [ 1 byte length, string ] - o IP v6 address: X'04', BND.ADDR = 16 byte - */ - - /* Calculate real packet size */ - if(socksreq[3] == 3) { - /* domain name */ - int addrlen = (int) socksreq[4]; - len = 5 + addrlen + 2; - } - else if(socksreq[3] == 4) { - /* IPv6 */ - len = 4 + 16 + 2; - } - else if(socksreq[3] == 1) { - len = 4 + 4 + 2; - } - else { - failf(data, "SOCKS5 reply has wrong address type."); - return CURLPX_BAD_ADDRESS_TYPE; - } - - /* At this point we already read first 10 bytes */ -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - if(!conn->socks5_gssapi_enctype) { - /* decrypt_gssapi_blockread already read the whole packet */ -#endif - if(len > 10) { - DEBUGASSERT(len <= sizeof(sx->buffer)); - sx->outstanding = len - 10; /* get the rest */ - sx->outp = &socksreq[10]; - sxstate(sx, cf, data, CONNECT_REQ_READ_MORE); - } - else { - sxstate(sx, cf, data, CONNECT_DONE); - break; - } -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - } -#endif - FALLTHROUGH(); - case CONNECT_REQ_READ_MORE: - presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS, - "SOCKS5 connect request address"); - if(CURLPX_OK != presult) - return presult; - else if(sx->outstanding) { - /* remain in reading state */ - return CURLPX_OK; - } - sxstate(sx, cf, data, CONNECT_DONE); - } - CURL_TRC_CF(data, cf, "SOCKS5 request granted."); - - return CURLPX_OK; /* Proxy was successful! */ -} - -static CURLcode connect_SOCKS(struct Curl_cfilter *cf, - struct socks_state *sxstate, - struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - CURLproxycode pxresult = CURLPX_OK; - struct connectdata *conn = cf->conn; - - switch(conn->socks_proxy.proxytype) { - case CURLPROXY_SOCKS5: - case CURLPROXY_SOCKS5_HOSTNAME: - pxresult = do_SOCKS5(cf, sxstate, data); - break; - - case CURLPROXY_SOCKS4: - case CURLPROXY_SOCKS4A: - pxresult = do_SOCKS4(cf, sxstate, data); - break; + case SOCKS_ST_FAILED: + DEBUGASSERT(sx->presult); + return sx->presult; default: - failf(data, "unknown proxytype option given"); - result = CURLE_COULDNT_CONNECT; - } /* switch proxytype */ - if(pxresult) { - result = CURLE_PROXY; - data->info.pxcode = pxresult; + DEBUGASSERT(0); + return socks_failed(sx, cf, data, CURLPX_SEND_REQUEST); } - - return result; } static void socks_proxy_cf_free(struct Curl_cfilter *cf) { struct socks_state *sxstate = cf->ctx; if(sxstate) { - free(sxstate); + Curl_bufq_free(&sxstate->iobuf); + curlx_free(sxstate); cf->ctx = NULL; } } @@ -1118,6 +1204,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, struct connectdata *conn = cf->conn; int sockindex = cf->sockindex; struct socks_state *sx = cf->ctx; + CURLproxycode pxresult = CURLPX_OK; if(cf->connected) { *done = TRUE; @@ -1129,17 +1216,15 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, return result; if(!sx) { - sx = calloc(1, sizeof(*sx)); - if(!sx) - return CURLE_OUT_OF_MEMORY; - cf->ctx = sx; - } + cf->ctx = sx = curlx_calloc(1, sizeof(*sx)); + if(!sx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } - if(sx->state == CONNECT_INIT) { /* for the secondary socket (FTP), use the "connect to host" * but ignore the "connect to port" (use the secondary port) */ - sxstate(sx, cf, data, CONNECT_SOCKS_INIT); sx->hostname = conn->bits.httpproxy ? conn->http_proxy.host.name : @@ -1148,36 +1233,62 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, sockindex == SECONDARYSOCKET ? conn->secondaryhostname : conn->host.name; sx->remote_port = - conn->bits.httpproxy ? (int)conn->http_proxy.port : + conn->bits.httpproxy ? conn->http_proxy.port : sockindex == SECONDARYSOCKET ? conn->secondary_port : conn->bits.conn_to_port ? conn->conn_to_port : - conn->remote_port; + (uint16_t)conn->remote_port; sx->proxy_user = conn->socks_proxy.user; sx->proxy_password = conn->socks_proxy.passwd; + Curl_bufq_init2(&sx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS, + BUFQ_OPT_SOFT_LIMIT); } - result = connect_SOCKS(cf, sx, data); - if(!result && sx->state == CONNECT_DONE) { - cf->connected = TRUE; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(Curl_trc_is_verbose(data)) { - struct ip_quadruple ipquad; - bool is_ipv6; - result = Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad); - if(result) - return result; + switch(conn->socks_proxy.proxytype) { + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + pxresult = socks5_connect(cf, sx, data); + break; + + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + pxresult = socks4_connect(cf, sx, data); + break; + + default: + failf(data, "unknown proxytype option given"); + result = CURLE_COULDNT_CONNECT; + goto out; + } + + if(pxresult) { + result = CURLE_PROXY; + data->info.pxcode = pxresult; + goto out; + } + else if(sx->state != SOCKS_ST_SUCCESS) + goto out; + +#ifdef CURLVERBOSE + if(Curl_trc_is_verbose(data)) { + struct ip_quadruple ipquad; + bool is_ipv6; + if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) infof(data, "Opened %sSOCKS connection from %s port %u to %s port %u " "(via %s port %u)", (sockindex == SECONDARYSOCKET) ? "2nd " : "", ipquad.local_ip, ipquad.local_port, sx->hostname, sx->remote_port, ipquad.remote_ip, ipquad.remote_port); - } -#endif - socks_proxy_cf_free(cf); + else + infof(data, "Opened %sSOCKS connection", + (sockindex == SECONDARYSOCKET) ? "2nd " : ""); } +#endif + socks_proxy_cf_free(cf); + cf->connected = TRUE; - *done = cf->connected; +out: + *done = (bool)cf->connected; return result; } @@ -1193,15 +1304,16 @@ static CURLcode socks_cf_adjust_pollset(struct Curl_cfilter *cf, * to wait on, we determine what to wait for. */ curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); switch(sx->state) { - case CONNECT_RESOLVING: - case CONNECT_SOCKS_READ: - case CONNECT_AUTH_READ: - case CONNECT_REQ_READ: - case CONNECT_REQ_READ_MORE: - result = Curl_pollset_set_in_only(data, ps, sock); + case SOCKS4_ST_SEND: + case SOCKS5_ST_REQ0_SEND: + case SOCKS5_ST_AUTH_SEND: + case SOCKS5_ST_REQ1_SEND: + CURL_TRC_CF(data, cf, "adjust pollset out (%d)", sx->state); + result = Curl_pollset_set_out_only(data, ps, sock); break; default: - result = Curl_pollset_set_out_only(data, ps, sock); + CURL_TRC_CF(data, cf, "adjust pollset in (%d)", sx->state); + result = Curl_pollset_set_in_only(data, ps, sock); break; } } @@ -1211,7 +1323,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); @@ -1255,7 +1366,7 @@ static CURLcode socks_cf_query(struct Curl_cfilter *cf, struct Curl_cftype Curl_cft_socks_proxy = { "SOCKS", - CF_TYPE_IP_CONNECT|CF_TYPE_PROXY, + CF_TYPE_IP_CONNECT | CF_TYPE_PROXY, 0, socks_proxy_cf_destroy, socks_proxy_cf_connect, diff --git a/lib/socks.h b/lib/socks.h index d60796316f..520fb75ddb 100644 --- a/lib/socks.h +++ b/lib/socks.h @@ -23,25 +23,20 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#ifdef CURL_DISABLE_PROXY -#define Curl_SOCKS4(a,b,c,d,e) CURLE_NOT_BUILT_IN -#define Curl_SOCKS5(a,b,c,d,e,f) CURLE_NOT_BUILT_IN -#define Curl_SOCKS_getsock(x,y,z) 0 -#else +#ifndef CURL_DISABLE_PROXY /* * Helper read-from-socket functions. Does the same as Curl_read() but it * blocks until all bytes amount of buffersize will be read. No more, no less. * * This is STUPID BLOCKING behavior */ -int Curl_blockread_all(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *buf, - size_t blen, - size_t *pnread); +CURLcode Curl_blockread_all(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, + size_t blen, + size_t *pnread); #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* @@ -56,6 +51,6 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, extern struct Curl_cftype Curl_cft_socks_proxy; -#endif /* CURL_DISABLE_PROXY */ +#endif /* !CURL_DISABLE_PROXY */ -#endif /* HEADER_CURL_SOCKS_H */ +#endif /* HEADER_CURL_SOCKS_H */ diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c index 0a7ddd5ff1..32db07044a 100644 --- a/lib/socks_gssapi.c +++ b/lib/socks_gssapi.c @@ -22,35 +22,26 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_PROXY) #include "curl_gssapi.h" #include "urldata.h" -#include "sendf.h" +#include "curl_trc.h" #include "cfilters.h" #include "connect.h" -#include "curlx/timeval.h" +#include "curlx/nonblock.h" #include "socks.h" -#include "curlx/warnless.h" -#include "strdup.h" +#include "curlx/strdup.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #define MAX_GSS_LEN 1024 -static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; - /* * Helper GSS-API error functions. */ @@ -74,8 +65,7 @@ static int check_gss_err(struct Curl_easy *data, GSS_C_NULL_OID, &msg_ctx, &status_string); if(maj_stat == GSS_S_COMPLETE) { - if(curlx_dyn_addn(&dbuf, status_string.value, - status_string.length)) + if(curlx_dyn_addn(&dbuf, status_string.value, status_string.length)) return 1; /* error */ gss_release_buffer(&min_stat, &status_string); break; @@ -92,8 +82,7 @@ static int check_gss_err(struct Curl_easy *data, GSS_C_NULL_OID, &msg_ctx, &status_string); if(maj_stat == GSS_S_COMPLETE) { - if(curlx_dyn_addn(&dbuf, status_string.value, - status_string.length)) + if(curlx_dyn_addn(&dbuf, status_string.value, status_string.length)) return 1; /* error */ gss_release_buffer(&min_stat, &status_string); break; @@ -117,7 +106,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, CURLcode code; size_t actualread; size_t nwritten; - int result; + CURLcode result; OM_uint32 gss_major_status, gss_minor_status, gss_status; OM_uint32 gss_ret_flags; int gss_conf_state, gss_enc; @@ -129,11 +118,11 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_name_t server = GSS_C_NO_NAME; gss_name_t gss_client_name = GSS_C_NO_NAME; unsigned short us_length; - char *user = NULL; unsigned char socksreq[4]; /* room for GSS-API exchange header only */ const char *serviceptr = data->set.str[STRING_PROXY_SERVICE_NAME] ? data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; const size_t serviceptr_length = strlen(serviceptr); + gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; /* GSS-API request looks like * +----+------+-----+----------------+ @@ -146,28 +135,29 @@ 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; gss_major_status = gss_import_name(&gss_minor_status, &service, - (gss_OID) GSS_C_NULL_OID, &server); + (gss_OID)GSS_C_NULL_OID, &server); } else { - service.value = malloc(serviceptr_length + - strlen(conn->socks_proxy.host.name) + 2); + service.value = curlx_malloc(serviceptr_length + + strlen(conn->socks_proxy.host.name) + 2); if(!service.value) return CURLE_OUT_OF_MEMORY; service.length = serviceptr_length + strlen(conn->socks_proxy.host.name) + 1; - msnprintf(service.value, service.length + 1, "%s@%s", - serviceptr, conn->socks_proxy.host.name); + curl_msnprintf(service.value, service.length + 1, "%s@%s", + serviceptr, conn->socks_proxy.host.name); gss_major_status = gss_import_name(&gss_minor_status, &service, GSS_C_NT_HOSTBASED_SERVICE, &server); } - gss_release_buffer(&gss_status, &service); /* clear allocated memory */ + curlx_safefree(service.value); + service.length = 0; if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_import_name()")) { @@ -178,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, @@ -192,12 +182,15 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, TRUE, &gss_ret_flags); - if(gss_token != GSS_C_NO_BUFFER) - gss_release_buffer(&gss_status, &gss_recv_token); + if(gss_token != GSS_C_NO_BUFFER) { + curlx_safefree(gss_recv_token.value); + gss_recv_token.length = 0; + } if(check_gss_err(data, gss_major_status, - gss_minor_status, "gss_init_sec_context")) { + gss_minor_status, "gss_init_sec_context") || + /* 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_recv_token); gss_release_buffer(&gss_status, &gss_send_token); Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to initial GSS-API token."); @@ -210,37 +203,32 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, us_length = htons((unsigned short)gss_send_token.length); memcpy(socksreq + 2, &us_length, sizeof(short)); - code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, - FALSE, &nwritten); + code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &nwritten); if(code || (nwritten != 4)) { failf(data, "Failed to send GSS-API authentication request."); gss_release_name(&gss_status, &server); - gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } code = Curl_conn_cf_send(cf->next, data, - (char *)gss_send_token.value, + gss_send_token.value, gss_send_token.length, FALSE, &nwritten); if(code || (gss_send_token.length != nwritten)) { failf(data, "Failed to send GSS-API authentication token."); gss_release_name(&gss_status, &server); - gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } - } gss_release_buffer(&gss_status, &gss_send_token); - gss_release_buffer(&gss_status, &gss_recv_token); if(gss_major_status != GSS_S_CONTINUE_NEEDED) break; - /* analyse response */ + /* analyze response */ /* GSS-API response looks like * +----+------+-----+----------------+ @@ -278,8 +266,15 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, memcpy(&us_length, socksreq + 2, sizeof(short)); us_length = ntohs(us_length); + if(!us_length) { + failf(data, "Invalid zero-length GSS-API authentication token."); + gss_release_name(&gss_status, &server); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + gss_recv_token.length = us_length; - gss_recv_token.value = malloc(us_length); + gss_recv_token.value = curlx_malloc(gss_recv_token.length); if(!gss_recv_token.value) { failf(data, "Could not allocate memory for GSS-API authentication " @@ -295,7 +290,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(result || (actualread != us_length)) { failf(data, "Failed to receive GSS-API authentication token."); gss_release_name(&gss_status, &server); - gss_release_buffer(&gss_status, &gss_recv_token); + curlx_safefree(gss_recv_token.value); + gss_recv_token.length = 0; Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -326,21 +322,12 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Failed to determine username."); return CURLE_COULDNT_CONNECT; } - user = malloc(gss_send_token.length + 1); - if(!user) { - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); - gss_release_name(&gss_status, &gss_client_name); - gss_release_buffer(&gss_status, &gss_send_token); - return CURLE_OUT_OF_MEMORY; - } - memcpy(user, gss_send_token.value, gss_send_token.length); - user[gss_send_token.length] = '\0'; + infof(data, "SOCKS5 server authenticated user %.*s with GSS-API.", + (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); - infof(data, "SOCKS5 server authenticated user %s with GSS-API.",user); - free(user); - user = NULL; /* Do encryption */ socksreq[0] = 1; /* GSS-API subnegotiation version */ @@ -357,8 +344,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, infof(data, "SOCKS5 server supports GSS-API %s data protection.", (gss_enc == 0) ? "no" : ((gss_enc == 1) ? "integrity" : "confidentiality")); - /* force for the moment to no data protection */ - gss_enc = 0; + /* * Sending the encryption type in clear seems wrong. It should be * protected with gss_seal()/gss_wrap(). See RFC1961 extract below @@ -395,7 +381,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, } else { gss_send_token.length = 1; - gss_send_token.value = Curl_memdup(&gss_enc, 1); + 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; @@ -406,20 +392,21 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, &gss_conf_state, &gss_w_token); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) { - gss_release_buffer(&gss_status, &gss_send_token); + curlx_safefree(gss_send_token.value); + gss_send_token.length = 0; gss_release_buffer(&gss_status, &gss_w_token); Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to wrap GSS-API encryption value into token."); return CURLE_COULDNT_CONNECT; } - gss_release_buffer(&gss_status, &gss_send_token); + curlx_safefree(gss_send_token.value); + gss_send_token.length = 0; us_length = htons((unsigned short)gss_w_token.length); memcpy(socksreq + 2, &us_length, sizeof(short)); } - code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE, - &nwritten); + code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &nwritten); if(code || (nwritten != 4)) { failf(data, "Failed to send GSS-API encryption request."); gss_release_buffer(&gss_status, &gss_w_token); @@ -429,8 +416,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, FALSE, - &nwritten); + code = Curl_conn_cf_send(cf->next, data, socksreq, 1, FALSE, &nwritten); if(code || (nwritten != 1)) { failf(data, "Failed to send GSS-API encryption type."); Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); @@ -438,7 +424,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, } } else { - code = Curl_conn_cf_send(cf->next, data, (char *)gss_w_token.value, + code = Curl_conn_cf_send(cf->next, data, gss_w_token.value, gss_w_token.length, FALSE, &nwritten); if(code || (gss_w_token.length != nwritten)) { failf(data, "Failed to send GSS-API encryption type."); @@ -474,8 +460,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, memcpy(&us_length, socksreq + 2, sizeof(short)); us_length = ntohs(us_length); + if(!us_length) { + failf(data, "Invalid zero-length GSS-API encryption token."); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + gss_recv_token.length = us_length; - gss_recv_token.value = malloc(gss_recv_token.length); + gss_recv_token.value = curlx_malloc(gss_recv_token.length); if(!gss_recv_token.value) { Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; @@ -485,7 +477,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(result || (actualread != us_length)) { failf(data, "Failed to receive GSS-API encryption type."); - gss_release_buffer(&gss_status, &gss_recv_token); + curlx_safefree(gss_recv_token.value); + gss_recv_token.length = 0; Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -496,13 +489,15 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, 0, GSS_C_QOP_DEFAULT); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) { - gss_release_buffer(&gss_status, &gss_recv_token); + curlx_safefree(gss_recv_token.value); + gss_recv_token.length = 0; gss_release_buffer(&gss_status, &gss_w_token); Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to unwrap GSS-API encryption value into token."); return CURLE_COULDNT_CONNECT; } - gss_release_buffer(&gss_status, &gss_recv_token); + curlx_safefree(gss_recv_token.value); + gss_recv_token.length = 0; if(gss_w_token.length != 1) { failf(data, "Invalid GSS-API encryption response length (%zu).", @@ -519,19 +514,21 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(gss_recv_token.length != 1) { failf(data, "Invalid GSS-API encryption response length (%zu).", gss_recv_token.length); - gss_release_buffer(&gss_status, &gss_recv_token); + curlx_safefree(gss_recv_token.value); + gss_recv_token.length = 0; Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(socksreq, gss_recv_token.value, gss_recv_token.length); - gss_release_buffer(&gss_status, &gss_recv_token); + curlx_safefree(gss_recv_token.value); + gss_recv_token.length = 0; } (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")); @@ -542,7 +539,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, return CURLE_OK; } -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c index 49210585b0..385312a368 100644 --- a/lib/socks_sspi.c +++ b/lib/socks_sspi.c @@ -22,26 +22,19 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_PROXY) #include "urldata.h" -#include "sendf.h" +#include "curl_trc.h" #include "cfilters.h" #include "connect.h" #include "strerror.h" -#include "curlx/timeval.h" +#include "curlx/nonblock.h" #include "socks.h" #include "curl_sspi.h" #include "curlx/multibyte.h" -#include "curlx/warnless.h" -#include "strdup.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" /* * Helper sspi error functions. @@ -63,50 +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; - int 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; - TimeStamp expiry; - 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"; - - /* 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 = strdup(service); + *service_namep = curlx_strdup(service); else - service_name = aprintf("%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; @@ -123,6 +136,158 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, sspi_send_token.cbBuffer = 0; sspi_send_token.pvBuffer = NULL; + (void)curlx_nonblock(sock, FALSE); + + for(;;) { + TCHAR *sname = curlx_convert_UTF8_to_tchar(service_name); + if(!sname) { + 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, + sname, + ISC_REQ_MUTUAL_AUTH | + ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_CONFIDENTIALITY | + ISC_REQ_REPLAY_DETECT, + 0, SECURITY_NATIVE_DREP, + &input_desc, 0, + sspi_context, + &output_desc, + sspi_ret_flagsp, NULL); + + curlx_free(sname); + curlx_safefree(sspi_recv_token.pvBuffer); + sspi_recv_token.cbBuffer = 0; + + if(check_sspi_err(data, status, "InitializeSecurityContext")) { + failf(data, "Failed to initialise security context."); + 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 */ + 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)); + + code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &written); + if(code || (written != 4)) { + failf(data, "Failed to send SSPI authentication request."); + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); + } + + code = Curl_conn_cf_send(cf->next, data, + sspi_send_token.pvBuffer, + sspi_send_token.cbBuffer, FALSE, &written); + if(code || (sspi_send_token.cbBuffer != written)) { + failf(data, "Failed to send SSPI authentication token."); + return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); + } + } + + if(sspi_send_token.pvBuffer) + socks5_free_token(&sspi_send_token, CURLE_OK); + sspi_send_token.cbBuffer = 0; + + curlx_safefree(sspi_recv_token.pvBuffer); + sspi_recv_token.cbBuffer = 0; + + if(status != SEC_I_CONTINUE_NEEDED) + break; + + result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); + if(result || (actualread != 4)) { + failf(data, "Failed to receive SSPI authentication response."); + return result ? result : CURLE_COULDNT_CONNECT; + } + + if(socksreq[1] == 255) { + failf(data, "User was rejected by the SOCKS5 server (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); + return CURLE_COULDNT_CONNECT; + } + + if(socksreq[1] != 1) { + failf(data, "Invalid SSPI authentication response type (%u %u).", + (unsigned int)socksreq[0], (unsigned int)socksreq[1]); + return CURLE_COULDNT_CONNECT; + } + + memcpy(&us_length, socksreq + 2, sizeof(short)); + us_length = ntohs(us_length); + + if(!us_length) { + failf(data, "Invalid zero-length SSPI authentication token."); + return CURLE_COULDNT_CONNECT; + } + + sspi_recv_token.cbBuffer = us_length; + sspi_recv_token.pvBuffer = curlx_malloc(us_length); + + 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."); + curlx_free(sspi_recv_token.pvBuffer); + return result ? result : CURLE_COULDNT_CONNECT; + } + + context_handle = sspi_context; + } + + return CURLE_OK; +} + +static CURLcode socks5_free(SecBuffer *sspi_w_token, + CURLcode result) +{ + curlx_safefree(sspi_w_token[0].pvBuffer); + curlx_safefree(sspi_w_token[1].pvBuffer); + curlx_safefree(sspi_w_token[2].pvBuffer); + return result; +} + +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; + if(sspi_ret_flags & ISC_REQ_CONFIDENTIALITY) + gss_enc = 2; + 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")); + sspi_w_token[0].pvBuffer = sspi_w_token[1].pvBuffer = sspi_w_token[2].pvBuffer = NULL; @@ -131,379 +296,134 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, wrap_desc.pBuffers = sspi_w_token; wrap_desc.ulVersion = SECBUFFER_VERSION; - cred_handle.dwLower = 0; - cred_handle.dwUpper = 0; - - names.sUserName = NULL; - - status = - Curl_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *)CURL_UNCONST(TEXT("Kerberos")), - SECPKG_CRED_OUTBOUND, - NULL, NULL, NULL, NULL, - &cred_handle, &expiry); - - 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); - if(!sname) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - status = - Curl_pSecFn->InitializeSecurityContext(&cred_handle, context_handle, - sname, - ISC_REQ_MUTUAL_AUTH | - ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_CONFIDENTIALITY | - ISC_REQ_REPLAY_DETECT, - 0, SECURITY_NATIVE_DREP, - &input_desc, 0, - &sspi_context, - &output_desc, - &sspi_ret_flags, - &expiry); - - curlx_unicodefree(sname); - - if(sspi_recv_token.pvBuffer) { - Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); - sspi_recv_token.pvBuffer = NULL; - sspi_recv_token.cbBuffer = 0; - } - - if(check_sspi_err(data, status, "InitializeSecurityContext")) { - failf(data, "Failed to initialise security context."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - if(sspi_send_token.cbBuffer) { - socksreq[0] = 1; /* GSS-API subnegotiation version */ - socksreq[1] = 1; /* authentication message type */ - us_length = htons((unsigned short)sspi_send_token.cbBuffer); - memcpy(socksreq + 2, &us_length, sizeof(short)); - - code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE, - &written); - if(code || (written != 4)) { - failf(data, "Failed to send SSPI authentication request."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - code = Curl_conn_cf_send(cf->next, data, - (char *)sspi_send_token.pvBuffer, - 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; - } - } - - if(sspi_send_token.pvBuffer) { - Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); - sspi_send_token.pvBuffer = NULL; - } - sspi_send_token.cbBuffer = 0; - - if(sspi_recv_token.pvBuffer) { - Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); - sspi_recv_token.pvBuffer = NULL; - } - sspi_recv_token.cbBuffer = 0; - - 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."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - /* ignore the first (VER) byte */ - if(socksreq[1] == 255) { /* status / message type */ - failf(data, "User was rejected by the SOCKS5 server (%u %u).", - (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - if(socksreq[1] != 1) { /* status / message type */ - failf(data, "Invalid SSPI authentication response type (%u %u).", - (unsigned int)socksreq[0], (unsigned int)socksreq[1]); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - memcpy(&us_length, socksreq + 2, sizeof(short)); - us_length = ntohs(us_length); - - sspi_recv_token.cbBuffer = us_length; - sspi_recv_token.pvBuffer = malloc(us_length); - - if(!sspi_recv_token.pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - 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."); - result = CURLE_COULDNT_CONNECT; - goto error; - } - - context_handle = &sspi_context; - } - - Curl_safefree(service_name); - - /* Everything is good so far, user was authenticated! */ - status = Curl_pSecFn->QueryCredentialsAttributes(&cred_handle, - SECPKG_CRED_ATTR_NAMES, - &names); - Curl_pSecFn->FreeCredentialsHandle(&cred_handle); - 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_unicodefree(user_utf8); -#endif - Curl_pSecFn->FreeContextBuffer(names.sUserName); - } - - /* Do encryption */ - socksreq[0] = 1; /* GSS-API subnegotiation version */ - socksreq[1] = 2; /* encryption message type */ - - gss_enc = 0; /* no data protection */ - /* do confidentiality protection if supported */ - 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") ); - /* force to no data protection, avoid encryption/decryption for now */ - gss_enc = 0; - /* - * 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(). - * - */ + 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 = malloc(sspi_sizes.cbSecurityTrailer); + 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].pvBuffer = malloc(1); - if(!sspi_w_token[1].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + sspi_w_token[1].BufferType = SECBUFFER_DATA; + sspi_w_token[1].pvBuffer = curlx_malloc(1); + 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 = malloc(sspi_sizes.cbBlockSize); - if(!sspi_w_token[2].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - status = Curl_pSecFn->EncryptMessage(&sspi_context, + sspi_w_token[2].pvBuffer = curlx_malloc(sspi_sizes.cbBlockSize); + 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; - } - sspi_send_token.cbBuffer = sspi_w_token[0].cbBuffer + - sspi_w_token[1].cbBuffer + - sspi_w_token[2].cbBuffer; - sspi_send_token.pvBuffer = malloc(sspi_send_token.cbBuffer); - if(!sspi_send_token.pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } + if(check_sspi_err(data, status, "EncryptMessage")) + return socks5_free(sspi_w_token, CURLE_COULDNT_CONNECT); - memcpy(sspi_send_token.pvBuffer, sspi_w_token[0].pvBuffer, - sspi_w_token[0].cbBuffer); - memcpy((PUCHAR) sspi_send_token.pvBuffer +(int)sspi_w_token[0].cbBuffer, + etbuf_size = sspi_w_token[0].cbBuffer + sspi_w_token[1].cbBuffer + + 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) + 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, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); - memcpy((PUCHAR) sspi_send_token.pvBuffer + - sspi_w_token[0].cbBuffer + - sspi_w_token[1].cbBuffer, + memcpy(etbuf + sspi_w_token[0].cbBuffer + sspi_w_token[1].cbBuffer, sspi_w_token[2].pvBuffer, sspi_w_token[2].cbBuffer); - Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - sspi_w_token[0].pvBuffer = NULL; - 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; - Curl_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); - sspi_w_token[2].pvBuffer = NULL; - sspi_w_token[2].cbBuffer = 0; + (void)socks5_free(sspi_w_token, CURLE_OK); - us_length = htons((unsigned short)sspi_send_token.cbBuffer); + us_length = htons((unsigned short)etbuf_size); memcpy(socksreq + 2, &us_length, sizeof(short)); } - code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE, - &written); + 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) { memcpy(socksreq, &gss_enc, 1); - code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, FALSE, - &written); + 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, - (char *)sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, FALSE, &written); - if(code || (sspi_send_token.cbBuffer != (size_t)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; } - if(sspi_send_token.pvBuffer) - Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); } result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive SSPI encryption response."); - 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)); us_length = ntohs(us_length); - sspi_w_token[0].cbBuffer = us_length; - sspi_w_token[0].pvBuffer = malloc(us_length); - if(!sspi_w_token[0].pvBuffer) { - result = CURLE_OUT_OF_MEMORY; - goto error; + if(!us_length) { + failf(data, "Invalid zero-length SSPI encryption token."); + return CURLE_COULDNT_CONNECT; } + sspi_w_token[0].cbBuffer = us_length; + sspi_w_token[0].pvBuffer = curlx_malloc(us_length); + 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."); - 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) { wrap_desc.cBuffers = 2; sspi_w_token[0].BufferType = SECBUFFER_STREAM; @@ -511,69 +431,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); if(check_sspi_err(data, status, "DecryptMessage")) { - failf(data, "Failed to query security context attributes."); - result = CURLE_COULDNT_CONNECT; - goto error; + if(sspi_w_token[1].pvBuffer) + Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + 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); - result = CURLE_COULDNT_CONNECT; - goto error; + if(sspi_w_token[1].pvBuffer) + Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); + curlx_free(sspi_w_token[0].pvBuffer); + return CURLE_COULDNT_CONNECT; } memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); - Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); } else { if(sspi_w_token[0].cbBuffer != 1) { failf(data, "Invalid SSPI encryption response length (%lu).", (unsigned long)sspi_w_token[0].cbBuffer); - 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_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); } - (void)curlx_nonblock(sock, TRUE); + curlx_free(sspi_w_token[0].pvBuffer); - infof(data, "SOCKS5 access with%s protection granted.", - (socksreq[0] == 0) ? "out GSS-API data": + infof(data, "SOCKS5 access with%s protection granted BUT NOT USED.", + (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; -error: - free(service_name); - Curl_pSecFn->FreeCredentialsHandle(&cred_handle); +} + +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; + + curlx_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); - if(sspi_recv_token.pvBuffer) - Curl_pSecFn->FreeContextBuffer(sspi_recv_token.pvBuffer); - if(sspi_send_token.pvBuffer) - Curl_pSecFn->FreeContextBuffer(sspi_send_token.pvBuffer); + 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); if(names.sUserName) Curl_pSecFn->FreeContextBuffer(names.sUserName); - if(sspi_w_token[0].pvBuffer) - Curl_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer); - if(sspi_w_token[1].pvBuffer) - Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - if(sspi_w_token[2].pvBuffer) - Curl_pSecFn->FreeContextBuffer(sspi_w_token[2].pvBuffer); return result; } #endif diff --git a/lib/speedcheck.c b/lib/speedcheck.c deleted file mode 100644 index b063e5d4f2..0000000000 --- a/lib/speedcheck.c +++ /dev/null @@ -1,80 +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" - -#include -#include "urldata.h" -#include "sendf.h" -#include "transfer.h" -#include "multiif.h" -#include "speedcheck.h" - -void Curl_speedinit(struct Curl_easy *data) -{ - memset(&data->state.keeps_speed, 0, sizeof(struct curltime)); -} - -/* - * @unittest: 1606 - */ -CURLcode Curl_speedcheck(struct Curl_easy *data, - struct curltime now) -{ - if(Curl_xfer_recv_is_paused(data)) - /* A paused transfer is not qualified for speed checks */ - return CURLE_OK; - - if((data->progress.current_speed >= 0) && data->set.low_speed_time) { - if(data->progress.current_speed < data->set.low_speed_limit) { - if(!data->state.keeps_speed.tv_sec) - /* under the limit at this very moment */ - data->state.keeps_speed = now; - else { - /* how long has it been under the limit */ - timediff_t howlong = curlx_timediff(now, data->state.keeps_speed); - - 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); - return CURLE_OPERATION_TIMEDOUT; - } - } - } - else - /* faster right now */ - data->state.keeps_speed.tv_sec = 0; - } - - if(data->set.low_speed_limit) - /* if low speed limit is enabled, set the expire timer to make this - connection's speed get checked again in a second */ - Curl_expire(data, 1000, EXPIRE_SPEEDCHECK); - - return CURLE_OK; -} diff --git a/lib/splay.c b/lib/splay.c index 3ca8678b2b..ddab7a4d6e 100644 --- a/lib/splay.c +++ b/lib/splay.c @@ -21,10 +21,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include "curlx/timeval.h" #include "splay.h" /* @@ -34,13 +32,13 @@ * zero : when i is equal to j * positive when : when i is larger than j */ -#define compare(i,j) curlx_timediff_us(i,j) +#define splay_compare(i, j) curlx_ptimediff_us(i, j) /* * Splay using the key i (which may or may not be in the tree.) The starting * root is t. */ -struct Curl_tree *Curl_splay(struct curltime i, +struct Curl_tree *Curl_splay(const struct curltime *pkey, struct Curl_tree *t) { struct Curl_tree N, *l, *r, *y; @@ -51,11 +49,11 @@ struct Curl_tree *Curl_splay(struct curltime i, l = r = &N; for(;;) { - timediff_t comp = compare(i, t->key); + timediff_t comp = splay_compare(pkey, &t->key); if(comp < 0) { if(!t->smaller) break; - if(compare(i, t->smaller->key) < 0) { + if(splay_compare(pkey, &t->smaller->key) < 0) { y = t->smaller; /* rotate smaller */ t->smaller = y->larger; y->larger = t; @@ -70,7 +68,7 @@ struct Curl_tree *Curl_splay(struct curltime i, else if(comp > 0) { if(!t->larger) break; - if(compare(i, t->larger->key) > 0) { + if(splay_compare(pkey, &t->larger->key) > 0) { y = t->larger; /* rotate larger */ t->larger = y->smaller; y->smaller = t; @@ -103,16 +101,16 @@ static const struct curltime SPLAY_SUBNODE = { * * @unittest: 1309 */ -struct Curl_tree *Curl_splayinsert(struct curltime i, +struct Curl_tree *Curl_splayinsert(const struct curltime *pkey, struct Curl_tree *t, struct Curl_tree *node) { DEBUGASSERT(node); if(t) { - t = Curl_splay(i, t); + t = Curl_splay(pkey, t); DEBUGASSERT(t); - if(compare(i, t->key) == 0) { + if(splay_compare(pkey, &t->key) == 0) { /* There already exists a node in the tree with the same key. Build a doubly-linked circular list of nodes. We add the new 'node' struct to the end of this list. */ @@ -130,18 +128,17 @@ struct Curl_tree *Curl_splayinsert(struct curltime i, if(!t) { node->smaller = node->larger = NULL; } - else if(compare(i, t->key) < 0) { + else if(splay_compare(pkey, &t->key) < 0) { node->smaller = t->smaller; node->larger = t; t->smaller = NULL; - } else { node->larger = t->larger; node->smaller = t; t->larger = NULL; } - node->key = i; + node->key = *pkey; /* no identical nodes (yet), we are the only one in the list of nodes */ node->samen = node; @@ -152,11 +149,11 @@ struct Curl_tree *Curl_splayinsert(struct curltime i, /* Finds and deletes the best-fit node from the tree. Return a pointer to the resulting tree. best-fit means the smallest node if it is not larger than the key */ -struct Curl_tree *Curl_splaygetbest(struct curltime i, +struct Curl_tree *Curl_splaygetbest(const struct curltime *pkey, struct Curl_tree *t, struct Curl_tree **removed) { - static const struct curltime tv_zero = {0, 0}; + static const struct curltime tv_zero = { 0, 0 }; struct Curl_tree *x; if(!t) { @@ -165,9 +162,9 @@ struct Curl_tree *Curl_splaygetbest(struct curltime i, } /* find smallest */ - t = Curl_splay(tv_zero, t); + t = Curl_splay(&tv_zero, t); DEBUGASSERT(t); - if(compare(i, t->key) < 0) { + if(splay_compare(pkey, &t->key) < 0) { /* even the smallest is too big */ *removed = NULL; return t; @@ -197,7 +194,6 @@ struct Curl_tree *Curl_splaygetbest(struct curltime i, return x; } - /* Deletes the node we point out from the tree if it is there. Stores a * pointer to the new resulting tree in 'newroot'. * @@ -220,7 +216,7 @@ int Curl_splayremove(struct Curl_tree *t, DEBUGASSERT(removenode); - if(compare(SPLAY_SUBNODE, removenode->key) == 0) { + if(splay_compare(&SPLAY_SUBNODE, &removenode->key) == 0) { /* It is a subnode within a 'same' linked list and thus we can unlink it easily. */ DEBUGASSERT(removenode->samen != removenode); @@ -238,14 +234,14 @@ int Curl_splayremove(struct Curl_tree *t, return 0; } - t = Curl_splay(removenode->key, t); + t = Curl_splay(&removenode->key, t); DEBUGASSERT(t); /* First make sure that we got the same root node as the one we want to remove, as otherwise we might be trying to remove a node that 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); @@ -256,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; @@ -270,7 +266,7 @@ int Curl_splayremove(struct Curl_tree *t, if(!t->smaller) x = t->larger; else { - x = Curl_splay(removenode->key, t->smaller); + x = Curl_splay(&removenode->key, t->smaller); DEBUGASSERT(x); x->larger = t->larger; } diff --git a/lib/splay.h b/lib/splay.h index ccc3781ec1..c6623f2ef0 100644 --- a/lib/splay.h +++ b/lib/splay.h @@ -24,6 +24,7 @@ * ***************************************************************************/ #include "curl_setup.h" + #include "curlx/timeval.h" /* only use function calls to access this struct */ @@ -36,14 +37,14 @@ struct Curl_tree { void *ptr; /* data the splay code does not care about */ }; -struct Curl_tree *Curl_splay(struct curltime i, +struct Curl_tree *Curl_splay(const struct curltime *pkey, struct Curl_tree *t); -struct Curl_tree *Curl_splayinsert(struct curltime key, +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(struct curltime key, +struct Curl_tree *Curl_splaygetbest(const struct curltime *pkey, struct Curl_tree *t, struct Curl_tree **removed); diff --git a/lib/strcase.c b/lib/strcase.c index ee5b9072c4..9f70f41bd6 100644 --- a/lib/strcase.c +++ b/lib/strcase.c @@ -21,65 +21,66 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "strcase.h" /* Mapping table to go from lowercase to uppercase for plain ASCII.*/ static const unsigned char touppermap[256] = { -0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, -22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, -41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, -60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, -79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 65, -66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, -85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, -150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, -166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, -182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, -198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, -214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, -230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, -246, 247, 248, 249, 250, 251, 252, 253, 254, 255 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255 }; /* Mapping table to go from uppercase to lowercase for plain ASCII.*/ static const unsigned char tolowermap[256] = { -0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, -22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, -42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, -111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, -96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, -112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, -128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, -144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, -160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, -176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, -192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, -208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, -224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, -240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255 }; - /* Portable, consistent toupper. Do not use toupper() because its behavior is altered by the current locale. */ char Curl_raw_toupper(char in) { - return (char)touppermap[(unsigned char) in]; + return (char)touppermap[(unsigned char)in]; } - /* Portable, consistent tolower. Do not use tolower() because its behavior is altered by the current locale. */ char Curl_raw_tolower(char in) { - return (char)tolowermap[(unsigned char) in]; + return (char)tolowermap[(unsigned char)in]; } /* Copy an upper case version of the string from src to dest. The @@ -115,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); @@ -133,7 +134,7 @@ int Curl_timestrcmp(const char *a, const char *b) if(a && b) { while(1) { - match |= a[i]^b[i]; + match |= a[i] ^ b[i]; if(!a[i] || !b[i]) break; i++; diff --git a/lib/strcase.h b/lib/strcase.h index 915c996926..54299812be 100644 --- a/lib/strcase.h +++ b/lib/strcase.h @@ -23,20 +23,19 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include +#include "curl_setup.h" char Curl_raw_toupper(char in); char Curl_raw_tolower(char in); /* checkprefix() is a shorter version of the above, used when the first argument is the string literal */ -#define checkprefix(a,b) curl_strnequal(b, STRCONST(a)) +#define checkprefix(a, b) curl_strnequal(b, STRCONST(a)) 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/lib/strequal.c b/lib/strequal.c index 17dd34b7f3..a352fda70b 100644 --- a/lib/strequal.c +++ b/lib/strequal.c @@ -21,10 +21,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include #include "strcase.h" /* @@ -44,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; @@ -75,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/lib/strerror.c b/lib/strerror.c index f0f36ed1b7..1e97c2829d 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -21,38 +21,19 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#ifdef HAVE_STRERROR_R -# if (!defined(HAVE_POSIX_STRERROR_R) && \ - !defined(HAVE_GLIBC_STRERROR_R)) || \ - (defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)) -# error "strerror_r MUST be either POSIX, glibc style" -# endif -#endif - -#include - -#ifdef USE_LIBIDN2 -#include -#endif - #ifdef USE_WINDOWS_SSPI #include "curl_sspi.h" #endif #include "curlx/winapi.h" #include "strerror.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "curlx/strcopy.h" -const char * -curl_easy_strerror(CURLcode error) +const char *curl_easy_strerror(CURLcode error) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE switch(error) { case CURLE_OK: return "No error"; @@ -68,7 +49,7 @@ curl_easy_strerror(CURLcode error) case CURLE_NOT_BUILT_IN: return "A requested feature, protocol or option was not found built-in in" - " this libcurl due to a build-time decision."; + " this libcurl due to a build-time decision."; case CURLE_COULDNT_RESOLVE_PROXY: return "Could not resolve proxy name"; @@ -342,10 +323,9 @@ curl_easy_strerror(CURLcode error) #endif } -const char * -curl_multi_strerror(CURLMcode 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"; @@ -402,10 +382,9 @@ curl_multi_strerror(CURLMcode error) #endif } -const char * -curl_share_strerror(CURLSHcode error) +const char *curl_share_strerror(CURLSHcode error) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE switch(error) { case CURLSHE_OK: return "No error"; @@ -438,10 +417,9 @@ curl_share_strerror(CURLSHcode error) #endif } -const char * -curl_url_strerror(CURLUcode error) +const char *curl_url_strerror(CURLUcode error) { -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE switch(error) { case CURLUE_OK: return "No error"; @@ -552,452 +530,141 @@ curl_url_strerror(CURLUcode error) #endif } -#ifdef USE_WINSOCK -/* This is a helper function for Curl_strerror that converts Winsock error - * codes (WSAGetLastError) to error messages. - * Returns NULL if no error message was found for error code. - */ -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 - - if(!len) - return NULL; - - *buf = '\0'; - -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)err; - return NULL; -#else - switch(err) { - case WSAEINTR: - p = "Call interrupted"; - break; - case WSAEBADF: - p = "Bad file"; - break; - case WSAEACCES: - p = "Bad access"; - break; - case WSAEFAULT: - p = "Bad argument"; - break; - case WSAEINVAL: - p = "Invalid arguments"; - break; - case WSAEMFILE: - p = "Out of file descriptors"; - break; - case WSAEWOULDBLOCK: - p = "Call would block"; - break; - case WSAEINPROGRESS: - case WSAEALREADY: - p = "Blocking call in progress"; - break; - case WSAENOTSOCK: - p = "Descriptor is not a socket"; - break; - case WSAEDESTADDRREQ: - p = "Need destination address"; - break; - case WSAEMSGSIZE: - p = "Bad message size"; - break; - case WSAEPROTOTYPE: - p = "Bad protocol"; - break; - case WSAENOPROTOOPT: - p = "Protocol option is unsupported"; - break; - case WSAEPROTONOSUPPORT: - p = "Protocol is unsupported"; - break; - case WSAESOCKTNOSUPPORT: - p = "Socket is unsupported"; - break; - case WSAEOPNOTSUPP: - p = "Operation not supported"; - break; - case WSAEAFNOSUPPORT: - p = "Address family not supported"; - break; - case WSAEPFNOSUPPORT: - p = "Protocol family not supported"; - break; - case WSAEADDRINUSE: - p = "Address already in use"; - break; - case WSAEADDRNOTAVAIL: - p = "Address not available"; - break; - case WSAENETDOWN: - p = "Network down"; - break; - case WSAENETUNREACH: - p = "Network unreachable"; - break; - case WSAENETRESET: - p = "Network has been reset"; - break; - case WSAECONNABORTED: - p = "Connection was aborted"; - break; - case WSAECONNRESET: - p = "Connection was reset"; - break; - case WSAENOBUFS: - p = "No buffer space"; - break; - case WSAEISCONN: - p = "Socket is already connected"; - break; - case WSAENOTCONN: - p = "Socket is not connected"; - break; - case WSAESHUTDOWN: - p = "Socket has been shut down"; - break; - case WSAETOOMANYREFS: - p = "Too many references"; - break; - case WSAETIMEDOUT: - p = "Timed out"; - break; - case WSAECONNREFUSED: - p = "Connection refused"; - break; - case WSAELOOP: - p = "Loop??"; - break; - case WSAENAMETOOLONG: - p = "Name too long"; - break; - case WSAEHOSTDOWN: - p = "Host down"; - break; - case WSAEHOSTUNREACH: - p = "Host unreachable"; - break; - case WSAENOTEMPTY: - p = "Not empty"; - break; - case WSAEPROCLIM: - p = "Process limit reached"; - break; - case WSAEUSERS: - p = "Too many users"; - break; - case WSAEDQUOT: - p = "Bad quota"; - break; - case WSAESTALE: - p = "Something is stale"; - break; - case WSAEREMOTE: - p = "Remote error"; - break; -#ifdef WSAEDISCON /* missing in SalfordC! */ - case WSAEDISCON: - p = "Disconnected"; - break; -#endif - /* Extended Winsock errors */ - case WSASYSNOTREADY: - p = "Winsock library is not ready"; - break; - case WSANOTINITIALISED: - p = "Winsock library not initialised"; - break; - case WSAVERNOTSUPPORTED: - p = "Winsock version not supported"; - break; - - /* getXbyY() errors (already handled in herrmsg): - * Authoritative Answer: Host not found */ - case WSAHOST_NOT_FOUND: - p = "Host not found"; - break; - - /* Non-Authoritative: Host not found, or SERVERFAIL */ - case WSATRY_AGAIN: - p = "Host not found, try again"; - break; - - /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ - case WSANO_RECOVERY: - p = "Unrecoverable error in call to nameserver"; - break; - - /* Valid name, no data record of requested type */ - case WSANO_DATA: - p = "No data record of requested type"; - break; - - default: - return NULL; - } - alen = strlen(p); - if(alen < len) - strcpy(buf, p); - return buf; -#endif -} -#endif /* USE_WINSOCK */ - -/* - * Our thread-safe and smart strerror() replacement. - * - * The 'err' argument passed in to this function MUST be a true errno number - * as reported on this system. We do no range checking on the number before - * we pass it to the "number-to-message" conversion function and there might - * be systems that do not do proper range checking in there themselves. - * - * We do not do range checking (on systems other than Windows) since there is - * no good reliable and portable way to do it. - * - * On Windows different types of error codes overlap. This function has an - * order of preference when trying to match error codes: - * CRT (errno), Winsock (WSAGetLastError), Windows API (GetLastError). - * - * It may be more correct to call one of the variant functions instead: - * Call Curl_sspi_strerror if the error code is definitely Windows SSPI. - * Call curlx_winapi_strerror if the error code is definitely Windows API. - */ -const char *Curl_strerror(int err, char *buf, size_t buflen) -{ -#ifdef _WIN32 - DWORD old_win_err = GetLastError(); -#endif - int old_errno = errno; - char *p; - - if(!buflen) - return NULL; - -#ifndef _WIN32 - DEBUGASSERT(err >= 0); -#endif - - *buf = '\0'; - -#ifdef _WIN32 -#ifndef UNDER_CE - /* 'sys_nerr' is the maximum errno number, it is not widely portable */ - if(err >= 0 && err < sys_nerr) - curl_msnprintf(buf, buflen, "%s", sys_errlist[err]); - else -#endif - { - if( -#ifdef USE_WINSOCK - !get_winsock_error(err, buf, buflen) && -#endif - !curlx_get_winapi_error(err, buf, buflen)) - curl_msnprintf(buf, buflen, "Unknown error %d (%#x)", err, err); - } -#else /* not Windows coming up */ - -#if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R) - /* - * The POSIX-style strerror_r() may set errno to ERANGE if insufficient - * storage is supplied via 'strerrbuf' and 'buflen' to hold the generated - * message string, or EINVAL if 'errnum' is not a valid error number. - */ - if(strerror_r(err, buf, buflen)) { - if('\0' == buf[0]) - curl_msnprintf(buf, buflen, "Unknown error %d", err); - } -#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R) - /* - * The glibc-style strerror_r() only *might* use the buffer we pass to - * the function, but it always returns the error message as a pointer, - * so we must copy that string unconditionally (if non-NULL). - */ - { - char buffer[256]; - char *msg = strerror_r(err, buffer, sizeof(buffer)); - if(msg) - curl_msnprintf(buf, buflen, "%s", msg); - else - curl_msnprintf(buf, buflen, "Unknown error %d", err); - } -#else - { - /* !checksrc! disable BANNEDFUNC 1 */ - const char *msg = strerror(err); - if(msg) - curl_msnprintf(buf, buflen, "%s", msg); - else - curl_msnprintf(buf, buflen, "Unknown error %d", err); - } -#endif - -#endif /* end of not Windows */ - - /* strip trailing '\r\n' or '\n'. */ - p = strrchr(buf, '\n'); - if(p && (p - buf) >= 2) - *p = '\0'; - p = strrchr(buf, '\r'); - if(p && (p - buf) >= 1) - *p = '\0'; - - if(errno != old_errno) - CURL_SETERRNO(old_errno); - -#ifdef _WIN32 - if(old_win_err != GetLastError()) - SetLastError(old_win_err); -#endif - - return buf; -} - #ifdef USE_WINDOWS_SSPI /* * Curl_sspi_strerror: - * Variant of Curl_strerror if the error code is definitely Windows SSPI. + * Variant of curlx_strerror if the error code is definitely Windows SSPI. */ -const char *Curl_sspi_strerror(int err, char *buf, size_t buflen) +const char *Curl_sspi_strerror(SECURITY_STATUS err, char *buf, size_t buflen) { #ifdef _WIN32 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"; - break; + case SEC_E_OK: + txt = "No error"; + break; #define SEC2TXT(sec) case sec: txt = #sec; break - SEC2TXT(CRYPT_E_REVOKED); - SEC2TXT(CRYPT_E_NO_REVOCATION_DLL); - SEC2TXT(CRYPT_E_NO_REVOCATION_CHECK); - SEC2TXT(CRYPT_E_REVOCATION_OFFLINE); - SEC2TXT(CRYPT_E_NOT_IN_REVOCATION_DATABASE); - SEC2TXT(SEC_E_ALGORITHM_MISMATCH); - SEC2TXT(SEC_E_BAD_BINDINGS); - SEC2TXT(SEC_E_BAD_PKGID); - SEC2TXT(SEC_E_BUFFER_TOO_SMALL); - SEC2TXT(SEC_E_CANNOT_INSTALL); - SEC2TXT(SEC_E_CANNOT_PACK); - SEC2TXT(SEC_E_CERT_EXPIRED); - SEC2TXT(SEC_E_CERT_UNKNOWN); - SEC2TXT(SEC_E_CERT_WRONG_USAGE); - SEC2TXT(SEC_E_CONTEXT_EXPIRED); - SEC2TXT(SEC_E_CROSSREALM_DELEGATION_FAILURE); - SEC2TXT(SEC_E_CRYPTO_SYSTEM_INVALID); - SEC2TXT(SEC_E_DECRYPT_FAILURE); - SEC2TXT(SEC_E_DELEGATION_POLICY); - SEC2TXT(SEC_E_DELEGATION_REQUIRED); - SEC2TXT(SEC_E_DOWNGRADE_DETECTED); - SEC2TXT(SEC_E_ENCRYPT_FAILURE); - SEC2TXT(SEC_E_ILLEGAL_MESSAGE); - SEC2TXT(SEC_E_INCOMPLETE_CREDENTIALS); - SEC2TXT(SEC_E_INCOMPLETE_MESSAGE); - SEC2TXT(SEC_E_INSUFFICIENT_MEMORY); - SEC2TXT(SEC_E_INTERNAL_ERROR); - SEC2TXT(SEC_E_INVALID_HANDLE); - SEC2TXT(SEC_E_INVALID_PARAMETER); - SEC2TXT(SEC_E_INVALID_TOKEN); - SEC2TXT(SEC_E_ISSUING_CA_UNTRUSTED); - SEC2TXT(SEC_E_ISSUING_CA_UNTRUSTED_KDC); - SEC2TXT(SEC_E_KDC_CERT_EXPIRED); - SEC2TXT(SEC_E_KDC_CERT_REVOKED); - SEC2TXT(SEC_E_KDC_INVALID_REQUEST); - SEC2TXT(SEC_E_KDC_UNABLE_TO_REFER); - SEC2TXT(SEC_E_KDC_UNKNOWN_ETYPE); - SEC2TXT(SEC_E_LOGON_DENIED); - SEC2TXT(SEC_E_MAX_REFERRALS_EXCEEDED); - SEC2TXT(SEC_E_MESSAGE_ALTERED); - SEC2TXT(SEC_E_MULTIPLE_ACCOUNTS); - SEC2TXT(SEC_E_MUST_BE_KDC); - SEC2TXT(SEC_E_NOT_OWNER); - SEC2TXT(SEC_E_NO_AUTHENTICATING_AUTHORITY); - SEC2TXT(SEC_E_NO_CREDENTIALS); - SEC2TXT(SEC_E_NO_IMPERSONATION); - SEC2TXT(SEC_E_NO_IP_ADDRESSES); - SEC2TXT(SEC_E_NO_KERB_KEY); - SEC2TXT(SEC_E_NO_PA_DATA); - SEC2TXT(SEC_E_NO_S4U_PROT_SUPPORT); - SEC2TXT(SEC_E_NO_TGT_REPLY); - SEC2TXT(SEC_E_OUT_OF_SEQUENCE); - SEC2TXT(SEC_E_PKINIT_CLIENT_FAILURE); - SEC2TXT(SEC_E_PKINIT_NAME_MISMATCH); - SEC2TXT(SEC_E_POLICY_NLTM_ONLY); - SEC2TXT(SEC_E_QOP_NOT_SUPPORTED); - SEC2TXT(SEC_E_REVOCATION_OFFLINE_C); - SEC2TXT(SEC_E_REVOCATION_OFFLINE_KDC); - SEC2TXT(SEC_E_SECPKG_NOT_FOUND); - SEC2TXT(SEC_E_SECURITY_QOS_FAILED); - SEC2TXT(SEC_E_SHUTDOWN_IN_PROGRESS); - SEC2TXT(SEC_E_SMARTCARD_CERT_EXPIRED); - SEC2TXT(SEC_E_SMARTCARD_CERT_REVOKED); - SEC2TXT(SEC_E_SMARTCARD_LOGON_REQUIRED); - SEC2TXT(SEC_E_STRONG_CRYPTO_NOT_SUPPORTED); - SEC2TXT(SEC_E_TARGET_UNKNOWN); - SEC2TXT(SEC_E_TIME_SKEW); - SEC2TXT(SEC_E_TOO_MANY_PRINCIPALS); - SEC2TXT(SEC_E_UNFINISHED_CONTEXT_DELETED); - SEC2TXT(SEC_E_UNKNOWN_CREDENTIALS); - SEC2TXT(SEC_E_UNSUPPORTED_FUNCTION); - SEC2TXT(SEC_E_UNSUPPORTED_PREAUTH); - SEC2TXT(SEC_E_UNTRUSTED_ROOT); - SEC2TXT(SEC_E_WRONG_CREDENTIAL_HANDLE); - SEC2TXT(SEC_E_WRONG_PRINCIPAL); - SEC2TXT(SEC_I_COMPLETE_AND_CONTINUE); - SEC2TXT(SEC_I_COMPLETE_NEEDED); - SEC2TXT(SEC_I_CONTEXT_EXPIRED); - SEC2TXT(SEC_I_CONTINUE_NEEDED); - SEC2TXT(SEC_I_INCOMPLETE_CREDENTIALS); - SEC2TXT(SEC_I_LOCAL_LOGON); - SEC2TXT(SEC_I_NO_LSA_CONTEXT); - SEC2TXT(SEC_I_RENEGOTIATE); - SEC2TXT(SEC_I_SIGNATURE_NEEDED); - default: - txt = "Unknown error"; + SEC2TXT(CRYPT_E_REVOKED); + SEC2TXT(CRYPT_E_NO_REVOCATION_DLL); + SEC2TXT(CRYPT_E_NO_REVOCATION_CHECK); + SEC2TXT(CRYPT_E_REVOCATION_OFFLINE); + SEC2TXT(CRYPT_E_NOT_IN_REVOCATION_DATABASE); + SEC2TXT(SEC_E_ALGORITHM_MISMATCH); + SEC2TXT(SEC_E_BAD_BINDINGS); + SEC2TXT(SEC_E_BAD_PKGID); + SEC2TXT(SEC_E_BUFFER_TOO_SMALL); + SEC2TXT(SEC_E_CANNOT_INSTALL); + SEC2TXT(SEC_E_CANNOT_PACK); + SEC2TXT(SEC_E_CERT_EXPIRED); + SEC2TXT(SEC_E_CERT_UNKNOWN); + SEC2TXT(SEC_E_CERT_WRONG_USAGE); + SEC2TXT(SEC_E_CONTEXT_EXPIRED); + SEC2TXT(SEC_E_CROSSREALM_DELEGATION_FAILURE); + SEC2TXT(SEC_E_CRYPTO_SYSTEM_INVALID); + SEC2TXT(SEC_E_DECRYPT_FAILURE); + SEC2TXT(SEC_E_DELEGATION_POLICY); + SEC2TXT(SEC_E_DELEGATION_REQUIRED); + SEC2TXT(SEC_E_DOWNGRADE_DETECTED); + SEC2TXT(SEC_E_ENCRYPT_FAILURE); + SEC2TXT(SEC_E_ILLEGAL_MESSAGE); + SEC2TXT(SEC_E_INCOMPLETE_CREDENTIALS); + SEC2TXT(SEC_E_INCOMPLETE_MESSAGE); + SEC2TXT(SEC_E_INSUFFICIENT_MEMORY); + SEC2TXT(SEC_E_INTERNAL_ERROR); + SEC2TXT(SEC_E_INVALID_HANDLE); + SEC2TXT(SEC_E_INVALID_PARAMETER); + SEC2TXT(SEC_E_INVALID_TOKEN); + SEC2TXT(SEC_E_ISSUING_CA_UNTRUSTED); + SEC2TXT(SEC_E_ISSUING_CA_UNTRUSTED_KDC); + SEC2TXT(SEC_E_KDC_CERT_EXPIRED); + SEC2TXT(SEC_E_KDC_CERT_REVOKED); + SEC2TXT(SEC_E_KDC_INVALID_REQUEST); + SEC2TXT(SEC_E_KDC_UNABLE_TO_REFER); + SEC2TXT(SEC_E_KDC_UNKNOWN_ETYPE); + SEC2TXT(SEC_E_LOGON_DENIED); + SEC2TXT(SEC_E_MAX_REFERRALS_EXCEEDED); + SEC2TXT(SEC_E_MESSAGE_ALTERED); + SEC2TXT(SEC_E_MULTIPLE_ACCOUNTS); + SEC2TXT(SEC_E_MUST_BE_KDC); + SEC2TXT(SEC_E_NOT_OWNER); + SEC2TXT(SEC_E_NO_AUTHENTICATING_AUTHORITY); + SEC2TXT(SEC_E_NO_CREDENTIALS); + SEC2TXT(SEC_E_NO_IMPERSONATION); + SEC2TXT(SEC_E_NO_IP_ADDRESSES); + SEC2TXT(SEC_E_NO_KERB_KEY); + SEC2TXT(SEC_E_NO_PA_DATA); + SEC2TXT(SEC_E_NO_S4U_PROT_SUPPORT); + SEC2TXT(SEC_E_NO_TGT_REPLY); + SEC2TXT(SEC_E_OUT_OF_SEQUENCE); + SEC2TXT(SEC_E_PKINIT_CLIENT_FAILURE); + SEC2TXT(SEC_E_PKINIT_NAME_MISMATCH); + SEC2TXT(SEC_E_POLICY_NLTM_ONLY); + SEC2TXT(SEC_E_QOP_NOT_SUPPORTED); + SEC2TXT(SEC_E_REVOCATION_OFFLINE_C); + SEC2TXT(SEC_E_REVOCATION_OFFLINE_KDC); + SEC2TXT(SEC_E_SECPKG_NOT_FOUND); + SEC2TXT(SEC_E_SECURITY_QOS_FAILED); + SEC2TXT(SEC_E_SHUTDOWN_IN_PROGRESS); + SEC2TXT(SEC_E_SMARTCARD_CERT_EXPIRED); + SEC2TXT(SEC_E_SMARTCARD_CERT_REVOKED); + SEC2TXT(SEC_E_SMARTCARD_LOGON_REQUIRED); + SEC2TXT(SEC_E_STRONG_CRYPTO_NOT_SUPPORTED); + SEC2TXT(SEC_E_TARGET_UNKNOWN); + SEC2TXT(SEC_E_TIME_SKEW); + SEC2TXT(SEC_E_TOO_MANY_PRINCIPALS); + SEC2TXT(SEC_E_UNFINISHED_CONTEXT_DELETED); + SEC2TXT(SEC_E_UNKNOWN_CREDENTIALS); + SEC2TXT(SEC_E_UNSUPPORTED_FUNCTION); + SEC2TXT(SEC_E_UNSUPPORTED_PREAUTH); + SEC2TXT(SEC_E_UNTRUSTED_ROOT); + SEC2TXT(SEC_E_WRONG_CREDENTIAL_HANDLE); + SEC2TXT(SEC_E_WRONG_PRINCIPAL); + SEC2TXT(SEC_I_COMPLETE_AND_CONTINUE); + SEC2TXT(SEC_I_COMPLETE_NEEDED); + SEC2TXT(SEC_I_CONTEXT_EXPIRED); + SEC2TXT(SEC_I_CONTINUE_NEEDED); + SEC2TXT(SEC_I_INCOMPLETE_CREDENTIALS); + SEC2TXT(SEC_I_LOCAL_LOGON); + SEC2TXT(SEC_I_NO_LSA_CONTEXT); + SEC2TXT(SEC_I_RENEGOTIATE); + SEC2TXT(SEC_I_SIGNATURE_NEEDED); + default: + txt = "Unknown error"; } if(err == SEC_E_ILLEGAL_MESSAGE) { curl_msnprintf(buf, buflen, - "SEC_E_ILLEGAL_MESSAGE (0x%08X) - This error usually " + "SEC_E_ILLEGAL_MESSAGE (0x%08lx) - This error usually " "occurs when a fatal SSL/TLS alert is received (e.g. " "handshake failed). More detail may be available in " "the Windows System event log.", err); } else { char msgbuf[256]; - if(curlx_get_winapi_error(err, msgbuf, sizeof(msgbuf))) - curl_msnprintf(buf, buflen, "%s (0x%08X) - %s", txt, err, msgbuf); + if(curlx_get_winapi_error((DWORD)err, msgbuf, sizeof(msgbuf))) + curl_msnprintf(buf, buflen, "%s (0x%08lx) - %s", txt, err, msgbuf); else - curl_msnprintf(buf, buflen, "%s (0x%08X)", txt, err); + 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"; - if(buflen > strlen(txt)) - strcpy(buf, txt); + curlx_strcopy(buf, buflen, STRCONST("Error")); #endif if(errno != old_errno) - CURL_SETERRNO(old_errno); + errno = old_errno; #ifdef _WIN32 if(old_win_err != GetLastError()) diff --git a/lib/strerror.h b/lib/strerror.h index 424fb5b7b5..4b9779ff2d 100644 --- a/lib/strerror.h +++ b/lib/strerror.h @@ -23,14 +23,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" -#include "urldata.h" - -#define STRERROR_LEN 256 /* a suitable length */ - -const char *Curl_strerror(int err, char *buf, size_t buflen); #ifdef USE_WINDOWS_SSPI -const char *Curl_sspi_strerror(int err, char *buf, size_t buflen); +const char *Curl_sspi_strerror(SECURITY_STATUS err, char *buf, size_t buflen); #endif #endif /* HEADER_CURL_STRERROR_H */ diff --git a/lib/system_win32.c b/lib/system_win32.c index a890a0ea98..1b052357b5 100644 --- a/lib/system_win32.c +++ b/lib/system_win32.c @@ -21,37 +21,20 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef _WIN32 -#include #include "system_win32.h" -#include "curlx/version_win32.h" #include "curl_sspi.h" -#include "curlx/warnless.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.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 @@ -64,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.*/ @@ -74,7 +57,7 @@ CURLcode Curl_win32_init(long flags) /* highest supported version. */ if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) || - HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested) ) { + HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested)) { /* Tell the user that we could not find a usable */ /* winsock.dll. */ @@ -95,49 +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 */ -#ifdef UNDER_CE - #define CURL_TEXT(n) TEXT(n) -#else - #define CURL_TEXT(n) (n) -#endif - IF_NAMETOINDEX_FN pIfNameToIndex = - CURLX_FUNCTION_CAST(IF_NAMETOINDEX_FN, - (GetProcAddress(s_hIpHlpApiDll, - CURL_TEXT("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 @@ -149,107 +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 -# ifdef UNDER_CE -# define LOADLIBARYEX L"LoadLibraryExW" -# else -# define LOADLIBARYEX "LoadLibraryExW" -# endif -#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) -{ -#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) - 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 = 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); - } - 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/lib/system_win32.h b/lib/system_win32.h index 6bf89d3d9d..8a51f09670 100644 --- a/lib/system_win32.h +++ b/lib/system_win32.h @@ -23,28 +23,15 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef _WIN32 - -#include - 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 */ +#endif /* _WIN32 */ #endif /* HEADER_CURL_SYSTEM_WIN32_H */ diff --git a/lib/telnet.c b/lib/telnet.c index cc827c1b3e..c5ce9c2c97 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -21,8 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" +#include "urldata.h" +#include "telnet.h" #ifndef CURL_DISABLE_TELNET @@ -46,50 +47,36 @@ #include #endif -#include "urldata.h" #include "url.h" -#include #include "transfer.h" #include "sendf.h" -#include "telnet.h" -#include "connect.h" +#include "curl_trc.h" #include "progress.h" -#include "system_win32.h" #include "arpa_telnet.h" #include "select.h" -#include "curlx/warnless.h" #include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - #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 @@ -99,24 +86,22 @@ #define CURL_EMPTY 0 #define CURL_OPPOSITE 1 - /* meta key for storing protocol meta at easy handle */ #define CURL_META_TELNET_EASY "meta:proto:telnet:easy" /* * Telnet receiver states for fsm */ -typedef enum -{ - CURL_TS_DATA = 0, - CURL_TS_IAC, - CURL_TS_WILL, - CURL_TS_WONT, - CURL_TS_DO, - CURL_TS_DONT, - CURL_TS_CR, - CURL_TS_SB, /* sub-option collection */ - CURL_TS_SE /* looking for sub-option end */ +typedef enum { + CURL_TS_DATA = 0, + CURL_TS_IAC, + CURL_TS_WILL, + CURL_TS_WONT, + CURL_TS_DO, + CURL_TS_DONT, + CURL_TS_CR, + CURL_TS_SB, /* sub-option collection */ + CURL_TS_SE /* looking for sub-option end */ } TelnetReceive; struct TELNET { @@ -129,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; @@ -142,148 +127,9 @@ 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 -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 void 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 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 */ -}; - - -static void telnet_easy_dtor(void *key, size_t klen, void *entry) -{ - struct TELNET *tn = entry; - (void)key; - (void)klen; - curl_slist_free_all(tn->telnet_vars); - curlx_dyn_free(&tn->out); - free(tn); -} - -static -CURLcode init_telnet(struct Curl_easy *data) -{ - struct TELNET *tn; - - tn = calloc(1, sizeof(struct TELNET)); - if(!tn) - return CURLE_OUT_OF_MEMORY; - - curlx_dyn_init(&tn->out, 0xffff); - - tn->telrcv_state = CURL_TS_DATA; - - /* Init suboptions */ - CURL_SB_CLEAR(tn); - - /* Set the options we want by default */ - tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES; - tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES; - - /* To be compliant with previous releases of libcurl - we enable this option by default. This behavior - can be changed thanks to the "BINARY" option in - CURLOPT_TELNETOPTIONS - */ - tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES; - tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES; - - /* We must allow the server to echo what we sent - but it is not necessary to request the server - to do so (it might forces the server to close - the connection). Hence, we ignore ECHO in the - negotiate function - */ - tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES; - - /* Set the subnegotiation fields to send information - just after negotiation passed (do/will) - - Default values are (0,0) initialized by calloc. - According to the RFC1013 it is valid: - A value equal to zero is acceptable for the width (or height), - and means that no character width (or height) is being sent. - In this case, the width (or height) that will be assumed by the - Telnet server is operating system specific (it will probably be - based upon the terminal type information that may have been sent - using the TERMINAL TYPE Telnet option). */ - tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES; - - 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 +#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) { @@ -318,7 +164,68 @@ static void printoption(struct Curl_easy *data, } } } -#endif +#endif /* !CURLVERBOSE */ + +static void telnet_easy_dtor(void *key, size_t klen, void *entry) +{ + struct TELNET *tn = entry; + (void)key; + (void)klen; + curl_slist_free_all(tn->telnet_vars); + curlx_dyn_free(&tn->out); + curlx_free(tn); +} + +static CURLcode init_telnet(struct Curl_easy *data) +{ + struct TELNET *tn; + + tn = curlx_calloc(1, sizeof(struct TELNET)); + if(!tn) + return CURLE_OUT_OF_MEMORY; + + curlx_dyn_init(&tn->out, 0xffff); + + tn->telrcv_state = CURL_TS_DATA; + + /* Init suboptions */ + CURL_SB_CLEAR(tn); + + /* Set the options we want by default */ + tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES; + tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES; + + /* To be compliant with previous releases of libcurl + we enable this option by default. This behavior + can be changed thanks to the "BINARY" option in + CURLOPT_TELNETOPTIONS + */ + tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES; + tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES; + + /* We must allow the server to echo what we sent + but it is not necessary to request the server + to do so (it might forces the server to close + the connection). Hence, we ignore ECHO in the + negotiate function + */ + tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES; + + /* 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: + A value equal to zero is acceptable for the width (or height), + and means that no character width (or height) is being sent. + In this case, the width (or height) that will be assumed by the + Telnet server is operating system specific (it will probably be + based upon the terminal type information that may have been sent + using the TERMINAL TYPE Telnet option). */ + tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES; + + return Curl_meta_set(data, CURL_META_TELNET_EASY, tn, telnet_easy_dtor); +} static void send_negotiation(struct Curl_easy *data, int cmd, int option) { @@ -333,15 +240,14 @@ static void send_negotiation(struct Curl_easy *data, int cmd, int option) bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3); if(bytes_written < 0) { int err = SOCKERRNO; - failf(data,"Sending data failed (%d)",err); + failf(data, "Sending data failed (%d)", err); } printoption(data, "SENT", cmd, option); } -static -void set_remote_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) { if(newstate == CURL_YES) { switch(tn->him[option]) { @@ -413,97 +319,8 @@ void set_remote_option(struct Curl_easy *data, struct TELNET *tn, } } -static -void rec_will(struct Curl_easy *data, struct TELNET *tn, int option) -{ - switch(tn->him[option]) { - case CURL_NO: - if(tn->him_preferred[option] == CURL_YES) { - tn->him[option] = CURL_YES; - send_negotiation(data, CURL_DO, option); - } - else - send_negotiation(data, CURL_DONT, option); - - break; - - case CURL_YES: - /* Already enabled */ - break; - - case CURL_WANTNO: - switch(tn->himq[option]) { - case CURL_EMPTY: - /* Error: DONT answered by WILL */ - tn->him[option] = CURL_NO; - break; - case CURL_OPPOSITE: - /* Error: DONT answered by WILL */ - tn->him[option] = CURL_YES; - tn->himq[option] = CURL_EMPTY; - break; - } - break; - - case CURL_WANTYES: - switch(tn->himq[option]) { - case CURL_EMPTY: - tn->him[option] = CURL_YES; - break; - case CURL_OPPOSITE: - tn->him[option] = CURL_WANTNO; - tn->himq[option] = CURL_EMPTY; - send_negotiation(data, CURL_DONT, option); - break; - } - break; - } -} - -static -void rec_wont(struct Curl_easy *data, struct TELNET *tn, int option) -{ - switch(tn->him[option]) { - case CURL_NO: - /* Already disabled */ - break; - - case CURL_YES: - tn->him[option] = CURL_NO; - send_negotiation(data, CURL_DONT, option); - break; - - case CURL_WANTNO: - switch(tn->himq[option]) { - case CURL_EMPTY: - tn->him[option] = CURL_NO; - break; - - case CURL_OPPOSITE: - tn->him[option] = CURL_WANTYES; - tn->himq[option] = CURL_EMPTY; - send_negotiation(data, CURL_DO, option); - break; - } - break; - - case CURL_WANTYES: - switch(tn->himq[option]) { - case CURL_EMPTY: - tn->him[option] = CURL_NO; - break; - case CURL_OPPOSITE: - tn->him[option] = CURL_NO; - tn->himq[option] = CURL_EMPTY; - break; - } - break; - } -} - -static void -set_local_option(struct Curl_easy *data, struct TELNET *tn, - int option, int newstate) +static void set_local_option(struct Curl_easy *data, struct TELNET *tn, + int option, int newstate) { if(newstate == CURL_YES) { switch(tn->us[option]) { @@ -575,8 +392,338 @@ set_local_option(struct Curl_easy *data, struct TELNET *tn, } } -static -void rec_do(struct Curl_easy *data, struct TELNET *tn, int option) +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]) { + case CURL_NO: + if(tn->him_preferred[option] == CURL_YES) { + tn->him[option] = CURL_YES; + send_negotiation(data, CURL_DO, option); + } + else + send_negotiation(data, CURL_DONT, option); + + break; + + case CURL_YES: + /* Already enabled */ + break; + + case CURL_WANTNO: + switch(tn->himq[option]) { + case CURL_EMPTY: + /* Error: DONT answered by WILL */ + tn->him[option] = CURL_NO; + break; + case CURL_OPPOSITE: + /* Error: DONT answered by WILL */ + tn->him[option] = CURL_YES; + tn->himq[option] = CURL_EMPTY; + break; + } + break; + + case CURL_WANTYES: + switch(tn->himq[option]) { + case CURL_EMPTY: + tn->him[option] = CURL_YES; + break; + case CURL_OPPOSITE: + tn->him[option] = CURL_WANTNO; + tn->himq[option] = CURL_EMPTY; + send_negotiation(data, CURL_DONT, option); + break; + } + break; + } +} + +static void rec_wont(struct Curl_easy *data, struct TELNET *tn, int option) +{ + switch(tn->him[option]) { + case CURL_NO: + /* Already disabled */ + break; + + case CURL_YES: + tn->him[option] = CURL_NO; + send_negotiation(data, CURL_DONT, option); + break; + + case CURL_WANTNO: + switch(tn->himq[option]) { + case CURL_EMPTY: + tn->him[option] = CURL_NO; + break; + + case CURL_OPPOSITE: + tn->him[option] = CURL_WANTYES; + tn->himq[option] = CURL_EMPTY; + send_negotiation(data, CURL_DO, option); + break; + } + break; + + case CURL_WANTYES: + switch(tn->himq[option]) { + case CURL_EMPTY: + tn->him[option] = CURL_NO; + break; + case CURL_OPPOSITE: + tn->him[option] = CURL_NO; + tn->himq[option] = CURL_EMPTY; + break; + } + break; + } +} + +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(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; + } + } + } +} + +/* 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) { switch(tn->us[option]) { case CURL_NO: @@ -634,8 +781,7 @@ void rec_do(struct Curl_easy *data, struct TELNET *tn, int option) } } -static -void rec_dont(struct Curl_easy *data, struct TELNET *tn, int option) +static void rec_dont(struct Curl_easy *data, struct TELNET *tn, int option) { switch(tn->us[option]) { case CURL_NO: @@ -675,120 +821,6 @@ 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[i]); - - 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: - pointer[length] = 0; - infof(data, " \"%s\"", &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; @@ -814,7 +846,7 @@ static CURLcode check_telnet_options(struct Curl_easy *data, DEBUGF(infof(data, "set a non ASCII username in telnet")); return CURLE_BAD_FUNCTION_ARGUMENT; } - msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user); + curl_msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user); beg = curl_slist_append(tn->telnet_vars, buffer); if(!beg) { curl_slist_free_all(tn->telnet_vars); @@ -827,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; @@ -896,8 +928,10 @@ static CURLcode check_telnet_options(struct Curl_easy *data, case 6: /* To take care or not of the 8th bit in data exchange */ if(curl_strnequal(option, "BINARY", 6)) { - int binary_option = atoi(arg); - if(binary_option != 1) { + const char *p = arg; + curl_off_t binary_option; + if(!curlx_str_number(&p, &binary_option, 1) && + (binary_option != 1)) { tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO; tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO; } @@ -925,14 +959,21 @@ static CURLcode check_telnet_options(struct Curl_easy *data, return result; } +/* 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 ban its use instead */ +static bool bad_option(const char *data) +{ + return !data || !!strchr(data, CURL_IAC); +} + /* * suboption() * * Look at the sub-option buffer, and try to be helpful to the other * side. */ - -static void suboption(struct Curl_easy *data, struct TELNET *tn) +static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) { struct curl_slist *v; unsigned char temp[2048]; @@ -941,135 +982,93 @@ static void suboption(struct Curl_easy *data, struct TELNET *tn) int err; struct connectdata *conn = data->conn; - printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2); - switch(CURL_SB_GET(tn)) { - case CURL_TELOPT_TTYPE: - len = strlen(tn->subopt_ttype) + 4 + 2; - msnprintf((char *)temp, sizeof(temp), - "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE, - CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE); - bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data,"Sending data failed (%d)",err); - } - printsub(data, '>', &temp[2], len-2); - break; - case CURL_TELOPT_XDISPLOC: - len = strlen(tn->subopt_xdisploc) + 4 + 2; - msnprintf((char *)temp, sizeof(temp), - "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC, - CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE); - bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data,"Sending data failed (%d)",err); - } - printsub(data, '>', &temp[2], len-2); - break; - case CURL_TELOPT_NEW_ENVIRON: - msnprintf((char *)temp, sizeof(temp), - "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON, - CURL_TELQUAL_IS); - len = 4; + if(!CURL_SB_LEN(tn)) /* ignore empty suboption */ + return CURLE_OK; - for(v = tn->telnet_vars; v; v = v->next) { - size_t tmplen = (strlen(v->data) + 1); - /* Add the variable if it fits */ - if(len + tmplen < (int)sizeof(temp)-6) { - char *s = strchr(v->data, ','); - if(!s) - len += msnprintf((char *)&temp[len], sizeof(temp) - len, - "%c%s", CURL_NEW_ENV_VAR, v->data); - else { - size_t vlen = s - v->data; - len += msnprintf((char *)&temp[len], sizeof(temp) - len, - "%c%.*s%c%s", CURL_NEW_ENV_VAR, - (int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s); - } + 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)) + return CURLE_BAD_FUNCTION_ARGUMENT; + if(strlen(tn->subopt_ttype) > 1000) { + failf(data, "Tool long telnet TTYPE"); + return CURLE_SEND_ERROR; + } + len = curl_msnprintf((char *)temp, sizeof(temp), "%c%c%c%c%s%c%c", + CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE, + CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, + CURL_SE); + bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); + + if(bytes_written < 0) { + err = SOCKERRNO; + failf(data, "Sending data failed (%d)", err); + return CURLE_SEND_ERROR; + } + printsub(data, '>', &temp[2], len-2); + break; + case CURL_TELOPT_XDISPLOC: + if(bad_option(tn->subopt_xdisploc)) + return CURLE_BAD_FUNCTION_ARGUMENT; + if(strlen(tn->subopt_xdisploc) > 1000) { + failf(data, "Tool long telnet XDISPLOC"); + return CURLE_SEND_ERROR; + } + len = curl_msnprintf((char *)temp, sizeof(temp), "%c%c%c%c%s%c%c", + CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC, + CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, + CURL_SE); + bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); + if(bytes_written < 0) { + err = SOCKERRNO; + failf(data, "Sending data failed (%d)", err); + return CURLE_SEND_ERROR; + } + printsub(data, '>', &temp[2], len - 2); + break; + case CURL_TELOPT_NEW_ENVIRON: + len = curl_msnprintf((char *)temp, sizeof(temp), "%c%c%c%c", + CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON, + CURL_TELQUAL_IS); + for(v = tn->telnet_vars; v; v = v->next) { + size_t tmplen = (strlen(v->data) + 1); + if(bad_option(v->data)) + return CURLE_BAD_FUNCTION_ARGUMENT; + /* Add the variable if it fits */ + if(len + tmplen < (int)sizeof(temp) - 6) { + 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); + else { + size_t vlen = s - v->data; + len += curl_msnprintf((char *)&temp[len], sizeof(temp) - len, + "%c%.*s%c%s", CURL_NEW_ENV_VAR, + (int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s); } } - msnprintf((char *)&temp[len], sizeof(temp) - len, - "%c%c", CURL_IAC, CURL_SE); - len += 2; - bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); - if(bytes_written < 0) { - err = SOCKERRNO; - failf(data,"Sending data failed (%d)",err); - } - printsub(data, '>', &temp[2], len-2); - break; - } - return; -} - - -/* - * 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); + } + curl_msnprintf((char *)&temp[len], sizeof(temp) - len, + "%c%c", CURL_IAC, CURL_SE); + len += 2; + bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); if(bytes_written < 0) { err = SOCKERRNO; failf(data, "Sending data failed (%d)", err); } + printsub(data, '>', &temp[2], len - 2); break; } + return CURLE_OK; } - -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 */ +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 */ { unsigned char c; CURLcode result; @@ -1087,9 +1086,9 @@ CURLcode telrcv(struct Curl_easy *data, } \ startwrite = -1 -#define writebyte() \ - if(startwrite < 0) \ - startwrite = in +#define writebyte() \ + if(startwrite < 0) \ + startwrite = in #define bufferflush() startskipping() @@ -1118,7 +1117,6 @@ CURLcode telrcv(struct Curl_easy *data, break; case CURL_TS_IAC: -process_iac: DEBUGASSERT(startwrite < 0); switch(c) { case CURL_WILL: @@ -1151,75 +1149,67 @@ process_iac: } break; - case CURL_TS_WILL: - printoption(data, "RCVD", CURL_WILL, c); - tn->please_negotiate = 1; - rec_will(data, tn, c); - tn->telrcv_state = CURL_TS_DATA; - break; + case CURL_TS_WILL: + printoption(data, "RCVD", CURL_WILL, c); + tn->please_negotiate = 1; + rec_will(data, tn, c); + tn->telrcv_state = CURL_TS_DATA; + break; - case CURL_TS_WONT: - printoption(data, "RCVD", CURL_WONT, c); - tn->please_negotiate = 1; - rec_wont(data, tn, c); - tn->telrcv_state = CURL_TS_DATA; - break; + case CURL_TS_WONT: + printoption(data, "RCVD", CURL_WONT, c); + tn->please_negotiate = 1; + rec_wont(data, tn, c); + tn->telrcv_state = CURL_TS_DATA; + break; - case CURL_TS_DO: - printoption(data, "RCVD", CURL_DO, c); - tn->please_negotiate = 1; - rec_do(data, tn, c); - tn->telrcv_state = CURL_TS_DATA; - break; + case CURL_TS_DO: + printoption(data, "RCVD", CURL_DO, c); + tn->please_negotiate = 1; + rec_do(data, tn, c); + tn->telrcv_state = CURL_TS_DATA; + break; - case CURL_TS_DONT: - printoption(data, "RCVD", CURL_DONT, c); - tn->please_negotiate = 1; - rec_dont(data, tn, c); - tn->telrcv_state = CURL_TS_DATA; - break; + case CURL_TS_DONT: + printoption(data, "RCVD", CURL_DONT, c); + tn->please_negotiate = 1; + rec_dont(data, tn, c); + tn->telrcv_state = CURL_TS_DATA; + break; - case CURL_TS_SB: - if(c == CURL_IAC) - tn->telrcv_state = CURL_TS_SE; - else - CURL_SB_ACCUM(tn, c); - break; + case CURL_TS_SB: + if(c == CURL_IAC) + tn->telrcv_state = CURL_TS_SE; + else + CURL_SB_ACCUM(tn, c); + break; - case CURL_TS_SE: - if(c != CURL_SE) { - if(c != CURL_IAC) { - /* - * This is an error. We only expect to get "IAC IAC" or "IAC SE". - * Several things may have happened. An IAC was not doubled, the - * IAC SE was left off, or another option got inserted into the - * suboption are all possibilities. If we assume that the IAC was - * not doubled, and really the IAC SE was left off, we could get - * into an infinite loop here. So, instead, we terminate the - * suboption, and process the partial suboption if we can. - */ - CURL_SB_ACCUM(tn, CURL_IAC); - CURL_SB_ACCUM(tn, c); - tn->subpointer -= 2; - CURL_SB_TERM(tn); - - printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c); - suboption(data, tn); /* handle sub-option */ - tn->telrcv_state = CURL_TS_IAC; - goto process_iac; - } - CURL_SB_ACCUM(tn, c); - tn->telrcv_state = CURL_TS_SB; + case CURL_TS_SE: + if(c != CURL_SE) { + if(c != CURL_IAC) { + /* + * This is an error. We only expect to get "IAC IAC" or "IAC SE". + * Several things may have happened. An IAC was not doubled, the IAC + * SE was left off, or another option got inserted into the + * suboption are all possibilities. + */ + failf(data, "telnet: suboption error"); + return CURLE_RECV_ERROR; } - else { - CURL_SB_ACCUM(tn, CURL_IAC); - CURL_SB_ACCUM(tn, CURL_SE); - tn->subpointer -= 2; - CURL_SB_TERM(tn); - suboption(data, tn); /* handle sub-option */ - tn->telrcv_state = CURL_TS_DATA; - } - break; + CURL_SB_ACCUM(tn, c); + tn->telrcv_state = CURL_TS_SB; + } + else { + CURL_SB_ACCUM(tn, CURL_IAC); + CURL_SB_ACCUM(tn, CURL_SE); + tn->subpointer -= 2; + CURL_SB_TERM(tn); + result = suboption(data, tn); /* handle sub-option */ + if(result) + return result; + tn->telrcv_state = CURL_TS_DATA; + } + break; } ++in; } @@ -1227,63 +1217,6 @@ process_iac: 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) { @@ -1303,21 +1236,18 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) WSANETWORKEVENTS events; HANDLE stdin_handle; HANDLE objs[2]; - DWORD obj_count; - DWORD wait_timeout; + DWORD obj_count; + DWORD wait_timeout; DWORD readfile_read; int err; #else timediff_t interval_ms; struct pollfd pfd[2]; int poll_cnt; - curl_off_t total_dl = 0; - curl_off_t total_ul = 0; ssize_t snread; #endif - struct curltime now; bool keepon = TRUE; - char buffer[4*1024]; + char buffer[4 * 1024]; struct TELNET *tn; *done = TRUE; /* unconditionally */ @@ -1336,9 +1266,9 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) #ifdef USE_WINSOCK /* We want to wait for both stdin and the socket. Since - ** the select() function in Winsock only works on sockets - ** we have to use the WaitForMultipleObjects() call. - */ + * the select() function in Winsock only works on sockets + * we have to use the WaitForMultipleObjects() call. + */ /* First, create a sockets event object */ event_handle = WSACreateEvent(); @@ -1348,9 +1278,9 @@ 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_OK; + return CURLE_RECV_ERROR; } /* The get the Windows file handle for stdin */ @@ -1362,9 +1292,8 @@ 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 */ + if(GetFileType(stdin_handle) == FILE_TYPE_PIPE || data->set.is_fread_set) { + /* Do not wait for stdin_handle, wait for event_handle */ obj_count = 1; /* Check stdin_handle per 100 milliseconds */ wait_timeout = 100; @@ -1381,8 +1310,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) FALSE, wait_timeout); switch(waitret) { - case WAIT_TIMEOUT: - { + case WAIT_TIMEOUT: { for(;;) { if(data->set.is_fread_set) { size_t n; @@ -1415,8 +1343,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) if(!readfile_read) break; - if(!ReadFile(stdin_handle, buffer, buf_size, - &readfile_read, NULL)) { + if(!ReadFile(stdin_handle, buffer, buf_size, &readfile_read, NULL)) { keepon = FALSE; result = CURLE_READ_ERROR; break; @@ -1432,10 +1359,8 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } break; - case WAIT_OBJECT_0 + 1: - { - if(!ReadFile(stdin_handle, buffer, buf_size, - &readfile_read, NULL)) { + case WAIT_OBJECT_0 + 1: { + if(!ReadFile(stdin_handle, buffer, buf_size, &readfile_read, NULL)) { keepon = FALSE; result = CURLE_READ_ERROR; break; @@ -1449,10 +1374,9 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } break; - case WAIT_OBJECT_0: - { + 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); @@ -1480,7 +1404,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) break; } - result = telrcv(data, tn, (unsigned char *) buffer, nread); + result = telrcv(data, tn, (unsigned char *)buffer, nread); if(result) { keepon = FALSE; break; @@ -1497,14 +1421,13 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) if(events.lNetworkEvents & FD_CLOSE) { keepon = FALSE; } + break; } - break; - - } + } /* switch */ if(data->set.timeout) { - now = curlx_now(); - if(curlx_timediff(now, conn->created) >= data->set.timeout) { + if(curlx_ptimediff_ms(Curl_pgrs_now(data), &conn->created) >= + data->set.timeout) { failf(data, "Time-out"); result = CURLE_OPERATION_TIMEDOUT; keepon = FALSE; @@ -1573,10 +1496,8 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) break; } - total_dl += nread; - result = Curl_pgrsSetDownloadCounter(data, total_dl); - if(!result) - result = telrcv(data, tn, (unsigned char *)buffer, nread); + Curl_pgrs_download_inc(data, nread); + result = telrcv(data, tn, (unsigned char *)buffer, nread); if(result) { keepon = FALSE; break; @@ -1600,7 +1521,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) else { /* read from user-supplied method */ snread = (int)data->state.fread_func(buffer, 1, sizeof(buffer), - data->state.in); + data->state.in); if(snread == CURL_READFUNC_ABORT) { keepon = FALSE; break; @@ -1615,8 +1536,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) keepon = FALSE; break; } - total_ul += snread; - Curl_pgrsSetUploadCounter(data, total_ul); + Curl_pgrs_upload_inc(data, (size_t)snread); } else if(snread < 0) keepon = FALSE; @@ -1625,17 +1545,18 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } /* poll switch statement */ if(data->set.timeout) { - now = curlx_now(); - if(curlx_timediff(now, conn->created) >= data->set.timeout) { + if(curlx_ptimediff_ms(Curl_pgrs_now(data), &conn->created) >= + data->set.timeout) { failf(data, "Time-out"); result = CURLE_OPERATION_TIMEDOUT; keepon = FALSE; } } - if(Curl_pgrsUpdate(data)) { - result = CURLE_ABORTED_BY_CALLBACK; - break; + if(!result) { + result = Curl_pgrsUpdate(data); + if(result) + keepon = FALSE; } } #endif @@ -1644,4 +1565,28 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) return result; } -#endif + +/* + * TELNET protocol handler. + */ +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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#endif /* !CURL_DISABLE_TELNET */ diff --git a/lib/telnet.h b/lib/telnet.h index 30782d8375..3848fff3ba 100644 --- a/lib/telnet.h +++ b/lib/telnet.h @@ -24,7 +24,7 @@ * ***************************************************************************/ #ifndef CURL_DISABLE_TELNET -extern const struct Curl_handler Curl_handler_telnet; +extern const struct Curl_protocol Curl_protocol_telnet; #endif #endif /* HEADER_CURL_TELNET_H */ diff --git a/lib/tftp.c b/lib/tftp.c index 4c2fadc2ca..dd006fb1cf 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -21,8 +21,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" +#include "urldata.h" +#include "tftp.h" #ifndef CURL_DISABLE_TFTP @@ -46,33 +47,25 @@ #include #endif -#include "urldata.h" -#include #include "cfilters.h" #include "cf-socket.h" #include "transfer.h" #include "sendf.h" -#include "tftp.h" +#include "curl_trc.h" #include "progress.h" #include "connect.h" -#include "strerror.h" #include "sockaddr.h" /* required for Curl_sockaddr_storage */ -#include "multiif.h" #include "url.h" #include "strcase.h" -#include "speedcheck.h" #include "select.h" #include "escape.h" +#include "curlx/strerr.h" #include "curlx/strparse.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "curlx/strcopy.h" /* RFC2348 allows the block size to be negotiated */ #define TFTP_BLKSIZE_DEFAULT 512 -#define TFTP_OPTION_BLKSIZE "blksize" +#define TFTP_OPTION_BLKSIZE "blksize" /* from RFC2349: */ #define TFTP_OPTION_TSIZE "tsize" @@ -126,6 +119,10 @@ struct tftp_packet { #define CURL_META_TFTP_CONN "meta:proto:tftp:conn" struct tftp_conn { + struct Curl_sockaddr_storage local_addr; + struct Curl_sockaddr_storage remote_addr; + struct tftp_packet rpacket; + struct tftp_packet spacket; tftp_state_t state; tftp_mode_t mode; tftp_error_t error; @@ -136,62 +133,13 @@ struct tftp_conn { int retry_time; int retry_max; time_t rx_time; - struct Curl_sockaddr_storage local_addr; - struct Curl_sockaddr_storage remote_addr; curl_socklen_t remote_addrlen; int rbytes; size_t sbytes; unsigned int blksize; unsigned int requested_blksize; unsigned short block; - struct tftp_packet rpacket; - struct tftp_packet spacket; -}; - - -/* 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 */ + BIT(remote_pinned); }; /********************************************************** @@ -206,12 +154,11 @@ const struct Curl_handler Curl_handler_tftp = { **********************************************************/ static CURLcode tftp_set_timeouts(struct tftp_conn *state) { - time_t maxtime, timeout; + time_t timeout; timediff_t timeout_ms; - bool start = (state->state == TFTP_STATE_START); /* Compute drop-dead time */ - timeout_ms = Curl_timeleft(state->data, NULL, start); + timeout_ms = Curl_timeleft_ms(state->data); if(timeout_ms < 0) { /* time-out, bail out, go home */ @@ -219,18 +166,16 @@ static CURLcode tftp_set_timeouts(struct tftp_conn *state) return CURLE_OPERATION_TIMEDOUT; } - if(timeout_ms > 0) - maxtime = (time_t)(timeout_ms + 500) / 1000; - else - maxtime = 3600; /* use for calculating block timeouts */ - /* Set per-block timeout to total */ - timeout = maxtime; + if(timeout_ms > 0) + timeout = (time_t)(timeout_ms + 500) / 1000; + else + timeout = 15; /* Average reposting an ACK after 5 seconds */ - state->retry_max = (int)timeout/5; + state->retry_max = (int)timeout / 5; - /* But bound the total number */ + /* Bound the total number */ if(state->retry_max < 3) state->retry_max = 3; @@ -238,12 +183,12 @@ static CURLcode tftp_set_timeouts(struct tftp_conn *state) state->retry_max = 50; /* Compute the re-ACK interval to suit the timeout */ - state->retry_time = (int)(timeout/state->retry_max); + state->retry_time = (int)(timeout / state->retry_max); if(state->retry_time < 1) state->retry_time = 1; infof(state->data, - "set timeouts for state %d; Total % " FMT_OFF_T ", retry %d maxtry %d", + "set timeouts for state %d; Total %" FMT_OFF_T ", retry %d maxtry %d", (int)state->state, timeout_ms, state->retry_time, state->retry_max); /* init RX time */ @@ -266,7 +211,6 @@ static void setpacketevent(struct tftp_packet *packet, unsigned short num) packet->data[1] = (unsigned char)(num & 0xff); } - static void setpacketblock(struct tftp_packet *packet, unsigned short num) { packet->data[2] = (unsigned char)(num >> 8); @@ -286,7 +230,7 @@ static unsigned short getrpacketblock(const struct tftp_packet *packet) static size_t tftp_strnlen(const char *string, size_t maxlen) { const char *end = memchr(string, '\0', maxlen); - return end ? (size_t) (end - string) : maxlen; + return end ? (size_t)(end - string) : maxlen; } static const char *tftp_option_get(const char *buf, size_t len, @@ -301,7 +245,7 @@ static const char *tftp_option_get(const char *buf, size_t len, return NULL; *option = buf; - loc += tftp_strnlen(buf + loc, len-loc); + loc += tftp_strnlen(buf + loc, len - loc); loc++; /* NULL term */ if(loc > len) @@ -381,24 +325,181 @@ static CURLcode tftp_parse_option_ack(struct tftp_conn *state, } static CURLcode tftp_option_add(struct tftp_conn *state, size_t *csize, - char *buf, const char *option) + size_t index, const char *option) { - if(( strlen(option) + *csize + 1) > (size_t)state->blksize) + char *buf = (char *)&state->spacket.data[index]; + size_t oplen = strlen(option); + size_t blen; + if((state->blksize <= index) || + (oplen + 1) > (size_t)(state->blksize - index)) return CURLE_TFTP_ILLEGAL; - strcpy(buf, option); - *csize += strlen(option) + 1; + blen = state->blksize - index; + curlx_strcopy(buf, blen, option, oplen); + *csize += oplen + 1; return CURLE_OK; } +/* the next blocknum is x + 1 but it needs to wrap at an unsigned 16-bit + boundary */ +#define NEXT_BLOCKNUM(x) (((x) + 1) & 0xffff) + +/********************************************************** + * + * 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. To handle it, 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++; + } + 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) { + state->state = TFTP_STATE_FIN; + return CURLE_OK; + } + + /* 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); + + 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_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 { + /* 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_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(data, "tftp_tx: internal error, event: %i", (int)event); + break; + } + + return result; +} + 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 + infof(state->data, "%s", "Connected for transmit"); + state->state = TFTP_STATE_TX; result = tftp_set_timeouts(state); if(result) @@ -406,15 +507,135 @@ static CURLcode tftp_connect_for_tx(struct tftp_conn *state, return tftp_tx(state, event); } +/********************************************************** + * + * tftp_rx + * + * Event handler for the RX state + * + **********************************************************/ +static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event) +{ + ssize_t sbytes; + int rblock; + struct Curl_easy *data = state->data; + char buffer[STRERROR_LEN]; + + switch(event) { + + case TFTP_EVENT_DATA: + /* Is this the block we expect? */ + rblock = getrpacketblock(&state->rpacket); + if(NEXT_BLOCKNUM(state->block) == rblock) { + /* This is the expected block. Reset counters and ACK it. */ + state->retries = 0; + } + else if(state->block == rblock) { + /* This is the last recently received block again. Log it and ACK it + again. */ + infof(data, "Received last DATA packet block %d again.", rblock); + } + else { + /* totally unexpected, log it */ + infof(data, + "Received unexpected DATA packet block %d, expecting block %d", + rblock, NEXT_BLOCKNUM(state->block)); + break; + } + + /* ACK this block. */ + state->block = (unsigned short)rblock; + 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; +} + 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 + infof(state->data, "%s", "Connected for receive"); + state->state = TFTP_STATE_RX; result = tftp_set_timeouts(state); if(result) @@ -462,60 +683,55 @@ static CURLcode tftp_send_first(struct tftp_conn *state, /* 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) > (state->blksize - strlen(mode) - 4)) { + if(strlen(filename) + strlen(mode) + 4 > state->blksize) { failf(data, "TFTP filename too long"); - free(filename); + curlx_free(filename); return CURLE_TFTP_ILLEGAL; /* too long filename field */ } - msnprintf((char *)state->spacket.data + 2, - state->blksize, - "%s%c%s%c", filename, '\0', mode, '\0'); - sbytes = 4 + strlen(filename) + strlen(mode); + 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 */ - msnprintf(buf, sizeof(buf), "%" FMT_OFF_T, - data->state.upload && (data->state.infilesize != -1) ? - data->state.infilesize : 0); + 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, - (char *)state->spacket.data + sbytes, - TFTP_OPTION_TSIZE); + result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_TSIZE); if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, - (char *)state->spacket.data + sbytes, buf); + result = tftp_option_add(state, &sbytes, sbytes, buf); /* add blksize option */ - msnprintf(buf, sizeof(buf), "%d", state->requested_blksize); + curl_msnprintf(buf, sizeof(buf), "%d", state->requested_blksize); if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, - (char *)state->spacket.data + sbytes, - TFTP_OPTION_BLKSIZE); + result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_BLKSIZE); if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, - (char *)state->spacket.data + sbytes, buf); + result = tftp_option_add(state, &sbytes, sbytes, buf); /* add timeout option */ - msnprintf(buf, sizeof(buf), "%d", state->retry_time); + curl_msnprintf(buf, sizeof(buf), "%d", state->retry_time); if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, - (char *)state->spacket.data + sbytes, - TFTP_OPTION_INTERVAL); + result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_INTERVAL); if(result == CURLE_OK) - result = tftp_option_add(state, &sbytes, - (char *)state->spacket.data + sbytes, buf); + result = tftp_option_add(state, &sbytes, sbytes, buf); if(result != CURLE_OK) { failf(data, "TFTP buffer too small for options"); - free(filename); return CURLE_TFTP_ILLEGAL; } } @@ -537,9 +753,9 @@ static CURLcode tftp_send_first(struct tftp_conn *state, (curl_socklen_t)remote_addr->addrlen); if(senddata != (ssize_t)sbytes) { char buffer[STRERROR_LEN]; - failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + failf(data, "%s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); + return CURLE_SEND_ERROR; } - free(filename); break; case TFTP_EVENT_OACK: @@ -565,283 +781,7 @@ static CURLcode tftp_send_first(struct tftp_conn *state, default: failf(state->data, "tftp_send_first: internal error"); - 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) - -/********************************************************** - * - * tftp_rx - * - * Event handler for the RX state - * - **********************************************************/ -static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event) -{ - ssize_t sbytes; - int rblock; - struct Curl_easy *data = state->data; - char buffer[STRERROR_LEN]; - - switch(event) { - - case TFTP_EVENT_DATA: - /* Is this the block we expect? */ - rblock = getrpacketblock(&state->rpacket); - if(NEXT_BLOCKNUM(state->block) == rblock) { - /* This is the expected block. Reset counters and ACK it. */ - state->retries = 0; - } - else if(state->block == rblock) { - /* This is the last recently received block again. Log it and ACK it - again. */ - infof(data, "Received last DATA packet block %d again.", rblock); - } - else { - /* totally unexpected, just log it */ - infof(data, - "Received unexpected DATA packet block %d, expecting block %d", - rblock, NEXT_BLOCKNUM(state->block)); - break; - } - - /* ACK this block. */ - state->block = (unsigned short)rblock; - 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", Curl_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", Curl_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", Curl_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", Curl_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++; - } - 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) { - state->state = TFTP_STATE_FIN; - return CURLE_OK; - } - - /* 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); - - 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", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); - return CURLE_SEND_ERROR; - } - /* Update the progress meter */ - k->writebytecount += state->sbytes; - Curl_pgrsSetUploadCounter(data, k->writebytecount); - break; - - 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 { - /* 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", Curl_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, - (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, "tftp_tx: internal error, event: %i", (int)(event)); - break; + return CURLE_TFTP_ILLEGAL; } return result; @@ -943,9 +883,9 @@ static void tftp_conn_dtor(void *key, size_t klen, void *entry) struct tftp_conn *state = entry; (void)key; (void)klen; - Curl_safefree(state->rpacket.data); - Curl_safefree(state->spacket.data); - free(state); + curlx_safefree(state->rpacket.data); + curlx_safefree(state->spacket.data); + curlx_free(state); } /********************************************************** @@ -962,10 +902,11 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) int need_blksize; struct connectdata *conn = data->conn; const struct Curl_sockaddr_ex *remote_addr = NULL; + CURLcode result; blksize = TFTP_BLKSIZE_DEFAULT; - state = calloc(1, sizeof(*state)); + state = curlx_calloc(1, sizeof(*state)); if(!state || Curl_conn_meta_set(conn, CURL_META_TFTP_CONN, state, tftp_conn_dtor)) return CURLE_OUT_OF_MEMORY; @@ -981,21 +922,21 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) need_blksize = TFTP_BLKSIZE_DEFAULT; if(!state->rpacket.data) { - state->rpacket.data = calloc(1, need_blksize + 2 + 2); + state->rpacket.data = curlx_calloc(1, need_blksize + 2 + 2); if(!state->rpacket.data) return CURLE_OUT_OF_MEMORY; } if(!state->spacket.data) { - state->spacket.data = calloc(1, need_blksize + 2 + 2); + state->spacket.data = curlx_calloc(1, need_blksize + 2 + 2); if(!state->spacket.data) 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; @@ -1013,7 +954,9 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) ((struct sockaddr *)&state->local_addr)->sa_family = (CURL_SA_FAMILY_T)(remote_addr->family); - tftp_set_timeouts(state); + result = tftp_set_timeouts(state); + if(result) + return result; if(!conn->bits.bound) { /* If not already bound, bind to any interface, random UDP port. If it is @@ -1034,7 +977,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) if(rc) { char buffer[STRERROR_LEN]; failf(data, "bind() failed; %s", - Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); + curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); return CURLE_COULDNT_CONNECT; } conn->bits.bound = TRUE; @@ -1090,18 +1033,33 @@ static CURLcode tftp_pollset(struct Curl_easy *data, static CURLcode tftp_receive_packet(struct Curl_easy *data, struct tftp_conn *state) { - curl_socklen_t fromlen; - CURLcode result = CURLE_OK; + CURLcode result = CURLE_OK; + struct Curl_sockaddr_storage remote_addr; + curl_socklen_t fromlen = sizeof(remote_addr); /* Receive the packet */ - fromlen = sizeof(state->remote_addr); state->rbytes = (int)recvfrom(state->sockfd, (void *)state->rpacket.data, (RECV_TYPE_ARG3)state->blksize + 4, 0, - (struct sockaddr *)&state->remote_addr, + (struct sockaddr *)&remote_addr, &fromlen); - state->remote_addrlen = fromlen; + if((state->rbytes >= 0) && fromlen) { + if(state->remote_pinned) { + /* pinned, verify that it comes from the same address */ + if((state->remote_addrlen != fromlen) || + memcmp(&remote_addr, &state->remote_addr, fromlen)) { + failf(data, "Data received from another address"); + return CURLE_RECV_ERROR; + } + } + else { + /* pin address on first use */ + state->remote_pinned = TRUE; + state->remote_addrlen = fromlen; + memcpy(&state->remote_addr, &remote_addr, fromlen); + } + } /* Sanity check packet length */ if(state->rbytes < 4) { @@ -1120,18 +1078,17 @@ 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, - state->rbytes-4); + (const char *)state->rpacket.data + 4, + state->rbytes - 4); if(result) { tftp_state_machine(state, TFTP_EVENT_ERROR); return result; } } break; - case TFTP_EVENT_ERROR: - { + 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) @@ -1155,9 +1112,10 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data, } /* Update the progress meter */ - if(Curl_pgrsUpdate(data)) { + result = Curl_pgrsUpdate(data); + if(result) { tftp_state_machine(state, TFTP_EVENT_ERROR); - return CURLE_ABORTED_BY_CALLBACK; + return result; } } return result; @@ -1179,12 +1137,11 @@ static timediff_t tftp_state_timeout(struct tftp_conn *state, if(event) *event = TFTP_EVENT_NONE; - timeout_ms = Curl_timeleft(state->data, NULL, - (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; - return 0; + return timeout_ms; } current = time(NULL); if(current > state->rx_time + state->retry_time) { @@ -1237,7 +1194,7 @@ static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done) /* bail out */ int error = SOCKERRNO; char buffer[STRERROR_LEN]; - failf(data, "%s", Curl_strerror(error, buffer, sizeof(buffer))); + failf(data, "%s", curlx_strerror(error, buffer, sizeof(buffer))); state->event = TFTP_EVENT_ERROR; } else if(rc) { @@ -1277,10 +1234,7 @@ static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done) /* The multi code does not have this logic for the DOING state so we provide it for TFTP since it may do the entire transfer in this state. */ - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, curlx_now()); + result = Curl_pgrsCheck(data); } return result; } @@ -1307,7 +1261,7 @@ static CURLcode tftp_perform(struct Curl_easy *data, bool *dophase_done) if((state->state == TFTP_STATE_FIN) || result) return result; - tftp_multi_statemach(data, dophase_done); + result = tftp_multi_statemach(data, dophase_done); if(*dophase_done) DEBUGF(infof(data, "DO phase is complete")); @@ -1315,7 +1269,6 @@ static CURLcode tftp_perform(struct Curl_easy *data, bool *dophase_done) return result; } - /********************************************************** * * tftp_do @@ -1358,37 +1311,45 @@ static CURLcode tftp_do(struct Curl_easy *data, bool *done) static CURLcode tftp_setup_connection(struct Curl_easy *data, struct connectdata *conn) { - char *type; + char *path = data->state.up.path; + size_t len = strlen(path); conn->transport_wanted = TRNSPRT_UDP; - /* TFTP URLs support an extension like ";mode=" that - * we will try to get now! */ - type = strstr(data->state.up.path, ";mode="); - - if(!type) - type = strstr(conn->host.rawalloc, ";mode="); - - if(type) { - char command; - *type = 0; /* it was in the middle of the hostname */ - command = Curl_raw_toupper(type[6]); - - switch(command) { - case 'A': /* ASCII mode */ - case 'N': /* NETASCII mode */ - data->state.prefer_ascii = TRUE; - break; - - case 'O': /* octet mode */ - case 'I': /* binary mode */ - default: - /* switch off ASCII */ - data->state.prefer_ascii = FALSE; - break; - } + /* TFTP URLs support a trailing ";mode=netascii" or ";mode=octet" */ + if((len >= 14) && !memcmp(&path[len - 14], ";mode=netascii", 14)) { + data->state.prefer_ascii = TRUE; + path[len - 14] = 0; /* cut it there */ + } + else if((len >= 11) && !memcmp(&path[len - 11], ";mode=octet", 11)) { + data->state.prefer_ascii = FALSE; + path[len - 11] = 0; /* cut it there */ } return CURLE_OK; } + +/* + * TFTP protocol handler. + */ +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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + #endif diff --git a/lib/tftp.h b/lib/tftp.h index 12404bf6d2..32310103b0 100644 --- a/lib/tftp.h +++ b/lib/tftp.h @@ -24,10 +24,10 @@ * ***************************************************************************/ #ifndef CURL_DISABLE_TFTP -extern const struct Curl_handler Curl_handler_tftp; +extern const struct Curl_protocol Curl_protocol_tftp; +#endif #define TFTP_BLKSIZE_MIN 8 #define TFTP_BLKSIZE_MAX 65464 -#endif #endif /* HEADER_CURL_TFTP_H */ diff --git a/lib/thrdpool.c b/lib/thrdpool.c new file mode 100644 index 0000000000..22faa396ed --- /dev/null +++ b/lib/thrdpool.c @@ -0,0 +1,481 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_THREADS + +#include "llist.h" +#include "curl_threads.h" +#include "curlx/timeval.h" +#include "thrdpool.h" +#ifdef CURLVERBOSE +#include "curl_trc.h" +#include "urldata.h" +#endif + + +struct thrdslot { + struct Curl_llist_node node; + struct curl_thrdpool *tpool; + curl_thread_t thread; + curl_cond_t await; + struct curltime starttime; + const char *work_description; + timediff_t work_timeout_ms; + uint32_t id; + BIT(running); + BIT(idle); +}; + +struct curl_thrdpool { + char *name; + uint64_t refcount; + curl_mutex_t lock; + curl_cond_t await; + struct Curl_llist slots; + struct Curl_llist zombies; + Curl_thrdpool_take_item_cb *fn_take; + Curl_thrdpool_process_item_cb *fn_process; + Curl_thrdpool_return_item_cb *fn_return; + void *fn_user_data; + CURLcode fatal_err; + uint32_t min_threads; + uint32_t max_threads; + uint32_t idle_time_ms; + uint32_t next_id; + BIT(aborted); + BIT(detached); +}; + +static void thrdpool_join_zombies(struct curl_thrdpool *tpool); +static bool thrdpool_unlink(struct curl_thrdpool *tpool, bool locked); + +static void thrdslot_destroy(struct thrdslot *tslot) +{ + DEBUGASSERT(tslot->thread == curl_thread_t_null); + DEBUGASSERT(!tslot->running); + Curl_cond_destroy(&tslot->await); + curlx_free(tslot); +} + +static void thrdslot_done(struct thrdslot *tslot) +{ + struct curl_thrdpool *tpool = tslot->tpool; + + DEBUGASSERT(Curl_node_llist(&tslot->node) == &tpool->slots); + Curl_node_remove(&tslot->node); + tslot->running = FALSE; + Curl_llist_append(&tpool->zombies, tslot, &tslot->node); + Curl_cond_signal(&tpool->await); +} + +static CURL_THREAD_RETURN_T CURL_STDCALL thrdslot_run(void *arg) +{ + struct thrdslot *tslot = arg; + struct curl_thrdpool *tpool = tslot->tpool; + void *item; + + Curl_mutex_acquire(&tpool->lock); + DEBUGASSERT(Curl_node_llist(&tslot->node) == &tpool->slots); + for(;;) { + while(!tpool->aborted) { + tslot->work_description = NULL; + tslot->work_timeout_ms = 0; + item = tpool->fn_take(tpool->fn_user_data, &tslot->work_description, + &tslot->work_timeout_ms); + if(!item) + break; + tslot->starttime = curlx_now(); + tslot->idle = FALSE; + Curl_mutex_release(&tpool->lock); + + tpool->fn_process(item); + + Curl_mutex_acquire(&tpool->lock); + tslot->work_description = NULL; + tpool->fn_return(item, tpool->aborted ? NULL : tpool->fn_user_data); + } + + if(tpool->aborted || + (Curl_llist_count(&tpool->slots) > tpool->max_threads)) + goto out; + + tslot->idle = TRUE; + tslot->starttime = curlx_now(); + thrdpool_join_zombies(tpool); + Curl_cond_signal(&tpool->await); + /* Only wait with idle timeout when we are above the minimum + * number of threads. Otherwise short idle timeouts will keep + * on activating threads that have no means to shut down. */ + if((tpool->idle_time_ms > 0) && + (Curl_llist_count(&tpool->slots) > tpool->min_threads)) { + CURLcode r = Curl_cond_timedwait(&tslot->await, &tpool->lock, + tpool->idle_time_ms); + if((r == CURLE_OPERATION_TIMEDOUT) && + (Curl_llist_count(&tpool->slots) > tpool->min_threads)) { + goto out; + } + } + else { + Curl_cond_wait(&tslot->await, &tpool->lock); + } + } + +out: + thrdslot_done(tslot); + if(!thrdpool_unlink(tslot->tpool, TRUE)) { + /* tpool not destroyed */ + Curl_mutex_release(&tpool->lock); + } + return 0; +} + +static CURLcode thrdslot_start(struct curl_thrdpool *tpool) +{ + struct thrdslot *tslot; + CURLcode result = CURLE_OUT_OF_MEMORY; + + tslot = curlx_calloc(1, sizeof(*tslot)); + if(!tslot) + goto out; + tslot->id = tpool->next_id++; + tslot->tpool = tpool; + tslot->thread = curl_thread_t_null; + Curl_cond_init(&tslot->await); + + tpool->refcount++; + tslot->running = TRUE; + tslot->thread = Curl_thread_create(thrdslot_run, tslot); + if(tslot->thread == curl_thread_t_null) { /* never started */ + tslot->running = FALSE; + thrdpool_unlink(tpool, TRUE); + result = CURLE_FAILED_INIT; + goto out; + } + + Curl_llist_append(&tpool->slots, tslot, &tslot->node); + tslot = NULL; + result = CURLE_OK; + +out: + if(tslot) + thrdslot_destroy(tslot); + return result; +} + +static void thrdpool_wake_all(struct curl_thrdpool *tpool) +{ + struct Curl_llist_node *e; + for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) { + struct thrdslot *tslot = Curl_node_elem(e); + Curl_cond_signal(&tslot->await); + } +} + +static void thrdpool_join_zombies(struct curl_thrdpool *tpool) +{ + struct Curl_llist_node *e; + + for(e = Curl_llist_head(&tpool->zombies); e; + e = Curl_llist_head(&tpool->zombies)) { + struct thrdslot *tslot = Curl_node_elem(e); + + Curl_node_remove(&tslot->node); + if(tslot->thread != curl_thread_t_null) { + Curl_mutex_release(&tpool->lock); + Curl_thread_join(&tslot->thread); + Curl_mutex_acquire(&tpool->lock); + tslot->thread = curl_thread_t_null; + } + thrdslot_destroy(tslot); + } +} + +static bool thrdpool_unlink(struct curl_thrdpool *tpool, bool locked) +{ + DEBUGASSERT(tpool->refcount); + if(tpool->refcount) + tpool->refcount--; + if(tpool->refcount) + return FALSE; + + /* no more references, free */ + DEBUGASSERT(tpool->aborted); + thrdpool_join_zombies(tpool); + if(locked) + Curl_mutex_release(&tpool->lock); + curlx_free(tpool->name); + Curl_cond_destroy(&tpool->await); + Curl_mutex_destroy(&tpool->lock); + curlx_free(tpool); + return TRUE; +} + +static CURLcode thrdpool_signal(struct curl_thrdpool *tpool, + uint32_t nthreads) +{ + struct Curl_llist_node *e, *n; + CURLcode result = CURLE_OK; + + DEBUGASSERT(!tpool->aborted); + thrdpool_join_zombies(tpool); + + for(e = Curl_llist_head(&tpool->slots); e && nthreads; e = n) { + struct thrdslot *tslot = Curl_node_elem(e); + n = Curl_node_next(e); + if(tslot->idle) { + Curl_cond_signal(&tslot->await); + --nthreads; + } + else if(!tslot->starttime.tv_sec && !tslot->starttime.tv_usec) { + /* starting thread, queries for work soon. */ + --nthreads; + } + } + + while(nthreads && !result && + Curl_llist_count(&tpool->slots) < tpool->max_threads) { + result = thrdslot_start(tpool); + if(result) + break; + --nthreads; + } + + return result; +} + +CURLcode Curl_thrdpool_set_props(struct curl_thrdpool *tpool, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms) +{ + CURLcode result = CURLE_OK; + size_t running; + + if(!max_threads || (min_threads > max_threads)) + return CURLE_BAD_FUNCTION_ARGUMENT; + + Curl_mutex_acquire(&tpool->lock); + tpool->min_threads = min_threads; + tpool->max_threads = max_threads; + tpool->idle_time_ms = idle_time_ms; + running = Curl_llist_count(&tpool->slots); + if(tpool->min_threads > running) { + result = thrdpool_signal(tpool, tpool->min_threads - (uint32_t)running); + } + Curl_mutex_release(&tpool->lock); + + return result; +} + +CURLcode Curl_thrdpool_create(struct curl_thrdpool **ptpool, + const char *name, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms, + Curl_thrdpool_take_item_cb *fn_take, + Curl_thrdpool_process_item_cb *fn_process, + Curl_thrdpool_return_item_cb *fn_return, + void *user_data) +{ + struct curl_thrdpool *tpool; + CURLcode result = CURLE_OUT_OF_MEMORY; + + tpool = curlx_calloc(1, sizeof(*tpool)); + if(!tpool) + goto out; + tpool->refcount = 1; + + Curl_mutex_init(&tpool->lock); + Curl_cond_init(&tpool->await); + Curl_llist_init(&tpool->slots, NULL); + Curl_llist_init(&tpool->zombies, NULL); + tpool->fn_take = fn_take; + tpool->fn_process = fn_process; + tpool->fn_return = fn_return; + tpool->fn_user_data = user_data; + + tpool->name = curlx_strdup(name); + if(!tpool->name) + goto out; + + result = Curl_thrdpool_set_props(tpool, min_threads, max_threads, + idle_time_ms); + +out: + if(result && tpool) { + tpool->aborted = TRUE; + thrdpool_unlink(tpool, FALSE); + tpool = NULL; + } + *ptpool = tpool; + return result; +} + +void Curl_thrdpool_destroy(struct curl_thrdpool *tpool, bool join) +{ + Curl_mutex_acquire(&tpool->lock); + + tpool->aborted = TRUE; + + while(join && Curl_llist_count(&tpool->slots)) { + thrdpool_wake_all(tpool); + Curl_cond_wait(&tpool->await, &tpool->lock); + } + + thrdpool_join_zombies(tpool); + + /* detach all still running threads */ + if(Curl_llist_count(&tpool->slots)) { + struct Curl_llist_node *e; + for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) { + struct thrdslot *tslot = Curl_node_elem(e); + if(tslot->thread != curl_thread_t_null) + Curl_thread_destroy(&tslot->thread); + } + tpool->detached = TRUE; + } + + if(!thrdpool_unlink(tpool, TRUE)) { + /* tpool not destroyed */ + Curl_mutex_release(&tpool->lock); + } +} + +CURLcode Curl_thrdpool_signal(struct curl_thrdpool *tpool, uint32_t nthreads) +{ + CURLcode result; + + Curl_mutex_acquire(&tpool->lock); + result = thrdpool_signal(tpool, nthreads); + Curl_mutex_release(&tpool->lock); + return result; +} + +static bool thrdpool_all_idle(struct curl_thrdpool *tpool) +{ + struct Curl_llist_node *e; + for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) { + struct thrdslot *tslot = Curl_node_elem(e); + if(!tslot->idle) + return FALSE; + } + return TRUE; +} + +CURLcode Curl_thrdpool_await_idle(struct curl_thrdpool *tpool, + uint32_t timeout_ms) +{ + CURLcode result = CURLE_OK; + struct curltime end = { 0 }; + + Curl_mutex_acquire(&tpool->lock); + DEBUGASSERT(!tpool->aborted); + if(tpool->aborted) { + result = CURLE_FAILED_INIT; + goto out; + } + + while(!thrdpool_all_idle(tpool)) { + if(timeout_ms) { + timediff_t remain_ms; + CURLcode r; + + if(!end.tv_sec && !end.tv_usec) { + end = curlx_now(); + end.tv_sec += (time_t)(timeout_ms / 1000); + end.tv_usec += (int)(timeout_ms % 1000) * 1000; + if(end.tv_usec >= 1000000) { + end.tv_sec++; + end.tv_usec -= 1000000; + } + } + remain_ms = curlx_timediff_ms(curlx_now(), end); + if(remain_ms <= 0) + r = CURLE_OPERATION_TIMEDOUT; + else + r = Curl_cond_timedwait(&tpool->await, &tpool->lock, + (uint32_t)remain_ms); + if(r == CURLE_OPERATION_TIMEDOUT) { + result = r; + break; + } + } + else { + Curl_cond_wait(&tpool->await, &tpool->lock); + } + } + +out: + thrdpool_join_zombies(tpool); + Curl_mutex_release(&tpool->lock); + return result; +} + +#ifdef CURLVERBOSE +void Curl_thrdpool_trace(struct curl_thrdpool *tpool, + struct Curl_easy *data) +{ + struct curl_trc_feat *feat = &Curl_trc_feat_threads; + if(Curl_trc_ft_is_verbose(data, feat)) { + struct Curl_llist_node *e; + struct curltime now = curlx_now(); + + Curl_mutex_acquire(&tpool->lock); + if(!Curl_llist_count(&tpool->slots)) { + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] no threads running", + tpool->name); + } + for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) { + struct thrdslot *tslot = Curl_node_elem(e); + timediff_t elapsed_ms = curlx_ptimediff_ms(&now, &tslot->starttime); + if(!tslot->running) { + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: not running", + tpool->name, tslot->id); + } + else if(!tslot->starttime.tv_sec && !tslot->starttime.tv_usec) { + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: starting...", + tpool->name, tslot->id); + } + else if(tslot->idle) { + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: idle for %" + FMT_TIMEDIFF_T "ms", + tpool->name, tslot->id, elapsed_ms); + } + else { + timediff_t remain_ms = tslot->work_timeout_ms ? + (tslot->work_timeout_ms - elapsed_ms) : 0; + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: busy %" + FMT_TIMEDIFF_T "ms, timeout in %" FMT_TIMEDIFF_T + "ms: %s", + tpool->name, tslot->id, elapsed_ms, remain_ms, + tslot->work_description); + } + } + Curl_mutex_release(&tpool->lock); + } +} +#endif + +#endif /* USE_THREADS */ diff --git a/lib/thrdpool.h b/lib/thrdpool.h new file mode 100644 index 0000000000..cc81fb6fcb --- /dev/null +++ b/lib/thrdpool.h @@ -0,0 +1,106 @@ +#ifndef HEADER_CURL_THRDPOOL_H +#define HEADER_CURL_THRDPOOL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" +#include "curlx/timediff.h" + +#ifdef USE_THREADS + +struct curl_thrdpool; +struct Curl_easy; + +/* Invoked under thread pool lock to get an "item" to work on. Must + * return NULL if there is nothing to do. + * Caller might return a descriptive string about the "item", where + * available. The string needs to have the same lifetime as the + * item itself. */ +typedef void *Curl_thrdpool_take_item_cb(void *user_data, + const char **pdescription, + timediff_t *ptimeout_ms); + +/* Invoked outside thread pool lock to process the item taken. */ +typedef void Curl_thrdpool_process_item_cb(void *item); + +/* Invoked under thread pool lock to return a processed item back + * to the producer. + * If the thread pool has been destroyed, `user_data` will be NULL + * and the callback is responsible to release all `item` resources. */ +typedef void Curl_thrdpool_return_item_cb(void *item, void *user_data); + +/* Create a new thread pool. + * @param name name of pool for tracing purposes + * @param min_threads minimum number of threads to have always running + * @param max_threads maximum number of threads running, ever. + * @param idle_time_ms maximum time a thread should wait for tasks to + * process before shutting down (unless the pool is + * already at minimum thread count), use 0 for + * infinite wait. + * @param fn_take take the next item to process + * @param fn_process process the item taken + * @param fn_return return the processed item + * @param user_data parameter passed to take/return callbacks + */ +CURLcode Curl_thrdpool_create(struct curl_thrdpool **ptpool, + const char *name, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms, + Curl_thrdpool_take_item_cb *fn_take, + Curl_thrdpool_process_item_cb *fn_process, + Curl_thrdpool_return_item_cb *fn_return, + void *user_data); + +/* Destroy the thread pool, release its resources. + * With `join` being TRUE, the call will wait for all threads to finish + * processing before returning. On FALSE, it will detach all threads + * running. Ongoing item processing will continue to run and + * `fn_return` will be invoked with NULL user_data before the thread exits. + */ +void Curl_thrdpool_destroy(struct curl_thrdpool *tpool, bool join); + +/* Signal the pool to wake up `nthreads` idle worker threads, possible + * creating new threads up to the max limit. The number should reflect + * the items that can actually be taken for processing right away, e.g. + * the producers "queue" length of outstanding items. + */ +CURLcode Curl_thrdpool_signal(struct curl_thrdpool *tpool, uint32_t nthreads); + +CURLcode Curl_thrdpool_await_idle(struct curl_thrdpool *tpool, + uint32_t timeout_ms); + +/* Change the properties of a threadpool. */ +CURLcode Curl_thrdpool_set_props(struct curl_thrdpool *tpool, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms); + +#ifdef CURLVERBOSE +void Curl_thrdpool_trace(struct curl_thrdpool *tpool, + struct Curl_easy *data); +#endif + +#endif /* USE_THREADS */ + +#endif /* HEADER_CURL_THRDPOOL_H */ diff --git a/lib/thrdqueue.c b/lib/thrdqueue.c new file mode 100644 index 0000000000..1521ccfbee --- /dev/null +++ b/lib/thrdqueue.c @@ -0,0 +1,411 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_THREADS + +#include "llist.h" +#include "curl_threads.h" +#include "thrdpool.h" +#include "thrdqueue.h" +#include "curlx/timeval.h" +#ifdef CURLVERBOSE +#include "curl_trc.h" +#include "urldata.h" +#endif + + +struct curl_thrdq { + char *name; + curl_mutex_t lock; + curl_cond_t await; + struct Curl_llist sendq; + struct Curl_llist recvq; + struct curl_thrdpool *tpool; + Curl_thrdq_item_free_cb *fn_free; + Curl_thrdq_item_process_cb *fn_process; + Curl_thrdq_ev_cb *fn_event; + void *fn_user_data; + uint32_t send_max_len; + BIT(aborted); +}; + +struct thrdq_item { + struct Curl_llist_node node; + Curl_thrdq_item_free_cb *fn_free; + Curl_thrdq_item_process_cb *fn_process; + void *item; + struct curltime start; + timediff_t timeout_ms; + const char *description; +}; + +static struct thrdq_item *thrdq_item_create(struct curl_thrdq *tqueue, + void *item, + const char *description, + timediff_t timeout_ms) +{ + struct thrdq_item *qitem; + + qitem = curlx_calloc(1, sizeof(*qitem)); + if(!qitem) + return NULL; + qitem->item = item; + qitem->description = description; + qitem->fn_free = tqueue->fn_free; + qitem->fn_process = tqueue->fn_process; + if(timeout_ms) { + qitem->start = curlx_now(); + qitem->timeout_ms = timeout_ms; + } + return qitem; +} + +static void thrdq_item_destroy(struct thrdq_item *qitem) +{ + if(qitem->item) + qitem->fn_free(qitem->item); + curlx_free(qitem); +} + +static void thrdq_item_list_dtor(void *user_data, void *elem) +{ + (void)user_data; + thrdq_item_destroy(elem); +} + +static void *thrdq_tpool_take(void *user_data, const char **pdescription, + timediff_t *ptimeout_ms) +{ + struct curl_thrdq *tqueue = user_data; + struct thrdq_item *qitem = NULL; + struct Curl_llist_node *e; + Curl_thrdq_ev_cb *fn_event = NULL; + void *fn_user_data = NULL; + + Curl_mutex_acquire(&tqueue->lock); + *pdescription = NULL; + *ptimeout_ms = 0; + if(!tqueue->aborted) { + e = Curl_llist_head(&tqueue->sendq); + if(e) { + struct curltime now = curlx_now(); + timediff_t timeout_ms; + while(e) { + qitem = Curl_node_take_elem(e); + timeout_ms = (!qitem->timeout_ms) ? 0 : + (qitem->timeout_ms - curlx_ptimediff_ms(&now, &qitem->start)); + if(timeout_ms < 0) { + /* timed out while queued, place on receive queue */ + Curl_llist_append(&tqueue->recvq, qitem, &qitem->node); + fn_event = tqueue->fn_event; + fn_user_data = tqueue->fn_user_data; + qitem = NULL; + e = Curl_llist_head(&tqueue->sendq); + continue; + } + else { + *pdescription = qitem->description; + *ptimeout_ms = timeout_ms; + break; + } + } + } + } + Curl_mutex_release(&tqueue->lock); + /* avoiding deadlocks */ + if(fn_event) + fn_event(tqueue, CURL_THRDQ_EV_ITEM_DONE, fn_user_data); + return qitem; +} + +static void thrdq_tpool_return(void *item, void *user_data) +{ + struct curl_thrdq *tqueue = user_data; + struct thrdq_item *qitem = item; + Curl_thrdq_ev_cb *fn_event = NULL; + void *fn_user_data = NULL; + + if(!tqueue) { + thrdq_item_destroy(item); + return; + } + + Curl_mutex_acquire(&tqueue->lock); + if(tqueue->aborted) { + thrdq_item_destroy(qitem); + } + else { + DEBUGASSERT(!Curl_node_llist(&qitem->node)); + Curl_llist_append(&tqueue->recvq, qitem, &qitem->node); + fn_event = tqueue->fn_event; + fn_user_data = tqueue->fn_user_data; + } + Curl_mutex_release(&tqueue->lock); + /* avoiding deadlocks */ + if(fn_event) + fn_event(tqueue, CURL_THRDQ_EV_ITEM_DONE, fn_user_data); +} + +static void thrdq_tpool_process(void *item) +{ + struct thrdq_item *qitem = item; + qitem->fn_process(qitem->item); +} + +static void thrdq_unlink(struct curl_thrdq *tqueue, bool locked, bool join) +{ + DEBUGASSERT(tqueue->aborted); + if(tqueue->tpool) { + if(locked) + Curl_mutex_release(&tqueue->lock); + Curl_thrdpool_destroy(tqueue->tpool, join); + tqueue->tpool = NULL; + if(locked) + Curl_mutex_acquire(&tqueue->lock); + } + + Curl_llist_destroy(&tqueue->sendq, NULL); + Curl_llist_destroy(&tqueue->recvq, NULL); + curlx_free(tqueue->name); + Curl_cond_destroy(&tqueue->await); + if(locked) + Curl_mutex_release(&tqueue->lock); + Curl_mutex_destroy(&tqueue->lock); + curlx_free(tqueue); +} + +CURLcode Curl_thrdq_create(struct curl_thrdq **ptqueue, + const char *name, + uint32_t max_len, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms, + Curl_thrdq_item_free_cb *fn_free, + Curl_thrdq_item_process_cb *fn_process, + Curl_thrdq_ev_cb *fn_event, + void *user_data) +{ + struct curl_thrdq *tqueue; + CURLcode result = CURLE_OUT_OF_MEMORY; + + tqueue = curlx_calloc(1, sizeof(*tqueue)); + if(!tqueue) + goto out; + + Curl_mutex_init(&tqueue->lock); + Curl_cond_init(&tqueue->await); + Curl_llist_init(&tqueue->sendq, thrdq_item_list_dtor); + Curl_llist_init(&tqueue->recvq, thrdq_item_list_dtor); + tqueue->fn_free = fn_free; + tqueue->fn_process = fn_process; + tqueue->fn_event = fn_event; + tqueue->fn_user_data = user_data; + tqueue->send_max_len = max_len; + + tqueue->name = curlx_strdup(name); + if(!tqueue->name) + goto out; + + result = Curl_thrdpool_create(&tqueue->tpool, name, + min_threads, max_threads, idle_time_ms, + thrdq_tpool_take, + thrdq_tpool_process, + thrdq_tpool_return, + tqueue); + +out: + if(result && tqueue) { + tqueue->aborted = TRUE; + thrdq_unlink(tqueue, FALSE, TRUE); + tqueue = NULL; + } + *ptqueue = tqueue; + return result; +} + +void Curl_thrdq_destroy(struct curl_thrdq *tqueue, bool join) +{ + Curl_mutex_acquire(&tqueue->lock); + DEBUGASSERT(!tqueue->aborted); + tqueue->aborted = TRUE; + thrdq_unlink(tqueue, TRUE, join); +} + +CURLcode Curl_thrdq_send(struct curl_thrdq *tqueue, void *item, + const char *description, timediff_t timeout_ms) +{ + CURLcode result = CURLE_AGAIN; + size_t signals = 0; + + Curl_mutex_acquire(&tqueue->lock); + if(tqueue->aborted) { + DEBUGASSERT(0); + result = CURLE_SEND_ERROR; + goto out; + } + if(timeout_ms < 0) { + result = CURLE_OPERATION_TIMEDOUT; + goto out; + } + + if(!tqueue->send_max_len || + (Curl_llist_count(&tqueue->sendq) < tqueue->send_max_len)) { + struct thrdq_item *qitem = thrdq_item_create(tqueue, item, description, + timeout_ms); + if(!qitem) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + item = NULL; + Curl_llist_append(&tqueue->sendq, qitem, &qitem->node); + signals = Curl_llist_count(&tqueue->sendq); + result = CURLE_OK; + } + +out: + Curl_mutex_release(&tqueue->lock); + /* Signal thread pool unlocked to avoid deadlocks. Since we added + * item to the queue already, it might have been taken for processing + * already. Any error in signalling the pool cannot be reported to + * the caller since it needs to give up ownership of item. */ + if(!result && signals) + (void)Curl_thrdpool_signal(tqueue->tpool, (uint32_t)signals); + return result; +} + +CURLcode Curl_thrdq_recv(struct curl_thrdq *tqueue, void **pitem) +{ + CURLcode result = CURLE_AGAIN; + struct Curl_llist_node *e; + + *pitem = NULL; + Curl_mutex_acquire(&tqueue->lock); + if(tqueue->aborted) { + DEBUGASSERT(0); + result = CURLE_RECV_ERROR; + goto out; + } + + e = Curl_llist_head(&tqueue->recvq); + if(e) { + struct thrdq_item *qitem = Curl_node_take_elem(e); + *pitem = qitem->item; + qitem->item = NULL; + thrdq_item_destroy(qitem); + result = CURLE_OK; + } +out: + Curl_mutex_release(&tqueue->lock); + return result; +} + +static void thrdq_llist_clean_matches(struct Curl_llist *llist, + Curl_thrdq_item_match_cb *fn_match, + void *match_data) +{ + struct Curl_llist_node *e, *n; + struct thrdq_item *qitem; + + for(e = Curl_llist_head(llist); e; e = n) { + n = Curl_node_next(e); + qitem = Curl_node_elem(e); + if(fn_match(qitem->item, match_data)) + Curl_node_remove(e); + } +} + +void Curl_thrdq_clear(struct curl_thrdq *tqueue, + Curl_thrdq_item_match_cb *fn_match, + void *match_data) +{ + Curl_mutex_acquire(&tqueue->lock); + if(tqueue->aborted) { + DEBUGASSERT(0); + goto out; + } + thrdq_llist_clean_matches(&tqueue->sendq, fn_match, match_data); + thrdq_llist_clean_matches(&tqueue->recvq, fn_match, match_data); +out: + Curl_mutex_release(&tqueue->lock); +} + +CURLcode Curl_thrdq_await_done(struct curl_thrdq *tqueue, + uint32_t timeout_ms) +{ + return Curl_thrdpool_await_idle(tqueue->tpool, timeout_ms); +} + +CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue, + uint32_t max_len, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms) +{ + CURLcode result; + size_t signals; + + Curl_mutex_acquire(&tqueue->lock); + tqueue->send_max_len = max_len; + signals = Curl_llist_count(&tqueue->sendq); + Curl_mutex_release(&tqueue->lock); + + result = Curl_thrdpool_set_props(tqueue->tpool, min_threads, + max_threads, idle_time_ms); + if(!result && signals) + result = Curl_thrdpool_signal(tqueue->tpool, (uint32_t)signals); + return result; +} + +#ifdef CURLVERBOSE +void Curl_thrdq_trace(struct curl_thrdq *tqueue, + struct Curl_easy *data) +{ + struct curl_trc_feat *feat = &Curl_trc_feat_threads; + if(Curl_trc_ft_is_verbose(data, feat)) { + struct Curl_llist_node *e; + struct thrdq_item *qitem; + + Curl_thrdpool_trace(tqueue->tpool, data); + Curl_mutex_acquire(&tqueue->lock); + if(!Curl_llist_count(&tqueue->sendq) && + !Curl_llist_count(&tqueue->recvq)) { + Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] empty", tqueue->name); + } + for(e = Curl_llist_head(&tqueue->sendq); e; e = Curl_node_next(e)) { + qitem = Curl_node_elem(e); + Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] in: %s", + tqueue->name, qitem->description); + } + for(e = Curl_llist_head(&tqueue->recvq); e; e = Curl_node_next(e)) { + qitem = Curl_node_elem(e); + Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] out: %s", + tqueue->name, qitem->description); + } + Curl_mutex_release(&tqueue->lock); + } +} +#endif + +#endif /* USE_THREADS */ diff --git a/lib/thrdqueue.h b/lib/thrdqueue.h new file mode 100644 index 0000000000..d267f5d091 --- /dev/null +++ b/lib/thrdqueue.h @@ -0,0 +1,118 @@ +#ifndef HEADER_CURL_THRDQUEUE_H +#define HEADER_CURL_THRDQUEUE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" +#include "curlx/timediff.h" + +#ifdef USE_THREADS + +struct Curl_easy; +struct curl_thrdq; + +typedef enum { + CURL_THRDQ_EV_ITEM_DONE /* an item has been processed and is ready */ +} Curl_thrdq_event; + +/* Notification callback when "events" happen in the queue. May be + * call from any thread, queue is not locked. */ +typedef void Curl_thrdq_ev_cb(const struct curl_thrdq *tqueue, + Curl_thrdq_event ev, + void *user_data); + +/* Process a queued item. Maybe call from any thread. Queue is + * not locked. */ +typedef void Curl_thrdq_item_process_cb(void *item); + +/* Free an item. May be called from any thread at any time for an + * item that is in the queue (either before or after processing). */ +typedef void Curl_thrdq_item_free_cb(void *item); + +/* Create a new queue processing "items" by a thread pool. + */ +CURLcode Curl_thrdq_create(struct curl_thrdq **ptqueue, + const char *name, + uint32_t max_len, /* 0 for unlimited */ + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms, + Curl_thrdq_item_free_cb *fn_free, + Curl_thrdq_item_process_cb *fn_process, + Curl_thrdq_ev_cb *fn_event, /* optional */ + void *user_data); + +/* Destroy the queue, free all queued items unprocessed and destroy + * the thread pool used. + * @param join TRUE when thread pool shall be joined. FALSE for + * detaching any running threads. + */ +void Curl_thrdq_destroy(struct curl_thrdq *tqueue, bool join); + +/* Send "item" onto the queue. The caller needs to clear any reference + * to "item" on success, e.g. the queue takes ownership. + * `description` is an optional string describing the item for tracing + * purposes. It needs to have the same lifetime as `item`. + * Returns CURLE_AGAIN when the queue has already been full. + * + * With`timeout_ms` != 0, items that get stuck that long in the send + * queue are removed and added to the receive queue right away. + */ +CURLcode Curl_thrdq_send(struct curl_thrdq *tqueue, void *item, + const char *description, timediff_t timeout_ms); + +/* Receive the oldest, processed item from the queue again, if there is one. + * The caller takes ownership of the item received, e.g. the queue + * relinquishes all references to item. + * Returns CURLE_AGAIN when there is no processed item, setting `pitem` + * to NULL. + */ +CURLcode Curl_thrdq_recv(struct curl_thrdq *tqueue, void **pitem); + +/* Return TRUE if the passed "item" matches. */ +typedef bool Curl_thrdq_item_match_cb(void *item, void *match_data); + +/* Clear all scheduled/processed items that match from the queue. This + * will *not* be able to clear items that are being processed. + */ +void Curl_thrdq_clear(struct curl_thrdq *tqueue, + Curl_thrdq_item_match_cb *fn_match, + void *match_data); + +CURLcode Curl_thrdq_await_done(struct curl_thrdq *tqueue, + uint32_t timeout_ms); + +CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue, + uint32_t max_len, /* 0 for unlimited */ + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms); + +#ifdef CURLVERBOSE +void Curl_thrdq_trace(struct curl_thrdq *tqueue, + struct Curl_easy *data); +#endif + +#endif /* USE_THREADS */ + +#endif /* HEADER_CURL_THRDQUEUE_H */ diff --git a/lib/transfer.c b/lib/transfer.c index 1297a3e82d..a2fce9331b 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_NETINET_IN_H @@ -39,9 +38,7 @@ #ifdef HAVE_SYS_IOCTL_H #include #endif -#ifndef UNDER_CE #include -#endif #ifdef HAVE_SYS_PARAM_H #include @@ -58,38 +55,27 @@ #endif #include "urldata.h" -#include -#include "netrc.h" -#include "content_encoding.h" #include "hostip.h" #include "cfilters.h" #include "cw-out.h" +#include "dnscache.h" #include "transfer.h" #include "sendf.h" -#include "speedcheck.h" +#include "curl_trc.h" #include "progress.h" #include "http.h" #include "url.h" #include "getinfo.h" -#include "vtls/vtls.h" -#include "vquic/vquic.h" -#include "select.h" #include "multiif.h" #include "connect.h" -#include "http2.h" -#include "mime.h" #include "hsts.h" #include "setopt.h" #include "headers.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "bufref.h" #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_IMAP) + !defined(CURL_DISABLE_IMAP) /* * checkheaders() checks the linked list of custom headers for a * particular header (prefix). Provide the prefix without colon! @@ -102,11 +88,11 @@ char *Curl_checkheaders(const struct Curl_easy *data, { struct curl_slist *head; DEBUGASSERT(thislen); - DEBUGASSERT(thisheader[thislen-1] != ':'); + DEBUGASSERT(thisheader[thislen - 1] != ':'); for(head = data->set.headers; head; head = head->next) { if(curl_strnequal(head->data, thisheader, thislen) && - Curl_headersep(head->data[thislen]) ) + Curl_headersep(head->data[thislen])) return head->data; } @@ -118,13 +104,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); } @@ -141,16 +127,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; } @@ -187,56 +171,46 @@ CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done) * @param buf buffer to keep response data received * @param blen length of `buf` * @param eos_reliable if EOS detection in underlying connection is reliable - * @param err error code in case of -1 return * @return number of bytes read or -1 for error */ -static ssize_t xfer_recv_resp(struct Curl_easy *data, - char *buf, size_t blen, - bool eos_reliable, - CURLcode *err) +static CURLcode xfer_recv_resp(struct Curl_easy *data, + char *buf, size_t blen, + bool eos_reliable, + size_t *pnread) { - size_t nread; + CURLcode result; DEBUGASSERT(blen > 0); + *pnread = 0; /* If we are reading BODY data and the connection does NOT handle EOF * and we know the size of the BODY data, limit the read amount */ if(!eos_reliable && !data->req.header && data->req.size != -1) { - curl_off_t totalleft = data->req.size - data->req.bytecount; - if(totalleft <= 0) - blen = 0; - else if(totalleft < (curl_off_t)blen) - blen = (size_t)totalleft; + blen = curlx_sotouz_range(data->req.size - data->req.bytecount, 0, blen); } else if(xfer_recv_shutdown_started(data)) { /* we already received everything. Do not try more. */ blen = 0; } - if(!blen) { - /* want nothing more */ - *err = CURLE_OK; - nread = 0; - } - else { - *err = Curl_xfer_recv(data, buf, blen, &nread); + if(blen) { + result = Curl_xfer_recv(data, buf, blen, pnread); + if(result) + return result; } - if(*err) - return -1; - if(nread == 0) { + if(*pnread == 0) { if(data->req.shutdown) { bool done; - *err = xfer_recv_shutdown(data, &done); - if(*err) - return -1; + result = xfer_recv_shutdown(data, &done); + if(result) + return result; if(!done) { - *err = CURLE_AGAIN; - return -1; + return CURLE_AGAIN; } } DEBUGF(infof(data, "sendrecv_dl: we are done")); } - return (ssize_t)nread; + return CURLE_OK; } /* @@ -245,17 +219,16 @@ static ssize_t xfer_recv_resp(struct Curl_easy *data, * buffer) */ static CURLcode sendrecv_dl(struct Curl_easy *data, - struct SingleRequest *k, - int *didwhat) + struct SingleRequest *k) { struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; char *buf, *xfer_buf; size_t blen, xfer_blen; int maxloops = 10; - curl_off_t total_received = 0; bool is_multiplex = FALSE; bool rcvd_eagain = FALSE; + bool is_eos = FALSE, rate_limited = FALSE; result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen); if(result) @@ -264,9 +237,7 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, /* This is where we loop until we have read everything there is to read or we get a CURLE_AGAIN */ do { - bool is_eos = FALSE; size_t bytestoread; - ssize_t nread; if(!is_multiplex) { /* Multiplexed connection have inherent handling of EOF and we do not @@ -278,29 +249,36 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, buf = xfer_buf; bytestoread = xfer_blen; - if(bytestoread && data->set.max_recv_speed > 0) { - /* In case of speed limit on receiving: if this loop already got - * a quarter of the quota, break out. We want to stutter a bit - * to keep in the limit, but too small receives will just cost - * cpu unnecessarily. */ - if(total_received >= (data->set.max_recv_speed / 4)) + 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)); +#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; - if(data->set.max_recv_speed < (curl_off_t)bytestoread) - bytestoread = (size_t)data->set.max_recv_speed; + } + if(dl_avail < (curl_off_t)bytestoread) + bytestoread = (size_t)dl_avail; } rcvd_eagain = FALSE; - nread = xfer_recv_resp(data, buf, bytestoread, is_multiplex, &result); - if(nread < 0) { - if(CURLE_AGAIN != result) + result = xfer_recv_resp(data, buf, bytestoread, is_multiplex, &blen); + if(result) { + if(result != CURLE_AGAIN) goto out; /* real error */ rcvd_eagain = TRUE; result = CURLE_OK; if(data->req.download_done && data->req.no_body && !data->req.resp_trailer) { DEBUGF(infof(data, "EAGAIN, download done, no trailer announced, " - "not waiting for EOS")); - nread = 0; + "not waiting for EOS")); + blen = 0; /* continue as if we received the EOS */ } else @@ -308,24 +286,15 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, } /* We only get a 0-length receive at the end of the response */ - blen = (size_t)nread; is_eos = (blen == 0); - *didwhat |= KEEP_RECV; if(!blen) { - /* if we receive 0 or less here, either the data transfer is done or the - server closed the connection and we bail out from this! */ - if(is_multiplex) - DEBUGF(infof(data, "nread == 0, stream closed, bailing")); - else - DEBUGF(infof(data, "nread <= 0, server closed connection, bailing")); result = Curl_req_stop_send_recv(data); if(result) goto out; if(k->eos_written) /* already did write this to client, leave */ break; } - total_received += blen; result = Curl_xfer_write_resp(data, buf, blen, is_eos); if(result || data->req.done) @@ -335,23 +304,23 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, * we should read the EOS. Which may arrive as meta data after * the bytes. Not taking it in might lead to RST of streams. */ if((!is_multiplex && data->req.download_done) || is_eos) { - data->req.keepon &= ~KEEP_RECV; + CURL_REQ_CLEAR_RECV(data); } - /* if we are PAUSEd or stopped receiving, leave the loop */ - if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV)) + /* if we stopped receiving, leave the loop */ + if(!CURL_REQ_WANT_RECV(data)) break; } while(maxloops--); - if(!Curl_xfer_is_blocked(data) && + if(!is_eos && !rate_limited && CURL_REQ_WANT_RECV(data) && (!rcvd_eagain || data_pending(data, rcvd_eagain))) { - /* Did not read until EAGAIN or there is still data pending + /* Did not read until EAGAIN/EOS or there is still data pending * in buffers. Mark as read-again via simulated SELECT results. */ Curl_multi_mark_dirty(data); CURL_TRC_M(data, "sendrecv_dl() no EAGAIN/pending data, mark as dirty"); } - if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) && + if(!CURL_REQ_WANT_RECV(data) && CURL_REQ_WANT_SEND(data) && (conn->bits.close || is_multiplex)) { /* When we have read the entire thing and the close bit is set, the server may now close the connection. If there is now any kind of sending going @@ -370,17 +339,13 @@ out: /* * Send data to upload to the server, when the socket is writable. */ -static CURLcode sendrecv_ul(struct Curl_easy *data, int *didwhat) +static CURLcode sendrecv_ul(struct Curl_easy *data) { - /* We should not get here when the sending is already done. It - * probably means that someone set `data-req.keepon |= KEEP_SEND` - * when it should not. */ + /* We should not get here when the sending is already done. */ DEBUGASSERT(!Curl_req_done_sending(data)); - if(!Curl_req_done_sending(data)) { - *didwhat |= KEEP_SEND; + if(!Curl_req_done_sending(data)) return Curl_req_send_more(data); - } return CURLE_OK; } @@ -388,13 +353,11 @@ static CURLcode sendrecv_ul(struct Curl_easy *data, int *didwhat) * Curl_sendrecv() is the low-level function to be called when data is to * be read and written to/from the connection. */ -CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp) +CURLcode Curl_sendrecv(struct Curl_easy *data) { struct SingleRequest *k = &data->req; CURLcode result = CURLE_OK; - int didwhat = 0; - DEBUGASSERT(nowp); if(Curl_xfer_is_blocked(data)) { result = CURLE_OK; goto out; @@ -402,46 +365,38 @@ CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp) /* We go ahead and do a read if we have a readable socket or if the stream was rewound (in which case we have data in a buffer) */ - if(k->keepon & KEEP_RECV) { - result = sendrecv_dl(data, k, &didwhat); + if(CURL_REQ_WANT_RECV(data)) { + result = sendrecv_dl(data, k); if(result || data->req.done) goto out; } /* If we still have writing to do, we check if we have a writable socket. */ - if(Curl_req_want_send(data) || (data->req.keepon & KEEP_SEND_TIMED)) { - result = sendrecv_ul(data, &didwhat); + if(Curl_req_want_send(data)) { + result = sendrecv_ul(data); if(result) goto out; } - if(!didwhat) { - /* Transfer wanted to send/recv, but nothing was possible. */ - result = Curl_conn_ev_data_idle(data); - if(result) - goto out; - } - - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, *nowp); + result = Curl_pgrsCheck(data); if(result) goto out; - if(k->keepon) { - if(0 > Curl_timeleft(data, nowp, FALSE)) { + if(CURL_REQ_WANT_IO(data)) { + 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 %" FMT_OFF_T " bytes received", - curlx_timediff(*nowp, data->progress.t_startsingle), + curlx_ptimediff_ms(Curl_pgrs_now(data), + &data->progress.t_startsingle), k->bytecount, k->size); } else { failf(data, "Operation timed out after %" FMT_TIMEDIFF_T " milliseconds with %" FMT_OFF_T " bytes received", - curlx_timediff(*nowp, data->progress.t_startsingle), + curlx_ptimediff_ms(Curl_pgrs_now(data), + &data->progress.t_startsingle), k->bytecount); } result = CURLE_OPERATION_TIMEDOUT; @@ -450,7 +405,7 @@ CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp) } 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) && @@ -460,16 +415,14 @@ CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp) result = CURLE_PARTIAL_FILE; goto out; } - if(Curl_pgrsUpdate(data)) { - result = CURLE_ABORTED_BY_CALLBACK; - goto out; - } } /* If there is nothing more to send/recv, the request is done */ - if((k->keepon & (KEEP_RECVBITS|KEEP_SENDBITS)) == 0) + if(!CURL_REQ_WANT_IO(data)) data->req.done = TRUE; + result = Curl_pgrsUpdate(data); + out: if(result) DEBUGF(infof(data, "Curl_sendrecv() -> %d", result)); @@ -485,6 +438,40 @@ void Curl_init_CONNECT(struct Curl_easy *data) data->state.upload = (data->state.httpreq == HTTPREQ_PUT); } +/* + * Restore the user credentials to those set in options. + */ +CURLcode Curl_reset_userpwd(struct Curl_easy *data) +{ + CURLcode result; + if(data->set.str[STRING_USERNAME] || data->set.str[STRING_PASSWORD]) + data->state.creds_from = CREDS_OPTION; + result = Curl_setstropt(&data->state.aptr.user, + data->set.str[STRING_USERNAME]); + if(!result) + result = Curl_setstropt(&data->state.aptr.passwd, + data->set.str[STRING_PASSWORD]); + return result; +} + +/* + * Restore the proxy credentials to those set in options. + */ +CURLcode Curl_reset_proxypwd(struct Curl_easy *data) +{ +#ifndef CURL_DISABLE_PROXY + CURLcode result = Curl_setstropt(&data->state.aptr.proxyuser, + data->set.str[STRING_PROXYUSERNAME]); + if(!result) + result = Curl_setstropt(&data->state.aptr.proxypasswd, + data->set.str[STRING_PROXYPASSWORD]); + return result; +#else + (void)data; + return CURLE_OK; +#endif +} + /* * Curl_pretransfer() is called immediately before a transfer starts, and only * once for one transfer no matter if it has redirects or do multi-pass @@ -494,6 +481,14 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) { CURLcode result = CURLE_OK; + /* Reset the retry count at the start of each request. + * If the retry count is not reset, when the connection drops, + * it will not enter the retry mechanism on CONN_MAX_RETRIES + 1 attempts + * and will immediately throw + * "Connection died, tried CONN_MAX_RETRIES times before giving up". + * By resetting it here, we ensure each new request starts fresh. */ + data->state.retrycount = 0; + if(!data->set.str[STRING_SET_URL] && !data->set.uh) { /* we cannot do anything without URL */ failf(data, "No URL set"); @@ -504,22 +499,18 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) is allowed to be changed by the user between transfers */ if(data->set.uh) { CURLUcode uc; - free(data->set.str[STRING_SET_URL]); + curlx_free(data->set.str[STRING_SET_URL]); uc = curl_url_get(data->set.uh, CURLUPART_URL, &data->set.str[STRING_SET_URL], 0); if(uc) { + /* clear the pointer to not point to freed memory anymore */ + Curl_bufref_set(&data->state.url, NULL, 0, NULL); failf(data, "No URL set"); return CURLE_URL_MALFORMAT; } } - /* since the URL may have been redirected in a previous use of this handle */ - if(data->state.url_alloc) { - Curl_safefree(data->state.url); - data->state.url_alloc = FALSE; - } - - data->state.url = data->set.str[STRING_SET_URL]; + Curl_bufref_set(&data->state.url, data->set.str[STRING_SET_URL], 0, NULL); if(data->set.postfields && data->set.set_resume_from) { /* we cannot */ @@ -536,6 +527,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) data->state.requests = 0; data->state.followlocation = 0; /* reset the location-follow counter */ data->state.this_is_a_follow = FALSE; /* reset this */ + data->state.http_ignorecustom = FALSE; /* use custom HTTP method */ data->state.errorbuf = FALSE; /* no error has occurred */ #ifndef CURL_DISABLE_HTTP Curl_http_neg_init(data, &data->state.http_neg); @@ -543,7 +535,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) data->state.authproblem = FALSE; data->state.authhost.want = data->set.httpauth; data->state.authproxy.want = data->set.proxyauth; - Curl_safefree(data->info.wouldredirect); + curlx_safefree(data->info.wouldredirect); Curl_data_priority_clear_state(data); if(data->state.httpreq == HTTPREQ_PUT) @@ -558,14 +550,17 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) data->state.infilesize = 0; /* If there is a list of cookie files to read, do it now! */ - Curl_cookie_loadfiles(data); + result = Curl_cookie_loadfiles(data); + if(!result) + Curl_cookie_run(data); /* activate */ /* If there is a list of host pairs to deal with */ - if(data->state.resolve) + if(!result && data->state.resolve) result = Curl_loadhostpairs(data); - /* If there is a list of hsts files to read */ - Curl_hsts_loadfiles(data); + if(!result) + /* If there is a list of hsts files to read */ + result = Curl_hsts_loadfiles(data); if(!result) { /* Allow data->set.use_port to set which port to use. This needs to be @@ -573,7 +568,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 *************************************************************/ @@ -596,7 +591,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) if(data->state.wildcardmatch) { struct WildcardData *wc; if(!data->wildcard) { - data->wildcard = calloc(1, sizeof(struct WildcardData)); + data->wildcard = curlx_calloc(1, sizeof(struct WildcardData)); if(!data->wildcard) return CURLE_OUT_OF_MEMORY; } @@ -604,11 +599,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) if(wc->state < CURLWC_INIT) { if(wc->ftpwc) wc->dtor(wc->ftpwc); - Curl_safefree(wc->pattern); - Curl_safefree(wc->path); - result = Curl_wildcard_init(wc); /* init wildcard structures */ - if(result) - return CURLE_OUT_OF_MEMORY; + curlx_safefree(wc->pattern); + curlx_safefree(wc->path); + Curl_wildcard_init(wc); /* init wildcard structures */ } } #endif @@ -617,34 +610,20 @@ 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(data->set.str[STRING_USERAGENT]) { - free(data->state.aptr.uagent); + if(!result && data->set.str[STRING_USERAGENT]) { + curlx_free(data->state.aptr.uagent); data->state.aptr.uagent = - aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]); + curl_maprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]); if(!data->state.aptr.uagent) return CURLE_OUT_OF_MEMORY; } - if(data->set.str[STRING_USERNAME] || - data->set.str[STRING_PASSWORD]) - data->state.creds_from = CREDS_OPTION; if(!result) - result = Curl_setstropt(&data->state.aptr.user, - data->set.str[STRING_USERNAME]); + result = Curl_reset_userpwd(data); if(!result) - result = Curl_setstropt(&data->state.aptr.passwd, - data->set.str[STRING_PASSWORD]); -#ifndef CURL_DISABLE_PROXY - if(!result) - result = Curl_setstropt(&data->state.aptr.proxyuser, - data->set.str[STRING_PROXYUSERNAME]); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxypasswd, - data->set.str[STRING_PROXYPASSWORD]); -#endif + result = Curl_reset_proxypwd(data); data->req.headerbytecount = 0; Curl_headers_cleanup(data); @@ -653,7 +632,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) /* Returns CURLE_OK *and* sets '*url' if a request retry is wanted. - NOTE: that the *url is malloc()ed. */ + NOTE: that the *url is curlx_malloc()ed. */ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) { struct connectdata *conn = data->conn; @@ -664,12 +643,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((data->req.bytecount + data->req.headerbytecount == 0) && - conn->bits.reuse && - (!data->req.no_body || (conn->handler->protocol & PROTO_FAMILY_HTTP)) + if(conn->bits.reuse && + (data->req.bytecount + data->req.headerbytecount == 0) && + ((!data->req.no_body && !data->req.done) || + (conn->scheme->protocol & PROTO_FAMILY_HTTP)) #ifndef CURL_DISABLE_RTSP && (data->set.rtspreq != RTSPREQ_RECEIVE) #endif @@ -683,7 +663,7 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) it again. Bad luck. Retry the same request on a fresh connect! */ retry = TRUE; else if(data->state.refused_stream && - (data->req.bytecount + data->req.headerbytecount == 0) ) { + (data->req.bytecount + data->req.headerbytecount == 0)) { /* This was sent on a refused stream, safe to rerun. A refused stream error can typically only happen on HTTP/2 level if the stream is safe to issue again, but the nghttp2 API can deliver the message to other @@ -703,16 +683,15 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url) } infof(data, "Connection died, retrying a fresh connect (retry count: %d)", data->state.retrycount); - *url = strdup(data->state.url); + *url = Curl_bufref_dup(&data->state.url); if(!*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; @@ -741,27 +720,24 @@ 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; - + CURL_REQ_SET_RECV(data); if(conn->send_idx != -1) - k->keepon |= KEEP_SEND; + CURL_REQ_SET_SEND(data); } - CURL_TRC_M(data, "xfer_setup: recv_idx=%d, send_idx=%d", conn->recv_idx, conn->send_idx); } @@ -808,10 +784,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 @@ -842,10 +818,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); @@ -898,8 +875,8 @@ CURLcode Curl_xfer_recv(struct Curl_easy *data, DEBUGASSERT(data->conn); DEBUGASSERT(data->set.buffer_size > 0); - if((size_t)data->set.buffer_size < blen) - blen = (size_t)data->set.buffer_size; + if(curlx_uitouz(data->set.buffer_size) < blen) + blen = curlx_uitouz(data->set.buffer_size); return Curl_conn_recv(data, data->conn->recv_idx, buf, blen, pnrcvd); } @@ -911,8 +888,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 = CURL_REQ_WANT_SEND(data); + bool want_recv = CURL_REQ_WANT_RECV(data); if(!want_send) return want_recv && Curl_xfer_recv_is_paused(data); else if(!want_recv) @@ -923,51 +900,53 @@ bool Curl_xfer_is_blocked(struct Curl_easy *data) bool Curl_xfer_send_is_paused(struct Curl_easy *data) { - return (data->req.keepon & KEEP_SEND_PAUSE); + return Curl_rlimit_is_blocked(&data->progress.ul.rlimit); } bool Curl_xfer_recv_is_paused(struct Curl_easy *data) { - return (data->req.keepon & KEEP_RECV_PAUSE); + return Curl_rlimit_is_blocked(&data->progress.dl.rlimit); } CURLcode Curl_xfer_pause_send(struct Curl_easy *data, bool enable) { CURLcode result = CURLE_OK; - if(enable) { - data->req.keepon |= KEEP_SEND_PAUSE; - } - else { - data->req.keepon &= ~KEEP_SEND_PAUSE; - if(Curl_creader_is_paused(data)) - result = Curl_creader_unpause(data); - } + Curl_rlimit_block(&data->progress.ul.rlimit, enable, Curl_pgrs_now(data)); + if(!enable && Curl_creader_is_paused(data)) + result = Curl_creader_unpause(data); + Curl_pgrsSendPause(data, enable); return result; } CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable) { CURLcode result = CURLE_OK; - if(enable) { - data->req.keepon |= KEEP_RECV_PAUSE; - } - else { - data->req.keepon &= ~KEEP_RECV_PAUSE; - if(Curl_cwriter_is_paused(data)) - result = Curl_cwriter_unpause(data); - } + Curl_rlimit_block(&data->progress.dl.rlimit, enable, Curl_pgrs_now(data)); + if(!enable && Curl_cwriter_is_paused(data)) + result = Curl_cwriter_unpause(data); Curl_conn_ev_data_pause(data, enable); + Curl_pgrsRecvPause(data, enable); return result; } -bool Curl_xfer_is_too_fast(struct Curl_easy *data) +bool Curl_xfer_is_secure(struct Curl_easy *data) { - struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist); - while(e) { - struct time_node *n = Curl_node_elem(e); - e = Curl_node_next(e); - if(n->eid == EXPIRE_TOOFAST) - return TRUE; + const struct Curl_scheme *scheme = NULL; + + if(data->conn) { + scheme = data->conn->scheme; + /* if we are connected, but not use SSL, the transfer is not secure. + * This covers an insecure http:// proxy that is not tunneling. + * We enforce tunneling for such cases, but better be sure here. */ + if(Curl_conn_is_connected(data->conn, FIRSTSOCKET) && + !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) + return FALSE; } - return FALSE; + else if(data->info.conn_scheme) { /* was connected once */ + scheme = Curl_get_scheme(data->info.conn_scheme); + } + else { /* never connected (yet?) */ + DEBUGASSERT(0); /* not implemented, would need to parse URL */ + } + return scheme ? (scheme->flags & PROTOPT_SSL) : FALSE; } diff --git a/lib/transfer.h b/lib/transfer.h index 6145efb4a5..b29e70b9ec 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -23,24 +23,26 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#define Curl_headersep(x) ((((x) == ':') || ((x) == ';'))) -#define Curl_headersep(x) ((((x)==':') || ((x)==';'))) char *Curl_checkheaders(const struct Curl_easy *data, const char *thisheader, const size_t thislen); void Curl_init_CONNECT(struct Curl_easy *data); +CURLcode Curl_reset_userpwd(struct Curl_easy *data); +CURLcode Curl_reset_proxypwd(struct Curl_easy *data); CURLcode Curl_pretransfer(struct Curl_easy *data); -CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp); +CURLcode Curl_sendrecv(struct Curl_easy *data); CURLcode Curl_retry_request(struct Curl_easy *data, char **url); bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc); /** * Write the transfer raw response bytes, as received from the connection. - * Will handle all passed bytes or return an error. By default, this will - * write the bytes as BODY to the client. Protocols may provide a + * Handle all passed bytes or return an error. By default, this writes + * the bytes as BODY to the client. Protocols may provide a * "write_resp" callback in their handler to add specific treatment. E.g. * HTTP parses response headers and passes them differently to the client. * @param data the transfer @@ -112,7 +114,7 @@ CURLcode Curl_xfer_flush(struct Curl_easy *data); /** * Send data on the socket/connection filter designated * for transfer's outgoing data. - * Will return CURLE_OK on blocking with (*pnwritten == 0). + * Return CURLE_OK on blocking with (*pnwritten == 0). */ CURLcode Curl_xfer_send(struct Curl_easy *data, const void *buf, size_t blen, bool eos, @@ -121,7 +123,7 @@ CURLcode Curl_xfer_send(struct Curl_easy *data, /** * Receive data on the socket/connection filter designated * for transfer's incoming data. - * Will return CURLE_AGAIN on blocking with (*pnrcvd == 0). + * Return CURLE_AGAIN on blocking with (*pnrcvd == 0). */ CURLcode Curl_xfer_recv(struct Curl_easy *data, char *buf, size_t blen, @@ -143,7 +145,8 @@ bool Curl_xfer_recv_is_paused(struct Curl_easy *data); CURLcode Curl_xfer_pause_send(struct Curl_easy *data, bool enable); CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable); -/* Query if transfer has expire timeout TOOFAST set. */ -bool Curl_xfer_is_too_fast(struct Curl_easy *data); +/* TRUE if the transfer is secure (e.g. TLS) from libcurl to the + * URL's host. */ +bool Curl_xfer_is_secure(struct Curl_easy *data); #endif /* HEADER_CURL_TRANSFER_H */ diff --git a/lib/uint-bset.c b/lib/uint-bset.c index 1c60f22adf..268bbedbd1 100644 --- a/lib/uint-bset.c +++ b/lib/uint-bset.c @@ -21,43 +21,37 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #include "uint-bset.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - #ifdef DEBUGBUILD -#define CURL_UINT_BSET_MAGIC 0x62757473 +#define CURL_UINT32_BSET_MAGIC 0x62757473 #endif -void Curl_uint_bset_init(struct uint_bset *bset) +void Curl_uint32_bset_init(struct uint32_bset *bset) { memset(bset, 0, sizeof(*bset)); #ifdef DEBUGBUILD - bset->init = CURL_UINT_BSET_MAGIC; + bset->init = CURL_UINT32_BSET_MAGIC; #endif } - -CURLcode Curl_uint_bset_resize(struct uint_bset *bset, unsigned int nmax) +CURLcode Curl_uint32_bset_resize(struct uint32_bset *bset, uint32_t nmax) { - unsigned int nslots = (nmax < (UINT_MAX-63)) ? - ((nmax + 63) / 64) : (UINT_MAX / 64); + uint32_t nslots = (nmax < (UINT32_MAX - 63)) ? + ((nmax + 63) / 64) : (UINT32_MAX / 64); - DEBUGASSERT(bset->init == CURL_UINT_BSET_MAGIC); + DEBUGASSERT(bset->init == CURL_UINT32_BSET_MAGIC); if(nslots != bset->nslots) { - curl_uint64_t *slots = calloc(nslots, sizeof(curl_uint64_t)); + uint64_t *slots = curlx_calloc(nslots, sizeof(uint64_t)); if(!slots) return CURLE_OUT_OF_MEMORY; if(bset->slots) { memcpy(slots, bset->slots, - (CURLMIN(nslots, bset->nslots) * sizeof(curl_uint64_t))); - free(bset->slots); + (CURLMIN(nslots, bset->nslots) * sizeof(uint64_t))); + curlx_free(bset->slots); } bset->slots = slots; bset->nslots = nslots; @@ -66,25 +60,25 @@ CURLcode Curl_uint_bset_resize(struct uint_bset *bset, unsigned int nmax) return CURLE_OK; } - -void Curl_uint_bset_destroy(struct uint_bset *bset) +void Curl_uint32_bset_destroy(struct uint32_bset *bset) { - DEBUGASSERT(bset->init == CURL_UINT_BSET_MAGIC); - free(bset->slots); + DEBUGASSERT(bset->init == CURL_UINT32_BSET_MAGIC); + curlx_free(bset->slots); memset(bset, 0, sizeof(*bset)); } #ifdef UNITTESTS -UNITTEST unsigned int Curl_uint_bset_capacity(struct uint_bset *bset) +UNITTEST uint32_t Curl_uint32_bset_capacity(struct uint32_bset *bset); +UNITTEST uint32_t Curl_uint32_bset_capacity(struct uint32_bset *bset) { return bset->nslots * 64; } #endif -unsigned int Curl_uint_bset_count(struct uint_bset *bset) +uint32_t Curl_uint32_bset_count(struct uint32_bset *bset) { - unsigned int i; - unsigned int n = 0; + uint32_t i; + uint32_t n = 0; for(i = 0; i < bset->nslots; ++i) { if(bset->slots[i]) n += CURL_POPCOUNT64(bset->slots[i]); @@ -92,9 +86,9 @@ unsigned int Curl_uint_bset_count(struct uint_bset *bset) return n; } -bool Curl_uint_bset_empty(struct uint_bset *bset) +bool Curl_uint32_bset_empty(struct uint32_bset *bset) { - unsigned int i; + uint32_t i; for(i = bset->first_slot_used; i < bset->nslots; ++i) { if(bset->slots[i]) return FALSE; @@ -102,48 +96,43 @@ bool Curl_uint_bset_empty(struct uint_bset *bset) return TRUE; } - -void Curl_uint_bset_clear(struct uint_bset *bset) +void Curl_uint32_bset_clear(struct uint32_bset *bset) { if(bset->nslots) { - memset(bset->slots, 0, bset->nslots * sizeof(curl_uint64_t)); - bset->first_slot_used = UINT_MAX; + memset(bset->slots, 0, bset->nslots * sizeof(uint64_t)); + bset->first_slot_used = UINT32_MAX; } } - -bool Curl_uint_bset_add(struct uint_bset *bset, unsigned int i) +bool Curl_uint32_bset_add(struct uint32_bset *bset, uint32_t i) { - unsigned int islot = i / 64; + uint32_t islot = i / 64; if(islot >= bset->nslots) return FALSE; - bset->slots[islot] |= ((curl_uint64_t)1 << (i % 64)); + bset->slots[islot] |= ((uint64_t)1 << (i % 64)); if(islot < bset->first_slot_used) bset->first_slot_used = islot; return TRUE; } - -void Curl_uint_bset_remove(struct uint_bset *bset, unsigned int i) +void Curl_uint32_bset_remove(struct uint32_bset *bset, uint32_t i) { size_t islot = i / 64; if(islot < bset->nslots) - bset->slots[islot] &= ~((curl_uint64_t)1 << (i % 64)); + bset->slots[islot] &= ~((uint64_t)1 << (i % 64)); } - -bool Curl_uint_bset_contains(struct uint_bset *bset, unsigned int i) +bool Curl_uint32_bset_contains(struct uint32_bset *bset, uint32_t i) { - unsigned int islot = i / 64; + uint32_t islot = i / 64; if(islot >= bset->nslots) return FALSE; - return (bset->slots[islot] & ((curl_uint64_t)1 << (i % 64))) != 0; + return (bset->slots[islot] & ((uint64_t)1 << (i % 64))) != 0; } - -bool Curl_uint_bset_first(struct uint_bset *bset, unsigned int *pfirst) +bool Curl_uint32_bset_first(struct uint32_bset *bset, uint32_t *pfirst) { - unsigned int i; + uint32_t i; for(i = bset->first_slot_used; i < bset->nslots; ++i) { if(bset->slots[i]) { *pfirst = (i * 64) + CURL_CTZ64(bset->slots[i]); @@ -151,15 +140,15 @@ bool Curl_uint_bset_first(struct uint_bset *bset, unsigned int *pfirst) return TRUE; } } - bset->first_slot_used = *pfirst = UINT_MAX; + bset->first_slot_used = *pfirst = UINT32_MAX; return FALSE; } -bool Curl_uint_bset_next(struct uint_bset *bset, unsigned int last, - unsigned int *pnext) +bool Curl_uint32_bset_next(struct uint32_bset *bset, uint32_t last, + uint32_t *pnext) { - unsigned int islot; - curl_uint64_t x; + uint32_t islot; + uint64_t x; ++last; /* look for number one higher than last */ islot = last / 64; /* the slot this would be in */ @@ -179,42 +168,41 @@ bool Curl_uint_bset_next(struct uint_bset *bset, unsigned int last, } } } - *pnext = UINT_MAX; /* a value we cannot store */ + *pnext = UINT32_MAX; /* a value we cannot store */ return FALSE; } #ifdef CURL_POPCOUNT64_IMPLEMENT -unsigned int Curl_popcount64(curl_uint64_t x) +uint32_t Curl_popcount64(uint64_t x) { /* Compute the "Hamming Distance" between 'x' and 0, * which is the number of set bits in 'x'. * See: https://en.wikipedia.org/wiki/Hamming_weight */ - const curl_uint64_t m1 = 0x5555555555555555LL; /* 0101+ */ - const curl_uint64_t m2 = 0x3333333333333333LL; /* 00110011+ */ - const curl_uint64_t m4 = 0x0f0f0f0f0f0f0f0fLL; /* 00001111+ */ + const uint64_t m1 = 0x5555555555555555LL; /* 0101+ */ + const uint64_t m2 = 0x3333333333333333LL; /* 00110011+ */ + const uint64_t m4 = 0x0f0f0f0f0f0f0f0fLL; /* 00001111+ */ /* 1 + 256^1 + 256^2 + 256^3 + ... + 256^7 */ - const curl_uint64_t h01 = 0x0101010101010101LL; + const uint64_t h01 = 0x0101010101010101LL; x -= (x >> 1) & m1; /* replace every 2 bits with bits present */ x = (x & m2) + ((x >> 2) & m2); /* replace every nibble with bits present */ x = (x + (x >> 4)) & m4; /* replace every byte with bits present */ - /* top 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ... which makes the + /* top 8 bits of x + (x << 8) + (x << 16) + (x << 24) + ... which makes the * top byte the sum of all individual 8 bytes, throw away the rest */ - return (unsigned int)((x * h01) >> 56); + return (uint32_t)((x * h01) >> 56); } #endif /* CURL_POPCOUNT64_IMPLEMENT */ - #ifdef CURL_CTZ64_IMPLEMENT -unsigned int Curl_ctz64(curl_uint64_t x) +uint32_t Curl_ctz64(uint64_t x) { - /* count trailing zeros in a curl_uint64_t. + /* count trailing zeros in a uint64_t. * divide and conquer to find the number of lower 0 bits */ - const curl_uint64_t ml32 = 0xFFFFFFFF; /* lower 32 bits */ - const curl_uint64_t ml16 = 0x0000FFFF; /* lower 16 bits */ - const curl_uint64_t ml8 = 0x000000FF; /* lower 8 bits */ - const curl_uint64_t ml4 = 0x0000000F; /* lower 4 bits */ - const curl_uint64_t ml2 = 0x00000003; /* lower 2 bits */ - unsigned int n; + const uint64_t ml32 = 0xFFFFFFFF; /* lower 32 bits */ + const uint64_t ml16 = 0x0000FFFF; /* lower 16 bits */ + const uint64_t ml8 = 0x000000FF; /* lower 8 bits */ + const uint64_t ml4 = 0x0000000F; /* lower 4 bits */ + const uint64_t ml2 = 0x00000003; /* lower 2 bits */ + uint32_t n; if(!x) return 64; @@ -239,6 +227,6 @@ unsigned int Curl_ctz64(curl_uint64_t x) n = n + 2; x = x >> 2; } - return n - (unsigned int)(x & 1); + return n - (uint32_t)(x & 1); } #endif /* CURL_CTZ64_IMPLEMENT */ diff --git a/lib/uint-bset.h b/lib/uint-bset.h index cc405cc656..ce0450c53c 100644 --- a/lib/uint-bset.h +++ b/lib/uint-bset.h @@ -25,8 +25,6 @@ ***************************************************************************/ #include "curl_setup.h" -#include - /* A bitset for unsigned int values. * It can hold the numbers from 0 - (nmax - 1), * rounded to the next 64 multiple. @@ -39,51 +37,48 @@ * the price of slightly slower operations. */ -struct uint_bset { - curl_uint64_t *slots; - unsigned int nslots; - unsigned int first_slot_used; +struct uint32_bset { + uint64_t *slots; + uint32_t nslots; + uint32_t first_slot_used; #ifdef DEBUGBUILD int init; #endif }; /* Initialize the bitset with capacity 0. */ -void Curl_uint_bset_init(struct uint_bset *bset); +void Curl_uint32_bset_init(struct uint32_bset *bset); /* Resize the bitset capacity to hold numbers from 0 to `nmax`, * which rounds up `nmax` to the next multiple of 64. */ -CURLcode Curl_uint_bset_resize(struct uint_bset *bset, unsigned int nmax); +CURLcode Curl_uint32_bset_resize(struct uint32_bset *bset, uint32_t nmax); /* Destroy the bitset, freeing all resources. */ -void Curl_uint_bset_destroy(struct uint_bset *bset); - -/* Get the bitset capacity, e.g. can hold numbers from 0 to capacity - 1. */ -unsigned int Curl_uint_bset_capacity(struct uint_bset *bset); +void Curl_uint32_bset_destroy(struct uint32_bset *bset); /* Get the cardinality of the bitset, e.g. numbers present in the set. */ -unsigned int Curl_uint_bset_count(struct uint_bset *bset); +uint32_t Curl_uint32_bset_count(struct uint32_bset *bset); /* TRUE of bitset is empty */ -bool Curl_uint_bset_empty(struct uint_bset *bset); +bool Curl_uint32_bset_empty(struct uint32_bset *bset); /* Clear the bitset, making it empty. */ -void Curl_uint_bset_clear(struct uint_bset *bset); +void Curl_uint32_bset_clear(struct uint32_bset *bset); /* Add the number `i` to the bitset. Return FALSE if the number is * outside the set's capacity. * Numbers can be added more than once, without making a difference. */ -bool Curl_uint_bset_add(struct uint_bset *bset, unsigned int i); +bool Curl_uint32_bset_add(struct uint32_bset *bset, uint32_t i); /* Remove the number `i` from the bitset. */ -void Curl_uint_bset_remove(struct uint_bset *bset, unsigned int i); +void Curl_uint32_bset_remove(struct uint32_bset *bset, uint32_t i); /* Return TRUE if the bitset contains number `i`. */ -bool Curl_uint_bset_contains(struct uint_bset *bset, unsigned int i); +bool Curl_uint32_bset_contains(struct uint32_bset *bset, uint32_t i); /* Get the first number in the bitset, e.g. the smallest. * Returns FALSE when the bitset is empty. */ -bool Curl_uint_bset_first(struct uint_bset *bset, unsigned int *pfirst); +bool Curl_uint32_bset_first(struct uint32_bset *bset, uint32_t *pfirst); /* Get the next number in the bitset, following `last` in natural order. * Put another way, this is the smallest number greater than `last` in @@ -96,20 +91,19 @@ bool Curl_uint_bset_first(struct uint_bset *bset, unsigned int *pfirst); * - added numbers lower than 'last' will not show up. * - removed numbers lower or equal to 'last' will not show up. * - removed numbers higher than 'last' will not be visited. */ -bool Curl_uint_bset_next(struct uint_bset *bset, unsigned int last, - unsigned int *pnext); - +bool Curl_uint32_bset_next(struct uint32_bset *bset, uint32_t last, + uint32_t *pnext); #ifndef CURL_POPCOUNT64 -#define CURL_POPCOUNT64(x) Curl_popcount64(x) +#define CURL_POPCOUNT64(x) Curl_popcount64(x) #define CURL_POPCOUNT64_IMPLEMENT -unsigned int Curl_popcount64(curl_uint64_t x); +uint32_t Curl_popcount64(uint64_t x); #endif /* !CURL_POPCOUNT64 */ #ifndef CURL_CTZ64 #define CURL_CTZ64(x) Curl_ctz64(x) #define CURL_CTZ64_IMPLEMENT -unsigned int Curl_ctz64(curl_uint64_t x); +uint32_t Curl_ctz64(uint64_t x); #endif /* !CURL_CTZ64 */ #endif /* HEADER_CURL_UINT_BSET_H */ diff --git a/lib/uint-hash.c b/lib/uint-hash.c index 6c66507887..b7535b6242 100644 --- a/lib/uint-hash.c +++ b/lib/uint-hash.c @@ -21,37 +21,29 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - #include "uint-hash.h" -#include "curl_memory.h" - -/* The last #include file should be: */ -#include "memdebug.h" /* random patterns for API verification */ #ifdef DEBUGBUILD -#define CURL_UINTHASHINIT 0x7117e779 +#define CURL_UINT32_HASHINIT 0x7117e779 #endif -static unsigned int uint_hash_hash(unsigned int id, unsigned int slots) +static uint32_t uint32_hash_hash(uint32_t id, uint32_t slots) { return (id % slots); } - struct uint_hash_entry { struct uint_hash_entry *next; - void *value; - unsigned int id; + void *value; + uint32_t id; }; -void Curl_uint_hash_init(struct uint_hash *h, - unsigned int slots, - Curl_uint_hash_dtor *dtor) +void Curl_uint32_hash_init(struct uint_hash *h, + uint32_t slots, + Curl_uint32_hash_dtor *dtor) { DEBUGASSERT(h); DEBUGASSERT(slots); @@ -61,16 +53,16 @@ void Curl_uint_hash_init(struct uint_hash *h, h->size = 0; h->slots = slots; #ifdef DEBUGBUILD - h->init = CURL_UINTHASHINIT; + h->init = CURL_UINT32_HASHINIT; #endif } -static struct uint_hash_entry *uint_hash_mk_entry(unsigned int id, void *value) +static struct uint_hash_entry *uint32_hash_mk_entry(uint32_t id, void *value) { struct uint_hash_entry *e; /* allocate the struct for the hash entry */ - e = malloc(sizeof(*e)); + e = curlx_malloc(sizeof(*e)); if(e) { e->id = id; e->next = NULL; @@ -79,8 +71,8 @@ static struct uint_hash_entry *uint_hash_mk_entry(unsigned int id, void *value) return e; } -static void uint_hash_entry_clear(struct uint_hash *h, - struct uint_hash_entry *e) +static void uint32_hash_entry_clear(struct uint_hash *h, + struct uint_hash_entry *e) { DEBUGASSERT(h); DEBUGASSERT(e); @@ -91,78 +83,78 @@ static void uint_hash_entry_clear(struct uint_hash *h, } } -static void uint_hash_entry_destroy(struct uint_hash *h, - struct uint_hash_entry *e) +static void uint32_hash_entry_destroy(struct uint_hash *h, + struct uint_hash_entry *e) { - uint_hash_entry_clear(h, e); - free(e); + uint32_hash_entry_clear(h, e); + curlx_free(e); } -static void uint_hash_entry_unlink(struct uint_hash *h, - struct uint_hash_entry **he_anchor, - struct uint_hash_entry *he) +static void uint32_hash_entry_unlink(struct uint_hash *h, + struct uint_hash_entry **he_anchor, + struct uint_hash_entry *he) { *he_anchor = he->next; --h->size; } -static void uint_hash_elem_link(struct uint_hash *h, - struct uint_hash_entry **he_anchor, - struct uint_hash_entry *he) +static void uint32_hash_elem_link(struct uint_hash *h, + struct uint_hash_entry **he_anchor, + struct uint_hash_entry *he) { he->next = *he_anchor; *he_anchor = he; ++h->size; } -#define CURL_UINT_HASH_SLOT(h,id) h->table[uint_hash_hash(id, h->slots)] -#define CURL_UINT_HASH_SLOT_ADDR(h,id) &CURL_UINT_HASH_SLOT(h,id) +#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_uint_hash_set(struct uint_hash *h, unsigned int id, void *value) +bool Curl_uint32_hash_set(struct uint_hash *h, uint32_t id, void *value) { struct uint_hash_entry *he, **slot; DEBUGASSERT(h); DEBUGASSERT(h->slots); - DEBUGASSERT(h->init == CURL_UINTHASHINIT); + DEBUGASSERT(h->init == CURL_UINT32_HASHINIT); if(!h->table) { - h->table = calloc(h->slots, sizeof(*he)); + h->table = curlx_calloc(h->slots, sizeof(*he)); if(!h->table) return FALSE; /* OOM */ } - slot = CURL_UINT_HASH_SLOT_ADDR(h, id); + slot = CURL_UINT32_HASH_SLOT_ADDR(h, id); for(he = *slot; he; he = he->next) { if(he->id == id) { /* existing key entry, overwrite by clearing old pointer */ - uint_hash_entry_clear(h, he); + uint32_hash_entry_clear(h, he); he->value = value; return TRUE; } } - he = uint_hash_mk_entry(id, value); + he = uint32_hash_mk_entry(id, value); if(!he) return FALSE; /* OOM */ - uint_hash_elem_link(h, slot, he); + uint32_hash_elem_link(h, slot, he); return TRUE; } -bool Curl_uint_hash_remove(struct uint_hash *h, unsigned int id) +bool Curl_uint32_hash_remove(struct uint_hash *h, uint32_t id) { DEBUGASSERT(h); DEBUGASSERT(h->slots); - DEBUGASSERT(h->init == CURL_UINTHASHINIT); + DEBUGASSERT(h->init == CURL_UINT32_HASHINIT); if(h->table) { struct uint_hash_entry *he, **he_anchor; - he_anchor = CURL_UINT_HASH_SLOT_ADDR(h, id); + he_anchor = CURL_UINT32_HASH_SLOT_ADDR(h, id); while(*he_anchor) { he = *he_anchor; if(id == he->id) { - uint_hash_entry_unlink(h, he_anchor, he); - uint_hash_entry_destroy(h, he); + uint32_hash_entry_unlink(h, he_anchor, he); + uint32_hash_entry_destroy(h, he); return TRUE; } he_anchor = &he->next; @@ -171,14 +163,14 @@ bool Curl_uint_hash_remove(struct uint_hash *h, unsigned int id) return FALSE; } -void *Curl_uint_hash_get(struct uint_hash *h, unsigned int id) +void *Curl_uint32_hash_get(struct uint_hash *h, uint32_t id) { DEBUGASSERT(h); - DEBUGASSERT(h->init == CURL_UINTHASHINIT); + DEBUGASSERT(h->init == CURL_UINT32_HASHINIT); if(h->table) { struct uint_hash_entry *he; DEBUGASSERT(h->slots); - he = CURL_UINT_HASH_SLOT(h, id); + he = CURL_UINT32_HASH_SLOT(h, id); while(he) { if(id == he->id) { return he->value; @@ -189,55 +181,49 @@ void *Curl_uint_hash_get(struct uint_hash *h, unsigned int id) return NULL; } -static void uint_hash_clear(struct uint_hash *h) +UNITTEST void uint_hash_clear(struct uint_hash *h); +UNITTEST void uint_hash_clear(struct uint_hash *h) { if(h && h->table) { struct uint_hash_entry *he, **he_anchor; size_t i; - DEBUGASSERT(h->init == CURL_UINTHASHINIT); + DEBUGASSERT(h->init == CURL_UINT32_HASHINIT); for(i = 0; i < h->slots; ++i) { he_anchor = &h->table[i]; while(*he_anchor) { he = *he_anchor; - uint_hash_entry_unlink(h, he_anchor, he); - uint_hash_entry_destroy(h, he); + uint32_hash_entry_unlink(h, he_anchor, he); + uint32_hash_entry_destroy(h, he); } } } } -#ifdef UNITTESTS -UNITTEST void Curl_uint_hash_clear(struct uint_hash *h) +void Curl_uint32_hash_destroy(struct uint_hash *h) { - uint_hash_clear(h); -} -#endif - -void Curl_uint_hash_destroy(struct uint_hash *h) -{ - DEBUGASSERT(h->init == CURL_UINTHASHINIT); + DEBUGASSERT(h->init == CURL_UINT32_HASHINIT); if(h->table) { uint_hash_clear(h); - Curl_safefree(h->table); + curlx_safefree(h->table); } DEBUGASSERT(h->size == 0); h->slots = 0; } -unsigned int Curl_uint_hash_count(struct uint_hash *h) +uint32_t Curl_uint32_hash_count(struct uint_hash *h) { - DEBUGASSERT(h->init == CURL_UINTHASHINIT); + DEBUGASSERT(h->init == CURL_UINT32_HASHINIT); return h->size; } -void Curl_uint_hash_visit(struct uint_hash *h, - Curl_uint_hash_visit_cb *cb, - void *user_data) +void Curl_uint32_hash_visit(struct uint_hash *h, + Curl_uint32_hash_visit_cb *cb, + void *user_data) { if(h && h->table && cb) { struct uint_hash_entry *he; size_t i; - DEBUGASSERT(h->init == CURL_UINTHASHINIT); + DEBUGASSERT(h->init == CURL_UINT32_HASHINIT); for(i = 0; i < h->slots; ++i) { for(he = h->table[i]; he; he = he->next) { if(!cb(he->id, he->value, user_data)) diff --git a/lib/uint-hash.h b/lib/uint-hash.h index 1b52dba4c4..3cbcf8f8f1 100644 --- a/lib/uint-hash.h +++ b/lib/uint-hash.h @@ -23,46 +23,37 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" -#include - -#include "llist.h" - -/* A version with unsigned int as key */ -typedef void Curl_uint_hash_dtor(unsigned int id, void *value); +/* A version with uint32_t as key */ +typedef void Curl_uint32_hash_dtor(uint32_t id, void *value); struct uint_hash_entry; -/* Hash for `unsigned int` as key */ +/* Hash for `uint32_t` as key */ struct uint_hash { struct uint_hash_entry **table; - Curl_uint_hash_dtor *dtor; - unsigned int slots; - unsigned int size; + Curl_uint32_hash_dtor *dtor; + uint32_t slots; + uint32_t size; #ifdef DEBUGBUILD int init; #endif }; +void Curl_uint32_hash_init(struct uint_hash *h, + uint32_t slots, + Curl_uint32_hash_dtor *dtor); +void Curl_uint32_hash_destroy(struct uint_hash *h); +bool Curl_uint32_hash_set(struct uint_hash *h, uint32_t id, void *value); +bool Curl_uint32_hash_remove(struct uint_hash *h, uint32_t id); +void *Curl_uint32_hash_get(struct uint_hash *h, uint32_t id); +uint32_t Curl_uint32_hash_count(struct uint_hash *h); -void Curl_uint_hash_init(struct uint_hash *h, - unsigned int slots, - Curl_uint_hash_dtor *dtor); -void Curl_uint_hash_destroy(struct uint_hash *h); -void Curl_uint_hash_clear(struct uint_hash *h); +typedef bool Curl_uint32_hash_visit_cb(uint32_t id, void *value, + void *user_data); -bool Curl_uint_hash_set(struct uint_hash *h, unsigned int id, void *value); -bool Curl_uint_hash_remove(struct uint_hash *h, unsigned int id); -void *Curl_uint_hash_get(struct uint_hash *h, unsigned int id); -unsigned int Curl_uint_hash_count(struct uint_hash *h); - - -typedef bool Curl_uint_hash_visit_cb(unsigned int id, void *value, - void *user_data); - -void Curl_uint_hash_visit(struct uint_hash *h, - Curl_uint_hash_visit_cb *cb, - void *user_data); +void Curl_uint32_hash_visit(struct uint_hash *h, + Curl_uint32_hash_visit_cb *cb, + void *user_data); #endif /* HEADER_CURL_UINT_HASH_H */ diff --git a/lib/uint-spbset.c b/lib/uint-spbset.c index d12cdcb4aa..d05b530258 100644 --- a/lib/uint-spbset.c +++ b/lib/uint-spbset.c @@ -21,44 +21,49 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #include "uint-bset.h" #include "uint-spbset.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - #ifdef DEBUGBUILD -#define CURL_UINT_SPBSET_MAGIC 0x70737362 +#define CURL_UINT32_SPBSET_MAGIC 0x70737362 #endif -/* Clear the bitset, making it empty. */ -UNITTEST void Curl_uint_spbset_clear(struct uint_spbset *bset); - -void Curl_uint_spbset_init(struct uint_spbset *bset) +void Curl_uint32_spbset_init(struct uint32_spbset *bset) { memset(bset, 0, sizeof(*bset)); #ifdef DEBUGBUILD - bset->init = CURL_UINT_SPBSET_MAGIC; + bset->init = CURL_UINT32_SPBSET_MAGIC; #endif } -void Curl_uint_spbset_destroy(struct uint_spbset *bset) +/* Clear the bitset, making it empty. */ +UNITTEST void Curl_uint32_spbset_clear(struct uint32_spbset *bset); +UNITTEST void Curl_uint32_spbset_clear(struct uint32_spbset *bset) { - DEBUGASSERT(bset->init == CURL_UINT_SPBSET_MAGIC); - Curl_uint_spbset_clear(bset); + struct uint32_spbset_chunk *next, *chunk; + + for(chunk = bset->head.next; chunk; chunk = next) { + next = chunk->next; + curlx_free(chunk); + } + memset(&bset->head, 0, sizeof(bset->head)); } -unsigned int Curl_uint_spbset_count(struct uint_spbset *bset) +void Curl_uint32_spbset_destroy(struct uint32_spbset *bset) { - struct uint_spbset_chunk *chunk; - unsigned int i, n = 0; + DEBUGASSERT(bset->init == CURL_UINT32_SPBSET_MAGIC); + Curl_uint32_spbset_clear(bset); +} + +uint32_t Curl_uint32_spbset_count(struct uint32_spbset *bset) +{ + struct uint32_spbset_chunk *chunk; + uint32_t i, n = 0; for(chunk = &bset->head; chunk; chunk = chunk->next) { - for(i = 0; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) { + for(i = 0; i < CURL_UINT32_SPBSET_CH_SLOTS; ++i) { if(chunk->slots[i]) n += CURL_POPCOUNT64(chunk->slots[i]); } @@ -66,37 +71,11 @@ unsigned int Curl_uint_spbset_count(struct uint_spbset *bset) return n; } -bool Curl_uint_spbset_empty(struct uint_spbset *bset) +static struct uint32_spbset_chunk *uint32_spbset_get_chunk( + struct uint32_spbset *bset, uint32_t i, bool grow) { - struct uint_spbset_chunk *chunk; - unsigned int i; - - for(chunk = &bset->head; chunk; chunk = chunk->next) { - for(i = 0; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) { - if(chunk->slots[i]) - return FALSE; - } - } - return TRUE; -} - -UNITTEST void Curl_uint_spbset_clear(struct uint_spbset *bset) -{ - struct uint_spbset_chunk *next, *chunk; - - for(chunk = bset->head.next; chunk; chunk = next) { - next = chunk->next; - free(chunk); - } - memset(&bset->head, 0, sizeof(bset->head)); -} - - -static struct uint_spbset_chunk * -uint_spbset_get_chunk(struct uint_spbset *bset, unsigned int i, bool grow) -{ - struct uint_spbset_chunk *chunk, **panchor = NULL; - unsigned int i_offset = (i & ~CURL_UINT_SPBSET_CH_MASK); + struct uint32_spbset_chunk *chunk, **panchor = NULL; + uint32_t i_offset = (i & ~CURL_UINT32_SPBSET_CH_MASK); if(!bset) return NULL; @@ -117,7 +96,7 @@ uint_spbset_get_chunk(struct uint_spbset *bset, unsigned int i, bool grow) return NULL; /* need a new one */ - chunk = calloc(1, sizeof(*chunk)); + chunk = curlx_calloc(1, sizeof(*chunk)); if(!chunk) return NULL; @@ -134,62 +113,59 @@ uint_spbset_get_chunk(struct uint_spbset *bset, unsigned int i, bool grow) return chunk; } - -bool Curl_uint_spbset_add(struct uint_spbset *bset, unsigned int i) +bool Curl_uint32_spbset_add(struct uint32_spbset *bset, uint32_t i) { - struct uint_spbset_chunk *chunk; - unsigned int i_chunk; + struct uint32_spbset_chunk *chunk; + uint32_t i_chunk; - chunk = uint_spbset_get_chunk(bset, i, TRUE); + chunk = uint32_spbset_get_chunk(bset, i, TRUE); if(!chunk) return FALSE; DEBUGASSERT(i >= chunk->offset); i_chunk = (i - chunk->offset); - DEBUGASSERT((i_chunk / 64) < CURL_UINT_SPBSET_CH_SLOTS); - chunk->slots[(i_chunk / 64)] |= ((curl_uint64_t)1 << (i_chunk % 64)); + DEBUGASSERT((i_chunk / 64) < CURL_UINT32_SPBSET_CH_SLOTS); + chunk->slots[(i_chunk / 64)] |= ((uint64_t)1 << (i_chunk % 64)); return TRUE; } - -void Curl_uint_spbset_remove(struct uint_spbset *bset, unsigned int i) +void Curl_uint32_spbset_remove(struct uint32_spbset *bset, uint32_t i) { - struct uint_spbset_chunk *chunk; - unsigned int i_chunk; + struct uint32_spbset_chunk *chunk; + uint32_t i_chunk; - chunk = uint_spbset_get_chunk(bset, i, FALSE); + chunk = uint32_spbset_get_chunk(bset, i, FALSE); if(chunk) { DEBUGASSERT(i >= chunk->offset); i_chunk = (i - chunk->offset); - DEBUGASSERT((i_chunk / 64) < CURL_UINT_SPBSET_CH_SLOTS); - chunk->slots[(i_chunk / 64)] &= ~((curl_uint64_t)1 << (i_chunk % 64)); + DEBUGASSERT((i_chunk / 64) < CURL_UINT32_SPBSET_CH_SLOTS); + chunk->slots[(i_chunk / 64)] &= ~((uint64_t)1 << (i_chunk % 64)); } } - -bool Curl_uint_spbset_contains(struct uint_spbset *bset, unsigned int i) +bool Curl_uint32_spbset_contains(struct uint32_spbset *bset, uint32_t i) { - struct uint_spbset_chunk *chunk; - unsigned int i_chunk; + struct uint32_spbset_chunk *chunk; + uint32_t i_chunk; - chunk = uint_spbset_get_chunk(bset, i, FALSE); + chunk = uint32_spbset_get_chunk(bset, i, FALSE); if(chunk) { DEBUGASSERT(i >= chunk->offset); i_chunk = (i - chunk->offset); - DEBUGASSERT((i_chunk / 64) < CURL_UINT_SPBSET_CH_SLOTS); + DEBUGASSERT((i_chunk / 64) < CURL_UINT32_SPBSET_CH_SLOTS); return (chunk->slots[i_chunk / 64] & - ((curl_uint64_t)1 << (i_chunk % 64))) != 0; + ((uint64_t)1 << (i_chunk % 64))) != 0; } return FALSE; } -bool Curl_uint_spbset_first(struct uint_spbset *bset, unsigned int *pfirst) +bool Curl_uint32_spbset_first(struct uint32_spbset *bset, uint32_t *pfirst) { - struct uint_spbset_chunk *chunk; - unsigned int i; + struct uint32_spbset_chunk *chunk; + uint32_t i; for(chunk = &bset->head; chunk; chunk = chunk->next) { - for(i = 0; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) { + for(i = 0; i < CURL_UINT32_SPBSET_CH_SLOTS; ++i) { if(chunk->slots[i]) { *pfirst = chunk->offset + ((i * 64) + CURL_CTZ64(chunk->slots[i])); return TRUE; @@ -200,30 +176,28 @@ bool Curl_uint_spbset_first(struct uint_spbset *bset, unsigned int *pfirst) return FALSE; } - -static bool uint_spbset_chunk_first(struct uint_spbset_chunk *chunk, - unsigned int *pfirst) +static bool uint32_spbset_chunk_first(struct uint32_spbset_chunk *chunk, + uint32_t *pfirst) { - unsigned int i; - for(i = 0; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) { + uint32_t i; + for(i = 0; i < CURL_UINT32_SPBSET_CH_SLOTS; ++i) { if(chunk->slots[i]) { *pfirst = chunk->offset + ((i * 64) + CURL_CTZ64(chunk->slots[i])); return TRUE; } } - *pfirst = UINT_MAX; /* a value we cannot store */ + *pfirst = UINT32_MAX; /* a value we cannot store */ return FALSE; } - -static bool uint_spbset_chunk_next(struct uint_spbset_chunk *chunk, - unsigned int last, - unsigned int *pnext) +static bool uint32_spbset_chunk_next(struct uint32_spbset_chunk *chunk, + uint32_t last, + uint32_t *pnext) { if(chunk->offset <= last) { - curl_uint64_t x; - unsigned int i = ((last - chunk->offset) / 64); - if(i < CURL_UINT_SPBSET_CH_SLOTS) { + uint64_t x; + uint32_t i = ((last - chunk->offset) / 64); + if(i < CURL_UINT32_SPBSET_CH_SLOTS) { x = (chunk->slots[i] >> (last % 64)); if(x) { /* more bits set, next is `last` + trailing0s of the shifted slot */ @@ -231,7 +205,7 @@ static bool uint_spbset_chunk_next(struct uint_spbset_chunk *chunk, return TRUE; } /* no more bits set in the last slot, scan forward */ - for(i = i + 1; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) { + for(i = i + 1; i < CURL_UINT32_SPBSET_CH_SLOTS; ++i) { if(chunk->slots[i]) { *pnext = chunk->offset + ((i * 64) + CURL_CTZ64(chunk->slots[i])); return TRUE; @@ -239,18 +213,18 @@ static bool uint_spbset_chunk_next(struct uint_spbset_chunk *chunk, } } } - *pnext = UINT_MAX; + *pnext = UINT32_MAX; return FALSE; } -bool Curl_uint_spbset_next(struct uint_spbset *bset, unsigned int last, - unsigned int *pnext) +bool Curl_uint32_spbset_next(struct uint32_spbset *bset, uint32_t last, + uint32_t *pnext) { - struct uint_spbset_chunk *chunk; - unsigned int last_offset; + struct uint32_spbset_chunk *chunk; + uint32_t last_offset; ++last; /* look for the next higher number */ - last_offset = (last & ~CURL_UINT_SPBSET_CH_MASK); + last_offset = (last & ~CURL_UINT32_SPBSET_CH_MASK); for(chunk = &bset->head; chunk; chunk = chunk->next) { if(chunk->offset >= last_offset) { @@ -260,17 +234,17 @@ bool Curl_uint_spbset_next(struct uint_spbset *bset, unsigned int last, if(chunk && (chunk->offset == last_offset)) { /* is there a number higher than last in this chunk? */ - if(uint_spbset_chunk_next(chunk, last, pnext)) + if(uint32_spbset_chunk_next(chunk, last, pnext)) return TRUE; /* not in this chunk */ chunk = chunk->next; } /* look for the first in the "higher" chunks, if there are any. */ while(chunk) { - if(uint_spbset_chunk_first(chunk, pnext)) + if(uint32_spbset_chunk_first(chunk, pnext)) return TRUE; chunk = chunk->next; } - *pnext = UINT_MAX; + *pnext = UINT32_MAX; return FALSE; } diff --git a/lib/uint-spbset.h b/lib/uint-spbset.h index bd2347902c..4b105c02ed 100644 --- a/lib/uint-spbset.h +++ b/lib/uint-spbset.h @@ -25,10 +25,8 @@ ***************************************************************************/ #include "curl_setup.h" -#include - -/* A "sparse" bitset for unsigned int values. - * It can hold any unsigned int value. +/* A "sparse" bitset for uint32_t values. + * It can hold any uint32_t value. * * Optimized for the case where only a small set of numbers need * to be kept, especially when "close" together. Then storage space @@ -36,48 +34,45 @@ */ /* 4 slots = 256 bits, keep this a 2^n value. */ -#define CURL_UINT_SPBSET_CH_SLOTS 4 -#define CURL_UINT_SPBSET_CH_MASK ((CURL_UINT_SPBSET_CH_SLOTS * 64) - 1) +#define CURL_UINT32_SPBSET_CH_SLOTS 4 +#define CURL_UINT32_SPBSET_CH_MASK ((CURL_UINT32_SPBSET_CH_SLOTS * 64) - 1) /* store the uint value from offset to - * (offset + (CURL_UINT_SPBSET_CHUNK_SLOTS * 64) - 1 */ -struct uint_spbset_chunk { - struct uint_spbset_chunk *next; - curl_uint64_t slots[CURL_UINT_SPBSET_CH_SLOTS]; - unsigned int offset; + * (offset + (CURL_UINT32_SPBSET_CHUNK_SLOTS * 64) - 1 */ +struct uint32_spbset_chunk { + struct uint32_spbset_chunk *next; + uint64_t slots[CURL_UINT32_SPBSET_CH_SLOTS]; + uint32_t offset; }; -struct uint_spbset { - struct uint_spbset_chunk head; +struct uint32_spbset { + struct uint32_spbset_chunk head; #ifdef DEBUGBUILD int init; #endif }; -void Curl_uint_spbset_init(struct uint_spbset *bset); +void Curl_uint32_spbset_init(struct uint32_spbset *bset); -void Curl_uint_spbset_destroy(struct uint_spbset *bset); +void Curl_uint32_spbset_destroy(struct uint32_spbset *bset); /* Get the cardinality of the bitset, e.g. numbers present in the set. */ -unsigned int Curl_uint_spbset_count(struct uint_spbset *bset); - -/* TRUE of bitset is empty */ -bool Curl_uint_spbset_empty(struct uint_spbset *bset); +uint32_t Curl_uint32_spbset_count(struct uint32_spbset *bset); /* Add the number `i` to the bitset. * Numbers can be added more than once, without making a difference. * Returns FALSE if allocations failed. */ -bool Curl_uint_spbset_add(struct uint_spbset *bset, unsigned int i); +bool Curl_uint32_spbset_add(struct uint32_spbset *bset, uint32_t i); /* Remove the number `i` from the bitset. */ -void Curl_uint_spbset_remove(struct uint_spbset *bset, unsigned int i); +void Curl_uint32_spbset_remove(struct uint32_spbset *bset, uint32_t i); /* Return TRUE if the bitset contains number `i`. */ -bool Curl_uint_spbset_contains(struct uint_spbset *bset, unsigned int i); +bool Curl_uint32_spbset_contains(struct uint32_spbset *bset, uint32_t i); /* Get the first number in the bitset, e.g. the smallest. * Returns FALSE when the bitset is empty. */ -bool Curl_uint_spbset_first(struct uint_spbset *bset, unsigned int *pfirst); +bool Curl_uint32_spbset_first(struct uint32_spbset *bset, uint32_t *pfirst); /* Get the next number in the bitset, following `last` in natural order. * Put another way, this is the smallest number greater than `last` in @@ -90,7 +85,7 @@ bool Curl_uint_spbset_first(struct uint_spbset *bset, unsigned int *pfirst); * - added numbers lower than 'last' will not show up. * - removed numbers lower or equal to 'last' will not show up. * - removed numbers higher than 'last' will not be visited. */ -bool Curl_uint_spbset_next(struct uint_spbset *bset, unsigned int last, - unsigned int *pnext); +bool Curl_uint32_spbset_next(struct uint32_spbset *bset, uint32_t last, + uint32_t *pnext); #endif /* HEADER_CURL_UINT_SPBSET_H */ diff --git a/lib/uint-table.c b/lib/uint-table.c index 21bcb6e1cf..e3162583dc 100644 --- a/lib/uint-table.c +++ b/lib/uint-table.c @@ -21,39 +21,30 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" + #include "uint-table.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - #ifdef DEBUGBUILD -#define CURL_UINT_TBL_MAGIC 0x62757473 +#define CURL_UINT32_TBL_MAGIC 0x62757473 #endif -/* Clear the table, making it empty. */ -UNITTEST void Curl_uint_tbl_clear(struct uint_tbl *tbl); - -void Curl_uint_tbl_init(struct uint_tbl *tbl, - Curl_uint_tbl_entry_dtor *entry_dtor) +void Curl_uint32_tbl_init(struct uint32_tbl *tbl, + Curl_uint32_tbl_entry_dtor *entry_dtor) { memset(tbl, 0, sizeof(*tbl)); tbl->entry_dtor = entry_dtor; - tbl->last_key_added = UINT_MAX; + tbl->last_key_added = UINT32_MAX; #ifdef DEBUGBUILD - tbl->init = CURL_UINT_TBL_MAGIC; + tbl->init = CURL_UINT32_TBL_MAGIC; #endif } - -static void uint_tbl_clear_rows(struct uint_tbl *tbl, - unsigned int from, - unsigned int upto_excluding) +static void uint32_tbl_clear_rows(struct uint32_tbl *tbl, + uint32_t from, + uint32_t upto_excluding) { - unsigned int i, end; + uint32_t i, end; end = CURLMIN(upto_excluding, tbl->nrows); for(i = from; i < end; ++i) { @@ -66,22 +57,21 @@ static void uint_tbl_clear_rows(struct uint_tbl *tbl, } } - -CURLcode Curl_uint_tbl_resize(struct uint_tbl *tbl, unsigned int nrows) +CURLcode Curl_uint32_tbl_resize(struct uint32_tbl *tbl, uint32_t nrows) { /* we use `tbl->nrows + 1` during iteration, want that to work */ - DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC); + DEBUGASSERT(tbl->init == CURL_UINT32_TBL_MAGIC); if(!nrows) return CURLE_BAD_FUNCTION_ARGUMENT; if(nrows != tbl->nrows) { - void **rows = calloc(nrows, sizeof(void *)); + void **rows = curlx_calloc(nrows, sizeof(void *)); if(!rows) return CURLE_OUT_OF_MEMORY; if(tbl->rows) { memcpy(rows, tbl->rows, (CURLMIN(nrows, tbl->nrows) * sizeof(void *))); if(nrows < tbl->nrows) - uint_tbl_clear_rows(tbl, nrows, tbl->nrows); - free(tbl->rows); + uint32_tbl_clear_rows(tbl, nrows, tbl->nrows); + curlx_free(tbl->rows); } tbl->rows = rows; tbl->nrows = nrows; @@ -89,50 +79,47 @@ CURLcode Curl_uint_tbl_resize(struct uint_tbl *tbl, unsigned int nrows) return CURLE_OK; } - -void Curl_uint_tbl_destroy(struct uint_tbl *tbl) +/* Clear the table, making it empty. */ +UNITTEST void Curl_uint32_tbl_clear(struct uint32_tbl *tbl); +UNITTEST void Curl_uint32_tbl_clear(struct uint32_tbl *tbl) { - DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC); - Curl_uint_tbl_clear(tbl); - free(tbl->rows); + DEBUGASSERT(tbl->init == CURL_UINT32_TBL_MAGIC); + uint32_tbl_clear_rows(tbl, 0, tbl->nrows); + DEBUGASSERT(!tbl->nentries); + tbl->last_key_added = UINT32_MAX; +} + +void Curl_uint32_tbl_destroy(struct uint32_tbl *tbl) +{ + DEBUGASSERT(tbl->init == CURL_UINT32_TBL_MAGIC); + Curl_uint32_tbl_clear(tbl); + curlx_free(tbl->rows); memset(tbl, 0, sizeof(*tbl)); } -UNITTEST void Curl_uint_tbl_clear(struct uint_tbl *tbl) -{ - DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC); - uint_tbl_clear_rows(tbl, 0, tbl->nrows); - DEBUGASSERT(!tbl->nentries); - tbl->last_key_added = UINT_MAX; -} - - -unsigned int Curl_uint_tbl_capacity(struct uint_tbl *tbl) +uint32_t Curl_uint32_tbl_capacity(struct uint32_tbl *tbl) { return tbl->nrows; } - -unsigned int Curl_uint_tbl_count(struct uint_tbl *tbl) +uint32_t Curl_uint32_tbl_count(struct uint32_tbl *tbl) { return tbl->nentries; } - -void *Curl_uint_tbl_get(struct uint_tbl *tbl, unsigned int key) +void *Curl_uint32_tbl_get(struct uint32_tbl *tbl, uint32_t key) { return (key < tbl->nrows) ? tbl->rows[key] : NULL; } - -bool Curl_uint_tbl_add(struct uint_tbl *tbl, void *entry, unsigned int *pkey) +bool Curl_uint32_tbl_add(struct uint32_tbl *tbl, void *entry, uint32_t *pkey) { - unsigned int key, start_pos; + uint32_t key, start_pos; - DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC); + DEBUGASSERT(tbl->init == CURL_UINT32_TBL_MAGIC); if(!entry || !pkey) return FALSE; - *pkey = UINT_MAX; + *pkey = UINT32_MAX; if(tbl->nentries == tbl->nrows) /* full */ return FALSE; @@ -161,21 +148,18 @@ bool Curl_uint_tbl_add(struct uint_tbl *tbl, void *entry, unsigned int *pkey) return FALSE; } - -void Curl_uint_tbl_remove(struct uint_tbl *tbl, unsigned int key) +void Curl_uint32_tbl_remove(struct uint32_tbl *tbl, uint32_t key) { - uint_tbl_clear_rows(tbl, key, key + 1); + uint32_tbl_clear_rows(tbl, key, key + 1); } - -bool Curl_uint_tbl_contains(struct uint_tbl *tbl, unsigned int key) +bool Curl_uint32_tbl_contains(struct uint32_tbl *tbl, uint32_t key) { return (key < tbl->nrows) ? !!tbl->rows[key] : FALSE; } - -static bool uint_tbl_next_at(struct uint_tbl *tbl, unsigned int key, - unsigned int *pkey, void **pentry) +static bool uint32_tbl_next_at(struct uint32_tbl *tbl, uint32_t key, + uint32_t *pkey, void **pentry) { for(; key < tbl->nrows; ++key) { if(tbl->rows[key]) { @@ -184,33 +168,32 @@ static bool uint_tbl_next_at(struct uint_tbl *tbl, unsigned int key, return TRUE; } } - *pkey = UINT_MAX; /* always invalid */ + *pkey = UINT32_MAX; /* always invalid */ *pentry = NULL; return FALSE; } -bool Curl_uint_tbl_first(struct uint_tbl *tbl, - unsigned int *pkey, void **pentry) +bool Curl_uint32_tbl_first(struct uint32_tbl *tbl, + uint32_t *pkey, void **pentry) { if(!pkey || !pentry) return FALSE; - if(tbl->nentries && uint_tbl_next_at(tbl, 0, pkey, pentry)) + if(tbl->nentries && uint32_tbl_next_at(tbl, 0, pkey, pentry)) return TRUE; DEBUGASSERT(!tbl->nentries); - *pkey = UINT_MAX; /* always invalid */ + *pkey = UINT32_MAX; /* always invalid */ *pentry = NULL; return FALSE; } - -bool Curl_uint_tbl_next(struct uint_tbl *tbl, unsigned int last_key, - unsigned int *pkey, void **pentry) +bool Curl_uint32_tbl_next(struct uint32_tbl *tbl, uint32_t last_key, + uint32_t *pkey, void **pentry) { if(!pkey || !pentry) return FALSE; - if(uint_tbl_next_at(tbl, last_key + 1, pkey, pentry)) + if(uint32_tbl_next_at(tbl, last_key + 1, pkey, pentry)) return TRUE; - *pkey = UINT_MAX; /* always invalid */ + *pkey = UINT32_MAX; /* always invalid */ *pentry = NULL; return FALSE; } diff --git a/lib/uint-table.h b/lib/uint-table.h index c74ec7ad63..398a26a648 100644 --- a/lib/uint-table.h +++ b/lib/uint-table.h @@ -25,17 +25,15 @@ ***************************************************************************/ #include "curl_setup.h" -#include - /* Destructor for a single table entry */ -typedef void Curl_uint_tbl_entry_dtor(unsigned int key, void *entry); +typedef void Curl_uint32_tbl_entry_dtor(uint32_t key, void *entry); -struct uint_tbl { +struct uint32_tbl { void **rows; /* array of void* holding entries */ - Curl_uint_tbl_entry_dtor *entry_dtor; - unsigned int nrows; /* length of `rows` array */ - unsigned int nentries; /* entries in table */ - unsigned int last_key_added; /* UINT_MAX or last key added */ + Curl_uint32_tbl_entry_dtor *entry_dtor; + uint32_t nrows; /* length of `rows` array */ + uint32_t nentries; /* entries in table */ + uint32_t last_key_added; /* UINT_MAX or last key added */ #ifdef DEBUGBUILD int init; #endif @@ -44,42 +42,42 @@ struct uint_tbl { /* Initialize the table with 0 capacity. * The optional `entry_dtor` is called when a table entry is removed, * Passing NULL means no action is taken on removal. */ -void Curl_uint_tbl_init(struct uint_tbl *tbl, - Curl_uint_tbl_entry_dtor *entry_dtor); +void Curl_uint32_tbl_init(struct uint32_tbl *tbl, + Curl_uint32_tbl_entry_dtor *entry_dtor); /* 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_uint_tbl_resize(struct uint_tbl *tbl, unsigned int nmax); +CURLcode Curl_uint32_tbl_resize(struct uint32_tbl *tbl, uint32_t nrows); /* Destroy the table, freeing all entries. */ -void Curl_uint_tbl_destroy(struct uint_tbl *tbl); +void Curl_uint32_tbl_destroy(struct uint32_tbl *tbl); /* Get the table capacity. */ -unsigned int Curl_uint_tbl_capacity(struct uint_tbl *tbl); +uint32_t Curl_uint32_tbl_capacity(struct uint32_tbl *tbl); /* Get the number of entries in the table. */ -unsigned int Curl_uint_tbl_count(struct uint_tbl *tbl); +uint32_t Curl_uint32_tbl_count(struct uint32_tbl *tbl); /* Get the entry for key or NULL if not present */ -void *Curl_uint_tbl_get(struct uint_tbl *tbl, unsigned int key); +void *Curl_uint32_tbl_get(struct uint32_tbl *tbl, uint32_t key); /* Add a new entry to the table and assign it a free key. * Returns FALSE if the table is full. * * Keys are assigned in a round-robin manner. * No matter the capacity, UINT_MAX is never assigned. */ -bool Curl_uint_tbl_add(struct uint_tbl *tbl, void *entry, unsigned int *pkey); +bool Curl_uint32_tbl_add(struct uint32_tbl *tbl, void *entry, uint32_t *pkey); /* Remove the entry with `key`. */ -void Curl_uint_tbl_remove(struct uint_tbl *tbl, unsigned int key); +void Curl_uint32_tbl_remove(struct uint32_tbl *tbl, uint32_t key); /* Return TRUE if the table contains an tryn with that keys. */ -bool Curl_uint_tbl_contains(struct uint_tbl *tbl, unsigned int key); +bool Curl_uint32_tbl_contains(struct uint32_tbl *tbl, uint32_t key); /* Get the first entry in the table (with the smallest `key`). * Returns FALSE if the table is empty. */ -bool Curl_uint_tbl_first(struct uint_tbl *tbl, - unsigned int *pkey, void **pentry); +bool Curl_uint32_tbl_first(struct uint32_tbl *tbl, + uint32_t *pkey, void **pentry); /* Get the next key in the table, following `last_key` in natural order. * Put another way, this is the smallest key greater than `last_key` in @@ -92,7 +90,7 @@ bool Curl_uint_tbl_first(struct uint_tbl *tbl, * - added keys lower than 'last_key' will not show up. * - removed keys lower or equal to 'last_key' will not show up. * - removed keys higher than 'last_key' will not be visited. */ -bool Curl_uint_tbl_next(struct uint_tbl *tbl, unsigned int last_key, - unsigned int *pkey, void **pentry); +bool Curl_uint32_tbl_next(struct uint32_tbl *tbl, uint32_t last_key, + uint32_t *pkey, void **pentry); #endif /* HEADER_CURL_UINT_TABLE_H */ diff --git a/lib/url.c b/lib/url.c index b52267cb67..c8a1fb276e 100644 --- a/lib/url.c +++ b/lib/url.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef HAVE_NETINET_IN_H @@ -59,38 +58,29 @@ #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 - -#include "doh.h" #include "urldata.h" -#include "netrc.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 "sendf.h" +#include "curl_addrinfo.h" +#include "curl_trc.h" #include "progress.h" #include "cookie.h" #include "strcase.h" -#include "strerror.h" #include "escape.h" -#include "share.h" -#include "content_encoding.h" +#include "curl_share.h" #include "http_digest.h" -#include "http_negotiate.h" -#include "select.h" #include "multiif.h" -#include "easyif.h" -#include "speedcheck.h" -#include "curlx/warnless.h" #include "getinfo.h" #include "pop3.h" #include "urlapi-int.h" @@ -98,39 +88,35 @@ #include "hsts.h" #include "noproxy.h" #include "cfilters.h" -#include "curl_krb5.h" #include "idn.h" +#include "http_proxy.h" +#include "conncache.h" +#include "multihandle.h" +#include "curlx/strdup.h" +#include "setopt.h" +#include "altsvc.h" +#include "curlx/dynbuf.h" +#include "headers.h" +#include "curlx/strerr.h" +#include "curlx/strparse.h" -/* And now for the protocols */ +/* Now for the protocols */ #include "ftp.h" #include "dict.h" #include "telnet.h" #include "tftp.h" #include "http.h" -#include "http2.h" #include "file.h" #include "curl_ldap.h" #include "vssh/ssh.h" #include "imap.h" #include "url.h" #include "connect.h" -#include "http_ntlm.h" -#include "curl_rtmp.h" #include "gopher.h" #include "mqtt.h" -#include "http_proxy.h" -#include "conncache.h" -#include "multihandle.h" -#include "strdup.h" -#include "setopt.h" -#include "altsvc.h" -#include "curlx/dynbuf.h" -#include "headers.h" -#include "curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" +#include "rtsp.h" +#include "smtp.h" +#include "ws.h" #ifdef USE_NGHTTP2 static void data_priority_cleanup(struct Curl_easy *data); @@ -138,37 +124,33 @@ 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 -#define UNIX_SOCKET_PREFIX "localhost" -#endif - /* Reject URLs exceeding this length */ #define MAX_URL_LEN 0xffff /* -* get_protocol_family() -* -* This is used to return the protocol family for a given protocol. -* -* Parameters: -* -* 'h' [in] - struct Curl_handler pointer. -* -* Returns the family as a single bit protocol identifier. -*/ -static curl_prot_t get_protocol_family(const struct Curl_handler *h) + * get_protocol_family() + * + * This is used to return the protocol family for a given protocol. + * + * Parameters: + * + * '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_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) @@ -178,25 +160,20 @@ void Curl_freeset(struct Curl_easy *data) enum dupblob j; for(i = (enum dupstring)0; i < STRING_LAST; i++) { - Curl_safefree(data->set.str[i]); + curlx_safefree(data->set.str[i]); } for(j = (enum dupblob)0; j < BLOB_LAST; j++) { - Curl_safefree(data->set.blobs[j]); + curlx_safefree(data->set.blobs[j]); } - if(data->state.referer_alloc) { - Curl_safefree(data->state.referer); - data->state.referer_alloc = FALSE; - } - data->state.referer = NULL; - if(data->state.url_alloc) { - Curl_safefree(data->state.url); - data->state.url_alloc = FALSE; - } - data->state.url = NULL; + 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); + curlx_safefree(data->set.mimepostp); +#endif #ifndef CURL_DISABLE_COOKIES curl_slist_free_all(data->state.cookielist); @@ -208,14 +185,14 @@ void Curl_freeset(struct Curl_easy *data) static void up_free(struct Curl_easy *data) { struct urlpieces *up = &data->state.up; - Curl_safefree(up->scheme); - Curl_safefree(up->hostname); - Curl_safefree(up->port); - Curl_safefree(up->user); - Curl_safefree(up->password); - Curl_safefree(up->options); - Curl_safefree(up->path); - Curl_safefree(up->query); + curlx_safefree(up->scheme); + curlx_safefree(up->hostname); + curlx_safefree(up->port); + curlx_safefree(up->user); + curlx_safefree(up->password); + curlx_safefree(up->options); + curlx_safefree(up->path); + curlx_safefree(up->query); curl_url_cleanup(data->state.uh); data->state.uh = NULL; } @@ -258,26 +235,27 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_expire_clear(data); /* shut off any timers left */ - data->magic = 0; /* force a clear AFTER the possibly enforced removal from - the multi handle, since that function uses the magic - field! */ - if(data->state.rangestringalloc) - free(data->state.range); + curlx_free(data->state.range); - /* freed here just in case DONE was not called */ + /* release any resolve information this transfer kept */ + Curl_resolv_destroy_all(data); + + data->set.verbose = FALSE; /* no more calls to DEBUGFUNCTION */ + data->magic = 0; /* force a clear AFTER the possibly enforced removal from + * the multi handle and async dns shutdown. The multi + * handle might check the magic and so might any + * DEBUGFUNCTION invoked for tracing */ + + /* freed here in case DONE was not called */ Curl_req_free(&data->req, data); /* Close down all open SSL info and sessions */ Curl_ssl_close_all(data); - Curl_safefree(data->state.first_host); + curlx_safefree(data->state.first_host); Curl_ssl_free_certinfo(data); - if(data->state.referer_alloc) { - Curl_safefree(data->state.referer); - data->state.referer_alloc = FALSE; - } - data->state.referer = NULL; + Curl_bufref_free(&data->state.referer); up_free(data); curlx_dyn_free(&data->state.headerb); @@ -295,50 +273,38 @@ CURLcode Curl_close(struct Curl_easy **datap) #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) Curl_http_auth_cleanup_digest(data); #endif - Curl_safefree(data->state.most_recent_ftp_entrypath); - Curl_safefree(data->info.contenttype); - Curl_safefree(data->info.wouldredirect); - - /* release any resolve information this transfer kept */ - Curl_async_destroy(data); - Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */ - Curl_resolv_unlink(data, &data->state.dns[1]); + curlx_safefree(data->state.most_recent_ftp_entrypath); + curlx_safefree(data->info.contenttype); + curlx_safefree(data->info.wouldredirect); data_priority_cleanup(data); /* No longer a dirty share, if it exists */ - if(data->share) { - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - data->share->dirty--; - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - } + if(Curl_share_easy_unlink(data)) + DEBUGASSERT(0); Curl_hash_destroy(&data->meta_hash); -#ifndef CURL_DISABLE_PROXY - Curl_safefree(data->state.aptr.proxyuserpwd); -#endif - Curl_safefree(data->state.aptr.uagent); - Curl_safefree(data->state.aptr.userpwd); - Curl_safefree(data->state.aptr.accept_encoding); - Curl_safefree(data->state.aptr.rangeline); - Curl_safefree(data->state.aptr.ref); - Curl_safefree(data->state.aptr.host); + curlx_safefree(data->state.aptr.uagent); + curlx_safefree(data->state.aptr.accept_encoding); + curlx_safefree(data->state.aptr.rangeline); + curlx_safefree(data->state.aptr.ref); + curlx_safefree(data->state.aptr.host); #ifndef CURL_DISABLE_COOKIES - Curl_safefree(data->state.aptr.cookiehost); + curlx_safefree(data->req.cookiehost); #endif #ifndef CURL_DISABLE_RTSP - Curl_safefree(data->state.aptr.rtsp_transport); + curlx_safefree(data->state.aptr.rtsp_transport); #endif - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); + curlx_safefree(data->state.aptr.user); + curlx_safefree(data->state.aptr.passwd); #ifndef CURL_DISABLE_PROXY - Curl_safefree(data->state.aptr.proxyuser); - Curl_safefree(data->state.aptr.proxypasswd); + curlx_safefree(data->state.aptr.proxyuser); + curlx_safefree(data->state.aptr.proxypasswd); #endif #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) Curl_mime_cleanpart(data->state.formp); - Curl_safefree(data->state.formp); + curlx_safefree(data->state.formp); #endif /* destruct wildcard structures if it is needed */ @@ -346,7 +312,7 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_freeset(data); Curl_headers_cleanup(data); Curl_netrc_cleanup(&data->state.netrc); - free(data); + curlx_free(data); return CURLE_OK; } @@ -354,20 +320,26 @@ CURLcode Curl_close(struct Curl_easy **datap) * Initialize the UserDefined fields within a Curl_easy. * This may be safely called on a new or existing Curl_easy. */ -CURLcode Curl_init_userdefined(struct Curl_easy *data) +void Curl_init_userdefined(struct Curl_easy *data) { struct UserDefined *set = &data->set; - CURLcode result = CURLE_OK; - set->out = stdout; /* default output to stdout */ + set->out = stdout; /* default output to stdout */ set->in_set = stdin; /* default input from stdin */ - set->err = stderr; /* default stderr to stderr */ + set->err = stderr; /* default stderr to stderr */ +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +#endif /* use fwrite as default function to store output */ set->fwrite_func = (curl_write_callback)fwrite; /* use fread as default function to read input */ set->fread_func_set = (curl_read_callback)fread; +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic pop +#endif set->is_fread_set = 0; set->seek_client = ZERO_NULL; @@ -402,8 +374,6 @@ CURLcode 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; @@ -416,7 +386,7 @@ CURLcode 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) @@ -427,36 +397,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->socks5_gssapi_nec = FALSE; #endif - /* Set the default CA cert bundle/path detected/specified at build time. - * - * If Schannel is the selected SSL backend then these locations are ignored. - * We allow setting CA location for Schannel when explicitly specified by - * the user via CURLOPT_CAINFO / --cacert. - */ - if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { -#ifdef CURL_CA_BUNDLE - result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE); - if(result) - return result; -#ifndef CURL_DISABLE_PROXY - result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY], - CURL_CA_BUNDLE); - if(result) - return result; -#endif -#endif -#ifdef CURL_CA_PATH - result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH); - if(result) - return result; -#ifndef CURL_DISABLE_PROXY - result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], CURL_CA_PATH); - if(result) - return result; -#endif -#endif - } - /* set default minimum TLS version */ #ifdef USE_SSL Curl_setopt_SSLVERSION(data, CURLOPT_SSLVERSION, CURL_SSLVERSION_DEFAULT); @@ -467,8 +407,8 @@ CURLcode 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; @@ -482,14 +422,14 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->sep_headers = TRUE; /* separated header lists by default */ set->buffer_size = READBUFFER_SIZE; set->upload_buffer_size = UPLOADBUFFER_DEFAULT; + set->upload_flags = CURLULFLAG_SEEN; set->happy_eyeballs_timeout = CURL_HET_DEFAULT; set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT; set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ set->conn_max_idle_ms = 118 * 1000; - set->conn_max_age_ms = 0; + set->conn_max_age_ms = 24 * 3600 * 1000; set->http09_allowed = FALSE; - set->httpwant = CURL_HTTP_VERSION_NONE - ; + set->httpwant = CURL_HTTP_VERSION_NONE; #if defined(USE_HTTP2) || defined(USE_HTTP3) memset(&set->priority, 0, sizeof(set->priority)); #endif @@ -498,8 +438,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->ws_raw_mode = FALSE; set->ws_no_auto_pong = FALSE; #endif - - return result; } /* easy->meta_hash destructor. Should never be called as elements @@ -507,7 +445,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) static void easy_meta_freeentry(void *p) { (void)p; - /* Will always be FALSE. Cannot use a 0 assert here since compilers + /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ DEBUGASSERT(p == NULL); @@ -523,14 +461,13 @@ static void easy_meta_freeentry(void *p) CURLcode Curl_open(struct Curl_easy **curl) { - CURLcode result; struct Curl_easy *data; /* simple start-up: alloc the struct, init it with zeroes and return */ - data = calloc(1, sizeof(struct Curl_easy)); + data = curlx_calloc(1, sizeof(struct Curl_easy)); if(!data) { /* this is a serious error */ - DEBUGF(fprintf(stderr, "Error: calloc of Curl_easy failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: calloc of Curl_easy failed\n")); return CURLE_OUT_OF_MEMORY; } @@ -540,35 +477,26 @@ CURLcode Curl_open(struct Curl_easy **curl) data->state.recent_conn_id = -1; /* and not assigned an id yet */ data->id = -1; - data->mid = UINT_MAX; - data->master_mid = UINT_MAX; + data->mid = UINT32_MAX; + data->master_mid = UINT32_MAX; data->progress.hide = TRUE; data->state.current_speed = -1; /* init to negative == impossible */ Curl_hash_init(&data->meta_hash, 23, Curl_hash_str, curlx_str_key_compare, easy_meta_freeentry); curlx_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER); + Curl_bufref_init(&data->state.url); + Curl_bufref_init(&data->state.referer); Curl_req_init(&data->req); Curl_initinfo(data); #ifndef CURL_DISABLE_HTTP Curl_llist_init(&data->state.httphdrs, NULL); #endif Curl_netrc_init(&data->state.netrc); + Curl_init_userdefined(data); - result = Curl_init_userdefined(data); - - if(result) { - curlx_dyn_free(&data->state.headerb); - Curl_freeset(data); - Curl_req_free(&data->req, data); - Curl_hash_destroy(&data->meta_hash); - data->magic = 0; - free(data); - data = NULL; - } - else - *curl = data; - return result; + *curl = data; + return CURLE_OK; } void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) @@ -577,9 +505,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); @@ -590,34 +518,31 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) #ifndef CURL_DISABLE_PROXY Curl_free_idnconverted_hostname(&conn->http_proxy.host); Curl_free_idnconverted_hostname(&conn->socks_proxy.host); - Curl_safefree(conn->http_proxy.user); - Curl_safefree(conn->socks_proxy.user); - Curl_safefree(conn->http_proxy.passwd); - Curl_safefree(conn->socks_proxy.passwd); - Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */ - Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */ + curlx_safefree(conn->http_proxy.user); + curlx_safefree(conn->socks_proxy.user); + curlx_safefree(conn->http_proxy.passwd); + curlx_safefree(conn->socks_proxy.passwd); + curlx_safefree(conn->http_proxy.host.rawalloc); /* http proxy name */ + curlx_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name */ #endif - Curl_sec_conn_destroy(conn); - Curl_safefree(conn->user); - Curl_safefree(conn->passwd); - Curl_safefree(conn->sasl_authzid); - Curl_safefree(conn->options); - 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); + curlx_safefree(conn->user); + curlx_safefree(conn->passwd); + curlx_safefree(conn->sasl_authzid); + curlx_safefree(conn->options); + curlx_safefree(conn->oauth_bearer); + curlx_safefree(conn->host.rawalloc); /* hostname buffer */ + curlx_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */ + curlx_safefree(conn->secondaryhostname); + curlx_safefree(conn->localdev); Curl_ssl_conn_config_cleanup(conn); #ifdef USE_UNIX_SOCKETS - Curl_safefree(conn->unix_domain_socket); + curlx_safefree(conn->unix_domain_socket); #endif - Curl_safefree(conn->destination); - Curl_uint_spbset_destroy(&conn->xfers_attached); + curlx_safefree(conn->destination); Curl_hash_destroy(&conn->meta_hash); - free(conn); /* free all the connection oriented data */ + curlx_free(conn); /* free all the connection oriented data */ } /* @@ -631,11 +556,11 @@ 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) && - (data->state.http_neg.allowed & (CURL_HTTP_V2x|CURL_HTTP_V3x))) + (data->state.http_neg.allowed & (CURL_HTTP_V2x | CURL_HTTP_V3x))) /* allows HTTP/2 or newer */ return TRUE; } @@ -647,44 +572,24 @@ static bool xfer_may_multiplex(const struct Curl_easy *data, } #ifndef CURL_DISABLE_PROXY -static bool -proxy_info_matches(const struct proxy_info *data, - const struct proxy_info *needle) +static bool proxy_info_matches(const struct proxy_info *data, + const struct proxy_info *needle) { 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, @@ -693,7 +598,7 @@ static bool conn_maxage(struct Curl_easy *data, timediff_t age_ms; if(data->set.conn_max_idle_ms) { - age_ms = curlx_timediff(now, conn->lastused); + age_ms = curlx_ptimediff_ms(&now, &conn->lastused); if(age_ms > data->set.conn_max_idle_ms) { infof(data, "Too old connection (%" FMT_TIMEDIFF_T " ms idle, max idle is %" FMT_TIMEDIFF_T " ms), disconnect it", @@ -703,7 +608,7 @@ static bool conn_maxage(struct Curl_easy *data, } if(data->set.conn_max_age_ms) { - age_ms = curlx_timediff(now, conn->created); + age_ms = curlx_ptimediff_ms(&now, &conn->created); if(age_ms > data->set.conn_max_age_ms) { infof(data, "Too old connection (created %" FMT_TIMEDIFF_T @@ -720,38 +625,25 @@ static bool conn_maxage(struct Curl_easy *data, * Return TRUE iff the given connection is considered dead. */ bool Curl_conn_seems_dead(struct connectdata *conn, - struct Curl_easy *data, - struct curltime *pnow) + struct Curl_easy *data) { DEBUGASSERT(!data->conn); if(!CONN_INUSE(conn)) { /* The check for a dead socket makes sense only if the connection is not in use */ bool dead; - struct curltime now; - if(!pnow) { - now = curlx_now(); - pnow = &now; - } - if(conn_maxage(data, conn, *pnow)) { + if(conn_maxage(data, conn, *Curl_pgrs_now(data))) { /* avoid check if already too old */ dead = TRUE; } - else if(conn->handler->connection_check) { + else if(conn->scheme->run->connection_is_dead) { /* 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; - - /* briefly attach the connection to this transfer for the purpose of - checking it */ + /* briefly attach the connection for the check */ Curl_attach_connection(data, conn); - - state = conn->handler->connection_check(data, conn, CONNCHECK_ISDEAD); - dead = (state & CONNRESULT_DEAD); - /* detach the connection again */ + dead = conn->scheme->run->connection_is_dead(data, conn); Curl_detach_connection(data); - } else { bool input_pending = FALSE; @@ -784,29 +676,19 @@ bool Curl_conn_seems_dead(struct connectdata *conn, } CURLcode Curl_conn_upkeep(struct Curl_easy *data, - struct connectdata *conn, - struct curltime *now) + struct connectdata *conn) { CURLcode result = CURLE_OK; - if(curlx_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms) + if(curlx_ptimediff_ms(Curl_pgrs_now(data), &conn->keepalive) <= + data->set.upkeep_interval_ms) return result; /* briefly attach for action */ Curl_attach_connection(data, conn); - if(conn->handler->connection_check) { - /* Do a protocol-specific keepalive check on the connection. */ - unsigned int rc; - rc = conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE); - if(rc & CONNRESULT_DEAD) - result = CURLE_RECV_ERROR; - } - else { - /* Do the generic action on the FIRSTSOCKET filter chain */ - result = Curl_conn_keep_alive(data, conn, FIRSTSOCKET); - } + result = Curl_conn_keep_alive(data, conn, FIRSTSOCKET); Curl_detach_connection(data); - conn->keepalive = *now; + conn->keepalive = *Curl_pgrs_now(data); return result; } @@ -818,8 +700,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 @@ -830,7 +712,9 @@ 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(req_tls); /* require TLS use from a clear-text start */ BIT(wait_pipe); BIT(force_reuse); BIT(seen_pending_conn); @@ -842,12 +726,12 @@ static bool url_match_connect_config(struct connectdata *conn, struct url_conn_match *m) { /* connect-only or to-be-closed connections will not be reused */ - if(conn->connect_only || conn->bits.close || conn->bits.no_reuse) + if(conn->bits.connect_only || conn->bits.close || conn->bits.no_reuse) return FALSE; /* ip_version must match */ - if(m->data->set.ipver != CURL_IPRESOLVE_WHATEVER - && m->data->set.ipver != conn->ip_version) + if(m->data->set.ipver != CURL_IPRESOLVE_WHATEVER && + m->data->set.ipver != conn->ip_version) return FALSE; if(m->needle->localdev || m->needle->localport) { @@ -900,8 +784,8 @@ static bool url_match_fully_connected(struct connectdata *conn, struct url_conn_match *m) { if(!Curl_conn_is_connected(conn, FIRSTSOCKET) || - conn->bits.asks_multiplex) { - /* Not yet connected, or not yet decided if it multiplexes. The later + conn->bits.upgrade_in_progress) { + /* Not yet connected, or a protocol upgrade is in progress. The later * happens for HTTP/2 Upgrade: requests that need a response. */ if(m->may_multiplex) { m->seen_pending_conn = TRUE; @@ -949,16 +833,16 @@ static bool url_match_multiplex_limits(struct connectdata *conn, if(CONN_INUSE(conn) && m->may_multiplex) { DEBUGASSERT(conn->bits.multiplex); /* If multiplexed, make sure we do not go over concurrency limit */ - if(CONN_ATTACHED(conn) >= + if(conn->attached_xfers >= Curl_multi_max_concurrent_streams(m->data->multi)) { infof(m->data, "client side MAX_CONCURRENT_STREAMS reached" - ", skip (%u)", CONN_ATTACHED(conn)); + ", skip (%u)", conn->attached_xfers); return FALSE; } - if(CONN_ATTACHED(conn) >= - Curl_conn_get_max_concurrent(m->data, conn, FIRSTSOCKET)) { + if(conn->attached_xfers >= + Curl_conn_get_max_concurrent(m->data, conn, FIRSTSOCKET)) { infof(m->data, "MAX_CONCURRENT_STREAMS reached, skip (%u)", - CONN_ATTACHED(conn)); + conn->attached_xfers); return FALSE; } /* When not multiplexed, we have a match here! */ @@ -970,17 +854,21 @@ 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; } else if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - /* We are not *requiring* SSL, however `conn` has it. If the - * protocol *family* is not the same, not a match. */ - if(get_protocol_family(conn->handler) != m->needle->handler->protocol) + /* If the protocol does not allow reuse of SSL connections OR + is of another protocol family, not a match. */ + if(!(m->needle->scheme->flags & PROTOPT_SSL_REUSE) || + (get_protocol_family(conn->scheme) != m->needle->scheme->protocol)) return FALSE; } + else if(m->req_tls) + /* a clear-text STARTTLS protocol with required TLS */ + return FALSE; return TRUE; } @@ -993,8 +881,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) { @@ -1011,9 +898,9 @@ static bool url_match_proxy_use(struct connectdata *conn, /* match SSL config to proxy */ if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) { DEBUGF(infof(m->data, - "Connection #%" FMT_OFF_T - " has different SSL proxy parameters, cannot reuse", - conn->connection_id)); + "Connection #%" FMT_OFF_T + " has different SSL proxy parameters, cannot reuse", + conn->connection_id)); return FALSE; } /* the SSL config to the server, which may apply here is checked @@ -1023,7 +910,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 @@ -1031,8 +918,8 @@ static bool url_match_http_multiplex(struct connectdata *conn, struct url_conn_match *m) { if(m->may_multiplex && - (m->data->state.http_neg.allowed & (CURL_HTTP_V2x|CURL_HTTP_V3x)) && - (m->needle->handler->protocol & CURLPROTO_HTTP) && + (m->data->state.http_neg.allowed & (CURL_HTTP_V2x | CURL_HTTP_V3x)) && + (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"); @@ -1051,26 +938,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; @@ -1079,8 +966,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, @@ -1090,13 +977,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; } @@ -1107,7 +994,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) || @@ -1133,23 +1020,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; } } @@ -1158,7 +1045,7 @@ static bool url_match_destination(struct connectdata *conn, if((m->needle->bits.conn_to_host && !curl_strequal( m->needle->conn_to_host.name, conn->conn_to_host.name)) || (m->needle->bits.conn_to_port && - m->needle->conn_to_port != conn->conn_to_port)) + m->needle->conn_to_port != conn->conn_to_port)) return FALSE; /* hostname and port must match */ @@ -1173,10 +1060,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; @@ -1196,11 +1082,17 @@ static bool url_match_auth_ntlm(struct connectdata *conn, if(m->want_ntlm_http) { if(Curl_timestrcmp(m->needle->user, conn->user) || Curl_timestrcmp(m->needle->passwd, conn->passwd)) { - /* we prefer a credential match, but this is at least a connection - that can be reused and "upgraded" to NTLM */ - if(conn->http_ntlm_state == NTLMSTATE_NONE) + that can be reused and "upgraded" to NTLM if it does + not have any auth ongoing. */ +#ifdef USE_SPNEGO + if((conn->http_ntlm_state == NTLMSTATE_NONE) + && (conn->http_negotiate_state == GSS_AUTHNONE)) { +#else + if(conn->http_ntlm_state == NTLMSTATE_NONE) { +#endif m->found = conn; + } return FALSE; } } @@ -1249,7 +1141,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) @@ -1261,6 +1210,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) if(!url_match_connect_config(conn, m)) return FALSE; + /* match for destination and protocol? */ if(!url_match_destination(conn, m)) return FALSE; @@ -1294,10 +1244,15 @@ 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; - if(!CONN_INUSE(conn) && Curl_conn_seems_dead(conn, m->data, NULL)) { + if(!CONN_INUSE(conn) && Curl_conn_seems_dead(conn, m->data)) { /* remove and disconnect. */ Curl_conn_terminate(m->data, conn, FALSE); return FALSE; @@ -1308,10 +1263,9 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) return TRUE; } -static bool url_match_result(bool result, void *userdata) +static bool url_match_result(void *userdata) { struct url_conn_match *match = userdata; - (void)result; if(match->found) { /* Attach it now while still under lock, so the connection does * no longer appear idle and can be reaped. */ @@ -1319,7 +1273,7 @@ static bool url_match_result(bool result, void *userdata) return TRUE; } else if(match->seen_single_use_conn && !match->seen_multiplex_conn) { - /* We've seen a single-use, existing connection to the destination and + /* We have seen a single-use, existing connection to the destination and * no multiplexed one. It seems safe to assume that the server does * not support multiplexing. */ match->wait_pipe = FALSE; @@ -1334,53 +1288,61 @@ 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; + bool success; + 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 + match.req_tls = data->set.use_ssl >= CURLUSESSL_CONTROL; + /* Find a connection in the pool that matches what "data + needle" * requires. If a suitable candidate is found, it is attached to "data". */ - result = Curl_cpool_find(data, needle->destination, - url_match_conn, url_match_result, &match); + success = Curl_cpool_find(data, needle->destination, + url_match_conn, url_match_result, &match); /* wait_pipe is TRUE if we encounter a bundle that is undecided. There * is no matching connection then, yet. */ - *usethis = match.found; - *force_reuse = match.force_reuse; - *waitpipe = match.wait_pipe; - return result; + *waitpipe = (bool)match.wait_pipe; + return success; } /* @@ -1388,7 +1350,7 @@ ConnectionExists(struct Curl_easy *data, */ static struct connectdata *allocate_conn(struct Curl_easy *data) { - struct connectdata *conn = calloc(1, sizeof(struct connectdata)); + struct connectdata *conn = curlx_calloc(1, sizeof(struct connectdata)); if(!conn) return NULL; @@ -1399,15 +1361,11 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->recv_idx = 0; /* default for receiving transfer data */ conn->send_idx = 0; /* default for sending transfer data */ conn->connection_id = -1; /* no ID */ - conn->remote_port = -1; /* unknown at this point */ - - /* Default protocol-independent behavior does not support persistent - connections, so we set this to force-close. Protocols that support - this need to set this to FALSE in their "curl_do" functions. */ - connclose(conn, "Default to force-close"); + conn->attached_xfers = 0; + conn->remote_port = 0; /* unknown at this point */ /* Store creation time to help future close decision making */ - conn->created = curlx_now(); + conn->created = *Curl_pgrs_now(data); /* Store current time to give a baseline to keepalive connection times. */ conn->keepalive = conn->created; @@ -1416,7 +1374,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]); @@ -1440,19 +1398,12 @@ 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->bits.connect_only = (bool)data->set.connect_only; conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */ - /* Initialize the attached xfers bitset */ - Curl_uint_spbset_init(&conn->xfers_attached); - -#ifdef HAVE_GSSAPI - conn->data_prot = PROT_CLEAR; -#endif - /* Store the local bind parameters that will be used for this connection */ if(data->set.str[STRING_DEVICE]) { - conn->localdev = strdup(data->set.str[STRING_DEVICE]); + conn->localdev = curlx_strdup(data->set.str[STRING_DEVICE]); if(!conn->localdev) goto error; } @@ -1469,231 +1420,12 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) #ifdef HAVE_GSSAPI conn->gssapi_delegation = data->set.gssapi_delegation; #endif + DEBUGF(infof(data, "alloc connection, bits.close=%d", conn->bits.close)); return conn; error: - free(conn->localdev); - free(conn); - return NULL; -} - -const struct Curl_handler *Curl_get_scheme_handler(const char *scheme) -{ - return Curl_getn_scheme_handler(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) -{ - /* table generated by schemetable.c: - 1. gcc schemetable.c && ./a.out - 2. check how small the table gets - 3. tweak the hash algorithm, then rerun from 1 - 4. when the table is good enough - 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 -#if defined(USE_SSH) && !defined(USE_WOLFSSH) - &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 - }; - - if(len && (len <= 7)) { - const char *s = scheme; - size_t l = len; - const struct Curl_handler *h; - unsigned int c = 978; - while(l) { - c <<= 5; - c += (unsigned int)Curl_raw_tolower(*s); - s++; - l--; - } - - h = protocols[c % 67]; - if(h && curl_strnequal(scheme, h->scheme, len) && !h->scheme[len]) - return h; - } + curlx_free(conn->localdev); + curlx_free(conn); return NULL; } @@ -1701,9 +1433,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 @@ -1714,7 +1446,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; } @@ -1725,12 +1457,11 @@ static CURLcode findprotocol(struct Curl_easy *data, create_conn() function when the connectdata struct is allocated. */ failf(data, "Protocol \"%s\" %s%s", protostr, p ? "disabled" : "not supported", - data->state.this_is_a_follow ? " (in redirect)":""); + data->state.this_is_a_follow ? " (in redirect)" : ""); return CURLE_UNSUPPORTED_PROTOCOL; } - CURLcode Curl_uc_to_curlcode(CURLUcode uc) { switch(uc) { @@ -1756,7 +1487,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 @@ -1768,35 +1499,26 @@ 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, - Curl_strerror(errno, buffer, sizeof(buffer))); + curlx_strerror(errno, buffer, sizeof(buffer))); #endif } else conn->scope_id = scopeidx; } -#endif /* HAVE_IF_NAMETOINDEX || _WIN32 */ +#endif /* HAVE_IF_NAMETOINDEX */ - free(zoneid); + curlx_free(zoneid); } } #else -#define zonefrom_url(a,b,c) Curl_nop_stmt +#define zonefrom_url(a, b, c) Curl_nop_stmt #endif /* @@ -1809,6 +1531,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, CURLU *uh; CURLUcode uc; char *hostname; + size_t hlen; bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); up_free(data); /* cleanup previous leftovers first */ @@ -1825,21 +1548,19 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; if(data->set.str[STRING_DEFAULT_PROTOCOL] && - !Curl_is_absolute_url(data->state.url, NULL, 0, TRUE)) { - char *url = aprintf("%s://%s", data->set.str[STRING_DEFAULT_PROTOCOL], - data->state.url); + !Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) { + char *url = curl_maprintf("%s://%s", + data->set.str[STRING_DEFAULT_PROTOCOL], + Curl_bufref_ptr(&data->state.url)); if(!url) return CURLE_OUT_OF_MEMORY; - if(data->state.url_alloc) - free(data->state.url); - data->state.url = url; - data->state.url_alloc = TRUE; + Curl_bufref_set(&data->state.url, url, 0, curl_free); } if(!use_set_uh) { char *newurl; - uc = curl_url_set(uh, CURLUPART_URL, data->state.url, (unsigned int) - (CURLU_GUESS_SCHEME | + uc = curl_url_set(uh, CURLUPART_URL, Curl_bufref_ptr(&data->state.url), + (unsigned int)(CURLU_GUESS_SCHEME | CURLU_NON_SUPPORT_SCHEME | (data->set.disallow_username_in_url ? CURLU_DISALLOW_USER : 0) | @@ -1853,10 +1574,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0); if(uc) return Curl_uc_to_curlcode(uc); - if(data->state.url_alloc) - free(data->state.url); - data->state.url = newurl; - data->state.url_alloc = TRUE; + Curl_bufref_set(&data->state.url, newurl, 0, curl_free); } uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); @@ -1872,25 +1590,25 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); return CURLE_URL_MALFORMAT; } + hostname = data->state.up.hostname; + hlen = hostname ? strlen(hostname) : 0; if(hostname && hostname[0] == '[') { /* This looks like an IPv6 address literal. See if there is an address scope. */ - size_t hlen; - conn->bits.ipv6_ip = TRUE; - /* cut off the brackets! */ + /* cut off the brackets after copying this! */ hostname++; - hlen = strlen(hostname); - hostname[hlen - 1] = 0; + hlen -= 2; zonefrom_url(uh, data, conn); } /* make sure the connect struct gets its own copy of the hostname */ - conn->host.rawalloc = strdup(hostname ? hostname : ""); + conn->host.rawalloc = curlx_strdup(hostname ? hostname : ""); if(!conn->host.rawalloc) return CURLE_OUT_OF_MEMORY; + conn->host.rawalloc[hlen] = 0; /* cut off for ipv6 case */ conn->host.name = conn->host.rawalloc; /************************************************************* @@ -1906,25 +1624,22 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, /* This MUST use the IDN decoded name */ if(Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) { char *url; - Curl_safefree(data->state.up.scheme); + curlx_safefree(data->state.up.scheme); uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); if(uc) return Curl_uc_to_curlcode(uc); - if(data->state.url_alloc) - Curl_safefree(data->state.url); + Curl_bufref_free(&data->state.url); /* after update, get the updated version */ uc = curl_url_get(uh, CURLUPART_URL, &url, 0); if(uc) return Curl_uc_to_curlcode(uc); uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); if(uc) { - free(url); + curlx_free(url); return Curl_uc_to_curlcode(uc); } - data->state.url = url; - data->state.url_alloc = TRUE; - infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", - data->state.url); + Curl_bufref_set(&data->state.url, url, 0, curl_free); + infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url); } } #endif @@ -1942,7 +1657,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; @@ -1964,7 +1679,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; @@ -1981,22 +1696,22 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options, CURLU_URLDECODE); if(!uc) { - conn->options = strdup(data->state.up.options); + conn->options = curlx_strdup(data->state.up.options); if(!conn->options) return CURLE_OUT_OF_MEMORY; } else if(uc != CURLUE_NO_OPTIONS) return Curl_uc_to_curlcode(uc); - uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, - CURLU_URLENCODE); + uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE); if(uc) return Curl_uc_to_curlcode(uc); uc = curl_url_get(uh, CURLUPART_PORT, &data->state.up.port, CURLU_DEFAULT_PORT); if(uc) { - if(!curl_strequal("file", data->state.up.scheme)) + if((uc == CURLUE_OUT_OF_MEMORY) || + !curl_strequal("file", data->state.up.scheme)) return CURLE_OUT_OF_MEMORY; } else { @@ -2013,18 +1728,19 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, conn->remote_port = (unsigned short)port; } - (void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); + uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); + if(uc && (uc != CURLUE_NO_QUERY)) + return CURLE_OUT_OF_MEMORY; #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 return CURLE_OK; } - /* * If we are doing a resumed transfer, we need to setup our stuff * properly. @@ -2035,12 +1751,12 @@ static CURLcode setup_range(struct Curl_easy *data) s->resume_from = data->set.set_resume_from; if(s->resume_from || data->set.str[STRING_SET_RANGE]) { if(s->rangestringalloc) - free(s->range); + curlx_free(s->range); if(s->resume_from) - s->range = aprintf("%" FMT_OFF_T "-", s->resume_from); + s->range = curl_maprintf("%" FMT_OFF_T "-", s->resume_from); else - s->range = strdup(data->set.str[STRING_SET_RANGE]); + s->range = curlx_strdup(data->set.str[STRING_SET_RANGE]); if(!s->range) return CURLE_OUT_OF_MEMORY; @@ -2056,7 +1772,6 @@ static CURLcode setup_range(struct Curl_easy *data) return CURLE_OK; } - /* * setup_connection_internals() - * @@ -2070,14 +1785,16 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, struct connectdata *conn) { const char *hostname; - int port; + uint16_t port; CURLcode result; - if(conn->handler->setup_connection) { - result = conn->handler->setup_connection(data, conn); + DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close)); + if(conn->scheme->run->setup_connection) { + result = conn->scheme->run->setup_connection(data, conn); if(result) return result; } + DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close)); /* Now create the destination name */ #ifndef CURL_DISABLE_PROXY @@ -2088,18 +1805,21 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, else #endif { - port = conn->remote_port; - if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; - else - hostname = conn->host.name; + port = conn->bits.conn_to_port ? + conn->conn_to_port : conn->remote_port; + hostname = conn->bits.conn_to_host ? + conn->conn_to_host.name : conn->host.name; } #ifdef USE_IPV6 - conn->destination = aprintf("%u/%d/%s", conn->scope_id, port, hostname); -#else - conn->destination = aprintf("%d/%s", port, hostname); + /* IPv6 addresses with a scope_id (0 is default == global) have a + * printable representation with a '%' suffix. */ + if(conn->scope_id) + conn->destination = curl_maprintf("[%s:%u]%%%d", hostname, port, + conn->scope_id); + else #endif + conn->destination = curl_maprintf("%s:%u", hostname, port); if(!conn->destination) return CURLE_OUT_OF_MEMORY; @@ -2109,15 +1829,14 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, return CURLE_OK; } - #ifndef CURL_DISABLE_PROXY #ifndef CURL_DISABLE_HTTP /**************************************************************** -* Detect what (if any) proxy to use. Remember that this selects a host -* name and is not limited to HTTP proxies only. -* The returned pointer must be freed by the caller (unless NULL) -****************************************************************/ + * Detect what (if any) proxy to use. Remember that this selects a host + * name and is not limited to HTTP proxies only. + * The returned pointer must be freed by the caller (unless NULL) + ****************************************************************/ static char *detect_proxy(struct Curl_easy *data, struct connectdata *conn) { @@ -2141,12 +1860,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); - msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", conn->handler->scheme); + curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", + conn->scheme->name); /* read the protocol proxy: */ proxy = curl_getenv(proxy_env); @@ -2205,11 +1923,10 @@ 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; - int port = -1; char *proxyuser = NULL; char *proxypasswd = NULL; char *host = NULL; @@ -2224,7 +1941,6 @@ static CURLcode parse_proxy(struct Curl_easy *data, bool is_unix_proxy = FALSE; #endif - if(!uhp) { result = CURLE_OUT_OF_MEMORY; goto error; @@ -2233,7 +1949,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, /* When parsing the proxy, allowing non-supported schemes since we have these made up ones for proxies. Guess scheme for URLs without it. */ uc = curl_url_set(uhp, CURLUPART_URL, proxy, - CURLU_NON_SUPPORT_SCHEME|CURLU_GUESS_SCHEME); + CURLU_NON_SUPPORT_SCHEME | CURLU_GUESS_SCHEME); if(!uc) { /* parsed okay as a URL */ uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, 0); @@ -2292,24 +2008,28 @@ static CURLcode parse_proxy(struct Curl_easy *data, proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy; proxyinfo->proxytype = (unsigned char)proxytype; - /* Is there a username and password given in this proxy url? */ + /* Is there a username and password given in this proxy URL? */ uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE); - if(uc && (uc != CURLUE_NO_USER)) + if(uc && (uc != CURLUE_NO_USER)) { + result = Curl_uc_to_curlcode(uc); goto error; + } uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE); - if(uc && (uc != CURLUE_NO_PASSWORD)) + if(uc && (uc != CURLUE_NO_PASSWORD)) { + result = Curl_uc_to_curlcode(uc); goto error; + } if(proxyuser || proxypasswd) { - free(proxyinfo->user); + curlx_free(proxyinfo->user); proxyinfo->user = proxyuser; result = Curl_setstropt(&data->state.aptr.proxyuser, proxyuser); proxyuser = NULL; if(result) goto error; - Curl_safefree(proxyinfo->passwd); + curlx_safefree(proxyinfo->passwd); if(!proxypasswd) { - proxypasswd = strdup(""); + proxypasswd = curlx_strdup(""); if(!proxypasswd) { result = CURLE_OUT_OF_MEMORY; goto error; @@ -2323,29 +2043,32 @@ static CURLcode parse_proxy(struct Curl_easy *data, conn->bits.proxy_user_passwd = TRUE; /* enable it */ } - (void)curl_url_get(uhp, CURLUPART_PORT, &portptr, 0); + uc = curl_url_get(uhp, CURLUPART_PORT, &portptr, 0); + if(uc == CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } if(portptr) { curl_off_t num; const char *p = portptr; - if(!curlx_str_number(&p, &num, 0xffff)) - port = (int)num; - free(portptr); + if(!curlx_str_number(&p, &num, UINT16_MAX)) + proxyinfo->port = (uint16_t)num; + /* Should we not error out when the port number is invalid? */ + curlx_free(portptr); } else { if(data->set.proxyport) /* None given in the proxy string, then get the default one if it is given */ - port = (int)data->set.proxyport; + proxyinfo->port = data->set.proxyport; else { if(IS_HTTPS_PROXY(proxytype)) - port = CURL_DEFAULT_HTTPS_PROXY_PORT; + proxyinfo->port = CURL_DEFAULT_HTTPS_PROXY_PORT; else - port = CURL_DEFAULT_PROXY_PORT; + proxyinfo->port = CURL_DEFAULT_PROXY_PORT; } } - if(port >= 0) - proxyinfo->port = port; /* now, clone the proxy hostname */ uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE); @@ -2363,13 +2086,13 @@ static CURLcode parse_proxy(struct Curl_easy *data, /* path will be "/", if no path was found */ if(strcmp("/", path)) { is_unix_proxy = TRUE; - free(host); - host = aprintf(UNIX_SOCKET_PREFIX"%s", path); + curlx_free(host); + host = curl_maprintf(UNIX_SOCKET_PREFIX "%s", path); if(!host) { result = CURLE_OUT_OF_MEMORY; goto error; } - free(proxyinfo->host.rawalloc); + curlx_free(proxyinfo->host.rawalloc); proxyinfo->host.rawalloc = host; proxyinfo->host.name = host; host = NULL; @@ -2378,12 +2101,12 @@ static CURLcode parse_proxy(struct Curl_easy *data, if(!is_unix_proxy) { #endif - free(proxyinfo->host.rawalloc); + curlx_free(proxyinfo->host.rawalloc); proxyinfo->host.rawalloc = host; if(host[0] == '[') { /* this is a numerical IPv6, strip off the brackets */ size_t len = strlen(host); - host[len-1] = 0; /* clear the trailing bracket */ + host[len - 1] = 0; /* clear the trailing bracket */ host++; zonefrom_url(uhp, data, conn); } @@ -2394,12 +2117,12 @@ static CURLcode parse_proxy(struct Curl_easy *data, #endif error: - free(proxyuser); - free(proxypasswd); - free(host); - free(scheme); + curlx_free(proxyuser); + curlx_free(proxypasswd); + curlx_free(host); + curlx_free(scheme); #ifdef USE_UNIX_SOCKETS - free(path); + curlx_free(path); #endif curl_url_cleanup(uhp); return result; @@ -2417,13 +2140,13 @@ static CURLcode parse_proxy_auth(struct Curl_easy *data, data->state.aptr.proxypasswd : ""; CURLcode result = CURLE_OUT_OF_MEMORY; - conn->http_proxy.user = strdup(proxyuser); + conn->http_proxy.user = curlx_strdup(proxyuser); if(conn->http_proxy.user) { - conn->http_proxy.passwd = strdup(proxypasswd); + conn->http_proxy.passwd = curlx_strdup(proxypasswd); if(conn->http_proxy.passwd) result = CURLE_OK; else - Curl_safefree(conn->http_proxy.user); + curlx_safefree(conn->http_proxy.user); } return result; } @@ -2451,7 +2174,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, * Detect what (if any) proxy to use *************************************************************/ if(data->set.str[STRING_PROXY]) { - proxy = strdup(data->set.str[STRING_PROXY]); + proxy = curlx_strdup(data->set.str[STRING_PROXY]); /* if global proxy is set, this is it */ if(!proxy) { failf(data, "memory shortage"); @@ -2461,7 +2184,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, } if(data->set.str[STRING_PRE_PROXY]) { - socksproxy = strdup(data->set.str[STRING_PRE_PROXY]); + socksproxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]); /* if global socks proxy is set, this is it */ if(!socksproxy) { failf(data, "memory shortage"); @@ -2484,33 +2207,34 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? data->set.str[STRING_NOPROXY] : no_proxy)) { - Curl_safefree(proxy); - Curl_safefree(socksproxy); + curlx_safefree(proxy); + curlx_safefree(socksproxy); } #ifndef CURL_DISABLE_HTTP else if(!proxy && !socksproxy) /* if the host is not in the noproxy list, detect proxy. */ proxy = detect_proxy(data, conn); #endif /* CURL_DISABLE_HTTP */ - Curl_safefree(no_proxy); + curlx_safefree(no_proxy); #ifdef USE_UNIX_SOCKETS /* For the time being do not mix proxy and Unix domain sockets. See #1274 */ if(proxy && conn->unix_domain_socket) { - free(proxy); + curlx_free(proxy); proxy = NULL; } #endif - if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) { - free(proxy); /* Do not bother with an empty proxy string or if the - protocol does not work with network */ + 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))) { - free(socksproxy); /* Do not bother with an empty socks proxy string or if - the protocol does not work with network */ + (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 */ socksproxy = NULL; } @@ -2520,18 +2244,18 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, * connection that may exist registered to the same proxy host. ***********************************************************************/ if(proxy || socksproxy) { - long ptype = conn->http_proxy.proxytype; if(proxy) { - result = parse_proxy(data, conn, proxy, ptype); - Curl_safefree(proxy); /* parse_proxy copies the proxy string */ + result = parse_proxy(data, conn, proxy, conn->http_proxy.proxytype); + curlx_safefree(proxy); /* parse_proxy copies the proxy string */ if(result) goto out; } if(socksproxy) { - result = parse_proxy(data, conn, socksproxy, ptype); + result = parse_proxy(data, conn, socksproxy, + conn->socks_proxy.proxytype); /* parse_proxy copies the socks proxy string */ - Curl_safefree(socksproxy); + curlx_safefree(socksproxy); if(result) goto out; } @@ -2543,10 +2267,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; @@ -2565,7 +2289,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, if(!conn->socks_proxy.user) { conn->socks_proxy.user = conn->http_proxy.user; conn->http_proxy.user = NULL; - free(conn->socks_proxy.passwd); + curlx_free(conn->socks_proxy.passwd); conn->socks_proxy.passwd = conn->http_proxy.passwd; conn->http_proxy.passwd = NULL; } @@ -2595,8 +2319,8 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, out: - free(socksproxy); - free(proxy); + curlx_free(socksproxy); + curlx_free(proxy); return result; } #endif /* CURL_DISABLE_PROXY */ @@ -2665,13 +2389,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; } @@ -2680,7 +2404,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; } @@ -2690,8 +2414,8 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, *passwdp = pbuf; return CURLE_OK; error: - free(ubuf); - free(pbuf); + curlx_free(ubuf); + curlx_free(pbuf); return CURLE_OUT_OF_MEMORY; } @@ -2706,13 +2430,12 @@ error: 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]; CURLUcode uc; conn->remote_port = data->set.use_port; - msnprintf(portbuf, sizeof(portbuf), "%d", conn->remote_port); + curl_msnprintf(portbuf, sizeof(portbuf), "%d", conn->remote_port); uc = curl_url_set(data->state.uh, CURLUPART_PORT, portbuf, 0); if(uc) return CURLE_OUT_OF_MEMORY; @@ -2749,16 +2472,16 @@ static CURLcode override_login(struct Curl_easy *data, char **optionsp = &conn->options; if(data->set.str[STRING_OPTIONS]) { - free(*optionsp); - *optionsp = strdup(data->set.str[STRING_OPTIONS]); + curlx_free(*optionsp); + *optionsp = curlx_strdup(data->set.str[STRING_OPTIONS]); if(!*optionsp) return CURLE_OUT_OF_MEMORY; } #ifndef CURL_DISABLE_NETRC if(data->set.use_netrc == CURL_NETRC_REQUIRED) { - Curl_safefree(*userp); - Curl_safefree(*passwdp); + curlx_safefree(*userp); + curlx_safefree(*passwdp); } conn->bits.netrc = FALSE; if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) { @@ -2776,9 +2499,11 @@ static CURLcode override_login(struct Curl_easy *data, NETRCcode ret = Curl_parsenetrc(&data->state.netrc, conn->host.name, userp, passwdp, data->set.str[STRING_NETRC_FILE]); - if(ret && ((ret == NETRC_NO_MATCH) || - (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { - infof(data, "Couldn't find host %s in the %s file; using defaults", + if(ret == NETRC_OUT_OF_MEMORY) + return CURLE_OUT_OF_MEMORY; + else if(ret && ((ret == NETRC_NO_MATCH) || + (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { + infof(data, "Could not find host %s in the %s file; using defaults", conn->host.name, (data->set.str[STRING_NETRC_FILE] ? data->set.str[STRING_NETRC_FILE] : ".netrc")); @@ -2789,8 +2514,8 @@ static CURLcode override_login(struct Curl_easy *data, return CURLE_READ_ERROR; } else { - if(!(conn->handler->flags&PROTOPT_USERPWDCTRL)) { - /* if the protocol can't handle control codes in credentials, make + 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)) { failf(data, "control code detected in .netrc credentials"); @@ -2804,14 +2529,14 @@ static CURLcode override_login(struct Curl_easy *data, } } if(url_provided) { - free(conn->user); - conn->user = strdup(*userp); + curlx_free(conn->user); + conn->user = curlx_strdup(*userp); if(!conn->user) return CURLE_OUT_OF_MEMORY; } /* no user was set but a password, set a blank user */ if(!*userp && *passwdp) { - *userp = strdup(""); + *userp = curlx_strdup(""); if(!*userp) return CURLE_OUT_OF_MEMORY; } @@ -2835,7 +2560,7 @@ static CURLcode override_login(struct Curl_easy *data, if(uc) return Curl_uc_to_curlcode(uc); if(!*userp) { - *userp = strdup(data->state.aptr.user); + *userp = curlx_strdup(data->state.aptr.user); if(!*userp) return CURLE_OUT_OF_MEMORY; } @@ -2852,7 +2577,7 @@ static CURLcode override_login(struct Curl_easy *data, if(uc) return Curl_uc_to_curlcode(uc); if(!*passwdp) { - *passwdp = strdup(data->state.aptr.passwd); + *passwdp = curlx_strdup(data->state.aptr.passwd); if(!*passwdp) return CURLE_OUT_OF_MEMORY; } @@ -2872,7 +2597,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 = ""; @@ -2880,14 +2605,14 @@ static CURLcode set_login(struct Curl_easy *data, } /* Store the default user */ if(!conn->user) { - conn->user = strdup(setuser); + conn->user = curlx_strdup(setuser); if(!conn->user) return CURLE_OUT_OF_MEMORY; } /* Store the default password */ if(!conn->passwd) { - conn->passwd = strdup(setpasswd); + conn->passwd = curlx_strdup(setpasswd); if(!conn->passwd) result = CURLE_OUT_OF_MEMORY; } @@ -2912,17 +2637,13 @@ 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; if(!host || !*host) return CURLE_OK; - host_dup = strdup(host); + host_dup = curlx_strdup(host); if(!host_dup) return CURLE_OUT_OF_MEMORY; @@ -2984,7 +2705,7 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data, /* now, clone the cleaned hostname */ DEBUGASSERT(hostptr); - *hostname_result = strdup(hostptr); + *hostname_result = curlx_strdup(hostptr); if(!*hostname_result) { result = CURLE_OUT_OF_MEMORY; goto error; @@ -2993,7 +2714,7 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data, *port_result = port; error: - free(host_dup); + curlx_free(host_dup); return result; } @@ -3021,19 +2742,15 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data, ptr++; } else { - /* check whether the URL's hostname matches */ - size_t hostname_to_match_len; - char *hostname_to_match = aprintf("%s%s%s", - conn->bits.ipv6_ip ? "[" : "", - conn->host.name, - conn->bits.ipv6_ip ? "]" : ""); - if(!hostname_to_match) - return CURLE_OUT_OF_MEMORY; - hostname_to_match_len = strlen(hostname_to_match); - host_match = curl_strnequal(ptr, hostname_to_match, - hostname_to_match_len); - free(hostname_to_match); - ptr += hostname_to_match_len; + /* check whether the URL's hostname matches. Use the URL hostname + * when it was an IPv6 address. Otherwise use the connection's hostname + * that has IDN conversion. */ + char *hostname_to_match = + (data->state.up.hostname && data->state.up.hostname[0] == '[') ? + data->state.up.hostname : conn->host.name; + size_t hlen = strlen(hostname_to_match); + host_match = curl_strnequal(ptr, hostname_to_match, hlen); + ptr += hlen; host_match = host_match && *ptr == ':'; ptr++; @@ -3047,7 +2764,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) && @@ -3094,13 +2811,13 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, else { /* no "connect to host" */ conn->bits.conn_to_host = FALSE; - Curl_safefree(host); + curlx_safefree(host); } if(port >= 0) { - conn->conn_to_port = port; + conn->conn_to_port = (uint16_t)port; conn->bits.conn_to_port = TRUE; - infof(data, "Connecting to port: %d", port); + infof(data, "Connecting to port: %u", conn->conn_to_port); } else { /* no "connect to port" */ @@ -3113,7 +2830,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") @@ -3127,6 +2844,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, struct altsvc *as = NULL; int allowed_alpns = ALPN_none; struct http_negotiation *neg = &data->state.http_neg; + bool same_dest = FALSE; DEBUGF(infof(data, "Alt-svc check wanted=%x, allowed=%x", neg->wanted, neg->allowed)); @@ -3150,7 +2868,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, hit = Curl_altsvc_lookup(data->asi, ALPN_h3, host, conn->remote_port, /* from */ &as /* to */, - allowed_alpns); + allowed_alpns, &same_dest); } #endif #ifdef USE_HTTP2 @@ -3160,7 +2878,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, hit = Curl_altsvc_lookup(data->asi, ALPN_h2, host, conn->remote_port, /* from */ &as /* to */, - allowed_alpns); + allowed_alpns, &same_dest); } #endif if(!hit && (neg->wanted & CURL_HTTP_V1x) && @@ -3169,11 +2887,30 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, hit = Curl_altsvc_lookup(data->asi, ALPN_h1, host, conn->remote_port, /* from */ &as /* to */, - allowed_alpns); + allowed_alpns, &same_dest); } - if(hit) { - char *hostd = strdup((char *)as->dst.host); + if(hit && same_dest) { + /* same destination, but more HTTPS version options */ + switch(as->dst.alpnid) { + case ALPN_h1: + neg->wanted |= CURL_HTTP_V1x; + neg->preferred = CURL_HTTP_V1x; + break; + case ALPN_h2: + neg->wanted |= CURL_HTTP_V2x; + neg->preferred = CURL_HTTP_V2x; + break; + case ALPN_h3: + neg->wanted |= CURL_HTTP_V3x; + neg->preferred = CURL_HTTP_V3x; + break; + default: /* should not be possible */ + break; + } + } + else if(hit) { + char *hostd = curlx_strdup(as->dst.host); if(!hostd) return CURLE_OUT_OF_MEMORY; conn->conn_to_host.rawalloc = hostd; @@ -3182,7 +2919,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, conn->conn_to_port = as->dst.port; conn->bits.conn_to_port = TRUE; conn->bits.altused = TRUE; - infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d", + infof(data, "Alt-svc connecting from [%s]%s:%u to [%s]%s:%u", Curl_alpnid2str(srcalpnid), host, conn->remote_port, Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port); if(srcalpnid != as->dst.alpnid) { @@ -3210,157 +2947,50 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, return result; } -#ifdef USE_UNIX_SOCKETS -static CURLcode resolve_unix(struct Curl_easy *data, - struct connectdata *conn, - char *unix_path, - struct Curl_dns_entry **pdns) +static void url_move_hostname(struct hostname *dest, struct hostname *src) { - struct Curl_dns_entry *hostaddr; - bool longpath = FALSE; - - 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. */ - hostaddr = 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); - if(!hostaddr->addr) { - if(longpath) - /* Long paths are not supported for now */ - failf(data, "Unix socket path too long: '%s'", unix_path); - free(hostaddr); - return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY; - } - - hostaddr->refcount = 1; /* connection is the only one holding this */ - *pdns = hostaddr; - return CURLE_OK; -} -#endif - -/************************************************************* - * Resolve the address of the server or proxy - *************************************************************/ -static CURLcode resolve_server(struct Curl_easy *data, - struct connectdata *conn, - bool *async, - struct Curl_dns_entry **pdns) -{ - struct hostname *ehost; - int eport; - timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); - const char *peertype = "host"; - CURLcode result; - - *pdns = NULL; - -#ifdef USE_UNIX_SOCKETS - { - char *unix_path = conn->unix_domain_socket; - -#ifndef CURL_DISABLE_PROXY - if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name && - !strncmp(UNIX_SOCKET_PREFIX"/", - conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX))) - unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1; -#endif - - if(unix_path) { - /* This only works if previous transport is TRNSPRT_TCP. Check it? */ - conn->transport_wanted = TRNSPRT_UNIX; - return resolve_unix(data, conn, unix_path, pdns); - } - } -#endif - -#ifndef CURL_DISABLE_PROXY - if(CONN_IS_PROXIED(conn)) { - ehost = conn->bits.socksproxy ? &conn->socks_proxy.host : - &conn->http_proxy.host; - eport = conn->bits.socksproxy ? conn->socks_proxy.port : - conn->http_proxy.port; - peertype = "proxy"; - } - else -#endif - { - ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host; - /* If not connecting via a proxy, extract the port from the URL, if it is - * there, thus overriding any defaults that might have been set above. */ - eport = conn->bits.conn_to_port ? conn->conn_to_port : conn->remote_port; - } - - /* Resolve target host right on */ - conn->hostname_resolve = strdup(ehost->name); - if(!conn->hostname_resolve) - return CURLE_OUT_OF_MEMORY; - - result = Curl_resolv_timeout(data, conn->hostname_resolve, - eport, conn->ip_version, - pdns, timeout_ms); - DEBUGASSERT(!result || !*pdns); - if(result == CURLE_AGAIN) { - *async = TRUE; - return CURLE_OK; - } - else if(result == CURLE_OPERATION_TIMEDOUT) { - failf(data, "Failed to resolve %s '%s' with timeout after %" - FMT_TIMEDIFF_T " ms", peertype, ehost->dispname, - curlx_timediff(curlx_now(), data->progress.t_startsingle)); - return CURLE_OPERATION_TIMEDOUT; - } - else if(result) { - failf(data, "Could not resolve %s: %s", peertype, ehost->dispname); - return result; - } - DEBUGASSERT(*pdns); - return CURLE_OK; + curlx_safefree(dest->rawalloc); + Curl_free_idnconverted_hostname(dest); + *dest = *src; + memset(src, 0, sizeof(*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 */ - free(existing->user); - 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 */ - free(existing->http_proxy.user); - free(existing->socks_proxy.user); - free(existing->http_proxy.passwd); - 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 @@ -3373,75 +3003,34 @@ 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. */ - Curl_free_idnconverted_hostname(&existing->host); - Curl_free_idnconverted_hostname(&existing->conn_to_host); - Curl_safefree(existing->host.rawalloc); - Curl_safefree(existing->conn_to_host.rawalloc); - existing->host = temp->host; - temp->host.rawalloc = NULL; - temp->host.encalloc = NULL; - existing->conn_to_host = temp->conn_to_host; - temp->conn_to_host.rawalloc = NULL; - existing->conn_to_port = temp->conn_to_port; - existing->remote_port = temp->remote_port; - free(existing->hostname_resolve); - existing->hostname_resolve = temp->hostname_resolve; - temp->hostname_resolve = NULL; + url_move_hostname(&conn->host, &needle->host); + url_move_hostname(&conn->conn_to_host, &needle->conn_to_host); - /* reuse init */ - existing->bits.reuse = TRUE; /* yes, we are reusing here */ - - 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) { (void)p; - /* Will always be FALSE. Cannot use a 0 assert here since compilers + /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ 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 *************************************************************/ - if(!data->state.url) { + if(!Curl_bufref_ptr(&data->state.url)) { result = CURLE_URL_MALFORMAT; goto out; } @@ -3450,40 +3039,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_str, curlx_str_key_compare, conn_meta_freeentry); + Curl_hash_init(&needle->meta_hash, 23, + Curl_hash_str, curlx_str_key_compare, conn_meta_freeentry); - /* GSSAPI related inits */ - Curl_sec_conn_init(conn); - - result = parseurlandfillconn(data, conn); + result = parseurlandfillconn(data, needle); if(result) goto out; if(data->set.str[STRING_SASL_AUTHZID]) { - conn->sasl_authzid = 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 = 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; } @@ -3491,19 +3071,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 = strdup(data->set.str[STRING_UNIX_SOCKET_PATH]); - if(!conn->unix_domain_socket) { + needle->unix_domain_socket = + curlx_strdup(data->set.str[STRING_UNIX_SOCKET_PATH]); + 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; @@ -3511,24 +3092,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; @@ -3536,7 +3117,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; @@ -3544,38 +3125,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 @@ -3583,105 +3165,145 @@ 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; +#ifdef USE_UNIX_SOCKETS + if(Curl_conn_get_unix_path(needle)) + needle->transport_wanted = TRNSPRT_UNIX; +#endif + } + +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); + 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_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)); - - 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 : @@ -3689,43 +3311,39 @@ 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. */ - - 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 != UINT_MAX) + if(data->master_mid != UINT32_MAX) CURL_TRC_M(data, "Allowing sub-requests (like DoH) to override " "max connection limit"); else { - infof(data, "No connections available, total of %ld reached.", + 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: @@ -3733,29 +3351,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(fprintf(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 @@ -3778,43 +3387,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; } @@ -3830,9 +3430,6 @@ CURLcode Curl_setup_conn(struct Curl_easy *data, CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - DEBUGASSERT(dns); - Curl_pgrsTime(data, TIMER_NAMELOOKUP); - if(!conn->bits.reuse) result = Curl_conn_setup(data, conn, FIRSTSOCKET, dns, CURL_CF_SSL_DEFAULT); @@ -3844,56 +3441,45 @@ CURLcode Curl_setup_conn(struct Curl_easy *data, return result; } -CURLcode Curl_connect(struct Curl_easy *data, - bool *asyncp, - bool *protocol_done) +CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected) { CURLcode result; struct connectdata *conn; - bool reused = FALSE; - *asyncp = FALSE; /* assume synchronous resolves by default */ - *protocol_done = FALSE; + *pconnected = FALSE; /* 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) { + if(result) + goto out; + + DEBUGASSERT(conn); + Curl_pgrsTime(data, TIMER_POSTQUEUE); + if(conn->bits.reuse) { + if(conn->attached_xfers > 1) + /* multiplexed */ + *pconnected = TRUE; + } + else if(conn->scheme->flags & PROTOPT_NONETWORK) { + Curl_pgrsTime(data, TIMER_NAMELOOKUP); + *pconnected = TRUE; + } + else { + result = Curl_conn_setup(data, conn, FIRSTSOCKET, NULL, + CURL_CF_SSL_DEFAULT); + if(!result) + result = Curl_headers_init(data); + CURL_TRC_M(data, "Curl_setup_conn() -> %d", result); + } + +out: + if(result == CURLE_NO_CONNECTION_AVAILABLE) DEBUGASSERT(!conn); - return result; - } - - if(!result) { - DEBUGASSERT(conn); - if(reused) { - if(CONN_ATTACHED(conn) > 1) - /* multiplexed */ - *protocol_done = TRUE; - } - else if(conn->handler->flags & PROTOPT_NONETWORK) { - *asyncp = FALSE; - Curl_pgrsTime(data, TIMER_NAMELOOKUP); - *protocol_done = TRUE; - } - else { - /************************************************************* - * Resolve the address of the server or proxy - *************************************************************/ - struct Curl_dns_entry *dns; - result = resolve_server(data, conn, asyncp, &dns); - if(!result) { - *asyncp = !dns; - if(dns) - /* DNS resolution is done: that is either because this is a reused - connection, in which case DNS was unnecessary, or because DNS - really did finish already (synch resolver/fast async resolve) */ - result = Curl_setup_conn(data, dns, protocol_done); - } - } - } if(result && conn) { /* We are not allowed to return failure with memory left allocated in the @@ -3917,7 +3503,6 @@ CURLcode Curl_connect(struct Curl_easy *data, CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) { - /* if this is a pushed stream, we need this: */ CURLcode result; if(conn) { @@ -3925,21 +3510,20 @@ 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; } data->state.done = FALSE; /* *_done() is not called yet */ + data->req.no_body = data->set.opt_no_body; if(data->req.no_body) /* in HTTP lingo, no body means using the HEAD request... */ data->state.httpreq = HTTPREQ_HEAD; result = Curl_req_start(&data->req, data); if(!result) { - Curl_speedinit(data); - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); + Curl_pgrsReset(data); } return result; } @@ -3963,7 +3547,7 @@ static void priority_remove_child(struct Curl_easy *parent, DEBUGASSERT(pnode); if(pnode) { *pnext = pnode->next; - free(pnode); + curlx_free(pnode); } child->set.priority.parent = 0; @@ -3982,7 +3566,7 @@ CURLcode Curl_data_priority_add_child(struct Curl_easy *parent, struct Curl_data_prio_node **tail; struct Curl_data_prio_node *pnode; - pnode = calloc(1, sizeof(*pnode)); + pnode = curlx_calloc(1, sizeof(*pnode)); if(!pnode) return CURLE_OUT_OF_MEMORY; pnode->data = child; @@ -4043,7 +3627,6 @@ void Curl_data_priority_clear_state(struct Curl_easy *data) #endif /* USE_HTTP2 || USE_HTTP3 */ - CURLcode Curl_conn_meta_set(struct connectdata *conn, const char *key, void *meta_data, Curl_meta_dtor *meta_dtor) { @@ -4065,11 +3648,6 @@ void *Curl_conn_meta_get(struct connectdata *conn, const char *key) return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1); } -CURLcode Curl_1st_err(CURLcode r1, CURLcode r2) -{ - return r1 ? r1 : r2; -} - CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2) { if(r1 && (r1 != CURLE_AGAIN)) diff --git a/lib/url.h b/lib/url.h index 11a69d4157..837dbcd2fa 100644 --- a/lib/url.h +++ b/lib/url.h @@ -26,24 +26,24 @@ #include "curl_setup.h" /* - * Prototypes for library-wide functions provided by url.c + * Prototypes for library-wide functions */ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn); CURLcode Curl_open(struct Curl_easy **curl); -CURLcode Curl_init_userdefined(struct Curl_easy *data); +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 *pconnected); 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 @@ -67,43 +67,25 @@ CURLcode Curl_conn_meta_set(struct connectdata *conn, const char *key, void Curl_conn_meta_remove(struct connectdata *conn, const char *key); void *Curl_conn_meta_get(struct connectdata *conn, const char *key); -/* Get protocol handler for a URI scheme - * @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); - #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless specified */ /** * Return TRUE iff the given connection is considered dead. - * @param nowp NULL or pointer to time being checked against. */ bool Curl_conn_seems_dead(struct connectdata *conn, - struct Curl_easy *data, - struct curltime *nowp); + struct Curl_easy *data); /** * Perform upkeep operations on the connection. */ CURLcode Curl_conn_upkeep(struct Curl_easy *data, - struct connectdata *conn, - struct curltime *now); - -/** - * Always eval all arguments, return the first result != CURLE_OK. - * A non-short-circuit evaluation. - */ -CURLcode Curl_1st_err(CURLcode r1, CURLcode r2); + struct connectdata *conn); /** * Always eval all arguments, return the first - * result != (CURLE_OK|CURLE_AGAIN) or `r1`. - * A non-short-circuit evaluation. + * result != (CURLE_OK | CURLE_AGAIN) or `r1`. */ CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2); diff --git a/lib/urlapi-int.h b/lib/urlapi-int.h index fbce1837ff..129ee0481f 100644 --- a/lib/urlapi-int.h +++ b/lib/urlapi-int.h @@ -24,6 +24,34 @@ * ***************************************************************************/ #include "curl_setup.h" +#include + +/* Internal representation of CURLU. Point to URL-encoded strings. */ +struct Curl_URL { + char *scheme; + char *user; + char *password; + char *options; /* IMAP only? */ + char *host; + char *zoneid; /* for numerical IPv6 addresses */ + char *port; + char *path; + char *query; + char *fragment; + unsigned short portnum; /* the numerical version (if 'port' is set) */ + BIT(query_present); /* to support blank */ + BIT(fragment_present); /* to support blank */ + BIT(guessed_scheme); /* when a URL without scheme is parsed */ +}; + +#define HOST_ERROR (-1) /* out of memory */ +#define HOST_NAME 1 +#define HOST_IPV4 2 +#define HOST_IPV6 3 + +#define QUERY_NO 2 +#define QUERY_NOT_YET 3 /* allow to change to query */ +#define QUERY_YES 4 size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, bool guess_scheme); @@ -32,9 +60,7 @@ CURLUcode Curl_url_set_authority(CURLU *u, const char *authority); CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace); -#ifdef UNITTESTS -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/lib/urlapi.c b/lib/urlapi.c index c89852794e..a3036dcf6a 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #include "urldata.h" @@ -29,31 +28,27 @@ #include "strcase.h" #include "url.h" #include "escape.h" -#include "curl_ctype.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" -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - - /* 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] == ':')) - - /* MS-DOS/Windows style drive prefix, optionally with - * a '|' instead of ':', followed by a slash or NUL */ -#define STARTS_WITH_URL_DRIVE_PREFIX(str) \ - ((('a' <= (str)[0] && (str)[0] <= 'z') || \ +#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] == ':' || (str)[1] == '|') && \ + ((str)[1] == ':')) +#endif + +/* MS-DOS/Windows style drive prefix, optionally with + * a '|' instead of ':', followed by a slash or NUL */ +#define STARTS_WITH_URL_DRIVE_PREFIX(str) \ + ((('a' <= (str)[0] && (str)[0] <= 'z') || \ + ('A' <= (str)[0] && (str)[0] <= 'Z')) && \ + ((str)[1] == ':' || (str)[1] == '|') && \ ((str)[2] == '/' || (str)[2] == '\\' || (str)[2] == 0)) /* scheme is not URL encoded, the longest libcurl supported ones are... */ @@ -68,41 +63,20 @@ #define AF_INET6 (AF_INET + 1) #endif -/* Internal representation of CURLU. Point to URL-encoded strings. */ -struct Curl_URL { - char *scheme; - char *user; - char *password; - char *options; /* IMAP only? */ - char *host; - char *zoneid; /* for numerical IPv6 addresses */ - char *port; - char *path; - char *query; - char *fragment; - unsigned short portnum; /* the numerical version (if 'port' is set) */ - BIT(query_present); /* to support blank */ - BIT(fragment_present); /* to support blank */ - BIT(guessed_scheme); /* when a URL without scheme is parsed */ -}; - #define DEFAULT_SCHEME "https" -static CURLUcode parseurl_and_replace(const char *url, CURLU *u, - unsigned int flags); - static void free_urlhandle(struct Curl_URL *u) { - free(u->scheme); - free(u->user); - free(u->password); - free(u->options); - free(u->host); - free(u->zoneid); - free(u->port); - free(u->path); - free(u->query); - free(u->fragment); + curlx_free(u->scheme); + curlx_free(u->user); + curlx_free(u->password); + curlx_free(u->options); + curlx_free(u->host); + curlx_free(u->zoneid); + curlx_free(u->port); + curlx_free(u->path); + curlx_free(u->query); + curlx_free(u->fragment); } /* @@ -126,28 +100,43 @@ 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. * + * This function re-encodes the string, meaning that it leaves already encoded + * bytes as-is and works by encoding only what *has* to be encoded - unless it + * has to uppercase the hex to normalize. + * + * Illegal percent-encoding sequences are left as-is. + * * URL encoding should be skipped for hostnames, otherwise IDN resolution * will fail. + * + * 'query' tells if it is a query part or not, or if it is allowed to + * "transition" into a query part with a question mark. + * + * @unittest: 1675 */ -static CURLUcode urlencode_str(struct dynbuf *o, const char *url, - size_t len, bool relative, - bool query) +UNITTEST CURLUcode urlencode_str(struct dynbuf *o, const char *url, + size_t len, bool relative, + unsigned int query); +UNITTEST CURLUcode urlencode_str(struct dynbuf *o, const char *url, + size_t len, bool relative, + unsigned int query) { /* we must add this with whitespace-replacing */ - bool left = !query; const unsigned char *iptr; - const unsigned char *host_sep = (const unsigned char *) url; + const unsigned char *host_sep = (const unsigned char *)url; CURLcode result = CURLE_OK; + DEBUGASSERT((query >= QUERY_NO) && (query <= QUERY_YES)); + if(!relative) { size_t n; - host_sep = (const unsigned char *) find_host_sep(url); + host_sep = (const unsigned char *)find_host_sep(url); /* output the first piece as-is */ n = (const char *)host_sep - url; @@ -157,20 +146,32 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url, for(iptr = host_sep; len && !result; iptr++, len--) { if(*iptr == ' ') { - if(left) + if(query != QUERY_YES) result = curlx_dyn_addn(o, "%20", 3); else result = curlx_dyn_addn(o, "+", 1); } else if((*iptr < ' ') || (*iptr >= 0x7f)) { - unsigned char out[3]={'%'}; + unsigned char out[3] = { '%' }; Curl_hexbyte(&out[1], *iptr); result = curlx_dyn_addn(o, out, 3); } + else if(*iptr == '%' && (len >= 3) && + ISXDIGIT(iptr[1]) && ISXDIGIT(iptr[2]) && + (ISLOWER(iptr[1]) || ISLOWER(iptr[2]))) { + /* uppercase it */ + unsigned char hex = (unsigned char)((curlx_hexval(iptr[1]) << 4) | + curlx_hexval(iptr[2])); + unsigned char out[3] = { '%' }; + Curl_hexbyte(&out[1], hex); + result = curlx_dyn_addn(o, out, 3); + iptr += 2; + len -= 2; + } else { result = curlx_dyn_addn(o, iptr, 1); - if(*iptr == '?') - left = FALSE; + if(*iptr == '?' && (query == QUERY_NOT_YET)) + query = QUERY_YES; } } @@ -202,7 +203,7 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, if(ISALPHA(url[0])) for(i = 1; i < MAX_SCHEME_LEN; ++i) { char s = url[i]; - if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) { + if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.'))) { /* RFC 3986 3.1 explains: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */ @@ -227,81 +228,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,12 +260,12 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, unsigned int flags, size_t *offset) /* to the hostname */ { - CURLUcode result = CURLUE_OK; + CURLUcode ures = CURLUE_OK; CURLcode ccode; 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 @@ -348,7 +274,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); @@ -364,7 +290,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! */ @@ -373,27 +299,29 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, (h && (h->flags & PROTOPT_URLOPTIONS)) ? &optionsp : NULL); if(ccode) { - result = CURLUE_BAD_LOGIN; + /* the only possible error from Curl_parse_login_details is out of + memory: */ + ures = CURLUE_OUT_OF_MEMORY; goto out; } if(userp) { if(flags & CURLU_DISALLOW_USER) { /* Option DISALLOW_USER is set and URL contains username. */ - result = CURLUE_USER_NOT_ALLOWED; + ures = CURLUE_USER_NOT_ALLOWED; goto out; } - free(u->user); + curlx_free(u->user); u->user = userp; } if(passwdp) { - free(u->password); + curlx_free(u->password); u->password = passwdp; } if(optionsp) { - free(u->options); + curlx_free(u->options); u->options = optionsp; } @@ -403,21 +331,23 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, out: - free(userp); - free(passwdp); - free(optionsp); + curlx_free(userp); + curlx_free(passwdp); + curlx_free(optionsp); u->user = NULL; u->password = NULL; u->options = NULL; - return result; + return ures; } +UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, + bool has_scheme); 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. */ @@ -442,8 +372,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! @@ -456,10 +386,10 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, if(curlx_str_number(&portptr, &port, 0xffff) || *portptr) return CURLUE_BAD_PORT_NUMBER; - u->portnum = (unsigned short) port; + u->portnum = (unsigned short)port; /* generate a new port number string to get rid of leading zeroes etc */ - free(u->port); - u->port = aprintf("%" CURL_FORMAT_CURL_OFF_T, port); + curlx_free(u->port); + u->port = curl_maprintf("%" CURL_FORMAT_CURL_OFF_T, port); if(!u->port) return CURLUE_OUT_OF_MEMORY; } @@ -467,9 +397,15 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, return CURLUE_OK; } -/* this assumes 'hostname' now starts with [ */ -static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, - size_t hlen) /* length of hostname */ +/* This function assumes 'hostname' now starts with [. It trims 'hostname' in + * place and it sets u->zoneid if present. + * + * @unittest: 1675 + */ +UNITTEST CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, + size_t hlen); +UNITTEST CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, + size_t hlen) /* length of hostname */ { size_t len; DEBUGASSERT(*hostname == '['); @@ -496,7 +432,7 @@ static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, if(!i || (']' != *h)) return CURLUE_BAD_IPV6; zoneid[i] = 0; - u->zoneid = strdup(zoneid); + u->zoneid = curlx_strdup(zoneid); if(!u->zoneid) return CURLUE_OUT_OF_MEMORY; hostname[len] = ']'; /* insert end bracket */ @@ -513,7 +449,7 @@ static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, hostname[hlen] = 0; /* end the address there */ if(curlx_inet_pton(AF_INET6, hostname, dest) != 1) return CURLUE_BAD_IPV6; - if(curlx_inet_ntop(AF_INET6, dest, hostname, hlen)) { + if(curlx_inet_ntop(AF_INET6, dest, hostname, hlen + 1)) { hlen = strlen(hostname); /* might be shorter now */ hostname[hlen + 1] = 0; } @@ -522,8 +458,10 @@ static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, return CURLUE_OK; } -static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, - size_t hlen) /* length of hostname */ +UNITTEST CURLUcode hostname_check(struct Curl_URL *u, char *hostname, + size_t hlen); +UNITTEST CURLUcode hostname_check(struct Curl_URL *u, char *hostname, + size_t hlen) /* length of hostname */ { size_t len; DEBUGASSERT(hostname); @@ -553,20 +491,17 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, * integers. * * Returns the host type. + * + * @unittest: 1675 */ -#define HOST_ERROR -1 /* out of memory */ - -#define HOST_NAME 1 -#define HOST_IPV4 2 -#define HOST_IPV6 3 - -static int ipv4_normalize(struct dynbuf *host) +UNITTEST int ipv4_normalize(struct dynbuf *host); +UNITTEST int ipv4_normalize(struct dynbuf *host) { bool done = FALSE; int n = 0; const char *c = curlx_dyn_ptr(host); - unsigned int parts[4] = {0, 0, 0, 0}; + unsigned int parts[4] = { 0, 0, 0, 0 }; CURLcode result = CURLE_OK; if(*c == '[') @@ -658,7 +593,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) @@ -674,7 +609,7 @@ static CURLUcode urldecode_host(struct dynbuf *host) return CURLUE_BAD_HOSTNAME; curlx_dyn_reset(host); result = curlx_dyn_addn(host, decoded, dlen); - free(decoded); + curlx_free(decoded); if(result) return cc2cu(result); } @@ -738,21 +673,21 @@ out: /* used for HTTP/2 server push */ CURLUcode Curl_url_set_authority(CURLU *u, const char *authority) { - CURLUcode result; + CURLUcode ures; struct dynbuf host; DEBUGASSERT(authority); curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH); - result = parse_authority(u, authority, strlen(authority), - CURLU_DISALLOW_USER, &host, !!u->scheme); - if(result) + ures = parse_authority(u, authority, strlen(authority), + CURLU_DISALLOW_USER, &host, !!u->scheme); + if(ures) curlx_dyn_free(&host); else { - free(u->host); + curlx_free(u->host); u->host = curlx_dyn_ptr(&host); } - return result; + return ures; } /* @@ -799,38 +734,36 @@ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) struct dynbuf out; CURLcode result = CURLE_OK; + /* variables for leading dot checks */ + const char *dinput = input; + size_t dlen = clen; + *outp = NULL; - /* the path always starts with a slash, and a slash has not dot */ + /* a single byte path cannot be cleaned up */ if(clen < 2) return 0; curlx_dyn_init(&out, clen + 1); - /* A. If the input buffer begins with a prefix of "../" or "./", then - remove that prefix from the input buffer; otherwise, */ - if(is_dot(&input, &clen)) { - const char *p = input; - size_t blen = clen; - - if(!clen) - /* . [end] */ - goto end; - else if(ISSLASH(*p)) { + /* if the input buffer begins with a prefix of "../" or "./", then remove + that prefix from the input buffer; otherwise, */ + if(is_dot(&dinput, &dlen)) { + if(ISSLASH(*dinput)) { /* one dot followed by a slash */ - input = p + 1; - clen--; + input = dinput + 1; + clen = dlen - 1; } - /* D. if the input buffer consists only of "." or "..", then remove - that from the input buffer; otherwise, */ - else if(is_dot(&p, &blen)) { - if(!blen) + /* if the input buffer consists only of "." or "..", then remove + that from the input buffer; otherwise, */ + else if(is_dot(&dinput, &dlen)) { + if(!dlen) /* .. [end] */ goto end; - else if(ISSLASH(*p)) { + else if(ISSLASH(*dinput)) { /* ../ */ - input = p + 1; - clen = blen - 1; + input = dinput + 1; + clen = dlen - 1; } } } @@ -839,9 +772,9 @@ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) if(ISSLASH(*input)) { const char *p = &input[1]; size_t blen = clen - 1; - /* B. if the input buffer begins with a prefix of "/./" or "/.", where - "." is a complete path segment, then replace that prefix with "/" in - the input buffer; otherwise, */ + /* if the input buffer begins with a prefix of "/./" or "/.", where "." + is a complete path segment, then replace that prefix with "/" in the + input buffer; otherwise, */ if(is_dot(&p, &blen)) { if(!blen) { /* /. */ result = curlx_dyn_addn(&out, "/", 1); @@ -853,16 +786,16 @@ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) continue; } - /* C. if the input buffer begins with a prefix of "/../" or "/..", - where ".." is a complete path segment, then replace that prefix - with "/" in the input buffer and remove the last segment and its - preceding "/" (if any) from the output buffer; otherwise, */ + /* if the input buffer begins with a prefix of "/../" or "/..", where + ".." is a complete path segment, then replace that prefix with "/" + in the input buffer and remove the last segment and its preceding + "/" (if any) from the output buffer; otherwise, */ else if(is_dot(&p, &blen) && (ISSLASH(*p) || !blen)) { /* 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); @@ -879,10 +812,10 @@ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) } } - /* E. move the first path segment in the input buffer to the end of - the output buffer, including the initial "/" character (if any) and - any subsequent characters up to, but not including, the next "/" - character or the end of the input buffer. */ + /* move the first path segment in the input buffer to the end of the + output buffer, including the initial "/" character (if any) and any + subsequent characters up to, but not including, the next "/" character + or the end of the input buffer. */ result = curlx_dyn_addn(&out, input, 1); input++; @@ -893,7 +826,7 @@ end: if(curlx_dyn_len(&out)) *outp = curlx_dyn_ptr(&out); else { - *outp = strdup(""); + *outp = curlx_strdup(""); if(!*outp) return 1; } @@ -901,197 +834,299 @@ end: return result ? 1 : 0; /* success */ } +/* + * @unittest: 1675 + */ +UNITTEST CURLUcode parse_file(const char *url, size_t urllen, CURLU *u, + const char **pathp, size_t *pathlenp); +UNITTEST CURLUcode parse_file(const char *url, size_t urllen, CURLU *u, + const char **pathp, size_t *pathlenp) +{ + const char *path; + size_t pathlen; + if(urllen <= 6) + /* file:/ is not enough to actually be a complete file: URL */ + return CURLUE_BAD_FILE_URL; + + /* 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; + + /* 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]; + + /* + * 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 + * + * 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 + /* Invalid file://hostname/, expected localhost or 127.0.0.1 or + none */ + return CURLUE_BAD_FILE_URL; + } + + path = ptr; + pathlen = urllen - (ptr - url); + } + +#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 */ + 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--; + } +#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 { + /* no scheme! */ + + if(!(flags & (CURLU_DEFAULT_SCHEME | CURLU_GUESS_SCHEME))) + return CURLUE_BAD_SCHEME; + + if(flags & CURLU_DEFAULT_SCHEME) + schemep = DEFAULT_SCHEME; + + /* + * The URL was badly formatted, let's try without scheme specified. + */ + *hostpp = url; + } + + if(schemep) { + u->scheme = curlx_strdup(schemep); + if(!u->scheme) + return CURLUE_OUT_OF_MEMORY; + } + return CURLUE_OK; +} + +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"; + + u->scheme = curlx_strdup(schemep); + if(!u->scheme) + return CURLUE_OUT_OF_MEMORY; + + u->guessed_scheme = TRUE; + return CURLUE_OK; +} + +static CURLUcode handle_fragment(CURLU *u, const char *fragment, + size_t fraglen, unsigned int flags) +{ + CURLUcode ures; + 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); + ures = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, QUERY_NO); + if(ures) + return ures; + u->fragment = curlx_dyn_ptr(&enc); + } + else { + u->fragment = curlx_memdup0(fragment + 1, fraglen - 1); + if(!u->fragment) + return CURLUE_OUT_OF_MEMORY; + } + } + return CURLUE_OK; +} + +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 ures; + curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + /* skip the leading question mark */ + ures = urlencode_str(&enc, query + 1, qlen - 1, TRUE, QUERY_YES); + if(ures) + return ures; + u->query = curlx_dyn_ptr(&enc); + } + else { + 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, + bool is_file) +{ + CURLUcode ures; + if(pathlen && (flags & CURLU_URLENCODE)) { + struct dynbuf enc; + curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + ures = urlencode_str(&enc, path, pathlen, TRUE, QUERY_NO); + if(ures) + return ures; + pathlen = curlx_dyn_len(&enc); + path = u->path = curlx_dyn_ptr(&enc); + } + + if(pathlen >= (size_t)(1 + !is_file)) { + /* paths for file:// scheme can be one byte, others need to be two */ + if(!u->path) { + 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 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) + 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 *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; + CURLUcode ures = CURLUE_OK; struct dynbuf host; + bool is_file = FALSE; DEBUGASSERT(url); curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH); - result = Curl_junkscan(url, &urllen, !!(flags & CURLU_ALLOW_SPACE)); - if(result) + ures = Curl_junkscan(url, &urllen, !!(flags & CURLU_ALLOW_SPACE)); + if(ures) goto fail; schemelen = Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf), - flags & (CURLU_GUESS_SCHEME| + 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; - - u->scheme = 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]; - - /* - * 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 { -#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; - } - - 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; -#endif - } - } - - 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; - } -#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--; - } -#endif - + is_file = TRUE; + ures = parse_file(url, urllen, u, &path, &pathlen); } else { - /* clear path */ - const char *schemep = NULL; - const char *hostp; + const char *hostp = NULL; size_t hostlen; - - 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((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|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. - */ - hostp = url; - } - - if(schemep) { - u->scheme = strdup(schemep); - if(!u->scheme) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - } + ures = parse_scheme(url, u, schemebuf, schemelen, flags, &hostp); + if(ures) + goto fail; /* find the end of the hostname + port number */ hostlen = strcspn(hostp, "/?#"); @@ -1100,159 +1135,49 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) /* this pathlen also contains the query and the fragment */ pathlen = urllen - (path - url); if(hostlen) { - - result = parse_authority(u, hostp, hostlen, flags, &host, schemelen); - 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 = strdup(schemep); - if(!u->scheme) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } - u->guessed_scheme = TRUE; - } + ures = parse_authority(u, hostp, hostlen, flags, &host, + u->scheme != NULL); + if(!ures && (flags & CURLU_GUESS_SCHEME) && !u->scheme) + ures = guess_scheme(u, &host); } else if(flags & CURLU_NO_AUTHORITY) { /* allowed to be empty. */ - if(curlx_dyn_add(&host, "")) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } + if(curlx_dyn_add(&host, "")) + ures = CURLUE_OUT_OF_MEMORY; } - else { - result = CURLUE_NO_HOST; - goto fail; + else + ures = CURLUE_NO_HOST; + } + if(!ures) { + /* 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); + ures = handle_fragment(u, fragment, fraglen, flags); + /* after this, pathlen still contains the query */ + pathlen -= fraglen; } } - - 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; - } - } - } - /* after this, pathlen still contains the query */ - pathlen -= fraglen; - } - - 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; - } - } - } - else { - /* single byte query */ - u->query = strdup(""); - if(!u->query) { - result = CURLUE_OUT_OF_MEMORY; - goto fail; - } + if(!ures) { + const char *query = memchr(path, '?', pathlen); + if(query) { + size_t qlen = pathlen - (query - path); + ures = handle_query(u, query, qlen, flags); + pathlen -= qlen; } } - - 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; - pathlen = curlx_dyn_len(&enc); - path = u->path = curlx_dyn_ptr(&enc); + if(!ures) + /* the fragment and query parts are trimmed off from the path */ + ures = handle_path(u, path, pathlen, flags, is_file); + if(!ures) { + u->host = curlx_dyn_ptr(&host); + return CURLUE_OK; } - - if(pathlen <= 1) { - /* there is no path left or just 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; - } - path = u->path; - } - else if(flags & CURLU_URLENCODE) - /* it might have encoded more than just 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(dedot) { - free(u->path); - u->path = dedot; - } - } - } - - u->host = curlx_dyn_ptr(&host); - - return result; fail: curlx_dyn_free(&host); free_urlhandle(u); - return result; + return ures; } /* @@ -1261,44 +1186,120 @@ fail: static CURLUcode parseurl_and_replace(const char *url, CURLU *u, unsigned int flags) { - CURLUcode result; + CURLUcode ures; CURLU tmpurl; memset(&tmpurl, 0, sizeof(tmpurl)); - result = parseurl(url, &tmpurl, flags); - if(!result) { + ures = parseurl(url, &tmpurl, flags); + if(!ures) { free_urlhandle(u); *u = tmpurl; } - return result; + return ures; +} + +/* + * 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, + QUERY_NOT_YET)) { + 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) { - return calloc(1, sizeof(struct Curl_URL)); + return curlx_calloc(1, sizeof(struct Curl_URL)); } void curl_url_cleanup(CURLU *u) { if(u) { free_urlhandle(u); - free(u); + curlx_free(u); } } #define DUP(dest, src, name) \ do { \ - if(src->name) { \ - dest->name = strdup(src->name); \ - if(!dest->name) \ + if((src)->name) { \ + (dest)->name = curlx_strdup((src)->name); \ + if(!(dest)->name) \ goto fail; \ } \ } while(0) CURLU *curl_url_dup(const CURLU *in) { - struct Curl_URL *u = calloc(1, sizeof(struct Curl_URL)); + struct Curl_URL *u = curlx_calloc(1, sizeof(struct Curl_URL)); if(u) { DUP(u, in, scheme); DUP(u, in, user); @@ -1321,8 +1322,8 @@ fail: } #ifndef USE_IDN -#define host_decode(x,y) CURLUE_LACKS_IDN -#define host_encode(x,y) CURLUE_LACKS_IDN +#define host_decode(x, y) CURLUE_LACKS_IDN +#define host_encode(x, y) CURLUE_LACKS_IDN #else static CURLUcode host_decode(const char *host, char **allochost) { @@ -1344,20 +1345,22 @@ static CURLUcode host_encode(const char *host, char **allochost) #endif static CURLUcode urlget_format(const CURLU *u, CURLUPart what, - const char *ptr, char **part, + const char *ptr, char **partp, bool plusdecode, unsigned int flags) { + CURLUcode uc = CURLUE_OK; size_t partlen = strlen(ptr); bool urldecode = (flags & CURLU_URLDECODE) ? 1 : 0; bool urlencode = (flags & CURLU_URLENCODE) ? 1 : 0; bool punycode = (flags & CURLU_PUNYCODE) && (what == CURLUPART_HOST); bool depunyfy = (flags & CURLU_PUNY2IDN) && (what == CURLUPART_HOST); - *part = Curl_memdup0(ptr, partlen); - if(!*part) + char *part = curlx_memdup0(ptr, partlen); + *partp = NULL; + if(!part) return CURLUE_OUT_OF_MEMORY; if(plusdecode) { /* convert + to space */ - char *plus = *part; + char *plus = part; size_t i = 0; for(i = 0; i < partlen; ++plus, i++) { if(*plus == '+') @@ -1369,46 +1372,44 @@ static CURLUcode urlget_format(const CURLU *u, CURLUPart what, size_t dlen; /* this unconditional rejection of control bytes is documented API behavior */ - CURLcode res = Curl_urldecode(*part, 0, &decoded, &dlen, REJECT_CTRL); - free(*part); - if(res) { - *part = NULL; + CURLcode res = Curl_urldecode(part, partlen, &decoded, &dlen, REJECT_CTRL); + curlx_free(part); + if(res) return CURLUE_URLDECODE; - } - *part = decoded; + part = decoded; partlen = dlen; } if(urlencode) { struct dynbuf enc; - CURLUcode uc; curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - uc = urlencode_str(&enc, *part, partlen, TRUE, what == CURLUPART_QUERY); + uc = urlencode_str(&enc, part, partlen, TRUE, what == CURLUPART_QUERY ? + QUERY_YES : QUERY_NO); + curlx_free(part); if(uc) return uc; - free(*part); - *part = curlx_dyn_ptr(&enc); + part = curlx_dyn_ptr(&enc); } else if(punycode) { if(!Curl_is_ASCII_name(u->host)) { - char *allochost = NULL; - CURLUcode ret = host_decode(*part, &allochost); - if(ret) - return ret; - free(*part); - *part = allochost; + char *punyversion = NULL; + uc = host_decode(part, &punyversion); + curlx_free(part); + if(uc) + return uc; + part = punyversion; } } else if(depunyfy) { if(Curl_is_ASCII_name(u->host)) { - char *allochost = NULL; - CURLUcode ret = host_encode(*part, &allochost); - if(ret) - return ret; - free(*part); - *part = allochost; + char *unpunified = NULL; + uc = host_encode(part, &unpunified); + curlx_free(part); + if(uc) + return uc; + part = unpunified; } } - + *partp = part; return CURLUE_OK; } @@ -1428,17 +1429,17 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) bool urlencode = (flags & CURLU_URLENCODE) ? 1 : 0; char portbuf[7]; if(u->scheme && curl_strequal("file", u->scheme)) { - url = aprintf("file://%s%s%s%s%s", - u->path, - show_query ? "?": "", - u->query ? u->query : "", - show_fragment ? "#": "", - u->fragment ? u->fragment : ""); + url = curl_maprintf("file://%s%s%s%s%s", + u->path, + show_query ? "?" : "", + u->query ? u->query : "", + 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; @@ -1447,12 +1448,12 @@ 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 */ if(h) { - msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); + curl_msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); port = portbuf; } } @@ -1500,27 +1501,27 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) } if(!(flags & CURLU_NO_GUESS_SCHEME) || !u->guessed_scheme) - msnprintf(schemebuf, sizeof(schemebuf), "%s://", scheme); + curl_msnprintf(schemebuf, sizeof(schemebuf), "%s://", scheme); else schemebuf[0] = 0; - url = aprintf("%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 : "", - options ? ";" : "", - options ? options : "", - (u->user || u->password || options) ? "@": "", - allochost ? allochost : u->host, - port ? ":": "", - port ? port : "", - u->path ? u->path : "/", - show_query ? "?": "", - u->query ? u->query : "", - show_fragment ? "#": "", - u->fragment ? u->fragment : ""); - free(allochost); + 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 : "", + options ? ";" : "", + options ? options : "", + (u->user || u->password || options) ? "@" : "", + allochost ? allochost : u->host, + port ? ":" : "", + port ? port : "", + u->path ? u->path : "/", + show_query ? "?" : "", + u->query ? u->query : "", + show_fragment ? "#" : "", + u->fragment ? u->fragment : ""); + curlx_free(allochost); } if(!url) return CURLUE_OUT_OF_MEMORY; @@ -1545,7 +1546,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; @@ -1572,20 +1573,20 @@ 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) { - msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); + curl_msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); ptr = portbuf; } } 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; @@ -1627,18 +1628,19 @@ 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 / "+" / "-" / "." ) */ + s++; while(--plen) { if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.')) s++; /* fine */ @@ -1663,10 +1665,10 @@ static CURLUcode set_url_port(CURLU *u, const char *provided_port) if(curlx_str_number(&provided_port, &port, 0xffff) || *provided_port) /* weirdly provided number, not good! */ return CURLUE_BAD_PORT_NUMBER; - tmp = aprintf("%" CURL_FORMAT_CURL_OFF_T, port); + tmp = curl_maprintf("%" CURL_FORMAT_CURL_OFF_T, port); if(!tmp) return CURLUE_OUT_OF_MEMORY; - free(u->port); + curlx_free(u->port); u->port = tmp; u->portnum = (unsigned short)port; return CURLUE_OK; @@ -1687,26 +1689,35 @@ static CURLUcode set_url(CURLU *u, const char *url, size_t part_size, if(!part_size) { /* a blank URL is not a valid URL unless we already have a complete one and this is a redirect */ - if(!curl_url_get(u, CURLUPART_URL, &oldurl, flags)) { + uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags); + if(!uc) { /* success, meaning the "" is a fine relative URL, but nothing changes */ - free(oldurl); + curlx_free(oldurl); return CURLUE_OK; } + if(uc == CURLUE_OUT_OF_MEMORY) + return uc; return CURLUE_MALFORMED_INPUT; } - /* if the new thing is absolute or the old one is not (we could not get an - * absolute URL in 'oldurl'), then replace the existing with the new. */ + /* if the new URL is absolute replace the existing with the new. */ if(Curl_is_absolute_url(url, NULL, 0, - flags & (CURLU_GUESS_SCHEME|CURLU_DEFAULT_SCHEME)) - || curl_url_get(u, CURLUPART_URL, &oldurl, flags)) { + flags & (CURLU_GUESS_SCHEME | CURLU_DEFAULT_SCHEME))) return parseurl_and_replace(url, u, flags); - } + + /* if the old URL is incomplete (we cannot get an absolute URL in + 'oldurl'), replace the existing with the new */ + uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags); + if(uc == CURLUE_OUT_OF_MEMORY) + return uc; + else if(uc) + return parseurl_and_replace(url, u, flags); + DEBUGASSERT(oldurl); /* it is set here */ /* apply the relative part to create a new URL */ uc = redirect_url(oldurl, url, u, flags); - free(oldurl); + curlx_free(oldurl); return uc; } @@ -1718,37 +1729,37 @@ static CURLUcode urlset_clear(CURLU *u, CURLUPart what) memset(u, 0, sizeof(struct Curl_URL)); break; case CURLUPART_SCHEME: - Curl_safefree(u->scheme); + curlx_safefree(u->scheme); u->guessed_scheme = FALSE; break; case CURLUPART_USER: - Curl_safefree(u->user); + curlx_safefree(u->user); break; case CURLUPART_PASSWORD: - Curl_safefree(u->password); + curlx_safefree(u->password); break; case CURLUPART_OPTIONS: - Curl_safefree(u->options); + curlx_safefree(u->options); break; case CURLUPART_HOST: - Curl_safefree(u->host); + curlx_safefree(u->host); break; case CURLUPART_ZONEID: - Curl_safefree(u->zoneid); + curlx_safefree(u->zoneid); break; case CURLUPART_PORT: u->portnum = 0; - Curl_safefree(u->port); + curlx_safefree(u->port); break; case CURLUPART_PATH: - Curl_safefree(u->path); + curlx_safefree(u->path); break; case CURLUPART_QUERY: - Curl_safefree(u->query); + curlx_safefree(u->query); u->query_present = FALSE; break; case CURLUPART_FRAGMENT: - Curl_safefree(u->fragment); + curlx_safefree(u->fragment); u->fragment_present = FALSE; break; default: @@ -1760,11 +1771,24 @@ static CURLUcode urlset_clear(CURLU *u, CURLUPart what) static bool allowed_in_path(unsigned char x) { switch(x) { - case '!': case '$': case '&': case '\'': - case '(': case ')': case '{': case '}': - case '[': case ']': case '*': case '+': - case ',': case ';': case '=': case ':': - case '@': case '/': + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '{': + case '}': + case '[': + case ']': + case '*': + case '+': + case ',': + case ';': + case '=': + case ':': + case '@': + case '/': return TRUE; } return FALSE; @@ -1813,7 +1837,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, break; case CURLUPART_HOST: storep = &u->host; - Curl_safefree(u->zoneid); + curlx_safefree(u->zoneid); break; case CURLUPART_ZONEID: storep = &u->zoneid; @@ -1845,7 +1869,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); @@ -1873,7 +1897,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, return cc2cu(result); } else { - unsigned char out[3]={'%'}; + unsigned char out[3] = { '%' }; Curl_hexbyte(&out[1], *i); result = curlx_dyn_addn(&enc, out, 3); if(result) @@ -1906,7 +1930,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, none is present at the end of the existing query already */ size_t querylen = u->query ? strlen(u->query) : 0; - bool addamperand = querylen && (u->query[querylen -1] != '&'); + bool addamperand = querylen && (u->query[querylen - 1] != '&'); if(querylen) { struct dynbuf qbuf; curlx_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH); @@ -1921,7 +1945,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, if(curlx_dyn_add(&qbuf, newp)) goto nomem; curlx_dyn_free(&enc); - free(*storep); + curlx_free(*storep); *storep = curlx_dyn_ptr(&qbuf); return CURLUE_OK; nomem: @@ -1940,7 +1964,7 @@ nomem: if(!n) bad = TRUE; /* empty hostname is not okay */ else if(!urlencode) { - /* if the host name part was not URL encoded here, it was set ready + /* if the hostname part was not URL encoded here, it was set ready URL encoded so we need to decode it to check */ size_t dlen; char *decoded = NULL; @@ -1948,7 +1972,7 @@ nomem: Curl_urldecode(newp, n, &decoded, &dlen, REJECT_CTRL); if(result || hostname_check(u, decoded, dlen)) bad = TRUE; - free(decoded); + curlx_free(decoded); } else if(hostname_check(u, (char *)CURL_UNCONST(newp), n)) bad = TRUE; @@ -1959,7 +1983,7 @@ nomem: } } - free(*storep); + curlx_free(*storep); *storep = (char *)CURL_UNCONST(newp); } return CURLUE_OK; diff --git a/lib/urldata.h b/lib/urldata.h index cf181af641..f83c006e72 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -23,79 +23,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* This file is for lib internal stuff */ - #include "curl_setup.h" -#define PORT_FTP 21 -#define PORT_FTPS 990 -#define PORT_TELNET 23 -#define PORT_HTTP 80 -#define PORT_HTTPS 443 -#define PORT_DICT 2628 -#define PORT_LDAP 389 -#define PORT_LDAPS 636 -#define PORT_TFTP 69 -#define PORT_SSH 22 -#define PORT_IMAP 143 -#define PORT_IMAPS 993 -#define PORT_POP3 110 -#define PORT_POP3S 995 -#define PORT_SMB 445 -#define PORT_SMBS 445 -#define PORT_SMTP 25 -#define PORT_SMTPS 465 /* sometimes called SSMTP */ -#define PORT_RTSP 554 -#define PORT_RTMP 1935 -#define PORT_RTMPT PORT_HTTP -#define PORT_RTMPS PORT_HTTPS -#define PORT_GOPHER 70 -#define PORT_MQTT 1883 - -struct curl_trc_featt; - -#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) -#endif - -#ifndef CURL_DISABLE_WEBSOCKETS -/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number, - * the rest are internal information. If we use higher bits we only do this on - * platforms that have a >= 64-bit type and then we use such a type for the - * protocol fields in the protocol handler. - */ -#define CURLPROTO_WS (1L<<30) -#define CURLPROTO_WSS ((curl_prot_t)1<<31) -#else -#define CURLPROTO_WS 0L -#define CURLPROTO_WSS 0L -#endif - -/* the default protocols accepting a redirect to */ -#define CURLPROTO_REDIR (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | \ - CURLPROTO_FTPS) - -/* This should be undefined once we need bit 32 or higher */ -#define PROTO_TYPE_SMALL - -#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 CURL_DEFAULT_USER "anonymous" +#define CURL_DEFAULT_USER "anonymous" #define CURL_DEFAULT_PASSWORD "ftp@example.com" #if !defined(_WIN32) && !defined(MSDOS) @@ -104,40 +35,15 @@ typedef unsigned int curl_prot_t; #define CURL_PREFER_LF_LINEENDS #endif -/* Convenience defines for checking protocols or their SSL based version. Each - protocol handler should only ever have a single CURLPROTO_ in its protocol - field. */ -#define PROTO_FAMILY_HTTP (CURLPROTO_HTTP|CURLPROTO_HTTPS|CURLPROTO_WS| \ - CURLPROTO_WSS) -#define PROTO_FAMILY_FTP (CURLPROTO_FTP|CURLPROTO_FTPS) -#define PROTO_FAMILY_POP3 (CURLPROTO_POP3|CURLPROTO_POP3S) -#define PROTO_FAMILY_SMB (CURLPROTO_SMB|CURLPROTO_SMBS) -#define PROTO_FAMILY_SMTP (CURLPROTO_SMTP|CURLPROTO_SMTPS) -#define PROTO_FAMILY_SSH (CURLPROTO_SCP|CURLPROTO_SFTP) - -#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) || \ - !defined(CURL_DISABLE_POP3) || !defined(CURL_DISABLE_FILE) -/* these protocols support CURLOPT_DIRLISTONLY */ -#define CURL_LIST_ONLY_PROTOCOL 1 -#endif - #define DEFAULT_CONNCACHE_SIZE 5 /* length of longest IPv6 address string including the trailing null */ #define MAX_IPADR_LEN sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") -/* Default FTP/IMAP etc response timeout in milliseconds */ -#define RESP_TIMEOUT (120*1000) - /* Max string input length is a precaution against abuse and to detect junk 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 @@ -147,21 +53,25 @@ typedef unsigned int curl_prot_t; #include "curlx/timeval.h" -#include - +#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" #include "splay.h" #include "curlx/dynbuf.h" +#include "bufref.h" #include "dynhds.h" #include "request.h" +#include "ratelimit.h" #include "netrc.h" /* On error return, the value of `pnwritten` has no meaning */ typedef CURLcode (Curl_send)(struct Curl_easy *data, /* transfer */ int sockindex, /* socketindex */ - const void *buf, /* data to write */ + const uint8_t *buf, /* data to write */ size_t len, /* amount to send */ bool eos, /* last chunk */ size_t *pnwritten); /* how much sent */ @@ -174,13 +84,9 @@ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ size_t *pnread); /* how much received */ #include "mime.h" -#include "imap.h" -#include "smtp.h" +#include "protocol.h" #include "ftp.h" -#include "file.h" -#include "vssh/ssh.h" #include "http.h" -#include "rtsp.h" #include "smb.h" #include "mqtt.h" #include "ftplistparser.h" @@ -188,16 +94,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_GSSAPI_H -# include -# else -# include -# endif -# ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H -# include -# 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,8 +122,8 @@ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ larger buffers can help further, but this is deemed a fair memory/speed compromise. */ #define UPLOADBUFFER_DEFAULT 65536 -#define UPLOADBUFFER_MAX (2*1024*1024) -#define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE +#define UPLOADBUFFER_MAX (2 * 1024 * 1024) +#define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE #define CURLEASY_MAGIC_NUMBER 0xc0dedbadU #ifdef DEBUGBUILD @@ -227,38 +131,15 @@ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ * are not NULL, but no longer have the MAGIC touch. This gives * 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)) + (((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) ? TRUE : \ + (DEBUGASSERT(!(x)), FALSE)) #else #define GOOD_EASY_HANDLE(x) \ ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) #endif -#ifdef HAVE_GSSAPI -/* Types needed for krb5-ftp connections */ -struct krb5buffer { - struct dynbuf buf; - size_t index; - BIT(eof_flag); -}; - -enum protection_level { - PROT_NONE, /* first in list */ - PROT_CLEAR, - PROT_SAFE, - PROT_CONFIDENTIAL, - PROT_PRIVATE, - PROT_CMD, - PROT_LAST /* last in list */ -}; -#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 dir (does not work on Windows) */ + char *CApath; /* certificate directory (does not work on Windows) */ char *CAfile; /* certificate to verify peer against */ char *issuercert; /* optional issuer certificate filename */ char *clientcert; @@ -275,9 +156,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 */ @@ -289,21 +170,24 @@ struct ssl_config_data { long certverifyresult; /* result from the certificate verification */ curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */ void *fsslctxp; /* parameter for call back */ - char *cert_type; /* format for certificate (default: PEM)*/ + char *cert_type; /* format for certificate (default: PEM) */ char *key; /* private key filename */ struct curl_blob *key_blob; char *key_type; /* format for private key (default: PEM) */ char *key_passwd; /* plain text private key password */ BIT(certinfo); /* gather lots of certificate info */ - BIT(earlydata); /* use tls1.3 early data */ + BIT(earlydata); /* use TLS 1.3 early data */ BIT(enable_beast); /* allow this flaw for interoperability's sake */ BIT(no_revoke); /* disable SSL certificate revocation checks */ BIT(no_partialchain); /* do not accept partial certificate chains */ BIT(revoke_best_effort); /* ignore SSL revocation offline/missing revocation list errors */ - BIT(native_ca_store); /* use the native ca store of operating system */ + BIT(native_ca_store); /* use the native CA store of operating system */ BIT(auto_client_cert); /* automatically locate and use a client certificate for authentication (Schannel) */ + BIT(custom_cafile); /* application has set custom CA file */ + BIT(custom_capath); /* application has set custom CA path */ + BIT(custom_cablob); /* application has set custom CA blob */ }; struct ssl_general_config { @@ -333,7 +217,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 @@ -359,13 +243,14 @@ 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 /* * Boolean values that concerns this connection. */ struct ConnectBits { + BIT(connect_only); #ifndef CURL_DISABLE_PROXY BIT(httpproxy); /* if set, this transfer is done through an HTTP proxy */ BIT(socksproxy); /* if set, this transfer is done through a socks proxy */ @@ -384,8 +269,6 @@ struct ConnectBits { that overrides the host in the URL */ BIT(conn_to_port); /* if set, this connection has a "connect to port" that overrides the port in the URL (remote port) */ - BIT(ipv6_ip); /* we communicate with a remote site specified with pure IPv6 - IP address */ BIT(ipv6); /* we communicate with a site using an IPv6 address */ BIT(do_more); /* this is set TRUE if the ->curl_do_more() function is supposed to be called, after ->curl_do() */ @@ -408,7 +291,7 @@ struct ConnectBits { #endif BIT(bound); /* set true if bind() has already been done on this socket/ connection */ - BIT(asks_multiplex); /* connection asks for multiplexing, but is not yet */ + BIT(upgrade_in_progress); /* protocol upgrade is in progress */ BIT(multiplex); /* connection is multiplexed */ BIT(tcp_fastopen); /* use TCP Fast Open */ BIT(tls_enable_alpn); /* TLS ALPN extension? */ @@ -427,6 +310,7 @@ struct ConnectBits { BIT(shutdown_handler); /* connection shutdown: handler shut down */ BIT(shutdown_filters); /* connection shutdown: filters shut down */ BIT(in_cpool); /* connection is kept in a connection pool */ + BIT(dns_resolved); /* DNS records for connection were resolved */ }; struct hostname { @@ -436,197 +320,36 @@ struct hostname { const char *dispname; /* name to display, as 'name' might be encoded */ }; -/* - * Flags on the keepon member of the Curl_transfer_keeper - */ - -#define KEEP_NONE 0 -#define KEEP_RECV (1<<0) /* there is or may be data to read */ -#define KEEP_SEND (1<<1) /* there is or may be data to write */ -#define KEEP_RECV_HOLD (1<<2) /* when set, no reading should be done but there - might still be data to read */ -#define KEEP_SEND_HOLD (1<<3) /* when set, no writing should be done but there - might still be data to write */ -#define KEEP_RECV_PAUSE (1<<4) /* reading is paused */ -#define KEEP_SEND_PAUSE (1<<5) /* writing is paused */ - -/* KEEP_SEND_TIMED is set when the transfer should attempt sending - * at timer (or other) events. A transfer waiting on a timer will - * remove KEEP_SEND to suppress POLLOUTs of the connection. - * Adding KEEP_SEND_TIMED will then attempt to send whenever the transfer - * enters the "readwrite" loop, e.g. when a timer fires. - * This is used in HTTP for 'Expect: 100-continue' waiting. */ -#define KEEP_SEND_TIMED (1<<6) - -#define KEEP_RECVBITS (KEEP_RECV | KEEP_RECV_HOLD | KEEP_RECV_PAUSE) -#define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE) - -/* transfer wants to send is not PAUSE or HOLD */ -#define CURL_WANT_SEND(data) \ - (((data)->req.keepon & KEEP_SENDBITS) == KEEP_SEND) -/* transfer receive is not on PAUSE or HOLD */ -#define CURL_WANT_RECV(data) \ - (((data)->req.keepon & KEEP_RECVBITS) == KEEP_RECV) - #define FIRSTSOCKET 0 #define SECONDARYSOCKET 1 -/* - * Specific protocol handler. - */ - -struct Curl_handler { - const char *scheme; /* URL scheme name in lowercase */ - - /* Complement to setup_connection_internals(). This is done before the - transfer "owns" the connection. */ - CURLcode (*setup_connection)(struct Curl_easy *data, - struct connectdata *conn); - - /* These two functions MUST be set to be protocol dependent */ - CURLcode (*do_it)(struct Curl_easy *data, bool *done); - CURLcode (*done)(struct Curl_easy *, CURLcode, bool); - - /* If the curl_do() function is better made in two halves, this - * curl_do_more() function will be called afterwards, if set. For example - * for doing the FTP stuff after the PASV/PORT command. - */ - CURLcode (*do_more)(struct Curl_easy *, int *); - - /* This function *MAY* be set to a protocol-dependent function that is run - * after the connect() and everything is done, as a step in the connection. - * The 'done' pointer points to a bool that should be set to TRUE if the - * function completes before return. If it does not complete, the caller - * should call the ->connecting() function until it is. - */ - CURLcode (*connect_it)(struct Curl_easy *data, bool *done); - - /* See above. */ - CURLcode (*connecting)(struct Curl_easy *data, bool *done); - CURLcode (*doing)(struct Curl_easy *data, bool *done); - - /* Called from the multi interface during the PROTOCONNECT phase, and it - should then return a proper fd set */ - CURLcode (*proto_pollset)(struct Curl_easy *data, - struct easy_pollset *ps); - - /* Called from the multi interface during the DOING phase, and it should - then return a proper fd set */ - CURLcode (*doing_pollset)(struct Curl_easy *data, - struct easy_pollset *ps); - - /* Called from the multi interface during the DO_MORE phase, and it should - then return a proper fd set */ - CURLcode (*domore_pollset)(struct Curl_easy *data, - struct easy_pollset *ps); - - /* Called from the multi interface during the DO_DONE, PERFORM and - WAITPERFORM phases, and it should then return a proper fd set. Not setting - this will make libcurl use the generic default one. */ - CURLcode (*perform_pollset)(struct Curl_easy *data, - struct easy_pollset *ps); - - /* This function *MAY* be set to a protocol-dependent function that is run - * by the curl_disconnect(), as a step in the disconnection. If the handler - * is called because the connection has been considered dead, - * dead_connection is set to TRUE. The connection is (again) associated with - * the transfer here. - */ - CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *, - bool dead_connection); - - /* If used, this function gets called from transfer.c to - allow the protocol to do extra handling in writing response to - the client. */ - CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen, - bool is_eos); - - /* If used, this function gets called from transfer.c to - allow the protocol to do extra handling in writing a single response - header line to the client. */ - CURLcode (*write_resp_hd)(struct Curl_easy *data, - const char *hd, size_t hdlen, bool is_eos); - - /* 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); - - /* attach() attaches this transfer to this connection */ - void (*attach)(struct Curl_easy *data, struct connectdata *conn); - - /* return CURLE_OK if a redirect to `newurl` should be followed, - CURLE_TOO_MANY_REDIRECTS otherwise. May alter `data` to change - the way the follow request is performed. */ - CURLcode (*follow)(struct Curl_easy *data, const char *newurl, - followtype type); - - int defport; /* Default port. */ - curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single - specific protocol bit */ - 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_* */ - -}; - -#define PROTOPT_NONE 0 /* nothing extra */ -#define PROTOPT_SSL (1<<0) /* uses SSL */ -#define PROTOPT_DUAL (1<<1) /* this protocol uses two connections */ -#define PROTOPT_CLOSEACTION (1<<2) /* need action before socket close */ -/* some protocols will have to call the underlying functions without regard to - what exact state the socket signals. IE even if the socket says "readable", - the send function might need to be called while uploading, or vice versa. -*/ -#define PROTOPT_DIRLOCK (1<<3) -#define PROTOPT_NONETWORK (1<<4) /* protocol does not use the network! */ -#define PROTOPT_NEEDSPWD (1<<5) /* needs a password, and if none is set it - gets a default */ -#define PROTOPT_NOURLQUERY (1<<6) /* protocol cannot handle - URL query strings (?foo=bar) ! */ -#define PROTOPT_CREDSPERREQUEST (1<<7) /* requires login credentials per - request instead of per connection */ -#define PROTOPT_ALPN (1<<8) /* set ALPN for this */ -/* (1<<9) was PROTOPT_STREAM, now free */ -#define PROTOPT_URLOPTIONS (1<<10) /* allow options part in the userinfo field - of the URL */ -#define PROTOPT_PROXY_AS_HTTP (1<<11) /* allow this non-HTTP scheme over a - HTTP proxy as HTTP proxies may know - this protocol and act as a gateway */ -#define PROTOPT_WILDCARD (1<<12) /* protocol supports wildcard matching */ -#define PROTOPT_USERPWDCTRL (1<<13) /* Allow "control bytes" (< 32 ASCII) in - username and password */ -#define PROTOPT_NOTCPPROXY (1<<14) /* this protocol cannot proxy over TCP */ - -#define CONNCHECK_NONE 0 /* No checks */ -#define CONNCHECK_ISDEAD (1<<0) /* Check if the connection is dead. */ -#define CONNCHECK_KEEPALIVE (1<<1) /* Perform any keepalive function. */ - -#define CONNRESULT_NONE 0 /* No extra information. */ -#define CONNRESULT_DEAD (1<<0) /* The connection is dead. */ +#define TRNSPRT_NONE 0 +#define TRNSPRT_TCP 3 +#define TRNSPRT_UDP 4 +#define TRNSPRT_QUIC 5 +#define TRNSPRT_UNIX 6 struct ip_quadruple { char remote_ip[MAX_IPADR_LEN]; char local_ip[MAX_IPADR_LEN]; - int remote_port; - int local_port; + uint16_t remote_port; + uint16_t local_port; + uint8_t transport; }; +#define CUR_IP_QUAD_HAS_PORTS(x) \ + (((x)->transport == TRNSPRT_TCP) || \ + ((x)->transport == TRNSPRT_UDP) || \ + ((x)->transport == TRNSPRT_QUIC)) + struct proxy_info { struct hostname host; - int port; - unsigned char proxytype; /* what kind of proxy that is in use */ + uint16_t port; + uint8_t proxytype; /* what kind of proxy that is in use */ char *user; /* proxy username string, allocated */ char *passwd; /* proxy password string, allocated */ }; -#define TRNSPRT_TCP 3 -#define TRNSPRT_UDP 4 -#define TRNSPRT_QUIC 5 -#define TRNSPRT_UNIX 6 - /* * The connectdata struct contains all fields and variables that should be * unique for an entire connection. @@ -642,8 +365,7 @@ struct connectdata { handle is still used by one or more easy handles and can only used by any other easy handle without careful consideration (== only for multiplexing) and it cannot be used by another multi handle! */ -#define CONN_INUSE(c) (!Curl_uint_spbset_empty(&(c)->xfers_attached)) -#define CONN_ATTACHED(c) Curl_uint_spbset_count(&(c)->xfers_attached) +#define CONN_INUSE(c) (!!(c)->attached_xfers) /**** Fields set when inited and not modified again */ curl_off_t connection_id; /* Contains a unique number to make it easier to @@ -657,7 +379,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 */ @@ -695,35 +416,20 @@ 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 was used on this connection. */ struct curltime keepalive; - /**** curl_get() phase fields */ - -#ifdef HAVE_GSSAPI - BIT(sec_complete); /* if Kerberos is enabled for this connection */ - unsigned char command_prot; /* enum protection_level */ - unsigned char data_prot; /* enum protection_level */ - unsigned char request_data_prot; /* enum protection_level */ - size_t buffer_size; - struct krb5buffer in_buffer; - void *app_data; - const struct Curl_sec_client_mech *mech; - struct sockaddr_in local_addr; -#endif - - struct uint_spbset xfers_attached; /* mids of attached transfers */ /* A connection cache from a SHARE might be used in several multi handles. * We MUST not reuse connections that are running in another multi, * 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 ************/ @@ -751,43 +457,43 @@ struct connectdata { that subsequent bound-requested connections are not accidentally reusing wrong connections. */ char *localdev; - unsigned short localportrange; - int waitfor; /* current READ/WRITE bits to wait for */ #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) int socks5_gssapi_enctype; #endif - /* The field below gets set in connect.c:connecthost() */ - int remote_port; /* the remote port, not the proxy port! */ - int conn_to_port; /* the remote port to connect to. valid only if - bits.conn_to_port is set */ + 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 + /* The field below gets set in connect.c:connecthost() */ + uint16_t remote_port; /* the remote port, not the proxy port! */ + uint16_t conn_to_port; /* the remote port to connect to. valid only if + bits.conn_to_port is set */ + uint16_t localportrange; + 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 */ }; #ifndef CURL_DISABLE_PROXY #define CURL_CONN_HOST_DISPNAME(c) \ - ((c)->bits.socksproxy ? (c)->socks_proxy.host.dispname : \ - (c)->bits.httpproxy ? (c)->http_proxy.host.dispname : \ - (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ - (c)->host.dispname) + ((c)->bits.socksproxy ? (c)->socks_proxy.host.dispname : \ + (c)->bits.httpproxy ? (c)->http_proxy.host.dispname : \ + (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ + (c)->host.dispname) #else #define CURL_CONN_HOST_DISPNAME(c) \ - (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ - (c)->host.dispname + (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ + (c)->host.dispname #endif /* The end of connectdata. */ @@ -803,15 +509,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 @@ -824,7 +530,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; @@ -833,23 +539,20 @@ struct PureInfo { BIT(used_proxy); /* the transfer used a proxy */ }; -struct pgrs_measure { - struct curltime start; /* when measure started */ - curl_off_t start_size; /* the 'cur_size' the measure started at */ -}; - struct pgrs_dir { curl_off_t total_size; /* total expected bytes */ curl_off_t cur_size; /* transferred bytes so far */ curl_off_t speed; /* bytes per second transferred */ - struct pgrs_measure limit; + struct Curl_rlimit rlimit; /* speed limiting / pausing */ }; struct Progress { + struct curltime now; /* current time of processing */ time_t lastshow; /* time() of the last displayed progress meter or NULL to force redraw at next call */ struct pgrs_dir ul; struct pgrs_dir dl; + curl_off_t deliver; /* amount of data delivered to application */ curl_off_t current_speed; /* uses the currently fastest transfer */ curl_off_t earlydata_sent; @@ -871,11 +574,11 @@ struct Progress { struct curltime t_startqueue; struct curltime t_acceptdata; -#define CURR_TIME (5 + 1) /* 6 entries for 5 seconds */ +#define CURL_SPEED_RECORDS (5 + 1) /* 6 entries for 5 seconds */ - curl_off_t speeder[ CURR_TIME ]; - struct curltime speeder_time[ CURR_TIME ]; - unsigned char speeder_c; + curl_off_t speed_amount[CURL_SPEED_RECORDS]; + struct curltime speed_time[CURL_SPEED_RECORDS]; + uint32_t speeder_c; BIT(hide); BIT(ul_size_known); BIT(dl_size_known); @@ -885,27 +588,27 @@ struct Progress { }; typedef enum { - RTSPREQ_NONE, /* first in list */ - RTSPREQ_OPTIONS, - RTSPREQ_DESCRIBE, - RTSPREQ_ANNOUNCE, - RTSPREQ_SETUP, - RTSPREQ_PLAY, - RTSPREQ_PAUSE, - RTSPREQ_TEARDOWN, - RTSPREQ_GET_PARAMETER, - RTSPREQ_SET_PARAMETER, - RTSPREQ_RECORD, - RTSPREQ_RECEIVE, - RTSPREQ_LAST /* last in list */ + RTSPREQ_NONE, /* first in list */ + RTSPREQ_OPTIONS, + RTSPREQ_DESCRIBE, + RTSPREQ_ANNOUNCE, + RTSPREQ_SETUP, + RTSPREQ_PLAY, + RTSPREQ_PAUSE, + RTSPREQ_TEARDOWN, + RTSPREQ_GET_PARAMETER, + RTSPREQ_SET_PARAMETER, + RTSPREQ_RECORD, + RTSPREQ_RECEIVE, + RTSPREQ_LAST /* last in list */ } 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 @@ -957,15 +660,6 @@ typedef enum { EXPIRE_LAST /* not an actual timer, used as a marker only */ } expire_id; - -typedef enum { - TRAILERS_NONE, - TRAILERS_INITIALIZED, - TRAILERS_SENDING, - TRAILERS_DONE -} trailers_state; - - /* * One instance for each timeout an easy handle can set. */ @@ -1000,8 +694,10 @@ struct UrlState { curl_off_t recent_conn_id; /* The most recent connection used, might no * longer exist */ struct dynbuf headerb; /* buffer to store headers in */ +#ifndef CURL_DISABLE_HSTS struct curl_slist *hstslist; /* list of HSTS files set by curl_easy_setopt(HSTS) calls */ +#endif curl_off_t current_speed; /* the ProgressShow() function sets this, bytes / second */ @@ -1012,10 +708,7 @@ struct UrlState { char *first_host; int first_remote_port; curl_prot_t first_remote_protocol; - - int retrycount; /* number of retries on a new connection */ int os_errno; /* filled in with errno whenever an error occurs */ - long followlocation; /* redirect counter */ int requests; /* request counter: redirects + authentication retakes */ #ifdef HAVE_SIGNAL /* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */ @@ -1028,9 +721,8 @@ struct UrlState { struct auth authhost; /* auth details for host */ struct auth authproxy; /* auth details for proxy */ - struct Curl_dns_entry *dns[2]; /* DNS to connect FIRST/SECONDARY */ #ifdef USE_CURL_ASYNC - struct Curl_async async; /* asynchronous name resolver data */ + struct Curl_resolv_async *async; /* asynchronous name resolver data */ #endif #ifdef USE_OPENSSL @@ -1057,11 +749,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 @@ -1075,8 +766,8 @@ struct UrlState { void *in; /* CURLOPT_READDATA */ CURLU *uh; /* URL handle for the current parsed URL */ struct urlpieces up; - char *url; /* work URL, copied from UserDefined */ - char *referer; /* referer string */ + struct bufref url; /* work URL, initially copied from UserDefined */ + struct bufref referer; /* referer string */ struct curl_slist *resolve; /* set to point to the set.resolve list when this should be dealt with in pretransfer */ #ifndef CURL_DISABLE_HTTP @@ -1091,15 +782,13 @@ struct UrlState { struct Curl_llist httphdrs; /* received headers */ struct curl_header headerout[2]; /* for external purposes */ struct Curl_header_store *prevhead; /* the latest added header */ - trailers_state trailers_state; /* whether we are sending trailers - and what stage are we at */ #endif #ifndef CURL_DISABLE_COOKIES struct curl_slist *cookielist; /* list of cookie files set by 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 @@ -1112,13 +801,9 @@ struct UrlState { struct dynamically_allocated_data { char *uagent; char *accept_encoding; - char *userpwd; char *rangeline; char *ref; char *host; -#ifndef CURL_DISABLE_COOKIES - char *cookiehost; -#endif #ifndef CURL_DISABLE_RTSP char *rtsp_transport; #endif @@ -1127,7 +812,6 @@ struct UrlState { char *user; char *passwd; #ifndef CURL_DISABLE_PROXY - char *proxyuserpwd; char *proxyuser; char *proxypasswd; #endif @@ -1135,7 +819,10 @@ struct UrlState { #ifndef CURL_DISABLE_HTTP struct http_negotiation http_neg; #endif - unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) + uint16_t followlocation; /* redirect counter */ + uint8_t retrycount; /* number of retries on a new connection, up to + CONN_MAX_RETRIES */ + 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 */ @@ -1169,8 +856,6 @@ struct UrlState { #ifdef CURL_LIST_ONLY_PROTOCOL BIT(list_only); /* list directory contents */ #endif - BIT(url_alloc); /* URL string is malloc()'ed */ - BIT(referer_alloc); /* referer string is malloc()ed */ BIT(wildcard_resolve); /* Set to true if any resolve change is a wildcard */ BIT(upload); /* upload request */ BIT(internal); /* internal: true if this easy handle was created for @@ -1181,6 +866,8 @@ struct UrlState { BIT(http_hd_te); /* Added HTTP header TE: */ BIT(http_hd_upgrade); /* Added HTTP header Upgrade: */ BIT(http_hd_h2_settings); /* Added HTTP header H2Settings: */ + BIT(maybe_folded); + BIT(leading_unfold); /* unfold started, this is the leading bytes */ #endif }; @@ -1206,8 +893,8 @@ enum dupstring { STRING_SSL_PINNEDPUBLICKEY, /* public key file to verify peer against */ STRING_SSL_CIPHER_LIST, /* list of ciphers to use */ STRING_SSL_CIPHER13_LIST, /* list of TLS 1.3 ciphers to use */ - STRING_SSL_CRLFILE, /* crl file to check certificate */ - STRING_SSL_ISSUERCERT, /* issuer cert file to check certificate */ + STRING_SSL_CRLFILE, /* CRL file to check certificate */ + STRING_SSL_ISSUERCERT, /* issuer cert file to check certificate */ STRING_SERVICE_NAME, /* Service name */ #ifndef CURL_DISABLE_PROXY STRING_CERT_PROXY, /* client certificate filename */ @@ -1220,7 +907,7 @@ enum dupstring { STRING_SSL_PINNEDPUBLICKEY_PROXY, /* public key file to verify proxy */ STRING_SSL_CIPHER_LIST_PROXY, /* list of ciphers to use */ STRING_SSL_CIPHER13_LIST_PROXY, /* list of TLS 1.3 ciphers to use */ - STRING_SSL_CRLFILE_PROXY, /* crl file to check certificate */ + STRING_SSL_CRLFILE_PROXY, /* CRL file to check certificate */ STRING_SSL_ISSUERCERT_PROXY, /* issuer cert file to check certificate */ STRING_PROXY_SERVICE_NAME, /* Proxy service name */ #endif @@ -1239,9 +926,6 @@ enum dupstring { STRING_FTP_ALTERNATIVE_TO_USER, /* command to send if USER/PASS fails */ STRING_FTPPORT, /* port to send with the FTP PORT command */ #endif -#ifdef HAVE_GSSAPI - STRING_KRB_LEVEL, /* krb security level */ -#endif #ifndef CURL_DISABLE_NETRC STRING_NETRC_FILE, /* if not NULL, use this instead of trying to find $HOME/.netrc */ @@ -1328,7 +1012,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 { @@ -1345,7 +1029,6 @@ enum dupblob { BLOB_LAST }; - struct UserDefined { FILE *err; /* the stderr user data goes here */ void *debugdata; /* the data that will be passed to fdebug */ @@ -1353,8 +1036,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 @@ -1364,7 +1047,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 */ @@ -1390,12 +1073,11 @@ struct UserDefined { void *progress_client; /* pointer to pass to the progress callback */ void *ioctl_client; /* pointer to pass to the ioctl callback */ timediff_t conn_max_idle_ms; /* max idle time to allow a connection that - is to be reused */ + is to be reused */ timediff_t conn_max_age_ms; /* max time since creation to allow a - connection that is to be reused */ + 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 */ @@ -1403,7 +1085,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 */ @@ -1417,17 +1099,17 @@ struct UserDefined { #ifndef CURL_DISABLE_PROXY struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */ struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */ - unsigned short proxyport; /* If non-zero, use this port number by + 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 */ @@ -1435,9 +1117,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 */ @@ -1451,14 +1133,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; @@ -1493,7 +1175,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 */ @@ -1502,50 +1185,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; + uint8_t 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 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 */ @@ -1604,11 +1286,8 @@ struct UserDefined { location: */ BIT(opt_no_body); /* as set with CURLOPT_NOBODY */ BIT(verbose); /* output verbosity */ -#ifdef HAVE_GSSAPI - BIT(krb); /* Kerberos connection requested */ -#endif 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 */ @@ -1650,10 +1329,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 @@ -1677,7 +1360,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, @@ -1688,8 +1371,8 @@ struct Curl_easy { /* once an easy handle is added to a multi, either explicitly by the * libcurl application or implicitly during `curl_easy_perform()`, * a unique identifier inside this one multi instance. */ - unsigned int mid; - unsigned int master_mid; /* if set, this transfer belongs to a master */ + uint32_t mid; + uint32_t master_mid; /* if set, this transfer belongs to a master */ multi_sub_xfer_done_cb *sub_xfer_done; struct connectdata *conn; diff --git a/lib/vauth/.checksrc b/lib/vauth/.checksrc deleted file mode 100644 index 22ca8e0b53..0000000000 --- a/lib/vauth/.checksrc +++ /dev/null @@ -1,5 +0,0 @@ -banfunc snprintf -banfunc sscanf -banfunc strerror -banfunc strtol -banfunc vsnprint diff --git a/lib/vauth/cleartext.c b/lib/vauth/cleartext.c index ebf026fcf5..7976adec9c 100644 --- a/lib/vauth/cleartext.c +++ b/lib/vauth/cleartext.c @@ -24,24 +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) || \ +#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_POP3) || \ (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) -#include -#include "../urldata.h" - -#include "vauth.h" -#include "../curlx/warnless.h" -#include "../sendf.h" -#include "../curl_printf.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" +#include "vauth/vauth.h" /* * Curl_auth_create_plain_message() @@ -63,35 +52,24 @@ CURLcode Curl_auth_create_plain_message(const char *authzid, const char *passwd, struct bufref *out) { - char *plainauth; - size_t plainlen; - size_t zlen; - size_t clen; - size_t plen; + size_t len; + char *auth; - zlen = (authzid == NULL ? 0 : strlen(authzid)); - clen = strlen(authcid); - plen = strlen(passwd); + size_t zlen = (authzid == NULL ? 0 : strlen(authzid)); + size_t clen = strlen(authcid); + size_t plen = strlen(passwd); - /* Compute binary message length. Check for overflows. */ - if((zlen > SIZE_MAX/4) || (clen > SIZE_MAX/4) || - (plen > (SIZE_MAX/2 - 2))) + if((zlen > CURL_MAX_INPUT_LENGTH) || (clen > CURL_MAX_INPUT_LENGTH) || + (plen > CURL_MAX_INPUT_LENGTH)) + return CURLE_TOO_LARGE; + + len = zlen + clen + plen + 2; + + auth = curl_maprintf("%s%c%s%c%s", authzid ? authzid : "", '\0', + authcid, '\0', passwd); + if(!auth) return CURLE_OUT_OF_MEMORY; - plainlen = zlen + clen + plen + 2; - - plainauth = malloc(plainlen + 1); - if(!plainauth) - return CURLE_OUT_OF_MEMORY; - - /* Calculate the reply */ - if(zlen) - memcpy(plainauth, authzid, zlen); - plainauth[zlen] = '\0'; - memcpy(plainauth + zlen + 1, authcid, clen); - plainauth[zlen + clen + 1] = '\0'; - memcpy(plainauth + zlen + clen + 2, passwd, plen); - plainauth[plainlen] = '\0'; - Curl_bufref_set(out, plainauth, plainlen, curl_free); + Curl_bufref_set(out, auth, len, curl_free); return CURLE_OK; } @@ -103,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); } /* @@ -126,8 +104,7 @@ void Curl_auth_create_login_message(const char *valuep, struct bufref *out) * * Returns void. */ -void Curl_auth_create_external_message(const char *user, - struct bufref *out) +void Curl_auth_create_external_message(const char *user, struct bufref *out) { /* This is the same formatting as the login message */ Curl_auth_create_login_message(user, out); diff --git a/lib/vauth/cram.c b/lib/vauth/cram.c index 3586e1012d..d3f2d137b7 100644 --- a/lib/vauth/cram.c +++ b/lib/vauth/cram.c @@ -23,24 +23,13 @@ * RFC2195 CRAM-MD5 authentication * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef CURL_DISABLE_DIGEST_AUTH -#include -#include "../urldata.h" - -#include "vauth.h" -#include "../curl_hmac.h" -#include "../curl_md5.h" -#include "../curlx/warnless.h" -#include "../curl_printf.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" - +#include "vauth/vauth.h" +#include "curl_hmac.h" +#include "curl_md5.h" /* * Curl_auth_create_cram_md5_message() @@ -68,21 +57,21 @@ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, /* Compute the digest using the password as the key */ ctxt = Curl_HMAC_init(&Curl_HMAC_MD5, - (const unsigned char *) passwdp, + (const unsigned char *)passwdp, curlx_uztoui(strlen(passwdp))); if(!ctxt) return CURLE_OUT_OF_MEMORY; /* Update the digest with the given challenge */ if(Curl_bufref_len(chlg)) - Curl_HMAC_update(ctxt, Curl_bufref_ptr(chlg), + Curl_HMAC_update(ctxt, Curl_bufref_uptr(chlg), curlx_uztoui(Curl_bufref_len(chlg))); /* Finalise the digest */ Curl_HMAC_final(ctxt, digest); /* Generate the response */ - response = aprintf( + response = curl_maprintf( "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", userp, digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index 898629a958..5ecfd60aad 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -24,30 +24,18 @@ * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifndef CURL_DISABLE_DIGEST_AUTH -#include - -#include "vauth.h" -#include "digest.h" -#include "../urldata.h" -#include "../curlx/base64.h" -#include "../curl_hmac.h" -#include "../curl_md5.h" -#include "../curl_sha256.h" -#include "../curl_sha512_256.h" -#include "../vtls/vtls.h" -#include "../curlx/warnless.h" -#include "../curlx/strparse.h" -#include "../curl_printf.h" -#include "../rand.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.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 */ @@ -142,81 +130,105 @@ 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++) - msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]); + curl_msnprintf((char *)&dest[i * 2], 3, "%02x", source[i]); } /* 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++) - msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]); + curl_msnprintf((char *)&dest[i * 2], 3, "%02x", source[i]); } /* 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; + else + result = curlx_dyn_addn(&out, s, 1); + if(result) + return NULL; + s++; } - - dest = malloc(n); - if(dest) { - char *d = dest; - s = source; - while(*s) { - if(*s == '"' || *s == '\\') { - *d++ = '\\'; - } - *d++ = *s++; - } - *d = '\0'; - } - - return dest; + return curlx_dyn_ptr(&out); } /* Retrieves the value for a corresponding key from the challenge string * returns TRUE if the key could be found, FALSE if it does not exists */ -static bool auth_digest_get_key_value(const char *chlg, - const char *key, - char *value, - size_t max_val_len, - char end_char) +static bool auth_digest_get_key_value(const char *chlg, const char *key, + char *buf, size_t buflen) { - char *find_pos; - size_t i; + /* keyword=[value],keyword2=[value] + The values may or may not be quoted. + */ - find_pos = strstr(chlg, key); - if(!find_pos) - return FALSE; + do { + struct Curl_str data; + struct Curl_str name; - find_pos += strlen(key); + curlx_str_passblanks(&chlg); - for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i) - value[i] = *find_pos++; - value[i] = '\0'; + if(!curlx_str_until(&chlg, &name, 64, '=') && + !curlx_str_single(&chlg, '=')) { + /* this is the key, get the value, possibly quoted */ + int rc = curlx_str_quotedword(&chlg, &data, 256); + if(rc == STRE_BEGQUOTE) + /* try unquoted until comma */ + rc = curlx_str_until(&chlg, &data, 256, ','); + if(rc) + return FALSE; /* weird */ - return TRUE; + if(curlx_str_cmp(&name, key)) { + /* if this is our key, return the value */ + size_t len = curlx_strlen(&data); + const char *src = curlx_str(&data); + size_t i; + size_t outlen = 0; + + if(len >= buflen) + /* does not fit */ + return FALSE; + + for(i = 0; i < len; i++) { + if(src[i] == '\\' && i + 1 < len) { + i++; /* skip backslash */ + } + buf[outlen++] = src[i]; + } + buf[outlen] = 0; + return TRUE; + } + if(curlx_str_single(&chlg, ',')) + return FALSE; + } + else /* odd syntax */ + break; + } while(1); + + return FALSE; } -static CURLcode auth_digest_get_qop_values(const char *options, int *value) +static void auth_digest_get_qop_values(const char *options, int *value) { struct Curl_str out; /* Initialise the output */ @@ -232,8 +244,6 @@ static CURLcode auth_digest_get_qop_values(const char *options, int *value) if(curlx_str_single(&options, ',')) break; } - - return CURLE_OK; } /* @@ -245,13 +255,13 @@ static CURLcode auth_digest_get_qop_values(const char *options, int *value) * Parameters: * * chlgref [in] - The challenge message. - * nonce [in/out] - The buffer where the nonce will be stored. + * nonce [in/out] - The buffer where the nonce is stored. * nlen [in] - The length of the nonce buffer. - * realm [in/out] - The buffer where the realm will be stored. + * realm [in/out] - The buffer where the realm is stored. * rlen [in] - The length of the realm buffer. - * alg [in/out] - The buffer where the algorithm will be stored. + * alg [in/out] - The buffer where the algorithm is stored. * alen [in] - The length of the algorithm buffer. - * qop [in/out] - The buffer where the qop-options will be stored. + * qop [in/out] - The buffer where the qop-options is stored. * qlen [in] - The length of the qop buffer. * * Returns CURLE_OK on success. @@ -262,28 +272,28 @@ static CURLcode auth_decode_digest_md5_message(const struct bufref *chlgref, char *alg, size_t alen, char *qop, size_t qlen) { - const char *chlg = (const char *) Curl_bufref_ptr(chlgref); + const char *chlg = Curl_bufref_ptr(chlgref); /* Ensure we have a valid challenge message */ if(!Curl_bufref_len(chlgref)) return CURLE_BAD_CONTENT_ENCODING; /* Retrieve nonce string from the challenge */ - if(!auth_digest_get_key_value(chlg, "nonce=\"", nonce, nlen, '\"')) + if(!auth_digest_get_key_value(chlg, "nonce", nonce, nlen)) return CURLE_BAD_CONTENT_ENCODING; /* Retrieve realm string from the challenge */ - if(!auth_digest_get_key_value(chlg, "realm=\"", realm, rlen, '\"')) { + if(!auth_digest_get_key_value(chlg, "realm", realm, rlen)) { /* Challenge does not have a realm, set empty string [RFC2831] page 6 */ *realm = '\0'; } /* Retrieve algorithm string from the challenge */ - if(!auth_digest_get_key_value(chlg, "algorithm=", alg, alen, ',')) + if(!auth_digest_get_key_value(chlg, "algorithm", alg, alen)) return CURLE_BAD_CONTENT_ENCODING; /* Retrieve qop-options string from the challenge */ - if(!auth_digest_get_key_value(chlg, "qop=\"", qop, qlen, '\"')) + if(!auth_digest_get_key_value(chlg, "qop", qop, qlen)) return CURLE_BAD_CONTENT_ENCODING; return CURLE_OK; @@ -331,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]; @@ -344,6 +354,9 @@ 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; + char *quserp; /* Decode the challenge message */ CURLcode result = auth_decode_digest_md5_message(chlg, @@ -361,9 +374,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, return CURLE_BAD_CONTENT_ENCODING; /* Get the qop-values from the qop-options */ - result = auth_digest_get_qop_values(qop_options, &qop_values); - if(result) - return result; + auth_digest_get_qop_values(qop_options, &qop_values); /* We only support auth quality-of-protection */ if(!(qop_values & DIGEST_QOP_VALUE_AUTH)) @@ -374,18 +385,18 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, if(result) return result; - /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */ + /* Good so far, now calculate A1 and H(A1) according to RFC 2831 */ ctxt = Curl_MD5_init(&Curl_DIGEST_MD5); if(!ctxt) return CURLE_OUT_OF_MEMORY; - Curl_MD5_update(ctxt, (const unsigned char *) userp, + Curl_MD5_update(ctxt, (const unsigned char *)userp, curlx_uztoui(strlen(userp))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) realm, + Curl_MD5_update(ctxt, (const unsigned char *)":", 1); + Curl_MD5_update(ctxt, (const unsigned char *)realm, curlx_uztoui(strlen(realm))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) passwdp, + Curl_MD5_update(ctxt, (const unsigned char *)":", 1); + Curl_MD5_update(ctxt, (const unsigned char *)passwdp, curlx_uztoui(strlen(passwdp))); Curl_MD5_final(ctxt, digest); @@ -393,18 +404,18 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, if(!ctxt) return CURLE_OUT_OF_MEMORY; - Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) nonce, + Curl_MD5_update(ctxt, (const unsigned char *)digest, MD5_DIGEST_LEN); + Curl_MD5_update(ctxt, (const unsigned char *)":", 1); + Curl_MD5_update(ctxt, (const unsigned char *)nonce, curlx_uztoui(strlen(nonce))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) cnonce, + Curl_MD5_update(ctxt, (const unsigned char *)":", 1); + Curl_MD5_update(ctxt, (const unsigned char *)cnonce, curlx_uztoui(strlen(cnonce))); Curl_MD5_final(ctxt, digest); /* Convert calculated 16 octet hex into 32 bytes string */ for(i = 0; i < MD5_DIGEST_LEN; i++) - msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]); + curl_msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]); /* Generate our SPN */ spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); @@ -414,58 +425,68 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, /* Calculate H(A2) */ ctxt = Curl_MD5_init(&Curl_DIGEST_MD5); if(!ctxt) { - free(spn); + curlx_free(spn); return CURLE_OUT_OF_MEMORY; } - Curl_MD5_update(ctxt, (const unsigned char *) method, + Curl_MD5_update(ctxt, (const unsigned char *)method, curlx_uztoui(strlen(method))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) spn, + Curl_MD5_update(ctxt, (const unsigned char *)":", 1); + Curl_MD5_update(ctxt, (const unsigned char *)spn, curlx_uztoui(strlen(spn))); Curl_MD5_final(ctxt, digest); for(i = 0; i < MD5_DIGEST_LEN; i++) - msnprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]); + curl_msnprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]); /* Now calculate the response hash */ ctxt = Curl_MD5_init(&Curl_DIGEST_MD5); if(!ctxt) { - free(spn); + curlx_free(spn); return CURLE_OUT_OF_MEMORY; } - Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) nonce, + Curl_MD5_update(ctxt, (const unsigned char *)HA1_hex, 2 * MD5_DIGEST_LEN); + Curl_MD5_update(ctxt, (const unsigned char *)":", 1); + Curl_MD5_update(ctxt, (const unsigned char *)nonce, curlx_uztoui(strlen(nonce))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); + Curl_MD5_update(ctxt, (const unsigned char *)":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) nonceCount, + Curl_MD5_update(ctxt, (const unsigned char *)nonceCount, curlx_uztoui(strlen(nonceCount))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) cnonce, + Curl_MD5_update(ctxt, (const unsigned char *)":", 1); + Curl_MD5_update(ctxt, (const unsigned char *)cnonce, curlx_uztoui(strlen(cnonce))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) qop, + Curl_MD5_update(ctxt, (const unsigned char *)":", 1); + Curl_MD5_update(ctxt, (const unsigned char *)qop, curlx_uztoui(strlen(qop))); - Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); + Curl_MD5_update(ctxt, (const unsigned char *)":", 1); - Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN); + Curl_MD5_update(ctxt, (const unsigned char *)HA2_hex, 2 * MD5_DIGEST_LEN); Curl_MD5_final(ctxt, digest); for(i = 0; i < MD5_DIGEST_LEN; i++) - msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]); + curl_msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]); - /* Generate the response */ - response = aprintf("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); - free(spn); + /* escape double quotes and backslashes in the username, realm and nonce as + necessary */ + qrealm = auth_digest_string_quoted(realm); + qnonce = auth_digest_string_quoted(nonce); + quserp = auth_digest_string_quoted(userp); + if(qrealm && qnonce && quserp) + /* Generate the response */ + response = curl_maprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\"," + "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\"," + "response=%s,qop=%s", + quserp, qrealm, qnonce, + cnonce, nonceCount, spn, resp_hash_hex, qop); + + curlx_free(qrealm); + curlx_free(qnonce); + curlx_free(quserp); + curlx_free(spn); if(!response) return CURLE_OUT_OF_MEMORY; @@ -510,8 +531,8 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, /* Extract a value=content pair */ if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) { if(curl_strequal(value, "nonce")) { - free(digest->nonce); - digest->nonce = strdup(content); + curlx_free(digest->nonce); + digest->nonce = curlx_strdup(content); if(!digest->nonce) return CURLE_OUT_OF_MEMORY; } @@ -522,14 +543,14 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, } } else if(curl_strequal(value, "realm")) { - free(digest->realm); - digest->realm = strdup(content); + curlx_free(digest->realm); + digest->realm = curlx_strdup(content); if(!digest->realm) return CURLE_OUT_OF_MEMORY; } else if(curl_strequal(value, "opaque")) { - free(digest->opaque); - digest->opaque = strdup(content); + curlx_free(digest->opaque); + digest->opaque = curlx_strdup(content); if(!digest->opaque) return CURLE_OUT_OF_MEMORY; } @@ -545,7 +566,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, if(curlx_str_casecompare(&out, DIGEST_QOP_VALUE_STRING_AUTH)) foundAuth = TRUE; else if(curlx_str_casecompare(&out, - DIGEST_QOP_VALUE_STRING_AUTH_INT)) + DIGEST_QOP_VALUE_STRING_AUTH_INT)) foundAuthInt = TRUE; if(curlx_str_single(&token, ',')) break; @@ -555,21 +576,21 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, /* Select only auth or auth-int. Otherwise, ignore */ if(foundAuth) { - free(digest->qop); - digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH); + curlx_free(digest->qop); + digest->qop = curlx_strdup(DIGEST_QOP_VALUE_STRING_AUTH); if(!digest->qop) return CURLE_OUT_OF_MEMORY; } else if(foundAuthInt) { - free(digest->qop); - digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT); + curlx_free(digest->qop); + digest->qop = curlx_strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT); if(!digest->qop) return CURLE_OUT_OF_MEMORY; } } else if(curl_strequal(value, "algorithm")) { - free(digest->algorithm); - digest->algorithm = strdup(content); + curlx_free(digest->algorithm); + digest->algorithm = curlx_strdup(content); if(!digest->algorithm) return CURLE_OUT_OF_MEMORY; @@ -584,16 +605,16 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, else if(curl_strequal(content, "SHA-512-256")) { #ifdef CURL_HAVE_SHA512_256 digest->algo = ALGO_SHA512_256; -#else /* ! CURL_HAVE_SHA512_256 */ +#else /* !CURL_HAVE_SHA512_256 */ return CURLE_NOT_BUILT_IN; -#endif /* ! CURL_HAVE_SHA512_256 */ +#endif /* CURL_HAVE_SHA512_256 */ } else if(curl_strequal(content, "SHA-512-256-SESS")) { #ifdef CURL_HAVE_SHA512_256 digest->algo = ALGO_SHA512_256SESS; -#else /* ! CURL_HAVE_SHA512_256 */ +#else /* !CURL_HAVE_SHA512_256 */ return CURLE_NOT_BUILT_IN; -#endif /* ! CURL_HAVE_SHA512_256 */ +#endif /* CURL_HAVE_SHA512_256 */ } else return CURLE_BAD_CONTENT_ENCODING; @@ -651,22 +672,21 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * 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 */ @@ -676,12 +696,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) @@ -695,26 +718,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(cnoncebuf, sizeof(cnoncebuf), - &cnonce, &cnonce_sz); - if(result) - return result; + goto oom; digest->cnonce = cnonce; } if(digest->userhash) { - hashthis = aprintf("%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)); - 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); } @@ -729,27 +753,31 @@ static CURLcode auth_create_digest_http_message( unq(nonce-value) ":" unq(cnonce-value) */ - hashthis = aprintf("%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)); - free(hashthis); + 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 = aprintf("%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)); - free(tmp); + result = hash(hashbuf, (unsigned char *)tmp, strlen(tmp)); + curlx_free(tmp); if(result) - return result; + goto oom; convert_to_ascii(hashbuf, ha1); } @@ -766,9 +794,17 @@ static CURLcode auth_create_digest_http_message( 5.1.1 of RFC 2616) */ - hashthis = aprintf("%s:%s", request, uripath); - if(!hashthis) - return CURLE_OUT_OF_MEMORY; + 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) { + 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 */ @@ -777,40 +813,41 @@ static CURLcode auth_create_digest_http_message( result = hash(hashbuf, (const unsigned char *)"", 0); if(result) { - free(hashthis); - return result; + curlx_free(hashthis); + goto oom; } convert_to_ascii(hashbuf, (unsigned char *)hashed); - hashthis2 = aprintf("%s:%s", hashthis, hashed); - free(hashthis); + 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)); - free(hashthis); + 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) { - hashthis = aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc, - digest->cnonce, digest->qop, ha2); - } - else { - hashthis = aprintf("%s:%s:%s", ha1, digest->nonce, ha2); + if(digest->qop) + hashthis = curl_maprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, + digest->nc, digest->cnonce, digest->qop, ha2); + else + hashthis = curl_maprintf("%s:%s:%s", ha1, digest->nonce, ha2); + + if(!hashthis) { + result = CURLE_OUT_OF_MEMORY; + goto oom; } - if(!hashthis) - return CURLE_OUT_OF_MEMORY; - - result = hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis)); - free(hashthis); + 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) @@ -819,116 +856,113 @@ static CURLcode auth_create_digest_http_message( nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" Digest parameters are all quoted strings. Username which is provided by - the user will need double quotes and backslashes within it escaped. - realm, nonce, and opaque will need backslashes as well as they were + the user needs double quotes and backslashes within it escaped. + realm, nonce, and opaque needs backslashes as well as they were de-escaped when copied from request header. cnonce is generated with web-safe characters. uri is already percent encoded. nc is 8 hex characters. algorithm and qop with standard values only contain web-safe 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 { - realm_quoted = malloc(1); + realm_quoted = curlx_malloc(1); if(realm_quoted) realm_quoted[0] = 0; } if(!realm_quoted) { - 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) { - free(realm_quoted); - free(userp_quoted); - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto oom; } if(digest->qop) { - response = aprintf("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 = aprintf("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); } - free(nonce_quoted); - free(realm_quoted); - 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) { - free(response); - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto oom; } - tmp = aprintf("%s, opaque=\"%s\"", response, opaque_quoted); - free(response); - free(opaque_quoted); - if(!tmp) - return CURLE_OUT_OF_MEMORY; - - response = tmp; + result = curlx_dyn_addf(&response, ", opaque=\"%s\"", opaque_quoted); + curlx_free(opaque_quoted); + if(result) + goto oom; } if(digest->algorithm) { /* Append the algorithm */ - tmp = aprintf("%s, algorithm=%s", response, digest->algorithm); - 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 = aprintf("%s, userhash=true", response); - 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; } /* @@ -946,7 +980,7 @@ static CURLcode auth_create_digest_http_message( * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. @@ -997,18 +1031,18 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, */ void Curl_auth_digest_cleanup(struct digestdata *digest) { - Curl_safefree(digest->nonce); - Curl_safefree(digest->cnonce); - Curl_safefree(digest->realm); - Curl_safefree(digest->opaque); - Curl_safefree(digest->qop); - Curl_safefree(digest->algorithm); + curlx_safefree(digest->nonce); + curlx_safefree(digest->cnonce); + curlx_safefree(digest->realm); + curlx_safefree(digest->opaque); + curlx_safefree(digest->qop); + curlx_safefree(digest->algorithm); digest->nc = 0; digest->algo = ALGO_MD5; /* default algorithm */ - digest->stale = FALSE; /* default means normal, not stale */ + digest->stale = FALSE; /* default means normal, not stale */ digest->userhash = FALSE; } -#endif /* !USE_WINDOWS_SSPI */ +#endif /* !USE_WINDOWS_SSPI */ -#endif /* !CURL_DISABLE_DIGEST_AUTH */ +#endif /* !CURL_DISABLE_DIGEST_AUTH */ diff --git a/lib/vauth/digest.h b/lib/vauth/digest.h index 99ce1f9138..5b8f3ae767 100644 --- a/lib/vauth/digest.h +++ b/lib/vauth/digest.h @@ -23,8 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include +#include "curl_setup.h" #ifndef CURL_DISABLE_DIGEST_AUTH diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index c74b8f8653..f0b6780fca 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -24,36 +24,27 @@ * RFC2831 DIGEST-MD5 authentication * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH) -#include - -#include "vauth.h" -#include "digest.h" -#include "../urldata.h" -#include "../curlx/warnless.h" -#include "../curlx/multibyte.h" -#include "../sendf.h" -#include "../strdup.h" -#include "../strcase.h" -#include "../strerror.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.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() -* -* This is used to evaluate if DIGEST is supported. -* -* Parameters: None -* -* Returns TRUE if DIGEST is supported by Windows SSPI. -*/ + * Curl_auth_is_digest_supported() + * + * This is used to evaluate if DIGEST is supported. + * + * Parameters: None + * + * Returns TRUE if DIGEST is supported by Windows SSPI. + */ bool Curl_auth_is_digest_supported(void) { PSecPkgInfo SecurityPackage; @@ -112,7 +103,6 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, SecBufferDesc resp_desc; SECURITY_STATUS status; unsigned long attrs; - TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ /* Ensure we have a valid challenge message */ if(!Curl_bufref_len(chlg)) { @@ -136,14 +126,14 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, Curl_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate our response buffer */ - output_token = malloc(token_max); + output_token = curlx_malloc(token_max); if(!output_token) return CURLE_OUT_OF_MEMORY; /* Generate our SPN */ spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); if(!spn) { - free(output_token); + curlx_free(output_token); return CURLE_OUT_OF_MEMORY; } @@ -151,8 +141,8 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, /* Populate our identity structure */ result = Curl_create_sspi_identity(userp, passwdp, &identity); if(result) { - free(spn); - free(output_token); + curlx_free(spn); + curlx_free(output_token); return result; } @@ -168,12 +158,12 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)), SECPKG_CRED_OUTBOUND, NULL, p_identity, NULL, NULL, - &credentials, &expiry); + &credentials, NULL); if(status != SEC_E_OK) { Curl_sspi_free_identity(p_identity); - free(spn); - free(output_token); + curlx_free(spn); + curlx_free(output_token); return CURLE_LOGIN_DENIED; } @@ -195,30 +185,26 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, /* Generate our response message */ status = Curl_pSecFn->InitializeSecurityContext(&credentials, NULL, spn, - 0, 0, 0, &chlg_desc, 0, - &context, &resp_desc, &attrs, - &expiry); + 0, 0, 0, &chlg_desc, 0, + &context, &resp_desc, &attrs, + NULL); if(status == SEC_I_COMPLETE_NEEDED || 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); - free(spn); - free(output_token); + curlx_free(spn); + curlx_free(output_token); 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; } @@ -234,7 +220,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, Curl_sspi_free_identity(p_identity); /* Free the SPN */ - free(spn); + curlx_free(spn); return result; } @@ -277,18 +263,18 @@ CURLcode Curl_override_sspi_http_realm(const char *chlg, if(!domain.tchar_ptr) return CURLE_OUT_OF_MEMORY; - dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr); + dup_domain.tchar_ptr = curlx_tcsdup(domain.tchar_ptr); if(!dup_domain.tchar_ptr) { - curlx_unicodefree(domain.tchar_ptr); + curlx_free(domain.tchar_ptr); return CURLE_OUT_OF_MEMORY; } - free(identity->Domain); + curlx_free(identity->Domain); identity->Domain = dup_domain.tbyte_ptr; identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr)); dup_domain.tchar_ptr = NULL; - curlx_unicodefree(domain.tchar_ptr); + curlx_free(domain.tchar_ptr); } else { /* Unknown specifier, ignore it! */ @@ -365,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; @@ -389,7 +375,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. @@ -411,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( @@ -430,7 +414,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* Allocate the output buffer according to the max token size as indicated by the security package */ - output_token = malloc(token_max); + output_token = curlx_malloc(token_max); if(!output_token) { return CURLE_OUT_OF_MEMORY; } @@ -443,10 +427,10 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, (passwdp && digest->passwd && Curl_timestrcmp(passwdp, digest->passwd))) { if(digest->http_context) { Curl_pSecFn->DeleteSecurityContext(digest->http_context); - Curl_safefree(digest->http_context); + curlx_safefree(digest->http_context); } - Curl_safefree(digest->user); - Curl_safefree(digest->passwd); + curlx_safefree(digest->user); + curlx_safefree(digest->passwd); } if(digest->http_context) { @@ -458,10 +442,10 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, chlg_buf[0].cbBuffer = 0; chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS; chlg_buf[1].pvBuffer = CURL_UNCONST(request); - chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request)); + chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *)request)); chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS; chlg_buf[2].pvBuffer = CURL_UNCONST(uripath); - chlg_buf[2].cbBuffer = curlx_uztoul(strlen((const char *) uripath)); + chlg_buf[2].cbBuffer = curlx_uztoul(strlen((const char *)uripath)); chlg_buf[3].BufferType = SECBUFFER_PKG_PARAMS; chlg_buf[3].pvBuffer = NULL; chlg_buf[3].cbBuffer = 0; @@ -474,10 +458,9 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, if(status == SEC_E_OK) output_token_len = chlg_buf[4].cbBuffer; else { /* delete the context so a new one can be made */ - infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx", - (long)status); + infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx", status); Curl_pSecFn->DeleteSecurityContext(digest->http_context); - Curl_safefree(digest->http_context); + curlx_safefree(digest->http_context); } } @@ -488,24 +471,24 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, SecBuffer resp_buf; SecBufferDesc resp_desc; unsigned long attrs; - TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ TCHAR *spn; /* free the copy of user/passwd used to make the previous identity */ - Curl_safefree(digest->user); - Curl_safefree(digest->passwd); + curlx_safefree(digest->user); + curlx_safefree(digest->passwd); if(userp && *userp) { /* Populate our identity structure */ if(Curl_create_sspi_identity(userp, passwdp, &identity)) { - free(output_token); + curlx_free(output_token); return CURLE_OUT_OF_MEMORY; } /* Populate our identity domain */ - if(Curl_override_sspi_http_realm((const char *) digest->input_token, + if(Curl_override_sspi_http_realm((const char *)digest->input_token, &identity)) { - free(output_token); + Curl_sspi_free_identity(&identity); + curlx_free(output_token); return CURLE_OUT_OF_MEMORY; } @@ -517,20 +500,22 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, p_identity = NULL; if(userp) { - digest->user = strdup(userp); + digest->user = curlx_strdup(userp); if(!digest->user) { - free(output_token); + curlx_free(output_token); + Curl_sspi_free_identity(p_identity); return CURLE_OUT_OF_MEMORY; } } if(passwdp) { - digest->passwd = strdup(passwdp); + digest->passwd = curlx_strdup(passwdp); if(!digest->passwd) { - free(output_token); - Curl_safefree(digest->user); + curlx_free(output_token); + Curl_sspi_free_identity(p_identity); + curlx_safefree(digest->user); return CURLE_OUT_OF_MEMORY; } } @@ -540,10 +525,10 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)), SECPKG_CRED_OUTBOUND, NULL, p_identity, NULL, NULL, - &credentials, &expiry); + &credentials, NULL); if(status != SEC_E_OK) { Curl_sspi_free_identity(p_identity); - free(output_token); + curlx_free(output_token); return CURLE_LOGIN_DENIED; } @@ -557,7 +542,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len); chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS; chlg_buf[1].pvBuffer = CURL_UNCONST(request); - chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request)); + chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *)request)); chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS; chlg_buf[2].pvBuffer = NULL; chlg_buf[2].cbBuffer = 0; @@ -570,56 +555,53 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, resp_buf.pvBuffer = output_token; resp_buf.cbBuffer = curlx_uztoul(token_max); - spn = curlx_convert_UTF8_to_tchar((const char *) uripath); + spn = curlx_convert_UTF8_to_tchar((const char *)uripath); if(!spn) { Curl_pSecFn->FreeCredentialsHandle(&credentials); Curl_sspi_free_identity(p_identity); - free(output_token); + curlx_free(output_token); return CURLE_OUT_OF_MEMORY; } /* Allocate our new context handle */ - digest->http_context = calloc(1, sizeof(CtxtHandle)); + digest->http_context = curlx_calloc(1, sizeof(CtxtHandle)); if(!digest->http_context) { - curlx_unicodefree(spn); + Curl_pSecFn->FreeCredentialsHandle(&credentials); + curlx_free(spn); Curl_sspi_free_identity(p_identity); - free(output_token); + curlx_free(output_token); return CURLE_OUT_OF_MEMORY; } /* Generate our response message */ status = Curl_pSecFn->InitializeSecurityContext(&credentials, NULL, - spn, - ISC_REQ_USE_HTTP_STYLE, 0, 0, - &chlg_desc, 0, - digest->http_context, - &resp_desc, &attrs, &expiry); - curlx_unicodefree(spn); + spn, + ISC_REQ_USE_HTTP_STYLE, 0, 0, + &chlg_desc, 0, + digest->http_context, + &resp_desc, &attrs, NULL); + curlx_free(spn); if(status == SEC_I_COMPLETE_NEEDED || 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); - free(output_token); + curlx_free(output_token); - Curl_safefree(digest->http_context); + curlx_safefree(digest->http_context); 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; } @@ -630,24 +612,15 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, Curl_sspi_free_identity(p_identity); } - resp = malloc(output_token_len + 1); + resp = curlx_memdup0((const char *)output_token, output_token_len); + curlx_free(output_token); if(!resp) { - free(output_token); - return CURLE_OUT_OF_MEMORY; } - /* Copy the generated response */ - memcpy(resp, output_token, output_token_len); - resp[output_token_len] = 0; - /* Return the response */ *outptr = resp; *outlen = output_token_len; - - /* Free the response buffer */ - free(output_token); - return CURLE_OK; } @@ -664,7 +637,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, void Curl_auth_digest_cleanup(struct digestdata *digest) { /* Free the input token */ - Curl_safefree(digest->input_token); + curlx_safefree(digest->input_token); /* Reset any variables */ digest->input_token_len = 0; @@ -672,12 +645,12 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) /* Delete security context */ if(digest->http_context) { Curl_pSecFn->DeleteSecurityContext(digest->http_context); - Curl_safefree(digest->http_context); + curlx_safefree(digest->http_context); } /* Free the copy of user/passwd used to make the identity for http_context */ - Curl_safefree(digest->user); - Curl_safefree(digest->passwd); + curlx_safefree(digest->user); + curlx_safefree(digest->passwd); } #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */ diff --git a/lib/vauth/gsasl.c b/lib/vauth/gsasl.c index 3684c8f4b2..958f4ffab7 100644 --- a/lib/vauth/gsasl.c +++ b/lib/vauth/gsasl.c @@ -23,24 +23,15 @@ * RFC5802 SCRAM-SHA-1 authentication * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_GSASL -#include - -#include "vauth.h" -#include "../urldata.h" -#include "../sendf.h" +#include "vauth/vauth.h" +#include "curl_trc.h" #include -/* The last 3 #include files should be in this order */ -#include "../curl_printf.h" -#include "../curl_memory.h" -#include "../memdebug.h" - bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, const char *mech, struct gsasldata *gsasl) @@ -49,7 +40,7 @@ bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, res = gsasl_init(&gsasl->ctx); if(res != GSASL_OK) { - failf(data, "gsasl init: %s\n", gsasl_strerror(res)); + failf(data, "gsasl init: %s", gsasl_strerror(res)); return FALSE; } @@ -74,7 +65,7 @@ CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, gsasl_property_set(gsasl->client, GSASL_AUTHID, userp); #if GSASL_VERSION_NUMBER >= 0x010b00 if(res != GSASL_OK) { - failf(data, "setting AUTHID failed: %s\n", gsasl_strerror(res)); + failf(data, "setting AUTHID failed: %s", gsasl_strerror(res)); return CURLE_OUT_OF_MEMORY; } #endif @@ -85,7 +76,7 @@ CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, gsasl_property_set(gsasl->client, GSASL_PASSWORD, passwdp); #if GSASL_VERSION_NUMBER >= 0x010b00 if(res != GSASL_OK) { - failf(data, "setting PASSWORD failed: %s\n", gsasl_strerror(res)); + failf(data, "setting PASSWORD failed: %s", gsasl_strerror(res)); return CURLE_OUT_OF_MEMORY; } #endif @@ -104,11 +95,10 @@ CURLcode Curl_auth_gsasl_token(struct Curl_easy *data, char *response; size_t outlen; - res = gsasl_step(gsasl->client, - (const char *) Curl_bufref_ptr(chlg), Curl_bufref_len(chlg), + res = gsasl_step(gsasl->client, Curl_bufref_ptr(chlg), Curl_bufref_len(chlg), &response, &outlen); if(res != GSASL_OK && res != GSASL_NEEDS_MORE) { - failf(data, "GSASL step: %s\n", gsasl_strerror(res)); + failf(data, "GSASL step: %s", gsasl_strerror(res)); return CURLE_BAD_CONTENT_ENCODING; } diff --git a/lib/vauth/krb5_gssapi.c b/lib/vauth/krb5_gssapi.c index fd46619d84..64c735be58 100644 --- a/lib/vauth/krb5_gssapi.c +++ b/lib/vauth/krb5_gssapi.c @@ -24,25 +24,16 @@ * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5) -#include +#include "vauth/vauth.h" +#include "curl_sasl.h" +#include "curl_gssapi.h" +#include "curl_trc.h" -#include "vauth.h" -#include "../curl_sasl.h" -#include "../urldata.h" -#include "../curl_gssapi.h" -#include "../sendf.h" -#include "../curl_printf.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" - -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -121,12 +112,12 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, Curl_gss_log_error(data, "gss_import_name() failed: ", major_status, minor_status); - free(spn); + curlx_free(spn); return CURLE_AUTH_ERROR; } - free(spn); + curlx_free(spn); } if(chlg) { @@ -160,11 +151,11 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, } if(output_token.value && output_token.length) { - result = Curl_bufref_memdup(out, output_token.value, output_token.length); + result = Curl_bufref_memdup0(out, output_token.value, output_token.length); gss_release_buffer(&unused_status, &output_token); } else - Curl_bufref_set(out, mutual_auth ? "": NULL, 0, NULL); + Curl_bufref_set(out, mutual_auth ? "" : NULL, 0, NULL); return result; } @@ -226,6 +217,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ if(output_token.length != 4) { infof(data, "GSSAPI handshake failure (invalid security data)"); + gss_release_buffer(&unused_status, &output_token); return CURLE_BAD_CONTENT_ENCODING; } @@ -248,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; } @@ -258,7 +250,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, messagelen = 4; if(authzid) messagelen += strlen(authzid); - message = malloc(messagelen); + message = curlx_malloc(messagelen); if(!message) return CURLE_OUT_OF_MEMORY; @@ -285,17 +277,17 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, if(GSS_ERROR(major_status)) { Curl_gss_log_error(data, "gss_wrap() failed: ", major_status, minor_status); - free(message); + curlx_free(message); return CURLE_AUTH_ERROR; } /* Return the response. */ - result = Curl_bufref_memdup(out, output_token.value, output_token.length); + result = Curl_bufref_memdup0(out, output_token.value, output_token.length); /* Free the output buffer */ gss_release_buffer(&unused_status, &output_token); /* Free the message buffer */ - free(message); + curlx_free(message); return result; } @@ -328,7 +320,7 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) } } -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c index ea26f82750..e7491be022 100644 --- a/lib/vauth/krb5_sspi.c +++ b/lib/vauth/krb5_sspi.c @@ -23,22 +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 - -#include "vauth.h" -#include "../urldata.h" -#include "../curlx/warnless.h" -#include "../curlx/multibyte.h" -#include "../sendf.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" +#include "vauth/vauth.h" +#include "curl_trc.h" /* * Curl_auth_is_gssapi_supported() @@ -107,7 +97,6 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, SecBufferDesc resp_desc; SECURITY_STATUS status; unsigned long attrs; - TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ if(!krb5->spn) { /* Generate our SPN */ @@ -132,7 +121,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, Curl_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate our response buffer */ - krb5->output_token = malloc(krb5->token_max); + krb5->output_token = curlx_malloc(krb5->token_max); if(!krb5->output_token) return CURLE_OUT_OF_MEMORY; } @@ -153,7 +142,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, krb5->p_identity = NULL; /* Allocate our credentials handle */ - krb5->credentials = calloc(1, sizeof(CredHandle)); + krb5->credentials = curlx_calloc(1, sizeof(CredHandle)); if(!krb5->credentials) return CURLE_OUT_OF_MEMORY; @@ -162,12 +151,12 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_KERBEROS)), SECPKG_CRED_OUTBOUND, NULL, krb5->p_identity, NULL, NULL, - krb5->credentials, &expiry); + krb5->credentials, NULL); if(status != SEC_E_OK) return CURLE_LOGIN_DENIED; /* Allocate our new context handle */ - krb5->context = calloc(1, sizeof(CtxtHandle)); + krb5->context = curlx_calloc(1, sizeof(CtxtHandle)); if(!krb5->context) return CURLE_OUT_OF_MEMORY; } @@ -204,8 +193,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, 0, SECURITY_NATIVE_DREP, chlg ? &chlg_desc : NULL, 0, &context, - &resp_desc, &attrs, - &expiry); + &resp_desc, &attrs, NULL); if(status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; @@ -220,7 +208,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, } if(resp_buf.cbBuffer) { - result = Curl_bufref_memdup(out, resp_buf.pvBuffer, resp_buf.cbBuffer); + result = Curl_bufref_memdup0(out, resp_buf.pvBuffer, resp_buf.cbBuffer); } else if(mutual_auth) Curl_bufref_set(out, "", 0, NULL); @@ -240,7 +228,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, * * data [in] - The session handle. * authzid [in] - The authorization identity if some. - * chlg [in] - The optional challenge message. + * chlg [in] - The challenge message. * krb5 [in/out] - The Kerberos 5 data struct being used and modified. * out [out] - The result storage. * @@ -252,6 +240,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, struct kerberos5data *krb5, struct bufref *out) { + CURLcode result = CURLE_OK; size_t offset = 0; size_t messagelen = 0; size_t appdatalen = 0; @@ -270,11 +259,8 @@ 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)) { infof(data, "GSSAPI handshake failure (empty security message)"); return CURLE_BAD_CONTENT_ENCODING; @@ -282,8 +268,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, /* Get our response size information */ status = Curl_pSecFn->QueryContextAttributes(krb5->context, - SECPKG_ATTR_SIZES, - &sizes); + SECPKG_ATTR_SIZES, &sizes); if(status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; @@ -333,14 +318,14 @@ 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; } /* Allocate the trailer */ - trailer = malloc(sizes.cbSecurityTrailer); + trailer = curlx_malloc(sizes.cbSecurityTrailer); if(!trailer) return CURLE_OUT_OF_MEMORY; @@ -348,11 +333,10 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, messagelen = 4; if(authzid) messagelen += strlen(authzid); - message = malloc(messagelen); + message = curlx_malloc(messagelen); if(!message) { - free(trailer); - - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto out; } /* Populate the message with the security layer and client supported receive @@ -368,12 +352,10 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, memcpy(message + 4, authzid, messagelen - 4); /* Allocate the padding */ - padding = malloc(sizes.cbBlockSize); + padding = curlx_malloc(sizes.cbBlockSize); if(!padding) { - free(message); - free(trailer); - - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto out; } /* Setup the "authentication data" security buffer */ @@ -392,28 +374,22 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, /* Encrypt the data */ status = Curl_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT, - &wrap_desc, 0); + &wrap_desc, 0); if(status != SEC_E_OK) { - free(padding); - free(message); - free(trailer); - if(status == SEC_E_INSUFFICIENT_MEMORY) - return CURLE_OUT_OF_MEMORY; - - return CURLE_AUTH_ERROR; + result = CURLE_OUT_OF_MEMORY; + else + result = CURLE_AUTH_ERROR; + goto out; } /* Allocate the encryption (wrap) buffer */ appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer + wrap_buf[2].cbBuffer; - appdata = malloc(appdatalen); + appdata = curlx_malloc(appdatalen); if(!appdata) { - free(padding); - free(message); - free(trailer); - - return CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; + goto out; } /* Populate the encryption buffer */ @@ -423,10 +399,14 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, offset += wrap_buf[1].cbBuffer; memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer); +out: /* Free all of our local buffers */ - free(padding); - free(message); - free(trailer); + curlx_free(padding); + curlx_free(message); + curlx_free(trailer); + + if(result) + return result; /* Return the response. */ Curl_bufref_set(out, appdata, appdatalen, curl_free); @@ -448,14 +428,14 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) /* Free our security context */ if(krb5->context) { Curl_pSecFn->DeleteSecurityContext(krb5->context); - free(krb5->context); + curlx_free(krb5->context); krb5->context = NULL; } /* Free our credentials handle */ if(krb5->credentials) { Curl_pSecFn->FreeCredentialsHandle(krb5->credentials); - free(krb5->credentials); + curlx_free(krb5->credentials); krb5->credentials = NULL; } @@ -464,8 +444,8 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) krb5->p_identity = NULL; /* Free the SPN and output token */ - Curl_safefree(krb5->spn); - Curl_safefree(krb5->output_token); + curlx_safefree(krb5->spn); + curlx_safefree(krb5->output_token); /* Reset any variables */ krb5->token_max = 0; diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c index 6164cd388b..1e485ed34d 100644 --- a/lib/vauth/ntlm.c +++ b/lib/vauth/ntlm.c @@ -21,8 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI) @@ -35,25 +34,15 @@ #define DEBUG_ME 0 -#include "../urldata.h" -#include "../sendf.h" -#include "../curl_ntlm_core.h" -#include "../curl_gethostname.h" -#include "../curlx/multibyte.h" -#include "../curl_md5.h" -#include "../curlx/warnless.h" -#include "../rand.h" -#include "../vtls/vtls.h" -#include "../strdup.h" - -#include "vauth.h" -#include "../curl_endian.h" -#include "../curl_printf.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.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 @@ -61,174 +50,179 @@ /* Flag bits definitions based on https://davenport.sourceforge.net/ntlm.html */ -#define NTLMFLAG_NEGOTIATE_UNICODE (1<<0) +#define NTLMFLAG_NEGOTIATE_UNICODE (1 << 0) /* Indicates that Unicode strings are supported for use in security buffer data. */ -#define NTLMFLAG_NEGOTIATE_OEM (1<<1) +#define NTLMFLAG_NEGOTIATE_OEM (1 << 1) /* Indicates that OEM strings are supported for use in security buffer data. */ -#define NTLMFLAG_REQUEST_TARGET (1<<2) +#define NTLMFLAG_REQUEST_TARGET (1 << 2) /* Requests that the server's authentication realm be included in the Type 2 message. */ -/* unknown (1<<3) */ -#define NTLMFLAG_NEGOTIATE_SIGN (1<<4) +#if DEBUG_ME +/* unknown (1 << 3) */ +#define NTLMFLAG_NEGOTIATE_SIGN (1 << 4) /* Specifies that authenticated communication between the client and server should carry a digital signature (message integrity). */ -#define NTLMFLAG_NEGOTIATE_SEAL (1<<5) +#define NTLMFLAG_NEGOTIATE_SEAL (1 << 5) /* Specifies that authenticated communication between the client and server should be encrypted (message confidentiality). */ -#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1<<6) +#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1 << 6) /* Indicates that datagram authentication is being used. */ -#define NTLMFLAG_NEGOTIATE_LM_KEY (1<<7) +#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) +#define NTLMFLAG_NEGOTIATE_NTLM_KEY (1 << 9) /* Indicates that NTLM authentication is being used. */ -/* unknown (1<<10) */ +#if DEBUG_ME +/* unknown (1 << 10) */ -#define NTLMFLAG_NEGOTIATE_ANONYMOUS (1<<11) +#define NTLMFLAG_NEGOTIATE_ANONYMOUS (1 << 11) /* Sent by the client in the Type 3 message to indicate that an anonymous context has been established. This also affects the response fields. */ -#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1<<12) +#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1 << 12) /* Sent by the client in the Type 1 message to indicate that a desired authentication realm is included in the message. */ -#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1<<13) +#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1 << 13) /* Sent by the client in the Type 1 message to indicate that the client workstation's name is included in the message. */ -#define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1<<14) +#define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1 << 14) /* 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) +#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1 << 15) /* Indicates that authenticated communication between the client and server should be signed with a "dummy" signature. */ -#define NTLMFLAG_TARGET_TYPE_DOMAIN (1<<16) +#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. */ -#define NTLMFLAG_TARGET_TYPE_SERVER (1<<17) +#define NTLMFLAG_TARGET_TYPE_SERVER (1 << 17) /* Sent by the server in the Type 2 message to indicate that the target authentication realm is a server. */ -#define NTLMFLAG_TARGET_TYPE_SHARE (1<<18) +#define NTLMFLAG_TARGET_TYPE_SHARE (1 << 18) /* 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) +#define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1 << 19) /* Indicates that the NTLM2 signing and sealing scheme should be used for protecting authenticated communications. */ -#define NTLMFLAG_REQUEST_INIT_RESPONSE (1<<20) +#if DEBUG_ME +#define NTLMFLAG_REQUEST_INIT_RESPONSE (1 << 20) /* unknown purpose */ -#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1<<21) +#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1 << 21) /* unknown purpose */ -#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1<<22) +#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1 << 22) /* unknown purpose */ +#endif -#define NTLMFLAG_NEGOTIATE_TARGET_INFO (1<<23) +#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) */ /* unknown (1<27) */ /* unknown (1<28) */ -#define NTLMFLAG_NEGOTIATE_128 (1<<29) +#define NTLMFLAG_NEGOTIATE_128 (1 << 29) /* Indicates that 128-bit encryption is supported. */ -#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1<<30) -/* Indicates that the client will provide an encrypted master key in +#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1 << 30) +/* Indicates that the client provides an encrypted master key in the "Session Key" field of the Type 3 message. */ -#define NTLMFLAG_NEGOTIATE_56 (1<<31) +#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 +#define DEBUG_OUT(x) x static void ntlm_print_flags(FILE *handle, unsigned long flags) { if(flags & NTLMFLAG_NEGOTIATE_UNICODE) - fprintf(handle, "NTLMFLAG_NEGOTIATE_UNICODE "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_UNICODE "); if(flags & NTLMFLAG_NEGOTIATE_OEM) - fprintf(handle, "NTLMFLAG_NEGOTIATE_OEM "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_OEM "); if(flags & NTLMFLAG_REQUEST_TARGET) - fprintf(handle, "NTLMFLAG_REQUEST_TARGET "); + curl_mfprintf(handle, "NTLMFLAG_REQUEST_TARGET "); if(flags & (1 << 3)) - fprintf(handle, "NTLMFLAG_UNKNOWN_3 "); + curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_3 "); if(flags & NTLMFLAG_NEGOTIATE_SIGN) - fprintf(handle, "NTLMFLAG_NEGOTIATE_SIGN "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_SIGN "); if(flags & NTLMFLAG_NEGOTIATE_SEAL) - fprintf(handle, "NTLMFLAG_NEGOTIATE_SEAL "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_SEAL "); if(flags & NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE) - fprintf(handle, "NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE "); if(flags & NTLMFLAG_NEGOTIATE_LM_KEY) - fprintf(handle, "NTLMFLAG_NEGOTIATE_LM_KEY "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_LM_KEY "); if(flags & NTLMFLAG_NEGOTIATE_NTLM_KEY) - fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM_KEY "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM_KEY "); if(flags & (1 << 10)) - fprintf(handle, "NTLMFLAG_UNKNOWN_10 "); + curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_10 "); if(flags & NTLMFLAG_NEGOTIATE_ANONYMOUS) - fprintf(handle, "NTLMFLAG_NEGOTIATE_ANONYMOUS "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_ANONYMOUS "); if(flags & NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED) - fprintf(handle, "NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED "); if(flags & NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED) - fprintf(handle, "NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED "); if(flags & NTLMFLAG_NEGOTIATE_LOCAL_CALL) - fprintf(handle, "NTLMFLAG_NEGOTIATE_LOCAL_CALL "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_LOCAL_CALL "); if(flags & NTLMFLAG_NEGOTIATE_ALWAYS_SIGN) - fprintf(handle, "NTLMFLAG_NEGOTIATE_ALWAYS_SIGN "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_ALWAYS_SIGN "); if(flags & NTLMFLAG_TARGET_TYPE_DOMAIN) - fprintf(handle, "NTLMFLAG_TARGET_TYPE_DOMAIN "); + curl_mfprintf(handle, "NTLMFLAG_TARGET_TYPE_DOMAIN "); if(flags & NTLMFLAG_TARGET_TYPE_SERVER) - fprintf(handle, "NTLMFLAG_TARGET_TYPE_SERVER "); + curl_mfprintf(handle, "NTLMFLAG_TARGET_TYPE_SERVER "); if(flags & NTLMFLAG_TARGET_TYPE_SHARE) - fprintf(handle, "NTLMFLAG_TARGET_TYPE_SHARE "); + curl_mfprintf(handle, "NTLMFLAG_TARGET_TYPE_SHARE "); if(flags & NTLMFLAG_NEGOTIATE_NTLM2_KEY) - fprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM2_KEY "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_NTLM2_KEY "); if(flags & NTLMFLAG_REQUEST_INIT_RESPONSE) - fprintf(handle, "NTLMFLAG_REQUEST_INIT_RESPONSE "); + curl_mfprintf(handle, "NTLMFLAG_REQUEST_INIT_RESPONSE "); if(flags & NTLMFLAG_REQUEST_ACCEPT_RESPONSE) - fprintf(handle, "NTLMFLAG_REQUEST_ACCEPT_RESPONSE "); + curl_mfprintf(handle, "NTLMFLAG_REQUEST_ACCEPT_RESPONSE "); if(flags & NTLMFLAG_REQUEST_NONNT_SESSION_KEY) - fprintf(handle, "NTLMFLAG_REQUEST_NONNT_SESSION_KEY "); + curl_mfprintf(handle, "NTLMFLAG_REQUEST_NONNT_SESSION_KEY "); if(flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) - fprintf(handle, "NTLMFLAG_NEGOTIATE_TARGET_INFO "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_TARGET_INFO "); if(flags & (1 << 24)) - fprintf(handle, "NTLMFLAG_UNKNOWN_24 "); + curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_24 "); if(flags & (1 << 25)) - fprintf(handle, "NTLMFLAG_UNKNOWN_25 "); + curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_25 "); if(flags & (1 << 26)) - fprintf(handle, "NTLMFLAG_UNKNOWN_26 "); + curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_26 "); if(flags & (1 << 27)) - fprintf(handle, "NTLMFLAG_UNKNOWN_27 "); + curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_27 "); if(flags & (1 << 28)) - fprintf(handle, "NTLMFLAG_UNKNOWN_28 "); + curl_mfprintf(handle, "NTLMFLAG_UNKNOWN_28 "); if(flags & NTLMFLAG_NEGOTIATE_128) - fprintf(handle, "NTLMFLAG_NEGOTIATE_128 "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_128 "); if(flags & NTLMFLAG_NEGOTIATE_KEY_EXCHANGE) - fprintf(handle, "NTLMFLAG_NEGOTIATE_KEY_EXCHANGE "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_KEY_EXCHANGE "); if(flags & NTLMFLAG_NEGOTIATE_56) - fprintf(handle, "NTLMFLAG_NEGOTIATE_56 "); + curl_mfprintf(handle, "NTLMFLAG_NEGOTIATE_56 "); } static void ntlm_print_hex(FILE *handle, const char *buf, size_t len) @@ -237,12 +231,12 @@ static void ntlm_print_hex(FILE *handle, const char *buf, size_t len) (void)handle; - fprintf(stderr, "0x"); + curl_mfprintf(stderr, "0x"); while(len-- > 0) - fprintf(stderr, "%02.2x", (unsigned int)*p++); + curl_mfprintf(stderr, "%02.2x", (unsigned int)*p++); } #else -# define DEBUG_OUT(x) Curl_nop_stmt +#define DEBUG_OUT(x) Curl_nop_stmt #endif /* @@ -265,13 +259,9 @@ static CURLcode ntlm_decode_type2_target(struct Curl_easy *data, { unsigned short target_info_len = 0; unsigned int target_info_offset = 0; - const unsigned char *type2 = Curl_bufref_ptr(type2ref); + 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]); @@ -284,9 +274,9 @@ static CURLcode ntlm_decode_type2_target(struct Curl_easy *data, return CURLE_BAD_CONTENT_ENCODING; } - free(ntlm->target_info); /* replace any previous data */ - ntlm->target_info = Curl_memdup(&type2[target_info_offset], - target_info_len); + curlx_free(ntlm->target_info); /* replace any previous data */ + ntlm->target_info = curlx_memdup(&type2[target_info_offset], + target_info_len); if(!ntlm->target_info) return CURLE_OUT_OF_MEMORY; } @@ -365,13 +355,9 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, */ CURLcode result = CURLE_OK; - const unsigned char *type2 = Curl_bufref_ptr(type2ref); + 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) || @@ -394,12 +380,12 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, } DEBUG_OUT({ - fprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags); + curl_mfprintf(stderr, "**** TYPE2 header flags=0x%08.8lx ", ntlm->flags); ntlm_print_flags(stderr, ntlm->flags); - fprintf(stderr, "\n nonce="); + curl_mfprintf(stderr, "\n nonce="); ntlm_print_hex(stderr, (char *)ntlm->nonce, 8); - fprintf(stderr, "\n****\n"); - fprintf(stderr, "**** Header %s\n ", header); + curl_mfprintf(stderr, "\n****\n"); + curl_mfprintf(stderr, "**** Header %s\n ", header); }); return result; @@ -412,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'; } } @@ -438,7 +424,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, const char *userp, const char *passwdp, const char *service, - const char *hostname, + const char *host, struct ntlmdata *ntlm, struct bufref *out) { @@ -459,7 +445,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, size_t size; char *ntlmbuf; - const char *host = ""; /* empty */ + const char *hostname = ""; /* empty */ const char *domain = ""; /* empty */ size_t hostlen = 0; size_t domlen = 0; @@ -470,42 +456,42 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, (void)userp; (void)passwdp; (void)service; - (void)hostname; + (void)host; /* Clean up any former leftovers and initialise to defaults */ Curl_auth_cleanup_ntlm(ntlm); - ntlmbuf = aprintf(NTLMSSP_SIGNATURE "%c" - "\x01%c%c%c" /* 32-bit type = 1 */ - "%c%c%c%c" /* 32-bit NTLM flag field */ - "%c%c" /* domain length */ - "%c%c" /* domain allocated space */ - "%c%c" /* domain name offset */ - "%c%c" /* 2 zeroes */ - "%c%c" /* host length */ - "%c%c" /* host allocated space */ - "%c%c" /* hostname offset */ - "%c%c" /* 2 zeroes */ - "%s" /* hostname */ - "%s", /* domain string */ - 0, /* trailing zero */ - 0, 0, 0, /* part of type-1 long */ + ntlmbuf = curl_maprintf(NTLMSSP_SIGNATURE "%c" + "\x01%c%c%c" /* 32-bit type = 1 */ + "%c%c%c%c" /* 32-bit NTLM flag field */ + "%c%c" /* domain length */ + "%c%c" /* domain allocated space */ + "%c%c" /* domain name offset */ + "%c%c" /* 2 zeroes */ + "%c%c" /* host length */ + "%c%c" /* host allocated space */ + "%c%c" /* hostname offset */ + "%c%c" /* 2 zeroes */ + "%s" /* hostname */ + "%s", /* domain string */ + 0, /* trailing zero */ + 0, 0, 0, /* part of type-1 long */ - LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM | - NTLMFLAG_REQUEST_TARGET | - NTLMFLAG_NEGOTIATE_NTLM_KEY | - NTLMFLAG_NEGOTIATE_NTLM2_KEY | - NTLMFLAG_NEGOTIATE_ALWAYS_SIGN), - SHORTPAIR(domlen), - SHORTPAIR(domlen), - SHORTPAIR(domoff), - 0, 0, - SHORTPAIR(hostlen), - SHORTPAIR(hostlen), - SHORTPAIR(hostoff), - 0, 0, - host, /* this is empty */ - domain /* this is empty */); + LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM | + NTLMFLAG_REQUEST_TARGET | + NTLMFLAG_NEGOTIATE_NTLM_KEY | + NTLMFLAG_NEGOTIATE_NTLM2_KEY | + NTLMFLAG_NEGOTIATE_ALWAYS_SIGN), + SHORTPAIR(domlen), + SHORTPAIR(domlen), + SHORTPAIR(domoff), + 0, 0, + SHORTPAIR(hostlen), + SHORTPAIR(hostlen), + SHORTPAIR(hostoff), + 0, 0, + hostname, /* this is empty */ + domain /* this is empty */); if(!ntlmbuf) return CURLE_OUT_OF_MEMORY; @@ -514,25 +500,25 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, size = 32 + hostlen + domlen; DEBUG_OUT({ - fprintf(stderr, "* TYPE1 header flags=0x%02.2x%02.2x%02.2x%02.2x " - "0x%08.8x ", - LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM | - NTLMFLAG_REQUEST_TARGET | - NTLMFLAG_NEGOTIATE_NTLM_KEY | - NTLMFLAG_NEGOTIATE_NTLM2_KEY | - NTLMFLAG_NEGOTIATE_ALWAYS_SIGN), - NTLMFLAG_NEGOTIATE_OEM | - NTLMFLAG_REQUEST_TARGET | - NTLMFLAG_NEGOTIATE_NTLM_KEY | - NTLMFLAG_NEGOTIATE_NTLM2_KEY | - NTLMFLAG_NEGOTIATE_ALWAYS_SIGN); + curl_mfprintf(stderr, "* TYPE1 header flags=0x%02.2x%02.2x%02.2x%02.2x " + "0x%08.8x ", + LONGQUARTET(NTLMFLAG_NEGOTIATE_OEM | + NTLMFLAG_REQUEST_TARGET | + NTLMFLAG_NEGOTIATE_NTLM_KEY | + NTLMFLAG_NEGOTIATE_NTLM2_KEY | + NTLMFLAG_NEGOTIATE_ALWAYS_SIGN), + NTLMFLAG_NEGOTIATE_OEM | + NTLMFLAG_REQUEST_TARGET | + NTLMFLAG_NEGOTIATE_NTLM_KEY | + NTLMFLAG_NEGOTIATE_NTLM2_KEY | + NTLMFLAG_NEGOTIATE_ALWAYS_SIGN); ntlm_print_flags(stderr, NTLMFLAG_NEGOTIATE_OEM | NTLMFLAG_REQUEST_TARGET | NTLMFLAG_NEGOTIATE_NTLM_KEY | NTLMFLAG_NEGOTIATE_NTLM2_KEY | NTLMFLAG_NEGOTIATE_ALWAYS_SIGN); - fprintf(stderr, "\n****\n"); + curl_mfprintf(stderr, "\n****\n"); }); Curl_bufref_set(out, ntlmbuf, size, curl_free); @@ -587,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 @@ -693,82 +679,84 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, hostoff = useroff + userlen; /* Create the big type-3 message binary blob */ - size = msnprintf((char *)ntlmbuf, NTLM_BUFSIZE, - NTLMSSP_SIGNATURE "%c" - "\x03%c%c%c" /* 32-bit type = 3 */ + size = curl_msnprintf((char *)ntlmbuf, NTLM_BUFSIZE, + NTLMSSP_SIGNATURE "%c" + "\x03%c%c%c" /* 32-bit type = 3 */ - "%c%c" /* LanManager length */ - "%c%c" /* LanManager allocated space */ - "%c%c" /* LanManager offset */ - "%c%c" /* 2 zeroes */ + "%c%c" /* LanManager length */ + "%c%c" /* LanManager allocated space */ + "%c%c" /* LanManager offset */ + "%c%c" /* 2 zeroes */ - "%c%c" /* NT-response length */ - "%c%c" /* NT-response allocated space */ - "%c%c" /* NT-response offset */ - "%c%c" /* 2 zeroes */ + "%c%c" /* NT-response length */ + "%c%c" /* NT-response allocated space */ + "%c%c" /* NT-response offset */ + "%c%c" /* 2 zeroes */ - "%c%c" /* domain length */ - "%c%c" /* domain allocated space */ - "%c%c" /* domain name offset */ - "%c%c" /* 2 zeroes */ + "%c%c" /* domain length */ + "%c%c" /* domain allocated space */ + "%c%c" /* domain name offset */ + "%c%c" /* 2 zeroes */ - "%c%c" /* user length */ - "%c%c" /* user allocated space */ - "%c%c" /* user offset */ - "%c%c" /* 2 zeroes */ + "%c%c" /* user length */ + "%c%c" /* user allocated space */ + "%c%c" /* user offset */ + "%c%c" /* 2 zeroes */ - "%c%c" /* host length */ - "%c%c" /* host allocated space */ - "%c%c" /* host offset */ - "%c%c" /* 2 zeroes */ + "%c%c" /* host length */ + "%c%c" /* host allocated space */ + "%c%c" /* host offset */ + "%c%c" /* 2 zeroes */ - "%c%c" /* session key length (unknown purpose) */ - "%c%c" /* session key allocated space (unknown purpose) */ - "%c%c" /* session key offset (unknown purpose) */ - "%c%c" /* 2 zeroes */ + "%c%c" /* session key length (unknown purpose) */ + "%c%c" /* session key allocated space + (unknown purpose) */ + "%c%c" /* session key offset (unknown purpose) */ + "%c%c" /* 2 zeroes */ - "%c%c%c%c", /* flags */ + "%c%c%c%c", /* flags */ - /* domain string */ - /* user string */ - /* host string */ - /* LanManager response */ - /* NT response */ + /* domain string */ + /* user string */ + /* host string */ + /* LanManager response */ + /* NT response */ - 0, /* null-termination */ - 0, 0, 0, /* type-3 long, the 24 upper bits */ + 0, /* null-termination */ + 0, 0, 0, /* type-3 long, the 24 upper bits */ - SHORTPAIR(0x18), /* LanManager response length, twice */ - SHORTPAIR(0x18), - SHORTPAIR(lmrespoff), - 0x0, 0x0, + SHORTPAIR(0x18), /* LanManager response length, + twice */ + SHORTPAIR(0x18), + SHORTPAIR(lmrespoff), + 0x0, 0x0, - SHORTPAIR(ntresplen), /* NT-response length, twice */ - SHORTPAIR(ntresplen), - SHORTPAIR(ntrespoff), - 0x0, 0x0, + SHORTPAIR(ntresplen), /* NT-response length, twice */ + SHORTPAIR(ntresplen), + SHORTPAIR(ntrespoff), + 0x0, 0x0, - SHORTPAIR(domlen), - SHORTPAIR(domlen), - SHORTPAIR(domoff), - 0x0, 0x0, + SHORTPAIR(domlen), + SHORTPAIR(domlen), + SHORTPAIR(domoff), + 0x0, 0x0, - SHORTPAIR(userlen), - SHORTPAIR(userlen), - SHORTPAIR(useroff), - 0x0, 0x0, + SHORTPAIR(userlen), + SHORTPAIR(userlen), + SHORTPAIR(useroff), + 0x0, 0x0, - SHORTPAIR(hostlen), - SHORTPAIR(hostlen), - SHORTPAIR(hostoff), - 0x0, 0x0, + SHORTPAIR(hostlen), + SHORTPAIR(hostlen), + SHORTPAIR(hostoff), + 0x0, 0x0, - 0x0, 0x0, - 0x0, 0x0, - 0x0, 0x0, - 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, + 0x0, 0x0, - LONGQUARTET(ntlm->flags)); + LONGQUARTET(ntlm->flags)); DEBUGASSERT(size == 64); DEBUGASSERT(size == (size_t)lmrespoff); @@ -780,38 +768,38 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, } DEBUG_OUT({ - fprintf(stderr, "**** TYPE3 header lmresp="); + curl_mfprintf(stderr, "**** TYPE3 header lmresp="); ntlm_print_hex(stderr, (char *)&ntlmbuf[lmrespoff], 0x18); }); /* ntresplen + size should not be risking an integer overflow here */ if(ntresplen + size > sizeof(ntlmbuf)) { failf(data, "incoming NTLM message too big"); - return CURLE_OUT_OF_MEMORY; + result = CURLE_TOO_LARGE; + goto error; } DEBUGASSERT(size == (size_t)ntrespoff); memcpy(&ntlmbuf[size], ptr_ntresp, ntresplen); size += ntresplen; DEBUG_OUT({ - fprintf(stderr, "\n ntresp="); + curl_mfprintf(stderr, "\n ntresp="); ntlm_print_hex(stderr, (char *)&ntlmbuf[ntrespoff], ntresplen); }); - free(ntlmv2resp);/* Free the dynamic buffer allocated for NTLMv2 */ - DEBUG_OUT({ - fprintf(stderr, "\n flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ", - LONGQUARTET(ntlm->flags), ntlm->flags); + curl_mfprintf(stderr, "\n flags=0x%02.2x%02.2x%02.2x%02.2x 0x%08.8x ", + LONGQUARTET(ntlm->flags), ntlm->flags); ntlm_print_flags(stderr, ntlm->flags); - fprintf(stderr, "\n****\n"); + curl_mfprintf(stderr, "\n****\n"); }); /* Make sure that the domain, user and host strings fit in the buffer before we copy them there. */ if(size + userlen + domlen + hostlen >= NTLM_BUFSIZE) { - failf(data, "user + domain + hostname too big"); - return CURLE_OUT_OF_MEMORY; + failf(data, "user + domain + hostname too big for NTLM"); + result = CURLE_TOO_LARGE; + goto error; } DEBUGASSERT(size == domoff); @@ -839,7 +827,10 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, size += hostlen; /* Return the binary blob. */ - result = Curl_bufref_memdup(out, ntlmbuf, size); + result = Curl_bufref_memdup0(out, ntlmbuf, size); + +error: + curlx_free(ntlmv2resp); /* Free the dynamic buffer allocated for NTLMv2 */ Curl_auth_cleanup_ntlm(ntlm); @@ -859,7 +850,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) { /* Free the target info */ - Curl_safefree(ntlm->target_info); + curlx_safefree(ntlm->target_info); /* Reset any variables */ ntlm->target_info_len = 0; diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index d45e2db38e..4c41eb21f4 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -21,24 +21,14 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(USE_WINDOWS_SSPI) && defined(USE_NTLM) -#include - -#include "vauth.h" -#include "../urldata.h" -#include "../curl_ntlm_core.h" -#include "../curlx/warnless.h" -#include "../curlx/multibyte.h" -#include "../sendf.h" -#include "../strdup.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" +#include "vauth/vauth.h" +#include "curl_ntlm_core.h" +#include "curl_trc.h" +#include "curlx/strdup.h" /* * Curl_auth_is_ntlm_supported() @@ -98,7 +88,6 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, SecBufferDesc type_1_desc; SECURITY_STATUS status; unsigned long attrs; - TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ /* Clean up any former leftovers and initialise to defaults */ Curl_auth_cleanup_ntlm(ntlm); @@ -118,7 +107,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, Curl_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate our output buffer */ - ntlm->output_token = malloc(ntlm->token_max); + ntlm->output_token = curlx_malloc(ntlm->token_max); if(!ntlm->output_token) return CURLE_OUT_OF_MEMORY; @@ -138,7 +127,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, ntlm->p_identity = NULL; /* Allocate our credentials handle */ - ntlm->credentials = calloc(1, sizeof(CredHandle)); + ntlm->credentials = curlx_calloc(1, sizeof(CredHandle)); if(!ntlm->credentials) return CURLE_OUT_OF_MEMORY; @@ -147,12 +136,12 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NTLM)), SECPKG_CRED_OUTBOUND, NULL, ntlm->p_identity, NULL, NULL, - ntlm->credentials, &expiry); + ntlm->credentials, NULL); if(status != SEC_E_OK) return CURLE_LOGIN_DENIED; /* Allocate our new context handle */ - ntlm->context = calloc(1, sizeof(CtxtHandle)); + ntlm->context = curlx_calloc(1, sizeof(CtxtHandle)); if(!ntlm->context) return CURLE_OUT_OF_MEMORY; @@ -170,13 +159,13 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, /* Generate our type-1 message */ status = Curl_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL, - ntlm->spn, - 0, 0, SECURITY_NETWORK_DREP, - NULL, 0, - ntlm->context, &type_1_desc, - &attrs, &expiry); + ntlm->spn, + 0, 0, SECURITY_NETWORK_DREP, + NULL, 0, + ntlm->context, &type_1_desc, + &attrs, NULL); if(status == SEC_I_COMPLETE_NEEDED || - status == SEC_I_COMPLETE_AND_CONTINUE) + status == SEC_I_COMPLETE_AND_CONTINUE) Curl_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc); else if(status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; @@ -202,31 +191,26 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, - const struct bufref *type2, + const struct bufref *type2ref, struct ntlmdata *ntlm) { -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif - /* Ensure we have a valid type-2 message */ - if(!Curl_bufref_len(type2)) { + if(!Curl_bufref_len(type2ref)) { infof(data, "NTLM handshake failure (empty type-2 message)"); return CURLE_BAD_CONTENT_ENCODING; } /* Store the challenge for later use */ - ntlm->input_token = Curl_memdup0((const char *)Curl_bufref_ptr(type2), - Curl_bufref_len(type2)); + ntlm->input_token = curlx_memdup0(Curl_bufref_ptr(type2ref), + Curl_bufref_len(type2ref)); if(!ntlm->input_token) return CURLE_OUT_OF_MEMORY; - ntlm->input_token_len = Curl_bufref_len(type2); + ntlm->input_token_len = Curl_bufref_len(type2ref); return CURLE_OK; } /* -* Curl_auth_create_ntlm_type3_message() * Curl_auth_create_ntlm_type3_message() * * This is used to generate an already encoded NTLM type-3 message ready for @@ -255,11 +239,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, SecBufferDesc type_3_desc; SECURITY_STATUS status; unsigned long attrs; - TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ -#ifdef CURL_DISABLE_VERBOSE_STRINGS - (void)data; -#endif (void)passwdp; (void)userp; @@ -273,13 +253,12 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS /* ssl context comes from schannel. - * When extended protection is used in IIS server, - * we have to pass a second SecBuffer to the SecBufferDesc - * otherwise IIS will not pass the authentication (401 response). - * Minimum supported version is Windows 7. - * https://docs.microsoft.com/en-us/security-updates - * /SecurityAdvisories/2009/973811 - */ + * When extended protection is used in IIS server, + * we have to pass a second SecBuffer to the SecBufferDesc + * otherwise IIS does not pass the authentication (401 response). + * Minimum supported version is Windows 7. + * https://learn.microsoft.com/security-updates/SecurityAdvisories/2009/973811 + */ if(ntlm->sslContext) { SEC_CHANNEL_BINDINGS channelBindings; SecPkgContext_Bindings pkgBindings; @@ -308,15 +287,15 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, /* Generate our type-3 message */ status = Curl_pSecFn->InitializeSecurityContext(ntlm->credentials, - ntlm->context, - ntlm->spn, - 0, 0, SECURITY_NETWORK_DREP, - &type_2_desc, - 0, ntlm->context, - &type_3_desc, - &attrs, &expiry); + ntlm->context, + ntlm->spn, + 0, 0, SECURITY_NETWORK_DREP, + &type_2_desc, + 0, ntlm->context, + &type_3_desc, + &attrs, NULL); if(status != SEC_E_OK) { - infof(data, "NTLM handshake failure (type-3 message): Status=%lx", + infof(data, "NTLM handshake failure (type-3 message): Status=0x%08lx", status); if(status == SEC_E_INSUFFICIENT_MEMORY) @@ -326,7 +305,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, } /* Return the response. */ - result = Curl_bufref_memdup(out, ntlm->output_token, type_3_buf.cbBuffer); + result = Curl_bufref_memdup0(out, ntlm->output_token, type_3_buf.cbBuffer); Curl_auth_cleanup_ntlm(ntlm); return result; } @@ -346,14 +325,14 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) /* Free our security context */ if(ntlm->context) { Curl_pSecFn->DeleteSecurityContext(ntlm->context); - free(ntlm->context); + curlx_free(ntlm->context); ntlm->context = NULL; } /* Free our credentials handle */ if(ntlm->credentials) { Curl_pSecFn->FreeCredentialsHandle(ntlm->credentials); - free(ntlm->credentials); + curlx_free(ntlm->credentials); ntlm->credentials = NULL; } @@ -362,13 +341,13 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) ntlm->p_identity = NULL; /* Free the input and output tokens */ - Curl_safefree(ntlm->input_token); - Curl_safefree(ntlm->output_token); + curlx_safefree(ntlm->input_token); + curlx_safefree(ntlm->output_token); /* Reset any variables */ ntlm->token_max = 0; - Curl_safefree(ntlm->spn); + curlx_safefree(ntlm->spn); } #endif /* USE_WINDOWS_SSPI && USE_NTLM */ diff --git a/lib/vauth/oauth2.c b/lib/vauth/oauth2.c index a84dc60daf..4541d1d551 100644 --- a/lib/vauth/oauth2.c +++ b/lib/vauth/oauth2.c @@ -23,23 +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_POP3) || \ (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP)) -#include -#include "../urldata.h" - -#include "vauth.h" -#include "../curlx/warnless.h" -#include "../curl_printf.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" +#include "vauth/vauth.h" /* * Curl_auth_create_oauth_bearer_message() @@ -67,11 +57,11 @@ CURLcode Curl_auth_create_oauth_bearer_message(const char *user, /* Generate the message */ if(port == 0 || port == 80) - oauth = aprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", user, host, - bearer); + oauth = curl_maprintf("n,a=%s,\1host=%s\1auth=Bearer %s\1\1", user, host, + bearer); else - oauth = aprintf("n,a=%s,\1host=%s\1port=%ld\1auth=Bearer %s\1\1", user, - host, port, bearer); + oauth = curl_maprintf("n,a=%s,\1host=%s\1port=%ld\1auth=Bearer %s\1\1", + user, host, port, bearer); if(!oauth) return CURLE_OUT_OF_MEMORY; @@ -98,7 +88,7 @@ CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, struct bufref *out) { /* Generate the message */ - char *xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer); + char *xoauth = curl_maprintf("user=%s\1auth=Bearer %s\1\1", user, bearer); if(!xoauth) return CURLE_OUT_OF_MEMORY; diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c index 4ed02a398c..38bb4c1422 100644 --- a/lib/vauth/spnego_gssapi.c +++ b/lib/vauth/spnego_gssapi.c @@ -23,26 +23,16 @@ * RFC4178 Simple and Protected GSS-API Negotiation Mechanism * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #if defined(HAVE_GSSAPI) && defined(USE_SPNEGO) -#include +#include "vauth/vauth.h" +#include "curlx/base64.h" +#include "curl_gssapi.h" +#include "curl_trc.h" -#include "vauth.h" -#include "../urldata.h" -#include "../curlx/base64.h" -#include "../curl_gssapi.h" -#include "../curlx/warnless.h" -#include "../curlx/multibyte.h" -#include "../sendf.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" - -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -96,7 +86,9 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; gss_channel_bindings_t chan_bindings = GSS_C_NO_CHANNEL_BINDINGS; +#ifdef GSS_C_CHANNEL_BOUND_FLAG struct gss_channel_bindings_struct chan; +#endif (void)user; (void)password; @@ -129,12 +121,12 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, Curl_gss_log_error(data, "gss_import_name() failed: ", major_status, minor_status); - free(spn); + curlx_free(spn); return CURLE_AUTH_ERROR; } - free(spn); + curlx_free(spn); } if(chlg64 && *chlg64) { @@ -157,12 +149,14 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, } /* Set channel binding data if available */ +#ifdef GSS_C_CHANNEL_BOUND_FLAG if(curlx_dyn_len(&nego->channel_binding_data)) { memset(&chan, 0, sizeof(struct gss_channel_bindings_struct)); chan.application_data.length = curlx_dyn_len(&nego->channel_binding_data); chan.application_data.value = curlx_dyn_ptr(&nego->channel_binding_data); chan_bindings = &chan; } +#endif /* Generate our challenge-response message */ major_status = Curl_gss_init_sec_context(data, @@ -177,7 +171,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, NULL); /* Free the decoded challenge as it is not required anymore */ - Curl_safefree(input_token.value); + curlx_safefree(input_token.value); nego->status = major_status; if(GSS_ERROR(major_status)) { @@ -217,7 +211,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, * data [in] - The session handle. * nego [in/out] - The Negotiate data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. @@ -294,7 +288,7 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) nego->havemultiplerequests = FALSE; } -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 9c48125261..1baf59320a 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -23,24 +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 - -#include "vauth.h" -#include "../urldata.h" -#include "../curlx/base64.h" -#include "../curlx/warnless.h" -#include "../curlx/multibyte.h" -#include "../sendf.h" -#include "../strerror.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" +#include "vauth/vauth.h" +#include "curlx/base64.h" +#include "curl_trc.h" +#include "strerror.h" /* * Curl_auth_is_spnego_supported() @@ -66,7 +56,6 @@ bool Curl_auth_is_spnego_supported(void) Curl_pSecFn->FreeContextBuffer(SecurityPackage); } - return status == SEC_E_OK; } @@ -105,11 +94,6 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, SecBufferDesc chlg_desc; SecBufferDesc resp_desc; unsigned long attrs; - TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ - -#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 @@ -128,7 +112,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(!nego->output_token) { /* Query the security package for Negotiate */ - nego->status = (DWORD)Curl_pSecFn->QuerySecurityPackageInfo( + nego->status = Curl_pSecFn->QuerySecurityPackageInfo( (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)), &SecurityPackage); if(nego->status != SEC_E_OK) { @@ -142,10 +126,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, Curl_pSecFn->FreeContextBuffer(SecurityPackage); /* Allocate our output buffer */ - nego->output_token = malloc(nego->token_max); + nego->output_token = curlx_malloc(nego->token_max); if(!nego->output_token) return CURLE_OUT_OF_MEMORY; - } + } if(!nego->credentials) { /* Do we have credentials to use or are we using single sign-on? */ @@ -163,22 +147,21 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, nego->p_identity = NULL; /* Allocate our credentials handle */ - nego->credentials = calloc(1, sizeof(CredHandle)); + nego->credentials = curlx_calloc(1, sizeof(CredHandle)); if(!nego->credentials) return CURLE_OUT_OF_MEMORY; /* Acquire our credentials handle */ - nego->status = (DWORD) - Curl_pSecFn->AcquireCredentialsHandle(NULL, + nego->status = Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)), SECPKG_CRED_OUTBOUND, NULL, nego->p_identity, NULL, NULL, - nego->credentials, &expiry); + nego->credentials, NULL); if(nego->status != SEC_E_OK) return CURLE_AUTH_ERROR; /* Allocate our new context handle */ - nego->context = calloc(1, sizeof(CtxtHandle)); + nego->context = curlx_calloc(1, sizeof(CtxtHandle)); if(!nego->context) return CURLE_OUT_OF_MEMORY; } @@ -207,22 +190,20 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS /* ssl context comes from Schannel. - * When extended protection is used in IIS server, - * we have to pass a second SecBuffer to the SecBufferDesc - * otherwise IIS will not pass the authentication (401 response). - * Minimum supported version is Windows 7. - * https://docs.microsoft.com/en-us/security-updates - * /SecurityAdvisories/2009/973811 - */ + * When extended protection is used in IIS server, + * we have to pass a second SecBuffer to the SecBufferDesc + * otherwise IIS does not pass the authentication (401 response). + * Minimum supported version is Windows 7. + * https://learn.microsoft.com/security-updates/SecurityAdvisories/2009/973811 + */ if(nego->sslContext) { SEC_CHANNEL_BINDINGS channelBindings; SecPkgContext_Bindings pkgBindings; pkgBindings.Bindings = &channelBindings; - nego->status = (DWORD)Curl_pSecFn->QueryContextAttributes( + nego->status = Curl_pSecFn->QueryContextAttributes( nego->sslContext, SECPKG_ATTR_ENDPOINT_BINDINGS, - &pkgBindings - ); + &pkgBindings); if(nego->status == SEC_E_OK) { chlg_desc.cBuffers++; chlg_buf[1].BufferType = SECBUFFER_CHANNEL_BINDINGS; @@ -243,25 +224,24 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, /* Generate our challenge-response message */ nego->status = - (DWORD)Curl_pSecFn->InitializeSecurityContext(nego->credentials, - chlg ? nego->context : NULL, - nego->spn, - ISC_REQ_CONFIDENTIALITY, - 0, SECURITY_NATIVE_DREP, - chlg ? &chlg_desc : NULL, - 0, nego->context, - &resp_desc, &attrs, - &expiry); + Curl_pSecFn->InitializeSecurityContext(nego->credentials, + chlg ? nego->context : NULL, + nego->spn, + ISC_REQ_CONFIDENTIALITY, + 0, SECURITY_NATIVE_DREP, + chlg ? &chlg_desc : NULL, + 0, nego->context, + &resp_desc, &attrs, NULL); /* Free the decoded challenge as it is not required anymore */ - free(chlg); + curlx_free(chlg); if(GSS_ERROR(nego->status)) { char buffer[STRERROR_LEN]; failf(data, "InitializeSecurityContext failed: %s", - Curl_sspi_strerror((int)nego->status, buffer, sizeof(buffer))); + Curl_sspi_strerror(nego->status, buffer, sizeof(buffer))); - if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY) + if(nego->status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; return CURLE_AUTH_ERROR; @@ -269,14 +249,13 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(nego->status == SEC_I_COMPLETE_NEEDED || nego->status == SEC_I_COMPLETE_AND_CONTINUE) { - nego->status = (DWORD)Curl_pSecFn->CompleteAuthToken(nego->context, - &resp_desc); + nego->status = Curl_pSecFn->CompleteAuthToken(nego->context, &resp_desc); if(GSS_ERROR(nego->status)) { char buffer[STRERROR_LEN]; failf(data, "CompleteAuthToken failed: %s", - Curl_sspi_strerror((int)nego->status, buffer, sizeof(buffer))); + Curl_sspi_strerror(nego->status, buffer, sizeof(buffer))); - if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY) + if(nego->status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; return CURLE_AUTH_ERROR; @@ -299,7 +278,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, * data [in] - The session handle. * nego [in/out] - The Negotiate data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. @@ -308,11 +287,11 @@ CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego, char **outptr, size_t *outlen) { /* Base64 encode the already generated response */ - CURLcode result = curlx_base64_encode((const char *) nego->output_token, + CURLcode result = curlx_base64_encode(nego->output_token, nego->output_token_length, outptr, outlen); if(!result && (!*outptr || !*outlen)) { - free(*outptr); + curlx_free(*outptr); result = CURLE_REMOTE_ACCESS_DENIED; } @@ -334,14 +313,14 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) /* Free our security context */ if(nego->context) { Curl_pSecFn->DeleteSecurityContext(nego->context); - free(nego->context); + curlx_free(nego->context); nego->context = NULL; } /* Free our credentials handle */ if(nego->credentials) { Curl_pSecFn->FreeCredentialsHandle(nego->credentials); - free(nego->credentials); + curlx_free(nego->credentials); nego->credentials = NULL; } @@ -350,8 +329,8 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) nego->p_identity = NULL; /* Free the SPN and output token */ - Curl_safefree(nego->spn); - Curl_safefree(nego->output_token); + curlx_safefree(nego->spn); + curlx_safefree(nego->output_token); /* Reset any variables */ nego->status = 0; diff --git a/lib/vauth/vauth.c b/lib/vauth/vauth.c index 1b44aa6de1..a00078fce1 100644 --- a/lib/vauth/vauth.c +++ b/lib/vauth/vauth.c @@ -21,21 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" -#include "../curl_setup.h" - -#include - -#include "vauth.h" -#include "../strdup.h" -#include "../urldata.h" -#include "../curlx/multibyte.h" -#include "../curl_printf.h" -#include "../url.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" +#include "vauth/vauth.h" +#include "curlx/multibyte.h" +#include "url.h" /* * Curl_auth_build_spn() @@ -62,11 +52,11 @@ char *Curl_auth_build_spn(const char *service, const char *host, /* Generate our SPN */ if(host && realm) - spn = aprintf("%s/%s@%s", service, host, realm); + spn = curl_maprintf("%s/%s@%s", service, host, realm); else if(host) - spn = aprintf("%s/%s", service, host); + spn = curl_maprintf("%s/%s", service, host); else if(realm) - spn = aprintf("%s@%s", service, realm); + spn = curl_maprintf("%s@%s", service, realm); /* Return our newly allocated SPN */ return spn; @@ -77,7 +67,6 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, { char *utf8_spn = NULL; TCHAR *tchar_spn = NULL; - TCHAR *dupe_tchar_spn = NULL; (void)realm; @@ -89,20 +78,15 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, formulate the SPN instead. */ /* Generate our UTF8 based SPN */ - utf8_spn = aprintf("%s/%s", service, host); + utf8_spn = curl_maprintf("%s/%s", service, host); if(!utf8_spn) return NULL; - /* Allocate and return a TCHAR based SPN. Since curlx_convert_UTF8_to_tchar - must be freed by curlx_unicodefree we will dupe the result so that the - pointer this function returns can be normally free'd. */ + /* Allocate and return a TCHAR based SPN. */ tchar_spn = curlx_convert_UTF8_to_tchar(utf8_spn); - free(utf8_spn); - if(!tchar_spn) - return NULL; - dupe_tchar_spn = _tcsdup(tchar_spn); - curlx_unicodefree(tchar_spn); - return dupe_tchar_spn; + curlx_free(utf8_spn); + + return tchar_spn; } #endif /* USE_WINDOWS_SSPI */ @@ -133,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); } @@ -148,7 +132,7 @@ bool Curl_auth_user_contains_domain(const char *user) } /* - * Curl_auth_ollowed_to_host() tells if authentication, cookies or other + * Curl_auth_allowed_to_host() tells if authentication, cookies or other * "sensitive data" can (still) be sent to this host. */ bool Curl_auth_allowed_to_host(struct Curl_easy *data) @@ -159,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; @@ -171,18 +154,16 @@ static void ntlm_conn_dtor(void *key, size_t klen, void *entry) (void)klen; DEBUGASSERT(ntlm); Curl_auth_cleanup_ntlm(ntlm); - free(ntlm); + curlx_free(ntlm); } struct ntlmdata *Curl_auth_ntlm_get(struct connectdata *conn, bool proxy) { - const char *key = proxy ? CURL_META_NTLM_PROXY_CONN : - CURL_META_NTLM_CONN; + const char *key = proxy ? CURL_META_NTLM_PROXY_CONN : CURL_META_NTLM_CONN; struct ntlmdata *ntlm = Curl_conn_meta_get(conn, key); if(!ntlm) { - ntlm = calloc(1, sizeof(*ntlm)); - if(!ntlm || - Curl_conn_meta_set(conn, key, ntlm, ntlm_conn_dtor)) + ntlm = curlx_calloc(1, sizeof(*ntlm)); + if(!ntlm || Curl_conn_meta_set(conn, key, ntlm, ntlm_conn_dtor)) return NULL; } return ntlm; @@ -190,14 +171,12 @@ struct ntlmdata *Curl_auth_ntlm_get(struct connectdata *conn, bool proxy) 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); + 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; @@ -205,25 +184,23 @@ static void krb5_conn_dtor(void *key, size_t klen, void *entry) (void)klen; DEBUGASSERT(krb5); Curl_auth_cleanup_gssapi(krb5); - free(krb5); + curlx_free(krb5); } struct kerberos5data *Curl_auth_krb5_get(struct connectdata *conn) { struct kerberos5data *krb5 = Curl_conn_meta_get(conn, CURL_META_KRB5_CONN); if(!krb5) { - krb5 = calloc(1, sizeof(*krb5)); + krb5 = curlx_calloc(1, sizeof(*krb5)); if(!krb5 || Curl_conn_meta_set(conn, CURL_META_KRB5_CONN, krb5, krb5_conn_dtor)) return NULL; } return krb5; } - #endif /* USE_KERBEROS5 */ #ifdef USE_GSASL - static void gsasl_conn_dtor(void *key, size_t klen, void *entry) { struct gsasldata *gsasl = entry; @@ -231,25 +208,23 @@ static void gsasl_conn_dtor(void *key, size_t klen, void *entry) (void)klen; DEBUGASSERT(gsasl); Curl_auth_gsasl_cleanup(gsasl); - free(gsasl); + curlx_free(gsasl); } struct gsasldata *Curl_auth_gsasl_get(struct connectdata *conn) { struct gsasldata *gsasl = Curl_conn_meta_get(conn, CURL_META_GSASL_CONN); if(!gsasl) { - gsasl = calloc(1, sizeof(*gsasl)); + gsasl = curlx_calloc(1, sizeof(*gsasl)); if(!gsasl || Curl_conn_meta_set(conn, CURL_META_GSASL_CONN, gsasl, gsasl_conn_dtor)) return NULL; } return gsasl; } - #endif /* USE_GSASL */ #ifdef USE_SPNEGO - static void nego_conn_dtor(void *key, size_t klen, void *entry) { struct negotiatedata *nego = entry; @@ -257,21 +232,18 @@ static void nego_conn_dtor(void *key, size_t klen, void *entry) (void)klen; DEBUGASSERT(nego); Curl_auth_cleanup_spnego(nego); - free(nego); + curlx_free(nego); } struct negotiatedata *Curl_auth_nego_get(struct connectdata *conn, bool proxy) { - const char *key = proxy ? CURL_META_NEGO_PROXY_CONN : - CURL_META_NEGO_CONN; + const char *key = proxy ? CURL_META_NEGO_PROXY_CONN : CURL_META_NEGO_CONN; struct negotiatedata *nego = Curl_conn_meta_get(conn, key); if(!nego) { - nego = calloc(1, sizeof(*nego)); - if(!nego || - Curl_conn_meta_set(conn, key, nego, nego_conn_dtor)) + nego = curlx_calloc(1, sizeof(*nego)); + if(!nego || Curl_conn_meta_set(conn, key, nego, nego_conn_dtor)) return NULL; } return nego; } - #endif /* USE_SPNEGO */ diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h index d9e73a0ed1..b5ff31414f 100644 --- a/lib/vauth/vauth.h +++ b/lib/vauth/vauth.h @@ -23,11 +23,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" -#include - -#include "../bufref.h" -#include "../curlx/dynbuf.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 @@ -205,7 +205,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, /* 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 */ @@ -233,19 +233,6 @@ CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, #ifdef USE_KERBEROS5 -#ifdef HAVE_GSSAPI -# ifdef HAVE_GSSGNU -# include -# elif defined HAVE_GSSAPI_GSSAPI_H -# include -# else -# include -# endif -# ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H -# include -# endif -#endif - /* meta key for storing KRB5 meta at connection */ #define CURL_META_KRB5_CONN "meta:auth:krb5:conn" @@ -276,7 +263,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, const char *passwdp, const char *service, const char *host, - const bool mutual, + const bool mutual_auth, const struct bufref *chlg, struct kerberos5data *krb5, struct bufref *out); @@ -311,13 +298,15 @@ struct negotiatedata { gss_ctx_id_t context; gss_name_t spn; gss_buffer_desc output_token; +#ifdef GSS_C_CHANNEL_BOUND_FLAG struct dynbuf channel_binding_data; +#endif #else #ifdef USE_WINDOWS_SSPI #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS CtxtHandle *sslContext; #endif - DWORD status; + SECURITY_STATUS status; CredHandle *credentials; CtxtHandle *context; SEC_WINNT_AUTH_IDENTITY identity; diff --git a/lib/version.c b/lib/version.c index a2d4867200..b3b0a46abb 100644 --- a/lib/version.c +++ b/lib/version.c @@ -21,20 +21,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "curl_setup.h" #ifdef USE_NGHTTP2 #include #endif -#include #include "urldata.h" #include "vtls/vtls.h" #include "http2.h" #include "vssh/ssh.h" #include "vquic/vquic.h" -#include "curl_printf.h" #include "easy_lock.h" #ifdef USE_ARES @@ -49,23 +46,18 @@ #include #endif -#ifdef USE_LIBRTMP -#include -#include "curl_rtmp.h" -#endif - #ifdef HAVE_LIBZ #include #endif #ifdef HAVE_BROTLI -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG /* Ignore -Wvla warnings in brotli headers */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wvla" #endif #include -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG #pragma GCC diagnostic pop #endif #endif @@ -78,8 +70,8 @@ #include #endif -#ifdef USE_OPENLDAP -#include +#ifndef CURL_DISABLE_LDAP +#include "curl_ldap.h" #endif #ifdef HAVE_BROTLI @@ -89,7 +81,7 @@ static void brotli_version(char *buf, size_t bufsz) unsigned int major = brotli_version >> 24; unsigned int minor = (brotli_version & 0x00FFFFFF) >> 12; unsigned int patch = brotli_version & 0x00000FFF; - (void)msnprintf(buf, bufsz, "brotli/%u.%u.%u", major, minor, patch); + (void)curl_msnprintf(buf, bufsz, "brotli/%u.%u.%u", major, minor, patch); } #endif @@ -100,29 +92,7 @@ static void zstd_version(char *buf, size_t bufsz) unsigned int major = version / (100 * 100); unsigned int minor = (version - (major * 100 * 100)) / 100; unsigned int patch = version - (major * 100 * 100) - (minor * 100); - (void)msnprintf(buf, bufsz, "zstd/%u.%u.%u", major, minor, patch); -} -#endif - -#ifdef USE_OPENLDAP -static void oldap_version(char *buf, size_t bufsz) -{ - LDAPAPIInfo api; - api.ldapai_info_version = LDAP_API_INFO_VERSION; - - if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) == LDAP_OPT_SUCCESS) { - 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) - - patch) / 100; - msnprintf(buf, bufsz, "%s/%u.%u.%u", - api.ldapai_vendor_name, major, minor, patch); - ldap_memfree(api.ldapai_vendor_name); - ber_memvfree((void **)api.ldapai_extensions); - } - else - msnprintf(buf, bufsz, "OpenLDAP"); + (void)curl_msnprintf(buf, bufsz, "zstd/%u.%u.%u", major, minor, patch); } #endif @@ -132,10 +102,10 @@ static void psl_version(char *buf, size_t bufsz) #if defined(PSL_VERSION_MAJOR) && (PSL_VERSION_MAJOR > 0 || \ PSL_VERSION_MINOR >= 11) int num = psl_check_version_number(0); - msnprintf(buf, bufsz, "libpsl/%d.%d.%d", - num >> 16, (num >> 8) & 0xff, num & 0xff); + curl_msnprintf(buf, bufsz, "libpsl/%d.%d.%d", + num >> 16, (num >> 8) & 0xff, num & 0xff); #else - msnprintf(buf, bufsz, "libpsl/%s", psl_get_version()); + curl_msnprintf(buf, bufsz, "libpsl/%s", psl_get_version()); #endif } #endif @@ -148,11 +118,11 @@ static void psl_version(char *buf, size_t bufsz) static void idn_version(char *buf, size_t bufsz) { #ifdef USE_LIBIDN2 - msnprintf(buf, bufsz, "libidn2/%s", idn2_check_version(NULL)); + curl_msnprintf(buf, bufsz, "libidn2/%s", idn2_check_version(NULL)); #elif defined(USE_WIN32_IDN) - msnprintf(buf, bufsz, "WinIDN"); + curl_msnprintf(buf, bufsz, "WinIDN"); #elif defined(USE_APPLE_IDN) - msnprintf(buf, bufsz, "AppleIDN"); + curl_msnprintf(buf, bufsz, "AppleIDN"); #endif } #endif @@ -203,13 +173,13 @@ char *curl_version(void) #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) char h3_version[30]; #endif -#ifdef USE_LIBRTMP - char rtmp_version[30]; -#endif #ifdef USE_GSASL char gsasl_buf[30]; #endif -#ifdef USE_OPENLDAP +#ifdef HAVE_GSSAPI + char gss_buf[40]; +#endif +#ifndef CURL_DISABLE_LDAP char ldap_buf[30]; #endif int i = 0; @@ -219,7 +189,7 @@ char *curl_version(void) /* Override version string when environment variable CURL_VERSION is set */ const char *debugversion = getenv("CURL_VERSION"); if(debugversion) { - msnprintf(out, sizeof(out), "%s", debugversion); + curl_msnprintf(out, sizeof(out), "%s", debugversion); return out; } #endif @@ -230,7 +200,7 @@ char *curl_version(void) src[i++] = ssl_version; #endif #ifdef HAVE_LIBZ - msnprintf(z_version, sizeof(z_version), "zlib/%s", zlibVersion()); + curl_msnprintf(z_version, sizeof(z_version), "zlib/%s", zlibVersion()); src[i++] = z_version; #endif #ifdef HAVE_BROTLI @@ -242,8 +212,8 @@ char *curl_version(void) src[i++] = zstd_ver; #endif #ifdef USE_ARES - msnprintf(cares_version, sizeof(cares_version), - "c-ares/%s", ares_version(NULL)); + curl_msnprintf(cares_version, sizeof(cares_version), + "c-ares/%s", ares_version(NULL)); src[i++] = cares_version; #endif #ifdef USE_IDN @@ -266,17 +236,23 @@ char *curl_version(void) Curl_quic_ver(h3_version, sizeof(h3_version)); src[i++] = h3_version; #endif -#ifdef USE_LIBRTMP - Curl_rtmp_version(rtmp_version, sizeof(rtmp_version)); - src[i++] = rtmp_version; -#endif #ifdef USE_GSASL - msnprintf(gsasl_buf, sizeof(gsasl_buf), "libgsasl/%s", - gsasl_check_version(NULL)); + curl_msnprintf(gsasl_buf, sizeof(gsasl_buf), "libgsasl/%s", + gsasl_check_version(NULL)); src[i++] = gsasl_buf; #endif -#ifdef USE_OPENLDAP - oldap_version(ldap_buf, sizeof(ldap_buf)); +#ifdef HAVE_GSSAPI +#ifdef HAVE_GSSGNU + curl_msnprintf(gss_buf, sizeof(gss_buf), "libgss/%s", GSS_VERSION); +#elif defined(CURL_KRB5_VERSION) + curl_msnprintf(gss_buf, sizeof(gss_buf), "mit-krb5/%s", CURL_KRB5_VERSION); +#else + curl_msnprintf(gss_buf, sizeof(gss_buf), "mit-krb5"); +#endif + src[i++] = gss_buf; +#endif /* HAVE_GSSAPI */ +#ifndef CURL_DISABLE_LDAP + Curl_ldap_version(ldap_buf, sizeof(ldap_buf)); src[i++] = ldap_buf; #endif @@ -343,38 +319,31 @@ static const char * const supported_protocols[] = { #ifndef CURL_DISABLE_LDAP "ldap", #if !defined(CURL_DISABLE_LDAPS) && \ - ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ - (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) + ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \ + (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL))) "ldaps", #endif #endif #ifndef CURL_DISABLE_MQTT "mqtt", #endif +#if defined(USE_SSL) && !defined(CURL_DISABLE_MQTT) + "mqtts", +#endif #ifndef CURL_DISABLE_POP3 "pop3", #endif #if defined(USE_SSL) && !defined(CURL_DISABLE_POP3) "pop3s", #endif -#ifdef USE_LIBRTMP - "rtmp", - "rtmpe", - "rtmps", - "rtmpt", - "rtmpte", - "rtmpts", -#endif #ifndef CURL_DISABLE_RTSP "rtsp", #endif -#if defined(USE_SSH) && !defined(USE_WOLFSSH) - "scp", -#endif #ifdef USE_SSH + "scp", "sftp", #endif -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) "smb", # ifdef USE_SSL "smbs", @@ -448,7 +417,7 @@ static int ech_present(curl_version_info_data *info) * Use FEATURE() macro to define an entry: this allows documentation check. */ -#define FEATURE(name, present, bitmask) {(name), (present), (bitmask)} +#define FEATURE(name, present, bitmask) { (name), (present), (bitmask) } struct feat { const char *name; @@ -460,8 +429,8 @@ static const struct feat features_table[] = { #ifndef CURL_DISABLE_ALTSVC FEATURE("alt-svc", NULL, CURL_VERSION_ALTSVC), #endif -#if defined(USE_ARES) && defined(CURLRES_THREADED) && defined(USE_HTTPSRR) - FEATURE("asyn-rr", NULL, 0), +#if defined(USE_ARES) && defined(USE_RESOLV_THREADED) && defined(USE_HTTPSRR) + FEATURE("asyn-rr", NULL, 0), #endif #ifdef CURLRES_ASYNCH FEATURE("AsynchDNS", NULL, CURL_VERSION_ASYNCHDNS), @@ -510,8 +479,7 @@ static const struct feat features_table[] = { #ifdef USE_KERBEROS5 FEATURE("Kerberos", NULL, CURL_VERSION_KERBEROS5), #endif -#if (SIZEOF_CURL_OFF_T > 4) && \ - ( (SIZEOF_OFF_T > 4) || defined(USE_WIN32_LARGE_FILES) ) +#if (SIZEOF_CURL_OFF_T > 4) && ((SIZEOF_OFF_T > 4) || defined(_WIN32)) FEATURE("Largefile", NULL, CURL_VERSION_LARGEFILE), #endif #ifdef HAVE_LIBZ @@ -526,6 +494,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 @@ -544,9 +519,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 @@ -559,9 +531,7 @@ static const struct feat features_table[] = { {NULL, NULL, 0} }; -static const char *feature_names[sizeof(features_table) / - sizeof(features_table[0])] = {NULL}; - +static const char *feature_names[CURL_ARRAYSIZE(features_table)] = { NULL }; static curl_version_info_data version_info = { CURLVERSION_NOW, @@ -660,7 +630,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 @@ -695,16 +665,12 @@ 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; -#ifdef USE_LIBRTMP - { - static char rtmp_version[30]; - Curl_rtmp_version(rtmp_version, sizeof(rtmp_version)); - version_info.rtmp_version = rtmp_version; - } -#endif - return &version_info; } diff --git a/lib/vquic/.checksrc b/lib/vquic/.checksrc deleted file mode 100644 index 22ca8e0b53..0000000000 --- a/lib/vquic/.checksrc +++ /dev/null @@ -1,5 +0,0 @@ -banfunc snprintf -banfunc sscanf -banfunc strerror -banfunc strtol -banfunc vsnprint diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index 6470f1506d..04c6ed1fee 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -21,8 +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 @@ -37,58 +36,51 @@ #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 "../sendf.h" -#include "../strdup.h" -#include "../rand.h" -#include "../multiif.h" -#include "../cfilters.h" -#include "../cf-socket.h" -#include "../connect.h" -#include "../progress.h" -#include "../strerror.h" -#include "../curlx/dynbuf.h" -#include "../http1.h" -#include "../select.h" -#include "../curlx/inet_pton.h" -#include "../transfer.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 "../curlx/warnless.h" - -/* The last 3 #include files should be in this order */ -#include "../curl_printf.h" -#include "../curl_memory.h" -#include "../memdebug.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-dns.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/vtls.h" +#include "vtls/vtls_scache.h" +#include "vquic/curl_ngtcp2.h" -#define QUIC_MAX_STREAMS (256*1024) -#define QUIC_MAX_DATA (1*1024*1024) -#define QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS) +#define QUIC_MAX_STREAMS (256 * 1024) +#define QUIC_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS) -/* 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) +/* We announce a small window size in transport param to the server, + * and grow that immediately to max when no rate limit is in place. + * We need to start small as we are not able to decrease it. */ +#define H3_STREAM_WINDOW_SIZE_INITIAL (32 * 1024) +#define H3_STREAM_WINDOW_SIZE_MAX (10 * 1024 * 1024) +#define H3_CONN_WINDOW_SIZE_MAX (100 * H3_STREAM_WINDOW_SIZE_MAX) + +#define H3_STREAM_CHUNK_SIZE (64 * 1024) #if H3_STREAM_CHUNK_SIZE < NGTCP2_MAX_UDP_PAYLOAD_SIZE #error H3_STREAM_CHUNK_SIZE smaller than NGTCP2_MAX_UDP_PAYLOAD_SIZE #endif @@ -98,15 +90,11 @@ * 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_POOL_SPARES 2 +/* The max amount of un-acked upload data we keep around per stream */ +#define H3_STREAM_SEND_BUFFER_MAX (10 * 1024 * 1024) #define H3_STREAM_SEND_CHUNKS \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) - + (H3_STREAM_SEND_BUFFER_MAX / H3_STREAM_CHUNK_SIZE) /* * Store ngtcp2 version info in this buffer. @@ -115,8 +103,8 @@ void Curl_ngtcp2_ver(char *p, size_t len) { const ngtcp2_info *ng2 = ngtcp2_version(0); const nghttp3_info *ht3 = nghttp3_version(0); - (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s", - ng2->version_str, ht3->version_str); + (void)curl_msnprintf(p, len, "ngtcp2/%s nghttp3/%s", + ng2->version_str, ht3->version_str); } struct cf_ngtcp2_ctx { @@ -143,13 +131,12 @@ struct cf_ngtcp2_ctx { struct bufc_pool stream_bufcp; /* chunk pool for streams */ struct dynbuf scratch; /* temp buffer for header construction */ struct uint_hash streams; /* hash `data->mid` to `h3_stream_ctx` */ - size_t max_stream_window; /* max flow window for one stream */ uint64_t used_bidi_streams; /* bidi streams we have opened */ uint64_t max_bidi_streams; /* max bidi streams we can open */ size_t earlydata_max; /* max amount of early data supported by server on session reuse */ - size_t earlydata_skip; /* sending bytes to skip when earlydata - * is accepted by peer */ + size_t earlydata_skip; /* sending bytes to skip when earlydata + is accepted by peer */ CURLcode tls_vrfy_result; /* result of TLS peer verification */ int qlogfd; BIT(initialized); @@ -161,8 +148,7 @@ struct cf_ngtcp2_ctx { /* How to access `call_data` from a cf_ngtcp2 filter */ #undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) \ - ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data +#define CF_CTX_CALL_DATA(cf) ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data static void h3_stream_hash_free(unsigned int id, void *stream); @@ -171,11 +157,10 @@ static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx) DEBUGASSERT(!ctx->initialized); ctx->qlogfd = -1; ctx->version = NGTCP2_PROTO_VER_MAX; - ctx->max_stream_window = H3_STREAM_WINDOW_SIZE; Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, H3_STREAM_POOL_SPARES); curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); - Curl_uint_hash_init(&ctx->streams, 63, h3_stream_hash_free); + Curl_uint32_hash_init(&ctx->streams, 63, h3_stream_hash_free); ctx->initialized = TRUE; } @@ -186,10 +171,10 @@ static void cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx *ctx) vquic_ctx_free(&ctx->q); Curl_bufcp_free(&ctx->stream_bufcp); curlx_dyn_free(&ctx->scratch); - Curl_uint_hash_destroy(&ctx->streams); + Curl_uint32_hash_destroy(&ctx->streams); Curl_ssl_peer_cleanup(&ctx->peer); } - free(ctx); + curlx_free(ctx); } static void cf_ngtcp2_setup_keep_alive(struct Curl_cfilter *cf, @@ -198,14 +183,14 @@ static void cf_ngtcp2_setup_keep_alive(struct Curl_cfilter *cf, struct cf_ngtcp2_ctx *ctx = cf->ctx; const ngtcp2_transport_params *rp; /* Peer should have sent us its transport parameters. If it - * announces a positive `max_idle_timeout` it will close the - * connection when it does not hear from us for that time. - * - * Some servers use this as a keep-alive timer at a rather low - * value. We are doing HTTP/3 here and waiting for the response - * to a request may take a considerable amount of time. We need - * to prevent the peer's QUIC stack from closing in this case. - */ + * announces a positive `max_idle_timeout` it closes the + * connection when it does not hear from us for that time. + * + * Some servers use this as a keep-alive timer at a rather low + * value. We are doing HTTP/3 here and waiting for the response + * to a request may take a considerable amount of time. We need + * to prevent the peer's QUIC stack from closing in this case. + */ if(!ctx->qconn) return; @@ -214,7 +199,7 @@ static void cf_ngtcp2_setup_keep_alive(struct Curl_cfilter *cf, ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); CURL_TRC_CF(data, cf, "no peer idle timeout, unset keep-alive"); } - else if(!Curl_uint_hash_count(&ctx->streams)) { + else if(!Curl_uint32_hash_count(&ctx->streams)) { ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); CURL_TRC_CF(data, cf, "no active streams, unset keep-alive"); } @@ -222,14 +207,13 @@ static void cf_ngtcp2_setup_keep_alive(struct Curl_cfilter *cf, ngtcp2_duration keep_ns; keep_ns = (rp->max_idle_timeout > 1) ? (rp->max_idle_timeout / 2) : 1; ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, keep_ns); - CURL_TRC_CF(data, cf, "peer idle timeout is %" FMT_PRIu64 "ms, " - "set keep-alive to %" FMT_PRIu64 " ms.", - (curl_uint64_t)(rp->max_idle_timeout / NGTCP2_MILLISECONDS), - (curl_uint64_t)(keep_ns / NGTCP2_MILLISECONDS)); + CURL_TRC_CF(data, cf, "peer idle timeout is %" PRIu64 "ms, " + "set keep-alive to %" PRIu64 " ms.", + (rp->max_idle_timeout / NGTCP2_MILLISECONDS), + (keep_ns / NGTCP2_MILLISECONDS)); } } - struct pkt_io_ctx; static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -242,26 +226,29 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, * All about the H3 internals of a stream */ struct h3_stream_ctx { - curl_int64_t id; /* HTTP/3 protocol identifier */ - struct bufq sendbuf; /* h3 request body */ - struct h1_req_parser h1; /* h1 request parsing */ + int64_t id; /* HTTP/3 protocol identifier */ + struct bufq sendbuf; /* h3 request body */ + struct h1_req_parser h1; /* h1 request parsing */ size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ - curl_uint64_t error3; /* HTTP/3 stream error code */ - curl_off_t upload_left; /* number of request bytes left to upload */ - int status_code; /* HTTP status code */ - CURLcode xfer_result; /* result from xfer_resp_write(_hd) */ - 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 */ + uint64_t error3; /* HTTP/3 stream error code */ + curl_off_t upload_left; /* number of request bytes left to upload */ + 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) */ + 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) { Curl_bufq_free(&stream->sendbuf); Curl_h1_req_parse_free(&stream->h1); - free(stream); + curlx_free(stream); } static void h3_stream_hash_free(unsigned int id, void *stream) @@ -283,28 +270,76 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, if(stream) return CURLE_OK; - stream = calloc(1, sizeof(*stream)); + stream = curlx_calloc(1, sizeof(*stream)); if(!stream) 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); stream->sendbuf_len_in_flight = 0; + stream->window_size_max = H3_STREAM_WINDOW_SIZE_INITIAL; Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) { + if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) { h3_stream_ctx_free(stream); return CURLE_OUT_OF_MEMORY; } - if(Curl_uint_hash_count(&ctx->streams) == 1) + if(Curl_uint32_hash_count(&ctx->streams) == 1) cf_ngtcp2_setup_keep_alive(cf, data); return CURLE_OK; } +#if NGTCP2_VERSION_NUM < 0x011100 +struct cf_ngtcp2_sfind_ctx { + int64_t stream_id; + struct h3_stream_ctx *stream; + uint32_t mid; +}; + +static bool cf_ngtcp2_sfind(uint32_t mid, void *value, void *user_data) +{ + struct cf_ngtcp2_sfind_ctx *fctx = user_data; + struct h3_stream_ctx *stream = value; + + if(fctx->stream_id == stream->id) { + fctx->mid = mid; + fctx->stream = stream; + return FALSE; + } + return TRUE; /* continue */ +} + +static struct h3_stream_ctx *cf_ngtcp2_get_stream(struct cf_ngtcp2_ctx *ctx, + int64_t stream_id) +{ + struct cf_ngtcp2_sfind_ctx fctx; + fctx.stream_id = stream_id; + fctx.stream = NULL; + Curl_uint32_hash_visit(&ctx->streams, cf_ngtcp2_sfind, &fctx); + return fctx.stream; +} +#else +static struct h3_stream_ctx *cf_ngtcp2_get_stream(struct cf_ngtcp2_ctx *ctx, + int64_t stream_id) +{ + struct Curl_easy *data = + ngtcp2_conn_get_stream_user_data(ctx->qconn, stream_id); + + if(!data) { + return NULL; + } + + return H3_STREAM_CTX(ctx, data); +} +#endif + static void cf_ngtcp2_stream_close(struct Curl_cfilter *cf, struct Curl_easy *data, struct h3_stream_ctx *stream) @@ -322,7 +357,7 @@ static void cf_ngtcp2_stream_close(struct Curl_cfilter *cf, NGHTTP3_H3_REQUEST_CANCELLED); result = cf_progress_egress(cf, data, NULL); if(result) - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cancel stream -> %d", + CURL_TRC_CF(data, cf, "[%" PRId64 "] cancel stream -> %d", stream->id, result); } } @@ -333,19 +368,14 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)cf; if(stream) { - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] easy handle is done", - stream->id); + CURL_TRC_CF(data, cf, "[%" PRId64 "] easy handle is done", stream->id); cf_ngtcp2_stream_close(cf, data, stream); - Curl_uint_hash_remove(&ctx->streams, data->mid); - if(!Curl_uint_hash_count(&ctx->streams)) + Curl_uint32_hash_remove(&ctx->streams, data->mid); + if(!Curl_uint32_hash_count(&ctx->streams)) cf_ngtcp2_setup_keep_alive(cf, 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; @@ -353,24 +383,31 @@ struct pkt_io_ctx { ngtcp2_path_storage ps; }; -static void pktx_update_time(struct pkt_io_ctx *pktx, +static void pktx_update_time(struct Curl_easy *data, + struct pkt_io_ctx *pktx, struct Curl_cfilter *cf) { struct cf_ngtcp2_ctx *ctx = cf->ctx; + const struct curltime *pnow = Curl_pgrs_now(data); - vquic_ctx_update_time(&ctx->q); - pktx->ts = (ngtcp2_tstamp)ctx->q.last_op.tv_sec * NGTCP2_SECONDS + - (ngtcp2_tstamp)ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS; + vquic_ctx_update_time(&ctx->q, pnow); + 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, struct Curl_cfilter *cf, struct Curl_easy *data) { + struct cf_ngtcp2_ctx *ctx = cf->ctx; + const struct curltime *pnow = Curl_pgrs_now(data); + pktx->cf = cf; pktx->data = data; ngtcp2_path_storage_zero(&pktx->ps); - pktx_update_time(pktx, cf); + vquic_ctx_set_time(&ctx->q, pnow); + 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, @@ -393,9 +430,9 @@ static void quic_printf(void *user_data, const char *fmt, ...) (void)ctx; /* need an easy handle to infof() message */ va_list ap; va_start(ap, fmt); - vfprintf(stderr, fmt, ap); + curl_mvfprintf(stderr, fmt, ap); va_end(ap); - fprintf(stderr, "\n"); + curl_mfprintf(stderr, "\n"); } #endif @@ -409,11 +446,10 @@ 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; } } - } static void quic_settings(struct cf_ngtcp2_ctx *ctx, @@ -434,13 +470,18 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx, s->initial_ts = pktx->ts; s->handshake_timeout = (data->set.connecttimeout > 0) ? data->set.connecttimeout * NGTCP2_MILLISECONDS : QUIC_HANDSHAKE_TIMEOUT; - s->max_window = 100 * ctx->max_stream_window; - s->max_stream_window = 10 * ctx->max_stream_window; - - t->initial_max_data = 10 * ctx->max_stream_window; - t->initial_max_stream_data_bidi_local = ctx->max_stream_window; - t->initial_max_stream_data_bidi_remote = ctx->max_stream_window; - t->initial_max_stream_data_uni = ctx->max_stream_window; + s->max_window = H3_CONN_WINDOW_SIZE_MAX; + s->max_stream_window = 0; /* disable ngtcp2 auto-tuning of window */ + s->no_pmtud = FALSE; +#ifdef NGTCP2_SETTINGS_V3 + /* try ten times the ngtcp2 defaults here for problems with Caddy */ + s->glitch_ratelim_burst = 1000 * 10; + s->glitch_ratelim_rate = 33 * 10; +#endif + t->initial_max_data = s->max_window; + t->initial_max_stream_data_bidi_local = H3_STREAM_WINDOW_SIZE_INITIAL; + t->initial_max_stream_data_bidi_remote = H3_STREAM_WINDOW_SIZE_INITIAL; + t->initial_max_stream_data_uni = t->initial_max_data; t->initial_max_streams_bidi = QUIC_MAX_STREAMS; t->initial_max_streams_uni = QUIC_MAX_STREAMS; t->max_idle_timeout = 0; /* no idle timeout from our side */ @@ -465,15 +506,25 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) if(!ctx || !data) return NGHTTP3_ERR_CALLBACK_FAILURE; - ctx->handshake_at = curlx_now(); + ctx->handshake_at = *Curl_pgrs_now(data); ctx->tls_handshake_complete = TRUE; - cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ Curl_vquic_report_handshake(&ctx->tls, cf, data); ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); - CURL_TRC_CF(data, cf, "handshake complete after %dms", - (int)curlx_timediff(ctx->handshake_at, ctx->started_at)); +#ifdef CURLVERBOSE + if(Curl_trc_is_verbose(data)) { + const ngtcp2_transport_params *rp; + rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); + CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T + "ms, remote transport[max_udp_payload=%" PRIu64 + ", initial_max_data=%" PRIu64 + "]", + curlx_ptimediff_ms(&ctx->handshake_at, &ctx->started_at), + rp->max_udp_payload_size, rp->initial_max_data); + } +#endif + /* In case of earlydata, where we simulate being connected, update * the handshake time when we really did connect */ if(ctx->use_earlydata) @@ -554,49 +605,46 @@ static void cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf, } static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, - int64_t sid, uint64_t offset, + int64_t stream_id, uint64_t offset, const uint8_t *buf, size_t buflen, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; - curl_int64_t stream_id = (curl_int64_t)sid; - 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, "[%" FMT_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, "[%" FMT_PRId64 "] error on known stream, " + CURL_TRC_CF(data, cf, "[%" PRId64 "] error on known stream, " "reset=%d, closed=%d", stream_id, stream->reset, stream->closed); } 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; } -static int -cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, - uint64_t offset, uint64_t datalen, void *user_data, - void *stream_user_data) +static int cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, + uint64_t offset, uint64_t datalen, + void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; @@ -616,13 +664,12 @@ cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, } static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, - int64_t sid, uint64_t app_error_code, + int64_t stream_id, uint64_t app_error_code, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; struct Curl_easy *data = stream_user_data; - curl_int64_t stream_id = (curl_int64_t)sid; int rv; (void)tconn; @@ -637,9 +684,8 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, } rv = nghttp3_conn_close_stream(ctx->h3conn, stream_id, app_error_code); - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] quic close(app_error=%" - FMT_PRIu64 ") -> %d", stream_id, (curl_uint64_t)app_error_code, - rv); + CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(app_error=%" + PRIu64 ") -> %d", stream_id, app_error_code, rv); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { cf_ngtcp2_h3_err_set(cf, data, rv); return NGTCP2_ERR_CALLBACK_FAILURE; @@ -648,22 +694,20 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, return 0; } -static int cb_stream_reset(ngtcp2_conn *tconn, int64_t sid, +static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, uint64_t final_size, uint64_t app_error_code, void *user_data, void *stream_user_data) { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; - curl_int64_t stream_id = (curl_int64_t)sid; struct Curl_easy *data = stream_user_data; int rv; (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, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv); + CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -701,9 +745,8 @@ static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, (void)tconn; ctx->max_bidi_streams = max_streams; if(data) - CURL_TRC_CF(data, cf, "max bidi streams now %" FMT_PRIu64 - ", used %" FMT_PRIu64, (curl_uint64_t)ctx->max_bidi_streams, - (curl_uint64_t)ctx->used_bidi_streams); + CURL_TRC_CF(data, cf, "max bidi streams now %" PRIu64 ", used %" PRIu64, + ctx->max_bidi_streams, ctx->used_bidi_streams); return 0; } @@ -725,8 +768,7 @@ static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, } stream = H3_STREAM_CTX(ctx, s_data); if(stream && stream->quic_flow_blocked) { - CURL_TRC_CF(s_data, cf, "[%" FMT_PRId64 "] unblock quic flow", - (curl_int64_t)stream_id); + CURL_TRC_CF(s_data, cf, "[%" PRId64 "] unblock quic flow", stream_id); stream->quic_flow_blocked = FALSE; Curl_multi_mark_dirty(s_data); } @@ -742,11 +784,12 @@ 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); } } +/* for ngtcp2 data, cidlen); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + cid->datalen = cidlen; + + result = Curl_rand(NULL, token->data, sizeof(token->data)); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + + return 0; +} +#endif + static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, void *user_data) { @@ -788,8 +852,8 @@ static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, } #if defined(_MSC_VER) && defined(_DLL) -# pragma warning(push) -# pragma warning(disable:4232) /* MSVC extension, dllimport identity */ +#pragma warning(push) +#pragma warning(disable:4232) /* MSVC extension, dllimport identity */ #endif static ngtcp2_callbacks ng_callbacks = { @@ -810,7 +874,7 @@ static ngtcp2_callbacks ng_callbacks = { cb_extend_max_local_streams_bidi, NULL, /* extend_max_local_streams_uni */ cb_rand, - cb_get_new_connection_id, + cb_get_new_connection_id, /* for ngtcp2 qconn); @@ -920,7 +990,7 @@ static CURLcode cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); c_exhaust = want_send && (!ngtcp2_conn_get_cwnd_left(ctx->qconn) || - !ngtcp2_conn_get_max_data_left(ctx->qconn)); + !ngtcp2_conn_get_max_data_left(ctx->qconn)); s_exhaust = want_send && stream && stream->id >= 0 && stream->quic_flow_blocked; want_recv = (want_recv || c_exhaust || s_exhaust); @@ -933,14 +1003,13 @@ static CURLcode cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, return result; } -static int cb_h3_stream_close(nghttp3_conn *conn, int64_t sid, +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_ngtcp2_ctx *ctx = cf->ctx; struct Curl_easy *data = stream_user_data; - curl_int64_t stream_id = (curl_int64_t)sid; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)conn; (void)stream_id; @@ -950,15 +1019,15 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t sid, return 0; stream->closed = TRUE; - stream->error3 = (curl_uint64_t)app_error_code; + stream->error3 = app_error_code; if(stream->error3 != NGHTTP3_H3_NO_ERROR) { stream->reset = TRUE; stream->send_closed = TRUE; - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] RESET: error %" FMT_PRIu64, + CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRIu64, stream->id, stream->error3); } else { - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] CLOSED", stream->id); + CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); } Curl_multi_mark_dirty(data); return 0; @@ -969,12 +1038,16 @@ static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf, struct h3_stream_ctx *stream, const char *buf, size_t blen, bool eos) { - - /* If we already encountered an error, skip further writes */ + /* This function returns no error intentionally, but records + * the result at the stream, skipping further writes once the + * `result` of the transfer is known. + * The stream is subsequently cancelled "higher up" in the filter's + * send/recv callbacks. Closing the stream here leads to SEND/RECV + * errors in other places that then overwrite the transfer's result. */ 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, "[%"FMT_PRId64"] error %d writing %zu " + CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu " "bytes of headers", stream->id, stream->xfer_result, blen); } } @@ -984,14 +1057,64 @@ static void h3_xfer_write_resp(struct Curl_cfilter *cf, struct h3_stream_ctx *stream, const char *buf, size_t blen, bool eos) { - - /* If we already encountered an error, skip further writes */ + /* This function returns no error intentionally, but records + * the result at the stream, skipping further writes once the + * `result` of the transfer is known. + * The stream is subsequently cancelled "higher up" in the filter's + * send/recv callbacks. Closing the stream here leads to SEND/RECV + * errors in other places that then overwrite the transfer's result. */ if(!stream->xfer_result) { stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos); /* If the transfer write is errored, we do not want any more data */ if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] error %d writing %zu bytes " - "of data", stream->id, stream->xfer_result, blen); + CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu bytes of data", + stream->id, stream->xfer_result, blen); + } + } +} + +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; + uint64_t cur_win, wanted_win = H3_STREAM_WINDOW_SIZE_MAX; + + /* how much does rate limiting allow us to acknowledge? */ + 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(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; + + 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); } } } @@ -1012,13 +1135,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); - if(blen) { - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] ACK %zu bytes of DATA", - stream->id, blen); - ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, blen); - ngtcp2_conn_extend_max_offset(ctx->qconn, blen); - } - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu", stream->id, blen); + + ngtcp2_conn_extend_max_offset(ctx->qconn, blen); + stream->rx_offset += blen; + if(stream->rx_offset_max < stream->rx_offset) + stream->rx_offset_max = stream->rx_offset; + + 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; } @@ -1028,23 +1153,27 @@ 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; } -static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, +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_ngtcp2_ctx *ctx = cf->ctx; struct Curl_easy *data = stream_user_data; - curl_int64_t stream_id = (curl_int64_t)sid; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); (void)conn; (void)stream_id; @@ -1054,9 +1183,10 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, 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, "[%" FMT_PRId64 "] end_headers, status=%d", + 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; @@ -1065,14 +1195,13 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, return 0; } -static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, +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_ngtcp2_ctx *ctx = cf->ctx; - curl_int64_t stream_id = (curl_int64_t)sid; nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); struct Curl_easy *data = stream_user_data; @@ -1093,7 +1222,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, result = Curl_http_decode_status(&stream->status_code, (const char *)h3val.base, h3val.len); if(result) - return -1; + return NGHTTP3_ERR_CALLBACK_FAILURE; curlx_dyn_reset(&ctx->scratch); result = curlx_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 ")); if(!result) @@ -1104,15 +1233,15 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, if(!result) h3_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch), curlx_dyn_len(&ctx->scratch), FALSE); - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] status: %s", + CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, curlx_dyn_ptr(&ctx->scratch)); if(result) { - return -1; + return NGHTTP3_ERR_CALLBACK_FAILURE; } } else { /* store as an HTTP1-style header */ - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] header: %.*s: %.*s", + CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", stream_id, (int)h3name.len, h3name.base, (int)h3val.len, h3val.base); curlx_dyn_reset(&ctx->scratch); @@ -1151,20 +1280,19 @@ static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, return 0; } -static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid, +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) { + void *stream_user_data) +{ struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; - curl_int64_t stream_id = (curl_int64_t)sid; 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); - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv); + CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { return NGHTTP3_ERR_CALLBACK_FAILURE; } @@ -1187,12 +1315,15 @@ static nghttp3_callbacks ngh3_callbacks = { NULL, /* end_stream */ cb_h3_reset_stream, NULL, /* shutdown */ - NULL, /* recv_settings */ -#ifdef NGHTTP3_CALLBACKS_V2 + 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 init_ngh3_conn(struct Curl_cfilter *cf, @@ -1258,32 +1389,41 @@ static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, return CURLE_OK; } -static ssize_t recv_closed_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream, - CURLcode *err) +static CURLcode recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + size_t *pnread) { - ssize_t nread = -1; - (void)cf; + *pnread = 0; if(stream->reset) { - failf(data, "HTTP/3 stream %" FMT_PRId64 " reset by server", stream->id); - *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; - goto out; + 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) { failf(data, - "HTTP/3 stream %" FMT_PRId64 " was closed cleanly, but before " + "HTTP/3 stream %" PRId64 " was closed cleanly, but before " "getting all response header fields, treated as error", stream->id); - *err = CURLE_HTTP3; - goto out; + return CURLE_HTTP3; } - *err = CURLE_OK; - nread = 0; - -out: - return nread; + return CURLE_OK; } /* incoming data frames on the h3 stream */ @@ -1295,9 +1435,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); @@ -1307,8 +1449,10 @@ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, *pnread = 0; /* handshake verification failed in callback, do not recv anything */ - if(ctx->tls_vrfy_result) - return ctx->tls_vrfy_result; + if(ctx->tls_vrfy_result) { + result = ctx->tls_vrfy_result; + goto denied; + } pktx_init(&pktx, cf, data); @@ -1317,30 +1461,38 @@ static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; } - if(cf_progress_ingress(cf, data, &pktx)) { - result = CURLE_RECV_ERROR; - goto out; + cf_ngtcp2_upd_rx_win(cf, data, stream); + + /* 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(!i && cf_progress_ingress(cf, data, &pktx)) { + result = CURLE_RECV_ERROR; + goto out; + } } - if(stream->xfer_result) { - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] xfer write failed", stream->id); - cf_ngtcp2_stream_close(cf, data, stream); - result = stream->xfer_result; - goto out; - } - else if(stream->closed) { - ssize_t nread = recv_closed_stream(cf, data, stream, &result); - if(nread > 0) - *pnread = (size_t)nread; - goto out; - } result = CURLE_AGAIN; out: - result = Curl_1st_err(result, cf_progress_egress(cf, data, &pktx)); - result = Curl_1st_err(result, check_and_set_expiry(cf, data, &pktx)); - - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(blen=%zu) -> %dm, %zu", + result = Curl_1st_fatal(result, cf_progress_egress(cf, data, &pktx)); + result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx)); +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(blen=%zu) -> %d, %zu", stream ? stream->id : -1, blen, result, *pnread); CF_DATA_RESTORE(cf, save); return result; @@ -1379,11 +1531,10 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, 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) +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_ngtcp2_ctx *ctx = cf->ctx; @@ -1431,12 +1582,11 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, } else if(!nwritten) { /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> AGAIN", - stream->id); + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", stream->id); return NGHTTP3_ERR_WOULDBLOCK; } - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> " + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", stream->id, (int)nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", @@ -1445,13 +1595,9 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, return (nghttp3_ssize)nvecs; } -/* Index where :authority header field will appear in request header - field list. */ -#define AUTHORITY_DST_IDX 3 - static CURLcode h3_stream_open(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, + const uint8_t *buf, size_t len, size_t *pnwritten) { struct cf_ngtcp2_ctx *ctx = cf->ctx; @@ -1462,7 +1608,6 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf, nghttp3_nv *nva = NULL; int rc = 0; unsigned int i; - ssize_t nwritten = -1; nghttp3_data_reader reader; nghttp3_data_reader *preader = NULL; CURLcode result; @@ -1480,11 +1625,12 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf, goto out; } - nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, &result); - if(nwritten < 0) + 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; - *pnwritten = (size_t)nwritten; - if(!stream->h1.done) { /* need more data */ goto out; @@ -1499,7 +1645,7 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf, Curl_h1_req_parse_free(&stream->h1); nheader = Curl_dynhds_count(&h2_headers); - nva = malloc(sizeof(nghttp3_nv) * nheader); + nva = curlx_malloc(sizeof(nghttp3_nv) * nheader); if(!nva) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1520,7 +1666,7 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf, result = CURLE_SEND_ERROR; goto out; } - stream->id = (curl_int64_t)sid; + stream->id = sid; ++ctx->used_bidi_streams; switch(data->state.httpreq) { @@ -1552,36 +1698,39 @@ static CURLcode h3_stream_open(struct Curl_cfilter *cf, if(rc) { switch(rc) { case NGHTTP3_ERR_CONN_CLOSING: - CURL_TRC_CF(data, cf, "h3sid[%" FMT_PRId64 "] failed to send, " + CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send, " "connection is closing", stream->id); break; default: - CURL_TRC_CF(data, cf, "h3sid[%" FMT_PRId64 "] failed to send -> " + CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> " "%d (%s)", stream->id, rc, nghttp3_strerror(rc)); break; } + cf_ngtcp2_stream_close(cf, data, stream); result = CURLE_SEND_ERROR; goto out; } + cf_ngtcp2_upd_rx_win(cf, data, stream); + if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" FMT_PRId64 "] OPENED stream for %s", - stream->id, data->state.url); + infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", + stream->id, Curl_bufref_ptr(&data->state.url)); for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" FMT_PRId64 "] [%.*s: %.*s]", stream->id, + infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id, (int)nva[i].namelen, nva[i].name, (int)nva[i].valuelen, nva[i].value); } } out: - free(nva); + curlx_free(nva); Curl_dynhds_free(&h2_headers); return result; } static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) { struct cf_ngtcp2_ctx *ctx = cf->ctx; @@ -1598,8 +1747,10 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, *pnwritten = 0; /* handshake verification failed in callback, do not send anything */ - if(ctx->tls_vrfy_result) - return ctx->tls_vrfy_result; + if(ctx->tls_vrfy_result) { + result = ctx->tls_vrfy_result; + goto denied; + } (void)eos; /* use for stream EOF and block handling */ result = cf_progress_ingress(cf, data, &pktx); @@ -1617,10 +1768,10 @@ 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, "[%" FMT_PRId64 "] xfer write failed", stream->id); + CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); cf_ngtcp2_stream_close(cf, data, stream); result = stream->xfer_result; goto out; @@ -1632,13 +1783,13 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, * body. This happens on 30x or 40x responses. * We silently discard the data sent, since this is not a transport * error situation. */ - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] discarding data" + CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" "on closed stream with response", stream->id); result = CURLE_OK; *pnwritten = len; goto out; } - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " "-> stream closed", stream->id, len); result = CURLE_HTTP3; goto out; @@ -1650,7 +1801,7 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else { result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send, add to " + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " "sendbuf(len=%zu) -> %d, %zu", stream->id, len, result, *pnwritten); if(result) @@ -1665,44 +1816,64 @@ static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, result = cf_progress_egress(cf, data, &pktx); out: - result = Curl_1st_err(result, check_and_set_expiry(cf, data, &pktx)); - - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send(len=%zu) -> %d, %zu", + result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx)); +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", stream ? stream->id : -1, len, result, *pnwritten); CF_DATA_RESTORE(cf, save); return result; } -static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, - struct sockaddr_storage *remote_addr, - socklen_t remote_addrlen, int ecn, - void *userp) +struct cf_ngtcp2_recv_ctx { + struct pkt_io_ctx *pktx; + size_t pkt_count; +}; + +static CURLcode cf_ngtcp2_recv_pkts(const unsigned char *buf, size_t buflen, + size_t gso_size, + struct sockaddr_storage *remote_addr, + socklen_t remote_addrlen, int ecn, + void *userp) { - struct pkt_io_ctx *pktx = userp; + struct cf_ngtcp2_recv_ctx *rctx = userp; + struct pkt_io_ctx *pktx = rctx->pktx; struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx; ngtcp2_pkt_info pi; ngtcp2_path path; + size_t offset, pktlen; int rv; + if(!rctx->pkt_count) { + pktx_update_time(pktx->data, pktx, pktx->cf); + ngtcp2_path_storage_zero(&pktx->ps); + } + + if(ecn) + 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; - rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts); - if(rv) { - CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)", - ngtcp2_strerror(rv), rv); - cf_ngtcp2_err_set(pktx->cf, pktx->data, rv); + for(offset = 0; offset < buflen; offset += gso_size) { + rctx->pkt_count++; + pktlen = ((offset + gso_size) <= buflen) ? gso_size : (buflen - offset); + rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, + buf + offset, pktlen, pktx->ts); + if(rv) { + CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)", + ngtcp2_strerror(rv), rv); + cf_ngtcp2_err_set(pktx->cf, pktx->data, rv); - if(rv == NGTCP2_ERR_CRYPTO) - /* this is a "TLS problem", but a failed certificate verification - is a common reason for this */ - return CURLE_PEER_FAILED_VERIFICATION; - return CURLE_RECV_ERROR; + if(rv == NGTCP2_ERR_CRYPTO) + /* this is a "TLS problem", but a failed certificate verification + is a common reason for this */ + return CURLE_PEER_FAILED_VERIFICATION; + return CURLE_RECV_ERROR; + } } - return CURLE_OK; } @@ -1712,6 +1883,7 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, { struct cf_ngtcp2_ctx *ctx = cf->ctx; struct pkt_io_ctx local_pktx; + struct cf_ngtcp2_recv_ctx rctx; CURLcode result = CURLE_OK; if(!pktx) { @@ -1723,7 +1895,10 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, if(result) return result; - return vquic_recv_packets(cf, data, &ctx->q, 1000, recv_pkt, pktx); + rctx.pktx = pktx; + rctx.pkt_count = 0; + return vquic_recv_packets(cf, data, &ctx->q, 1000, + cf_ngtcp2_recv_pkts, &rctx); } /** @@ -1782,13 +1957,13 @@ static CURLcode read_pkt_to_send(void *userp, else if(n < 0) { switch(n) { case NGTCP2_ERR_STREAM_DATA_BLOCKED: { - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, x->data); + struct h3_stream_ctx *stream; DEBUGASSERT(ndatalen == -1); nghttp3_conn_block_stream(ctx->h3conn, stream_id); - CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] block quic flow", - (curl_int64_t)stream_id); - DEBUGASSERT(stream); - if(stream) + CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] block quic flow", + stream_id); + stream = cf_ngtcp2_get_stream(ctx, stream_id); + if(stream) /* it might be not one of our h3 streams? */ stream->quic_flow_blocked = TRUE; n = 0; break; @@ -1817,7 +1992,7 @@ static CURLcode read_pkt_to_send(void *userp, /* we add the amount of data bytes to the flow windows */ int rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen); if(rv) { - failf(x->data, "nghttp3_conn_add_write_offset returned error: %s\n", + failf(x->data, "nghttp3_conn_add_write_offset returned error: %s", nghttp3_strerror(rv)); return CURLE_SEND_ERROR; } @@ -1837,9 +2012,10 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, { struct cf_ngtcp2_ctx *ctx = cf->ctx; size_t nread; - size_t max_payload_size, path_max_payload_size, max_pktcnt; + size_t max_payload_size, path_max_payload_size; size_t pktcnt = 0; size_t gsolen = 0; /* this disables gso until we have a clue */ + size_t send_quantum; CURLcode curlcode; struct pkt_io_ctx local_pktx; @@ -1848,7 +2024,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, pktx = &local_pktx; } else { - pktx_update_time(pktx, cf); + pktx_update_time(data, pktx, cf); ngtcp2_path_storage_zero(&pktx->ps); } @@ -1870,76 +2046,73 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, * This is called PMTUD (Path Maximum Transmission Unit Discovery). * Since a PMTUD might be rejected right on send, we do not want it * be followed by other packets of lesser size. Because those would - * also fail then. So, if we detect a PMTUD while buffering, we flush. + * also fail then. If we detect a PMTUD while buffering, we flush. */ max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn); path_max_payload_size = - ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); - /* maximum number of packets buffered before we flush to the socket */ - max_pktcnt = CURLMIN(MAX_PKT_BURST, - ctx->q.sendbuf.chunk_size / max_payload_size); - + ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); + send_quantum = ngtcp2_conn_get_send_quantum(ctx->qconn); + CURL_TRC_CF(data, cf, "egress, collect and send packets, quantum=%zu", + send_quantum); for(;;) { /* add the next packet to send, if any, to our buffer */ curlcode = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, read_pkt_to_send, pktx, &nread); - if(curlcode) { - if(curlcode != CURLE_AGAIN) - return curlcode; - /* Nothing more to add, flush and leave */ - curlcode = vquic_send(cf, data, &ctx->q, gsolen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; + if(curlcode == CURLE_AGAIN) + break; + else if(curlcode) + return curlcode; + else { + size_t buflen = Curl_bufq_len(&ctx->q.sendbuf); + if((buflen >= send_quantum) || + ((buflen + gsolen) >= ctx->q.sendbuf.chunk_size)) + break; + DEBUGASSERT(nread > 0); + ++pktcnt; + if(pktcnt == 1) { + /* first packet in buffer. This is either of a known, "good" + * payload size or it is a PMTUD. We shall see. */ + gsolen = nread; } - goto out; - } - - DEBUGASSERT(nread > 0); - if(pktcnt == 0) { - /* first packet in buffer. This is either of a known, "good" - * payload size or it is a PMTUD. We will see. */ - gsolen = nread; - } - 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. - * Flush the buffer before the last add. */ - curlcode = vquic_send_tail_split(cf, data, &ctx->q, - gsolen, nread, nread); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; + else if(nread > gsolen || + (gsolen > path_max_payload_size && nread != gsolen)) { + /* 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); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; } - return curlcode; + pktcnt = 0; } - pktcnt = 0; - continue; - } - - if(++pktcnt >= max_pktcnt || nread < gsolen) { - /* Reached MAX_PKT_BURST *or* - * the capacity of our buffer *or* - * last add was shorter than the previous ones, flush */ - curlcode = vquic_send(cf, data, &ctx->q, gsolen); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - Curl_expire(data, 1, EXPIRE_QUIC); - return CURLE_OK; - } - return curlcode; + else if(nread < gsolen) { + /* Reached capacity of our buffer *or* + * last add was shorter than the previous ones, flush */ + break; } - /* pktbuf has been completely sent */ - pktcnt = 0; } } -out: + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + /* time to send */ + CURL_TRC_CF(data, cf, "egress, send collected %zu packets in %zu bytes", + pktcnt, Curl_bufq_len(&ctx->q.sendbuf)); + curlcode = vquic_send(cf, data, &ctx->q, gsolen); + if(curlcode) { + if(curlcode == CURLE_AGAIN) { + Curl_expire(data, 1, EXPIRE_QUIC); + return CURLE_OK; + } + return curlcode; + } + pktx_update_time(data, pktx, cf); + ngtcp2_conn_update_pkt_tx_time(ctx->qconn, pktx->ts); + } return CURLE_OK; } @@ -1985,19 +2158,11 @@ static CURLcode cf_ngtcp2_cntrl(struct Curl_cfilter *cf, } break; } - case CF_CTRL_DATA_IDLE: { - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - CURL_TRC_CF(data, cf, "data idle"); - if(stream && !stream->closed) { - result = check_and_set_expiry(cf, data, NULL); - if(result) - CURL_TRC_CF(data, cf, "data idle, check_and_set_expiry -> %d", result); - } - break; - } case CF_CTRL_CONN_INFO_UPDATE: - if(!cf->sockindex && cf->connected) + if(!cf->sockindex && cf->connected) { cf->conn->httpversion_seen = 30; + Curl_conn_set_multiplex(cf->conn); + } break; default: break; @@ -2013,7 +2178,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); @@ -2079,8 +2244,8 @@ static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf, (uint8_t *)buffer, sizeof(buffer), &ctx->last_error, pktx.ts); CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" - FMT_PRIu64 ") -> %d", ctx->last_error.type, - (curl_uint64_t)ctx->last_error.error_code, (int)nwritten); + PRIu64 ") -> %d", ctx->last_error.type, + ctx->last_error.error_code, (int)nwritten); /* there are cases listed in ngtcp2 documentation where this call * may fail. Since we are doing a connection shutdown as graceful * as we can, such an error is ignored here. */ @@ -2120,7 +2285,7 @@ static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf, if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { /* Sent everything off. ngtcp2 seems to have no support for graceful - * shutdowns. So, we are done. */ + * shutdowns. We are done. */ CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); *done = TRUE; result = CURLE_OK; @@ -2196,7 +2361,6 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) #endif Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid, SSL_version(ssl), "h3", quic_tp, quic_tp_len); - return 1; } return 0; } @@ -2204,22 +2368,34 @@ 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) { - case 1: return "ClientHello"; - case 2: return "ServerHello"; - case 4: return "SessionTicket"; - case 8: return "EncryptedExtensions"; - case 11: return "Certificate"; - case 13: return "CertificateRequest"; - case 15: return "CertificateVerify"; - case 20: return "Finished"; - case 24: return "KeyUpdate"; - case 254: return "MessageHash"; + case 1: + return "ClientHello"; + case 2: + return "ServerHello"; + case 4: + return "SessionTicket"; + case 8: + return "EncryptedExtensions"; + case 11: + return "Certificate"; + case 13: + return "CertificateRequest"; + case 15: + return "CertificateVerify"; + case 20: + return "Finished"; + case 24: + return "KeyUpdate"; + case 254: + return "MessageHash"; } return "Unknown"; } +#endif static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype, unsigned when, unsigned int incoming, @@ -2306,7 +2482,6 @@ static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, void *user_data) { struct curl_tls_ctx *ctx = user_data; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); #ifdef USE_OPENSSL #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) @@ -2323,7 +2498,7 @@ static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, return CURLE_FAILED_INIT; } #endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */ - if(ssl_config->primary.cache_session) { + if(Curl_ssl_scache_use(cf, data)) { /* Enable the session cache because it is a prerequisite for the * "new session" callback. Use the "external storage" mode to prevent * OpenSSL from creating an internal session cache. @@ -2339,7 +2514,7 @@ static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed"); return CURLE_FAILED_INIT; } - if(ssl_config->primary.cache_session) { + if(Curl_ssl_scache_use(cf, data)) { gnutls_handshake_set_hook_function(ctx->gtls.session, GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, quic_gtls_handshake_cb); @@ -2350,7 +2525,7 @@ static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); return CURLE_FAILED_INIT; } - if(ssl_config->primary.cache_session) { + if(Curl_ssl_scache_use(cf, data)) { /* Register to get notified when a new session is received */ wolfSSL_CTX_sess_set_new_cb(ctx->wssl.ssl_ctx, wssl_quic_new_session_cb); } @@ -2383,8 +2558,8 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, #endif /* WOLFSSL_EARLY_DATA */ #endif #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ - (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)) - if((!ctx->earlydata_max)) { + (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)) + if(!ctx->earlydata_max) { CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); } else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { @@ -2420,6 +2595,18 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, return result; } +static bool cf_ngtcp2_need_httpsrr(struct Curl_easy *data) +{ +#ifdef USE_OPENSSL + return Curl_ossl_need_httpsrr(data); +#elif defined(USE_WOLFSSL) + return Curl_wssl_need_httpsrr(data); +#else + (void)data; + return FALSE; +#endif +} + /* * Might be called twice for happy eyeballs. */ @@ -2433,9 +2620,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, CURLcode result; const struct Curl_sockaddr_ex *sockaddr = NULL; int qfd; -static const struct alpn_spec ALPN_SPEC_H3 = { - { "h3", "h3-29" }, 2 -}; + static const struct alpn_spec ALPN_SPEC_H3 = { { "h3", "h3-29" }, 2 }; DEBUGASSERT(ctx->initialized); ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; @@ -2452,12 +2637,11 @@ static const struct alpn_spec ALPN_SPEC_H3 = { ctx->qlogfd = qfd; /* -1 if failure above */ quic_settings(ctx, data, pktx); - result = vquic_ctx_init(&ctx->q); + result = vquic_ctx_init(data, &ctx->q); if(result) return result; - Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL); - if(!sockaddr) + if(Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL)) return CURLE_QUIC_CONNECT_ERROR; ctx->q.local_addrlen = sizeof(ctx->q.local_addr); rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, @@ -2507,7 +2691,7 @@ static const struct alpn_spec ALPN_SPEC_H3 = { #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); @@ -2522,7 +2706,6 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, struct cf_ngtcp2_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; struct cf_call_data save; - struct curltime now; struct pkt_io_ctx pktx; if(cf->connected) { @@ -2538,13 +2721,18 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, } *done = FALSE; - now = curlx_now(); - pktx_init(&pktx, cf, data); + if(cf_ngtcp2_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); + return CURLE_OK; + } + + pktx_init(&pktx, cf, data); CF_DATA_SAVE(save, cf, data); if(!ctx->qconn) { - ctx->started_at = now; + ctx->started_at = *Curl_pgrs_now(data); result = cf_connect_start(cf, data, &pktx); if(result) goto out; @@ -2571,27 +2759,46 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "peer verified"); cf->connected = TRUE; *done = TRUE; - connkeep(cf->conn, "HTTP/3 default"); } } out: - if(result == CURLE_RECV_ERROR && ctx->qconn && + if(ctx->qconn && + ((result == CURLE_RECV_ERROR) || (result == CURLE_SEND_ERROR)) && ngtcp2_conn_in_draining_period(ctx->qconn)) { - /* 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; + const ngtcp2_ccerr *cerr = ngtcp2_conn_get_ccerr(ctx->qconn); + + result = CURLE_COULDNT_CONNECT; + if(cerr) { + CURL_TRC_CF(data, cf, "connect error, type=%d, code=%" PRIu64, + cerr->type, cerr->error_code); + switch(cerr->type) { + case NGTCP2_CCERR_TYPE_VERSION_NEGOTIATION: + CURL_TRC_CF(data, cf, "error in version negotiation"); + break; + default: + if(cerr->error_code >= NGTCP2_CRYPTO_ERROR) { + CURL_TRC_CF(data, cf, "crypto error, tls alert=%u", + (unsigned int)(cerr->error_code & 0xffU)); + } + else if(cerr->error_code == NGTCP2_CONNECTION_REFUSED) { + CURL_TRC_CF(data, cf, "connection refused by server"); + /* When a QUIC server instance is shutting down, it may send us a + * CONNECTION_CLOSE with this code right away. We want + * to keep on trying in this case. */ + result = CURLE_WEIRD_SERVER_REPLY; + } + } + } } -#ifndef CURL_DISABLE_VERBOSE_STRINGS +#ifdef CURLVERBOSE if(result) { struct ip_quadruple ip; - 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)); + 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 && ctx->qconn) { @@ -2623,7 +2830,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, } else if(ctx->max_bidi_streams) { uint64_t avail_bidi_streams = 0; - uint64_t max_streams = CONN_ATTACHED(cf->conn); + uint64_t max_streams = cf->conn->attached_xfers; if(ctx->max_bidi_streams > ctx->used_bidi_streams) avail_bidi_streams = ctx->max_bidi_streams - ctx->used_bidi_streams; max_streams += avail_bidi_streams; @@ -2633,13 +2840,14 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, *pres1 = (int)Curl_multi_max_concurrent_streams(data->multi); CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: " "MAX_CONCURRENT -> %d (%u in use)", - cf->conn->connection_id, *pres1, CONN_ATTACHED(cf->conn)); + cf->conn->connection_id, *pres1, cf->conn->attached_xfers); CF_DATA_RESTORE(cf, save); return CURLE_OK; } case CF_QUERY_CONNECT_REPLY_MS: if(ctx->q.got_first_byte) { - timediff_t ms = curlx_timediff(ctx->q.first_byte_at, ctx->started_at); + timediff_t ms = curlx_ptimediff_ms(&ctx->q.first_byte_at, + &ctx->started_at); *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX; } else @@ -2697,12 +2905,17 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, goto out; /* We do not announce a max idle timeout, but when the peer does - * it will close the connection when it expires. */ + * it closes the connection when it expires. */ rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); if(rp && rp->max_idle_timeout) { - timediff_t idletime = curlx_timediff(curlx_now(), ctx->q.last_io); - if(idletime > 0 && (uint64_t)idletime > rp->max_idle_timeout) - goto out; + timediff_t idletime_ms = + curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->q.last_io); + if(idletime_ms > 0) { + uint64_t max_idle_ms = + (uint64_t)(rp->max_idle_timeout / NGTCP2_MILLISECONDS); + if((uint64_t)idletime_ms > max_idle_ms) + goto out; + } } if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) @@ -2746,14 +2959,13 @@ struct Curl_cftype Curl_cft_http3 = { CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai) + struct Curl_sockaddr_ex *addr) { struct cf_ngtcp2_ctx *ctx = NULL; - struct Curl_cfilter *cf = NULL, *udp_cf = NULL; + struct Curl_cfilter *cf = NULL; CURLcode result; - (void)data; - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -2763,41 +2975,23 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); if(result) goto out; + cf->conn = conn; - result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); + result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC); if(result) goto out; - - cf->conn = conn; - udp_cf->conn = cf->conn; - udp_cf->sockindex = cf->sockindex; - cf->next = udp_cf; + cf->next->conn = cf->conn; + cf->next->sockindex = cf->sockindex; out: *pcf = (!result) ? cf : NULL; if(result) { - if(udp_cf) - Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); - Curl_safefree(cf); - cf_ngtcp2_ctx_free(ctx); + if(cf) + Curl_conn_cf_discard_chain(&cf, data); + else if(ctx) + cf_ngtcp2_ctx_free(ctx); } return result; } -bool Curl_conn_is_ngtcp2(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; -} - #endif diff --git a/lib/vquic/curl_ngtcp2.h b/lib/vquic/curl_ngtcp2.h index 86753a3a6b..185272ace0 100644 --- a/lib/vquic/curl_ngtcp2.h +++ b/lib/vquic/curl_ngtcp2.h @@ -23,8 +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) @@ -47,18 +46,14 @@ struct Curl_cfilter; -#include "../urldata.h" +#include "urldata.h" void Curl_ngtcp2_ver(char *p, size_t len); CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai); - -bool Curl_conn_is_ngtcp2(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex); + struct Curl_sockaddr_ex *addr); #endif #endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */ diff --git a/lib/vquic/curl_osslq.c b/lib/vquic/curl_osslq.c deleted file mode 100644 index 3586ad0d55..0000000000 --- a/lib/vquic/curl_osslq.c +++ /dev/null @@ -1,2463 +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 "../hash.h" -#include "../sendf.h" -#include "../strdup.h" -#include "../rand.h" -#include "../multiif.h" -#include "../cfilters.h" -#include "../cf-socket.h" -#include "../connect.h" -#include "../progress.h" -#include "../strerror.h" -#include "../curlx/dynbuf.h" -#include "../http1.h" -#include "../select.h" -#include "../curlx/inet_pton.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 "../curlx/warnless.h" - -/* The last 3 #include files should be in this order */ -#include "../curl_printf.h" -#include "../curl_memory.h" -#include "../memdebug.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) - strcpy(buf, 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)) { - } - 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 { - curl_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 = (curl_int64_t)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 */ - uint64_t max_idle_ms; /* max idle time for QUIC connection */ - SSL_POLL_ITEM *poll_items; /* Array for polling on writable state */ - struct Curl_easy **curl_items; /* Array of easy objs */ - size_t 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_uint_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_uint_hash_destroy(&ctx->streams); - Curl_ssl_peer_cleanup(&ctx->peer); - free(ctx->poll_items); - free(ctx->curl_items); - } - 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; - curl_int64_t stream_id = (curl_int64_t)SSL_get_stream_id(stream_ssl); - - if(h3->remote_ctrl_n >= CURL_ARRAYSIZE(h3->remote_ctrl)) { - /* rejected, we are full */ - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] rejecting remote stream", - stream_id); - SSL_free(stream_ssl); - return CURLE_FAILED_INIT; - } - switch(SSL_get_stream_type(stream_ssl)) { - case SSL_STREAM_TYPE_READ: { - struct cf_osslq_stream *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, "[%" FMT_PRId64 "] accepted remote uni stream", - stream_id); - break; - } - default: - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reject remote non-uni-read" - " stream", stream_id); - SSL_free(stream_ssl); - return CURLE_FAILED_INIT; - } - 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; - 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; - - Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); - if(sockerr && detail == SSL_ERROR_SYSCALL) - Curl_strerror(sockerr, extramsg, sizeof(extramsg)); - 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; - - cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - - 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 */ - curl_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); - 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 = 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_uint_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, "[%"FMT_PRId64"] 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_uint_hash_remove(&ctx->streams, data->mid); - } -} - -struct cf_ossq_find_ctx { - curl_int64_t stream_id; - struct h3_stream_ctx *stream; -}; - -static bool cf_osslq_find_stream(unsigned int 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_uint_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, "[%" FMT_PRId64 "] RESET: error %" FMT_PRIu64, - stream->s.id, stream->error3); - } - else { - CURL_TRC_CF(data, cf, "[%" FMT_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 += (size_t)nwritten; - - if(nwritten < memlen) { - /* This MUST not happen. Our recbuf is dimensioned to hold the - * full max_stream_window and then some for this very 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, "[%" FMT_PRId64 "] DATA len=%zu, ERROR %d", - stream->s.id, buflen, result); - return NGHTTP3_ERR_CALLBACK_FAILURE; - } - stream->download_recvd += (curl_off_t)buflen; - CURL_TRC_CF(data, cf, "[%" FMT_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, "[%" FMT_PRId64 "] deferred consume %zu bytes", - stream->s.id, consumed); - return 0; -} - -static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid, - 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; - curl_int64_t stream_id = sid; - 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 = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", - stream->status_code); - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] status: %s", stream_id, line); - result = write_resp_raw(cf, data, line, ncopy, FALSE); - if(result) { - return -1; - } - } - else { - /* store as an HTTP1-style header */ - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] header: %.*s: %.*s", - stream_id, (int)h3name.len, h3name.base, - (int)h3val.len, h3val.base); - result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE); - 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 sid, - 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; - curl_int64_t stream_id = sid; - 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, "[%" FMT_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 sid, - 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; - curl_int64_t stream_id = sid; - 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, "[%" FMT_PRId64 "] stop_sending", stream_id); - cf_osslq_stream_close(&stream->s); - return 0; -} - -static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid, - 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; - curl_int64_t stream_id = sid; - 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, "[%" FMT_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, "[%" FMT_PRId64 "] read req body -> AGAIN", - stream->s.id); - return NGHTTP3_ERR_WOULDBLOCK; - } - - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> " - "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", - stream->s.id, (int)nvecs, - *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", - nwritten, Curl_bufq_len(&stream->sendbuf), - 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 */ -#ifdef NGHTTP3_CALLBACKS_V2 - NULL, /* recv_origin */ - NULL, /* end_origin */ - NULL, /* rand */ -#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(&ctx->q); - if(result) - goto out; - - result = CURLE_QUIC_CONNECT_ERROR; - Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &peer_addr, NULL); - if(!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); - /* 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, "[%" FMT_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; - SSL_get_stream_read_error_code(x->s->ssl, &app_error_code); - CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] h3_quic_recv -> RESET, " - "rv=%d, app_err=%" FMT_PRIu64, - x->s->id, rv, (curl_uint64_t)app_error_code); - if(app_error_code != NGHTTP3_H3_NO_ERROR) { - x->s->reset = TRUE; - } - 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, "[%" FMT_PRId64 "] forward %zu bytes " - "to nghttp3 -> %zd", s->id, blen, nread); - if(nread < 0) { - failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s", - 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, "[%" FMT_PRId64 "] close nghttp3 stream -> %d", - s->id, rv); - if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { - failf(data, "nghttp3_conn_close_stream returned error: %s", - nghttp3_strerror(rv)); - result = CURLE_RECV_ERROR; - goto out; - } - } - } - } -out: - if(result) - CURL_TRC_CF(data, cf, "[%" FMT_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(unsigned int 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; - - (void)cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data); - } - - 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(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_uint_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(unsigned int 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_uint_hash_count(&ctx->streams)) { - size_t nmax = Curl_uint_hash_count(&ctx->streams); - ctx->items_max = 0; - tmpptr = realloc(ctx->poll_items, nmax * sizeof(SSL_POLL_ITEM)); - if(!tmpptr) { - free(ctx->poll_items); - ctx->poll_items = NULL; - res = CURLE_OUT_OF_MEMORY; - goto out; - } - ctx->poll_items = tmpptr; - - tmpptr = realloc(ctx->curl_items, nmax * sizeof(struct Curl_easy *)); - if(!tmpptr) { - 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_uint_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 %" - FMT_PRId64, (curl_int64_t)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, "[%" FMT_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, "[%"FMT_PRId64 "] send %zu bytes to " - "QUIC blocked", s->id, vec[i].len); - written = 0; - nghttp3_conn_block_stream(ctx->h3.conn, s->id); - s->send_blocked = blocked = TRUE; - break; - default: - failf(data, "[%"FMT_PRId64 "] send %zu bytes to QUIC, SSL error %d", - s->id, vec[i].len, detail); - result = cf_osslq_ssl_err(cf, data, detail, CURLE_HTTP3); - goto out; - } - } - } - - 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 = curlx_now(); - 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\n", - 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\n", - nghttp3_strerror(rv)); - result = CURLE_SEND_ERROR; - goto out; - } - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] forwarded %zu/%zu h3 bytes " - "to QUIC, eos=%d", s->id, acked_len, total_len, eos); - } - - if(eos && !s->send_blocked && !eos_written) { - /* wrote everything and H3 indicates end of stream */ - CURL_TRC_CF(data, cf, "[%" FMT_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; - struct curltime now; - 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; - now = curlx_now(); - CF_DATA_SAVE(save, cf, data); - - if(!ctx->tls.ossl.ssl) { - ctx->started_at = now; - 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 = curlx_now(); - } - } - - /* 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 */ - ctx->handshake_at = now; - ctx->q.last_io = now; - CURL_TRC_CF(data, cf, "handshake complete after %dms", - (int)curlx_timediff(now, ctx->started_at)); - result = cf_osslq_verify_peer(cf, data); - if(!result) { - CURL_TRC_CF(data, cf, "peer verified"); - cf->connected = TRUE; - *done = TRUE; - connkeep(cf->conn, "HTTP/3 default"); - } - } - else { - int detail = SSL_get_error(ctx->tls.ossl.ssl, err); - switch(detail) { - case SSL_ERROR_WANT_READ: - ctx->q.last_io = now; - CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV"); - goto out; - case SSL_ERROR_WANT_WRITE: - ctx->q.last_io = now; - 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 = now; - 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; - - 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 ssize_t h3_stream_open(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *buf, size_t len, - CURLcode *err) -{ - 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; - ssize_t nwritten = -1; - nghttp3_data_reader reader; - nghttp3_data_reader *preader = NULL; - - Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); - - *err = h3_data_setup(cf, data); - if(*err) - goto out; - stream = H3_STREAM_CTX(ctx, data); - DEBUGASSERT(stream); - if(!stream) { - *err = CURLE_FAILED_INIT; - goto out; - } - - nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); - if(nwritten < 0) - goto out; - if(!stream->h1.done) { - /* need more data */ - goto out; - } - DEBUGASSERT(stream->h1.req); - - *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); - if(*err) { - nwritten = -1; - goto out; - } - /* no longer needed */ - Curl_h1_req_parse_free(&stream->h1); - - nheader = Curl_dynhds_count(&h2_headers); - nva = malloc(sizeof(nghttp3_nv) * nheader); - if(!nva) { - *err = CURLE_OUT_OF_MEMORY; - nwritten = -1; - 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); - *err = cf_osslq_stream_open(&stream->s, ctx->tls.ossl.ssl, 0, - &ctx->stream_bufcp, data); - if(*err) { - failf(data, "cannot get bidi streams"); - *err = 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[%"FMT_PRId64"] failed to send, " - "connection is closing", stream->s.id); - break; - default: - CURL_TRC_CF(data, cf, "h3sid[%"FMT_PRId64 "] failed to send -> %d (%s)", - stream->s.id, rc, nghttp3_strerror(rc)); - break; - } - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; - } - - if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" FMT_PRId64 "] OPENED stream for %s", - stream->s.id, data->state.url); - for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" FMT_PRId64 "] [%.*s: %.*s]", - stream->s.id, - (int)nva[i].namelen, nva[i].name, - (int)nva[i].valuelen, nva[i].value); - } - } - -out: - free(nva); - Curl_dynhds_free(&h2_headers); - return nwritten; -} - -static CURLcode cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, - size_t *pnwritten) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - struct cf_call_data save; - ssize_t nwritten; - 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; - - if(!stream || stream->s.id < 0) { - nwritten = h3_stream_open(cf, data, buf, len, &result); - if(nwritten < 0) { - CURL_TRC_CF(data, cf, "failed to open stream -> %d", result); - goto out; - } - stream = H3_STREAM_CTX(ctx, data); - *pnwritten = (size_t)nwritten; - } - 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, "[%" FMT_PRId64 "] discarding data" - "on closed stream with response", stream->s.id); - result = CURLE_OK; - *pnwritten = len; - goto out; - } - CURL_TRC_CF(data, cf, "[%" FMT_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, "[%" FMT_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, "[%" FMT_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 %" FMT_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 %" FMT_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 = H3_STREAM_CTX(ctx, data); - struct cf_call_data save; - CURLcode result = CURLE_OK; - - (void)ctx; - CF_DATA_SAVE(save, cf, data); - DEBUGASSERT(cf->connected); - DEBUGASSERT(ctx); - DEBUGASSERT(ctx->tls.ossl.ssl); - DEBUGASSERT(ctx->h3.conn); - *pnread = 0; - - 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, "[%" FMT_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, "[%" FMT_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, "[%" FMT_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_DATA_IDLE: { - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - CURL_TRC_CF(data, cf, "data idle"); - if(stream && !stream->closed) { - result = check_and_set_expiry(cf, data); - } - break; - } - case CF_CTRL_CONN_INFO_UPDATE: - if(!cf->sockindex && cf->connected) - cf->conn->httpversion_seen = 30; - 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 = ctx->max_idle_ms; - 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: %zums", (size_t)idle_ms); - idletime = curlx_timediff(curlx_now(), ctx->q.last_io); - if(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 += CONN_ATTACHED(cf->conn); - *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_timediff(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, *udp_cf = NULL; - CURLcode result; - - (void)data; - ctx = 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; - - result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); - if(result) - goto out; - - cf->conn = conn; - udp_cf->conn = cf->conn; - udp_cf->sockindex = cf->sockindex; - cf->next = udp_cf; - -out: - *pcf = (!result) ? cf : NULL; - if(result) { - if(udp_cf) - Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); - Curl_safefree(cf); - 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)msnprintf(p, len, "nghttp3/%s", ht3->version_str); -} - -#endif /* !CURL_DISABLE_HTTP && USE_OPENSSL_QUIC && USE_NGHTTP3 */ diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index 179ccf8aa1..8a24e1f6a5 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -21,69 +21,56 @@ * 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 "../sendf.h" -#include "../strdup.h" -#include "../rand.h" -#include "../multiif.h" -#include "../connect.h" -#include "../progress.h" -#include "../strerror.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 "../curlx/inet_pton.h" -#include "../vtls/openssl.h" -#include "../vtls/keylog.h" -#include "../vtls/vtls.h" -/* The last 3 #include files should be in this order */ -#include "../curl_printf.h" -#include "../curl_memory.h" -#include "../memdebug.h" +#include "bufq.h" +#include "uint-hash.h" +#include "urldata.h" +#include "cfilters.h" +#include "cf-dns.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/vtls.h" /* HTTP/3 error values defined in RFC 9114, ch. 8.1 */ -#define CURL_H3_NO_ERROR (0x0100) +#define CURL_H3_NO_ERROR 0x0100 -#define QUIC_MAX_STREAMS (100) +#define MAX_PKT_BURST 10 -#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 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) -#define H3_STREAM_SEND_CHUNKS \ - (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) + (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) /* * Store quiche version info in this buffer. */ void Curl_quiche_ver(char *p, size_t len) { - (void)msnprintf(p, len, "quiche/%s", quiche_version()); + (void)curl_msnprintf(p, len, "quiche/%s", quiche_version()); } struct cf_quiche_ctx { @@ -97,8 +84,9 @@ struct cf_quiche_ctx { uint8_t scid[QUICHE_MAX_CONN_ID_LEN]; struct curltime started_at; /* time the current attempt started */ struct curltime handshake_at; /* time connect handshake finished */ - struct bufc_pool stream_bufcp; /* chunk pool for streams */ struct uint_hash streams; /* hash `data->mid` to `stream_ctx` */ + struct dynbuf h1hdr; /* temp buffer for header construction */ + struct bufq writebuf; /* temp buffer for writing bodies */ curl_off_t data_recvd; BIT(initialized); BIT(goaway); /* got GOAWAY from server */ @@ -112,7 +100,7 @@ static int debug_log_init = 0; static void quiche_debug_log(const char *line, void *argp) { (void)argp; - fprintf(stderr, "%s\n", line); + curl_mfprintf(stderr, "%s\n", line); } #endif @@ -127,9 +115,10 @@ static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx) debug_log_init = 1; } #endif - Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, - H3_STREAM_POOL_SPARES); - Curl_uint_hash_init(&ctx->streams, 63, h3_stream_hash_free); + curlx_dyn_init(&ctx->h1hdr, CURL_MAX_HTTP_HEADER); + Curl_uint32_hash_init(&ctx->streams, 63, h3_stream_hash_free); + Curl_bufq_init2(&ctx->writebuf, H3_STREAM_CHUNK_SIZE, H3_STREAM_RECV_CHUNKS, + BUFQ_OPT_SOFT_LIMIT); ctx->data_recvd = 0; ctx->initialized = TRUE; } @@ -137,27 +126,36 @@ 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); vquic_ctx_free(&ctx->q); - Curl_bufcp_free(&ctx->stream_bufcp); - Curl_uint_hash_destroy(&ctx->streams); + Curl_uint32_hash_destroy(&ctx->streams); + curlx_dyn_free(&ctx->h1hdr); + Curl_bufq_free(&ctx->writebuf); } - free(ctx); + curlx_free(ctx); } static void cf_quiche_ctx_close(struct cf_quiche_ctx *ctx) { - if(ctx->h3c) + if(ctx->h3c) { quiche_h3_conn_free(ctx->h3c); - if(ctx->h3config) + ctx->h3c = NULL; + } + if(ctx->h3config) { quiche_h3_config_free(ctx->h3config); - if(ctx->qconn) + ctx->h3config = NULL; + } + if(ctx->qconn) { quiche_conn_free(ctx->qconn); - if(ctx->cfg) + ctx->qconn = NULL; + } + if(ctx->cfg) { quiche_config_free(ctx->cfg); + ctx->cfg = NULL; + } } static CURLcode cf_flush_egress(struct Curl_cfilter *cf, @@ -167,24 +165,24 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, * All about the H3 internals of a stream */ struct h3_stream_ctx { - curl_uint64_t id; /* HTTP/3 protocol stream identifier */ - struct bufq recvbuf; /* h3 response */ + uint64_t id; /* HTTP/3 protocol stream identifier */ struct h1_req_parser h1; /* h1 request parsing */ - curl_uint64_t error3; /* HTTP/3 stream error code */ - BIT(opened); /* TRUE after stream has been opened */ - BIT(closed); /* TRUE on stream close */ - BIT(reset); /* TRUE on stream reset */ - BIT(send_closed); /* stream is locally closed */ + uint64_t error3; /* HTTP/3 stream error code */ + int status_code; /* HTTP status code */ + CURLcode xfer_result; /* result from cf_quiche_write_(hd/body) */ + BIT(opened); /* TRUE after stream has been opened */ + BIT(closed); /* TRUE on stream close */ + BIT(reset); /* TRUE on stream reset */ + BIT(send_closed); /* stream is locally closed */ BIT(resp_hds_complete); /* final response has been received */ - BIT(resp_got_header); /* TRUE when h3 stream has recvd some HEADER */ - BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ + BIT(resp_got_header); /* TRUE when h3 stream has recvd some HEADER */ + BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ }; static void h3_stream_ctx_free(struct h3_stream_ctx *stream) { - Curl_bufq_free(&stream->recvbuf); Curl_h1_req_parse_free(&stream->h1); - free(stream); + curlx_free(stream); } static void h3_stream_hash_free(unsigned int id, void *stream) @@ -206,7 +204,7 @@ struct cf_quiche_visit_ctx { void *user_data; }; -static bool cf_quiche_stream_do(unsigned int mid, void *val, void *user_data) +static bool cf_quiche_stream_do(uint32_t mid, void *val, void *user_data) { struct cf_quiche_visit_ctx *vctx = user_data; struct h3_stream_ctx *stream = val; @@ -227,7 +225,7 @@ static void cf_quiche_for_all_streams(struct Curl_cfilter *cf, vctx.multi = multi; vctx.cb = do_cb; vctx.user_data = user_data; - Curl_uint_hash_visit(&ctx->streams, cf_quiche_stream_do, &vctx); + Curl_uint32_hash_visit(&ctx->streams, cf_quiche_stream_do, &vctx); } static bool cf_quiche_do_resume(struct Curl_cfilter *cf, @@ -239,7 +237,7 @@ static bool cf_quiche_do_resume(struct Curl_cfilter *cf, if(stream->quic_flow_blocked) { stream->quic_flow_blocked = FALSE; Curl_multi_mark_dirty(sdata); - CURL_TRC_CF(sdata, cf, "[%"FMT_PRIu64"] unblock", stream->id); + CURL_TRC_CF(sdata, cf, "[%" PRIu64 "] unblock", stream->id); } return TRUE; } @@ -252,6 +250,7 @@ static bool cf_quiche_do_expire(struct Curl_cfilter *cf, (void)stream; (void)user_data; CURL_TRC_CF(sdata, cf, "conn closed, mark as dirty"); + stream->xfer_result = CURLE_SEND_ERROR; Curl_multi_mark_dirty(sdata); return TRUE; } @@ -265,16 +264,14 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, if(stream) return CURLE_OK; - stream = calloc(1, sizeof(*stream)); + stream = curlx_calloc(1, sizeof(*stream)); if(!stream) return CURLE_OUT_OF_MEMORY; stream->id = -1; - Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, - H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); - if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) { + if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) { h3_stream_ctx_free(stream); return CURLE_OUT_OF_MEMORY; } @@ -282,29 +279,39 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf, return CURLE_OK; } +static void cf_quiche_stream_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + CURLcode result; + + if(ctx->qconn && !stream->closed) { + quiche_conn_stream_shutdown(ctx->qconn, stream->id, + QUICHE_SHUTDOWN_READ, CURL_H3_NO_ERROR); + if(!stream->send_closed) { + quiche_conn_stream_shutdown(ctx->qconn, stream->id, + QUICHE_SHUTDOWN_WRITE, CURL_H3_NO_ERROR); + stream->send_closed = TRUE; + } + stream->closed = TRUE; + result = cf_flush_egress(cf, data); + if(result) + CURL_TRC_CF(data, cf, "[%" PRIu64 "] stream close, flush egress -> %d", + stream->id, result); + } +} + static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - CURLcode result; (void)cf; if(stream) { - CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] easy handle is done", stream->id); - if(ctx->qconn && !stream->closed) { - quiche_conn_stream_shutdown(ctx->qconn, stream->id, - QUICHE_SHUTDOWN_READ, CURL_H3_NO_ERROR); - if(!stream->send_closed) { - quiche_conn_stream_shutdown(ctx->qconn, stream->id, - QUICHE_SHUTDOWN_WRITE, CURL_H3_NO_ERROR); - stream->send_closed = TRUE; - } - stream->closed = TRUE; - result = cf_flush_egress(cf, data); - if(result) - CURL_TRC_CF(data, cf, "data_done, flush egress -> %d", result); - } - Curl_uint_hash_remove(&ctx->streams, data->mid); + CURL_TRC_CF(data, cf, "[%" PRIu64 "] easy handle is done", stream->id); + cf_quiche_stream_close(cf, data, stream); + Curl_uint32_hash_remove(&ctx->streams, data->mid); } } @@ -316,79 +323,106 @@ static void cf_quiche_expire_conn_closed(struct Curl_cfilter *cf, cf_quiche_for_all_streams(cf, data->multi, cf_quiche_do_expire, NULL); } -/* - * 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, +static void cf_quiche_write_hd(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *mem, size_t memlen) + struct h3_stream_ctx *stream, + const char *buf, size_t blen, bool eos) { - struct cf_quiche_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(nwritten < memlen) { - /* This MUST not happen. Our recbuf is dimensioned to hold the - * full max_stream_window and then some for this very reason. */ - DEBUGASSERT(0); - return CURLE_RECV_ERROR; + /* This function returns no error intentionally, but records + * the result at the stream, skipping further writes once the + * `result` of the transfer is known. + * The stream is subsequently cancelled "higher up" in the filter's + * send/recv callbacks. Closing the stream here leads to SEND/RECV + * errors in other places that then overwrite the transfer's result. */ + 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, "[%" PRIu64 "] error %d writing %zu " + "bytes of headers", stream->id, stream->xfer_result, blen); } - return result; } struct cb_ctx { struct Curl_cfilter *cf; struct Curl_easy *data; + struct h3_stream_ctx *stream; }; +static bool is_valid_h3_header(const uint8_t *hdr, size_t hlen) +{ + while(hlen--) { + switch(*hdr++) { + case '\n': + case '\r': + case '\0': + return FALSE; + } + } + return TRUE; +} + static int cb_each_header(uint8_t *name, size_t name_len, uint8_t *value, size_t value_len, void *argp) { struct cb_ctx *x = argp; - struct cf_quiche_ctx *ctx = x->cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, x->data); - CURLcode result; + struct Curl_cfilter *cf = x->cf; + struct Curl_easy *data = x->data; + struct h3_stream_ctx *stream = x->stream; + struct cf_quiche_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; - if(!stream) - return CURLE_OK; + if(!stream || stream->xfer_result) + return 1; /* abort iteration */ - if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7)) { - CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRIu64 "] status: %.*s", - stream->id, (int)value_len, value); - result = write_resp_raw(x->cf, x->data, "HTTP/3 ", sizeof("HTTP/3 ") - 1); + if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7) && + is_valid_h3_header(value, value_len)) { + curlx_dyn_reset(&ctx->h1hdr); + result = Curl_http_decode_status(&stream->status_code, + (const char *)value, value_len); if(!result) - result = write_resp_raw(x->cf, x->data, value, value_len); + result = curlx_dyn_addn(&ctx->h1hdr, STRCONST("HTTP/3 ")); if(!result) - result = write_resp_raw(x->cf, x->data, " \r\n", 3); + result = curlx_dyn_addn(&ctx->h1hdr, (const char *)value, value_len); + if(!result) + result = curlx_dyn_addn(&ctx->h1hdr, STRCONST(" \r\n")); + 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, "[%" PRIu64 "] status: %s", + stream->id, curlx_dyn_ptr(&ctx->h1hdr)); } else { - CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRIu64 "] header: %.*s: %.*s", - stream->id, (int)name_len, name, - (int)value_len, value); - result = write_resp_raw(x->cf, x->data, name, name_len); - if(!result) - result = write_resp_raw(x->cf, x->data, ": ", 2); - if(!result) - result = write_resp_raw(x->cf, x->data, value, value_len); - if(!result) - result = write_resp_raw(x->cf, x->data, "\r\n", 2); + 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, "[%" PRIu64 "] header: %.*s: %.*s", + stream->id, (int)name_len, name, + (int)value_len, value); + curlx_dyn_reset(&ctx->h1hdr); + result = curlx_dyn_addn(&ctx->h1hdr, (const char *)name, name_len); + if(!result) + result = curlx_dyn_addn(&ctx->h1hdr, STRCONST(": ")); + if(!result) + result = curlx_dyn_addn(&ctx->h1hdr, (const char *)value, value_len); + if(!result) + result = curlx_dyn_addn(&ctx->h1hdr, STRCONST("\r\n")); + if(!result) + cf_quiche_write_hd(cf, data, stream, curlx_dyn_ptr(&ctx->h1hdr), + curlx_dyn_len(&ctx->h1hdr), FALSE); + } + else + CURL_TRC_CF(x->data, x->cf, "[%" PRIu64 "] ignore %zu bytes bad header", + stream->id, value_len + name_len); } + if(result) { - CURL_TRC_CF(x->data, x->cf, "[%"FMT_PRIu64"] on header error %d", + CURL_TRC_CF(x->data, x->cf, "[%" PRIu64 "] on header error %d", stream->id, result); + if(!stream->xfer_result) + stream->xfer_result = result; } - return result; + return stream->xfer_result ? 1 : 0; } static CURLcode stream_resp_read(void *reader_ctx, @@ -405,160 +439,143 @@ static CURLcode stream_resp_read(void *reader_ctx, return CURLE_RECV_ERROR; nread = quiche_h3_recv_body(ctx->h3c, ctx->qconn, stream->id, buf, len); - if(nread >= 0) { - *pnread = (size_t)nread; - return CURLE_OK; - } - else + if(!curlx_sztouz(nread, pnread)) return CURLE_AGAIN; + return CURLE_OK; } -static CURLcode cf_recv_body(struct Curl_cfilter *cf, - struct Curl_easy *data) +static void cf_quiche_flush_body(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream) +{ + struct cf_quiche_ctx *ctx = cf->ctx; + const uint8_t *buf; + size_t blen; + + while(stream && !stream->xfer_result) { + if(Curl_bufq_peek(&ctx->writebuf, &buf, &blen)) { + stream->xfer_result = Curl_xfer_write_resp( + data, (const char *)buf, blen, FALSE); + Curl_bufq_skip(&ctx->writebuf, blen); + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" PRIu64 "] error %d writing %zu bytes" + " of data", stream->id, stream->xfer_result, blen); + } + } + else + break; + } + Curl_bufq_reset(&ctx->writebuf); +} + +static void cf_quiche_recv_body(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream) { struct cf_quiche_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); size_t nread; struct cb_ctx cb_ctx; CURLcode result = CURLE_OK; if(!stream) - return CURLE_RECV_ERROR; - - if(!stream->resp_hds_complete) { - result = write_resp_raw(cf, data, "\r\n", 2); - if(result) - return result; - stream->resp_hds_complete = TRUE; - } + return; + /* Even when the transfer has already errored, we need to receive + * the data from quiche, as quiche otherwise gets stuck and + * raise events to receive over and over again. */ cb_ctx.cf = cf; cb_ctx.data = data; - result = Curl_bufq_slurp(&stream->recvbuf, - stream_resp_read, &cb_ctx, &nread); - - if(result && result != CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] recv_body error %zu", - stream->id, nread); - failf(data, "Error %d in HTTP/3 response body for stream[%"FMT_PRIu64"]", - result, stream->id); - stream->closed = TRUE; - stream->reset = TRUE; - stream->send_closed = TRUE; - streamclose(cf->conn, "Reset of stream"); - return result; + cb_ctx.stream = stream; + Curl_bufq_reset(&ctx->writebuf); + while(!result) { + result = Curl_bufq_slurp(&ctx->writebuf, + stream_resp_read, &cb_ctx, &nread); + if(!result) + cf_quiche_flush_body(cf, data, stream); + else if(result == CURLE_AGAIN) + break; + else if(result) { + CURL_TRC_CF(data, cf, "[%" PRIu64 "] recv_body error %d", + stream->id, result); + failf(data, "[%" PRIu64 "] Error %d in HTTP/3 response body for stream", + stream->id, result); + stream->closed = TRUE; + stream->reset = TRUE; + stream->send_closed = TRUE; + if(!stream->xfer_result) + stream->xfer_result = result; + } } - return CURLE_OK; + cf_quiche_flush_body(cf, data, stream); } -#ifdef DEBUGBUILD -static const char *cf_ev_name(quiche_h3_event *ev) -{ - switch(quiche_h3_event_type(ev)) { - case QUICHE_H3_EVENT_HEADERS: - return "HEADERS"; - case QUICHE_H3_EVENT_DATA: - return "DATA"; - case QUICHE_H3_EVENT_RESET: - return "RESET"; - case QUICHE_H3_EVENT_FINISHED: - return "FINISHED"; - case QUICHE_H3_EVENT_GOAWAY: - return "GOAWAY"; - default: - return "Unknown"; - } -} -#else -#define cf_ev_name(x) "" -#endif - -static CURLcode h3_process_event(struct Curl_cfilter *cf, +static void cf_quiche_process_ev(struct Curl_cfilter *cf, struct Curl_easy *data, struct h3_stream_ctx *stream, quiche_h3_event *ev) { - struct cb_ctx cb_ctx; - CURLcode result = CURLE_OK; - int rc; - if(!stream) - return CURLE_OK; + return; + switch(quiche_h3_event_type(ev)) { - case QUICHE_H3_EVENT_HEADERS: + case QUICHE_H3_EVENT_HEADERS: { + struct cb_ctx cb_ctx; stream->resp_got_header = TRUE; cb_ctx.cf = cf; cb_ctx.data = data; - rc = quiche_h3_event_for_each_header(ev, cb_each_header, &cb_ctx); - if(rc) { - failf(data, "Error %d in HTTP/3 response header for stream[%" - FMT_PRIu64"]", rc, stream->id); - return CURLE_RECV_ERROR; - } - CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] <- [HEADERS]", stream->id); + cb_ctx.stream = stream; + quiche_h3_event_for_each_header(ev, cb_each_header, &cb_ctx); + CURL_TRC_CF(data, cf, "[%" PRIu64 "] <- [HEADERS]", stream->id); + Curl_multi_mark_dirty(data); break; - + } case QUICHE_H3_EVENT_DATA: - if(!stream->closed) { - result = cf_recv_body(cf, data); + if(!stream->resp_hds_complete) { + stream->resp_hds_complete = TRUE; + cf_quiche_write_hd(cf, data, stream, "\r\n", 2, FALSE); } + cf_quiche_recv_body(cf, data, stream); + CURL_TRC_CF(data, cf, "[%" PRIu64 "] <- [DATA]", stream->id); + Curl_multi_mark_dirty(data); break; case QUICHE_H3_EVENT_RESET: - CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] RESET", stream->id); + CURL_TRC_CF(data, cf, "[%" PRIu64 "] RESET", stream->id); stream->closed = TRUE; stream->reset = TRUE; stream->send_closed = TRUE; - streamclose(cf->conn, "Reset of stream"); + Curl_multi_mark_dirty(data); break; case QUICHE_H3_EVENT_FINISHED: - CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] CLOSED", stream->id); + CURL_TRC_CF(data, cf, "[%" PRIu64 "] CLOSED", stream->id); if(!stream->resp_hds_complete) { - result = write_resp_raw(cf, data, "\r\n", 2); - if(result) - return result; stream->resp_hds_complete = TRUE; + cf_quiche_write_hd(cf, data, stream, "\r\n", 2, TRUE); } stream->closed = TRUE; - streamclose(cf->conn, "End of stream"); + Curl_multi_mark_dirty(data); break; case QUICHE_H3_EVENT_GOAWAY: - CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] <- [GOAWAY]", stream->id); + CURL_TRC_CF(data, cf, "[%" PRIu64 "] <- [GOAWAY]", stream->id); break; default: - CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] recv, unhandled event %d", + CURL_TRC_CF(data, cf, "[%" PRIu64 "] recv, unhandled event %d", stream->id, quiche_h3_event_type(ev)); break; } - return result; -} - -static CURLcode cf_quiche_ev_process(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream, - quiche_h3_event *ev) -{ - CURLcode result = h3_process_event(cf, data, stream, ev); - Curl_multi_mark_dirty(data); - if(result) - CURL_TRC_CF(data, cf, "error processing event %s " - "for [%"FMT_PRIu64"] -> %d", cf_ev_name(ev), - stream->id, result); - return result; } struct cf_quich_disp_ctx { - curl_uint64_t stream_id; + uint64_t stream_id; struct Curl_cfilter *cf; struct Curl_multi *multi; quiche_h3_event *ev; - CURLcode result; }; -static bool cf_quiche_disp_event(unsigned int mid, void *val, void *user_data) +static bool cf_quiche_disp_event(uint32_t mid, void *val, void *user_data) { struct cf_quich_disp_ctx *dctx = user_data; struct h3_stream_ctx *stream = val; @@ -566,7 +583,7 @@ static bool cf_quiche_disp_event(unsigned int mid, void *val, void *user_data) if(stream->id == dctx->stream_id) { struct Curl_easy *sdata = Curl_multi_get_easy(dctx->multi, mid); if(sdata) - dctx->result = cf_quiche_ev_process(dctx->cf, sdata, stream, dctx->ev); + cf_quiche_process_ev(dctx->cf, sdata, stream, dctx->ev); return FALSE; /* stop iterating */ } return TRUE; @@ -581,33 +598,32 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf, /* Take in the events and distribute them to the transfers. */ while(ctx->h3c) { - curl_int64_t stream3_id = quiche_h3_conn_poll(ctx->h3c, ctx->qconn, &ev); - if(stream3_id == QUICHE_H3_ERR_DONE) { + int64_t rv = quiche_h3_conn_poll(ctx->h3c, ctx->qconn, &ev); + if(rv == QUICHE_H3_ERR_DONE) { break; } - else if(stream3_id < 0) { - CURL_TRC_CF(data, cf, "error poll: %"FMT_PRId64, stream3_id); + else if(rv < 0) { + CURL_TRC_CF(data, cf, "error poll: %" PRId64, rv); return CURLE_HTTP3; } else { - struct cf_quich_disp_ctx dctx; - dctx.stream_id = (curl_uint64_t)stream3_id; - dctx.cf = cf; - dctx.multi = data->multi; - dctx.ev = ev; - dctx.result = CURLE_OK; stream = H3_STREAM_CTX(ctx, data); - if(stream && stream->id == dctx.stream_id) { + if(stream && stream->id == (uint64_t)rv) { /* event for calling transfer */ - CURLcode result = cf_quiche_ev_process(cf, data, stream, ev); + cf_quiche_process_ev(cf, data, stream, ev); quiche_h3_event_free(ev); - if(result) - return result; + if(stream->xfer_result) + return stream->xfer_result; } else { /* another transfer, do not return errors, as they are not for * the calling transfer */ - Curl_uint_hash_visit(&ctx->streams, cf_quiche_disp_event, &dctx); + struct cf_quich_disp_ctx dctx; + dctx.stream_id = (uint64_t)rv; + dctx.cf = cf; + dctx.multi = data->multi; + dctx.ev = ev; + Curl_uint32_hash_visit(&ctx->streams, cf_quiche_disp_event, &dctx); quiche_h3_event_free(ev); } } @@ -621,56 +637,63 @@ struct recv_ctx { int pkts; }; -static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, - struct sockaddr_storage *remote_addr, - socklen_t remote_addrlen, int ecn, - void *userp) +static CURLcode cf_quiche_recv_pkts(const unsigned char *buf, size_t buflen, + size_t gso_size, + struct sockaddr_storage *remote_addr, + socklen_t remote_addrlen, int ecn, + void *userp) { struct recv_ctx *r = userp; struct cf_quiche_ctx *ctx = r->cf->ctx; quiche_recv_info recv_info; - ssize_t nread; + size_t pktlen, offset, nread; + ssize_t rv; (void)ecn; - ++r->pkts; recv_info.to = (struct sockaddr *)&ctx->q.local_addr; recv_info.to_len = ctx->q.local_addrlen; recv_info.from = (struct sockaddr *)remote_addr; recv_info.from_len = remote_addrlen; - nread = quiche_conn_recv(ctx->qconn, - (unsigned char *)CURL_UNCONST(pkt), pktlen, - &recv_info); - if(nread < 0) { - if(QUICHE_ERR_DONE == nread) { - if(quiche_conn_is_draining(ctx->qconn)) { - CURL_TRC_CF(r->data, r->cf, "ingress, connection is draining"); + for(offset = 0; offset < buflen; offset += gso_size) { + pktlen = ((offset + gso_size) <= buflen) ? gso_size : (buflen - offset); + rv = quiche_conn_recv(ctx->qconn, + (unsigned char *)CURL_UNCONST(buf + offset), + pktlen, &recv_info); + if(!curlx_sztouz(rv, &nread)) { + if(QUICHE_ERR_DONE == rv) { + if(quiche_conn_is_draining(ctx->qconn)) { + CURL_TRC_CF(r->data, r->cf, "ingress, connection is draining"); + return CURLE_RECV_ERROR; + } + if(quiche_conn_is_closed(ctx->qconn)) { + CURL_TRC_CF(r->data, r->cf, "ingress, connection is closed"); + return CURLE_RECV_ERROR; + } + CURL_TRC_CF(r->data, r->cf, "ingress, quiche is DONE"); + return CURLE_OK; + } + else if(QUICHE_ERR_TLS_FAIL == rv) { + long verify_ok = SSL_get_verify_result(ctx->tls.ossl.ssl); + if(verify_ok != X509_V_OK) { + failf(r->data, "SSL certificate problem: %s", + X509_verify_cert_error_string(verify_ok)); + return CURLE_PEER_FAILED_VERIFICATION; + } + failf(r->data, "ingress, quiche reports TLS fail"); return CURLE_RECV_ERROR; } - if(quiche_conn_is_closed(ctx->qconn)) { - CURL_TRC_CF(r->data, r->cf, "ingress, connection is closed"); + else { + failf(r->data, "quiche reports error %zd on receive", rv); return CURLE_RECV_ERROR; } - CURL_TRC_CF(r->data, r->cf, "ingress, quiche is DONE"); - return CURLE_OK; } - else if(QUICHE_ERR_TLS_FAIL == nread) { - long verify_ok = SSL_get_verify_result(ctx->tls.ossl.ssl); - if(verify_ok != X509_V_OK) { - failf(r->data, "SSL certificate problem: %s", - X509_verify_cert_error_string(verify_ok)); - return CURLE_PEER_FAILED_VERIFICATION; - } + else if(nread < pktlen) { + CURL_TRC_CF(r->data, r->cf, "ingress, quiche only read %zu/%zu bytes", + nread, pktlen); } - else { - failf(r->data, "quiche_conn_recv() == %zd", nread); - return CURLE_RECV_ERROR; - } - } - else if((size_t)nread < pktlen) { - CURL_TRC_CF(r->data, r->cf, "ingress, quiche only read %zd/%zu bytes", - nread, pktlen); + ++r->pkts; } return CURLE_OK; @@ -692,7 +715,8 @@ static CURLcode cf_process_ingress(struct Curl_cfilter *cf, rctx.data = data; rctx.pkts = 0; - result = vquic_recv_packets(cf, data, &ctx->q, 1000, recv_pkt, &rctx); + result = vquic_recv_packets(cf, data, &ctx->q, 1000, + cf_quiche_recv_pkts, &rctx); if(result) return result; @@ -717,18 +741,17 @@ static CURLcode read_pkt_to_send(void *userp, { struct read_ctx *x = userp; struct cf_quiche_ctx *ctx = x->cf->ctx; - ssize_t n; + ssize_t rv; *pnread = 0; - n = quiche_conn_send(ctx->qconn, buf, buflen, &x->send_info); - if(n == QUICHE_ERR_DONE) + rv = quiche_conn_send(ctx->qconn, buf, buflen, &x->send_info); + if(rv == QUICHE_ERR_DONE) return CURLE_AGAIN; - if(n < 0) { - failf(x->data, "quiche_conn_send returned %zd", n); + if(!curlx_sztouz(rv, pnread)) { + failf(x->data, "quiche_conn_send returned %zd", rv); return CURLE_SEND_ERROR; } - *pnread = (size_t)n; return CURLE_OK; } @@ -742,8 +765,8 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, struct cf_quiche_ctx *ctx = cf->ctx; size_t nread; CURLcode result; - curl_int64_t expiry_ns; - curl_int64_t timeout_ns; + int64_t expiry_ns; + int64_t timeout_ns; struct read_ctx readx; size_t pkt_count, gsolen; @@ -756,7 +779,7 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, else failf(data, "connection closed by server"); /* Connection timed out, expire all transfers belonging to it - * as will not get any more POLL events here. */ + * as it does not get any more POLL events here. */ cf_quiche_expire_conn_closed(cf, data); return CURLE_SEND_ERROR; } @@ -813,7 +836,7 @@ out: timeout_ns = quiche_conn_timeout_as_nanos(ctx->qconn); if(timeout_ns % 1000000) timeout_ns += 1000000; - /* expire resolution is milliseconds */ + /* expire resolution is milliseconds */ Curl_expire(data, (timeout_ns / 1000000), EXPIRE_QUIC); return result; } @@ -829,15 +852,29 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, DEBUGASSERT(stream); *pnread = 0; if(stream->reset) { - failf(data, - "HTTP/3 stream %" FMT_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, "[%" FMT_PRIu64 "] cf_recv, was reset -> %d", + CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv, was reset -> %d", stream->id, result); } else if(!stream->resp_got_header) { - failf(data, - "HTTP/3 stream %" FMT_PRIu64 " was closed cleanly, but before " + failf(data, "HTTP/3 stream %" PRIu64 " was closed cleanly, but before " "getting all response header fields, treated as error", stream->id); result = CURLE_HTTP3; @@ -846,132 +883,107 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, } static CURLcode cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, size_t *pnread) + char *buf, size_t blen, size_t *pnread) { struct cf_quiche_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result = CURLE_OK; *pnread = 0; - vquic_ctx_update_time(&ctx->q); + (void)buf; + (void)blen; + vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); if(!stream) return CURLE_RECV_ERROR; - - if(!Curl_bufq_is_empty(&stream->recvbuf)) { - result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread); - CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] read recvbuf(len=%zu) " - "-> %d, %zu", stream->id, len, result, *pnread); - if(result) - goto out; - } - - if(cf_process_ingress(cf, data)) { + result = cf_process_ingress(cf, data); + if(result) { CURL_TRC_CF(data, cf, "cf_recv, error on ingress"); - result = CURLE_RECV_ERROR; 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); - CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] read recvbuf(len=%zu) " - "-> %d, %zu", stream->id, len, result, *pnread); - if(result) - goto out; + if(stream->xfer_result) { + cf_quiche_stream_close(cf, data, stream); + result = stream->xfer_result; + goto out; } - - if(*pnread) { - if(stream->closed) - Curl_multi_mark_dirty(data); - } - else { - if(stream->closed) - result = recv_closed_stream(cf, data, pnread); - else if(quiche_conn_is_draining(ctx->qconn)) { - failf(data, "QUIC connection is draining"); - result = CURLE_HTTP3; - } - else - result = CURLE_AGAIN; + else if(stream->closed) + result = recv_closed_stream(cf, data, pnread); + else if(quiche_conn_is_draining(ctx->qconn)) { + failf(data, "QUIC connection is draining"); + result = CURLE_HTTP3; } + else + result = CURLE_AGAIN; out: - result = Curl_1st_err(result, cf_flush_egress(cf, data)); + result = Curl_1st_fatal(result, cf_flush_egress(cf, data)); if(*pnread > 0) ctx->data_recvd += *pnread; - CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] cf_recv(total=%" - FMT_OFF_T ") -> %d, %zu", - stream->id, ctx->data_recvd, result, *pnread); + CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv(len=%zu) -> %d, %zu, total=%" + FMT_OFF_T, stream->id, blen, result, *pnread, ctx->data_recvd); return result; } static CURLcode cf_quiche_send_body(struct Curl_cfilter *cf, struct Curl_easy *data, struct h3_stream_ctx *stream, - const void *buf, size_t len, bool eos, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) { struct cf_quiche_ctx *ctx = cf->ctx; - ssize_t nwritten; + ssize_t rv; *pnwritten = 0; - nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id, - (uint8_t *)CURL_UNCONST(buf), len, eos); - if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) { - /* Blocked on flow control and should HOLD sending. But when do we open - * again? */ + rv = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id, + (uint8_t *)CURL_UNCONST(buf), len, eos); + if(rv == QUICHE_H3_ERR_DONE || (rv == 0 && len > 0)) { + /* Blocked on flow control and should HOLD sending. + When do we open again? */ if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { - CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" PRIu64 "] send_body(len=%zu) " "-> window exhausted", stream->id, len); stream->quic_flow_blocked = TRUE; } return CURLE_AGAIN; } - else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) { - CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " + else if(rv == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) { + CURL_TRC_CF(data, cf, "[%" PRIu64 "] send_body(len=%zu) " "-> invalid stream state", stream->id, len); return CURLE_HTTP3; } - else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { - CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " - "-> exceeds size", stream->id, len); + else if(rv == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { + CURL_TRC_CF(data, cf, "[%" PRIu64 "] send_body(len=%zu) -> exceeds size", + stream->id, len); return CURLE_SEND_ERROR; } - else if(nwritten < 0) { - CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " - "-> quiche err %zd", stream->id, len, nwritten); + else if(!curlx_sztouz(rv, pnwritten)) { + CURL_TRC_CF(data, cf, "[%" PRIu64 "] send_body(len=%zu) -> quiche err %zd", + stream->id, len, rv); return CURLE_SEND_ERROR; } else { - if(eos && (len == (size_t)nwritten)) + if(eos && (len == *pnwritten)) stream->send_closed = TRUE; - CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send body(len=%zu, " - "eos=%d) -> %zd", - stream->id, len, stream->send_closed, nwritten); - *pnwritten = (size_t)nwritten; + CURL_TRC_CF(data, cf, "[%" PRIu64 "] send body(len=%zu, eos=%d) -> %zu", + stream->id, len, stream->send_closed, *pnwritten); return CURLE_OK; } } -/* Index where :authority header field will appear in request header - field list. */ -#define AUTHORITY_DST_IDX 3 - static CURLcode h3_open_stream(struct Curl_cfilter *cf, struct Curl_easy *data, - const char *buf, size_t blen, bool eos, + const uint8_t *buf, size_t blen, bool eos, size_t *pnwritten) { struct cf_quiche_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); size_t nheader, i; - curl_int64_t stream3_id; + int64_t rv; struct dynhds h2_headers; quiche_h3_header *nva = NULL; CURLcode result = CURLE_OK; - ssize_t nwritten; *pnwritten = 0; if(!stream) { @@ -985,8 +997,12 @@ static CURLcode h3_open_stream(struct Curl_cfilter *cf, Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); DEBUGASSERT(stream); - nwritten = Curl_h1_req_parse_read(&stream->h1, buf, blen, NULL, 0, &result); - if(nwritten < 0) + + result = Curl_h1_req_parse_read(&stream->h1, buf, blen, NULL, + !data->state.http_ignorecustom ? + data->set.str[STRING_CUSTOMREQUEST] : NULL, + 0, pnwritten); + if(result) goto out; if(!stream->h1.done) { /* need more data */ @@ -1002,7 +1018,7 @@ static CURLcode h3_open_stream(struct Curl_cfilter *cf, Curl_h1_req_parse_free(&stream->h1); nheader = Curl_dynhds_count(&h2_headers); - nva = malloc(sizeof(quiche_h3_header) * nheader); + nva = curlx_malloc(sizeof(quiche_h3_header) * nheader); if(!nva) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1016,70 +1032,69 @@ static CURLcode h3_open_stream(struct Curl_cfilter *cf, nva[i].value_len = e->valuelen; } - *pnwritten = (size_t)nwritten; buf += *pnwritten; blen -= *pnwritten; if(eos && !blen) stream->send_closed = TRUE; - stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader, - stream->send_closed); - CURL_TRC_CF(data, cf, "quiche_send_request() -> %" FMT_PRIu64, stream3_id); - if(stream3_id < 0) { - if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) { + rv = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader, + stream->send_closed); + CURL_TRC_CF(data, cf, "quiche_send_request() -> %" PRId64, rv); + if(rv < 0) { + if(QUICHE_H3_ERR_STREAM_BLOCKED == rv) { /* quiche seems to report this error if the connection window is * exhausted. Which happens frequently and intermittent. */ - CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] blocked", stream->id); + CURL_TRC_CF(data, cf, "[%" PRIu64 "] blocked", stream->id); stream->quic_flow_blocked = TRUE; result = CURLE_AGAIN; goto out; } else { - CURL_TRC_CF(data, cf, "send_request(%s) -> %" FMT_PRIu64, - data->state.url, stream3_id); + CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRIu64, + Curl_bufref_ptr(&data->state.url), rv); } result = CURLE_SEND_ERROR; goto out; } DEBUGASSERT(!stream->opened); - stream->id = stream3_id; + stream->id = (uint64_t)rv; stream->opened = TRUE; stream->closed = FALSE; stream->reset = FALSE; if(Curl_trc_is_verbose(data)) { - infof(data, "[HTTP/3] [%" FMT_PRIu64 "] OPENED stream for %s", - stream->id, data->state.url); + infof(data, "[HTTP/3] [%" PRIu64 "] OPENED stream for %s", + stream->id, Curl_bufref_ptr(&data->state.url)); for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" FMT_PRIu64 "] [%.*s: %.*s]", stream->id, + infof(data, "[HTTP/3] [%" PRIu64 "] [%.*s: %.*s]", stream->id, (int)nva[i].name_len, nva[i].name, (int)nva[i].value_len, nva[i].value); } } if(blen) { /* after the headers, there was request BODY data */ - size_t bwritten; + size_t nwritten; CURLcode r2 = CURLE_OK; - r2 = cf_quiche_send_body(cf, data, stream, buf, blen, eos, &bwritten); - if(r2 && (CURLE_AGAIN != r2)) { /* real error, fail */ + r2 = cf_quiche_send_body(cf, data, stream, buf, blen, eos, &nwritten); + if(r2 && (r2 != CURLE_AGAIN)) { /* real error, fail */ result = r2; } - else if(bwritten > 0) { - *pnwritten += (size_t)bwritten; + else if(nwritten > 0) { + *pnwritten += nwritten; } } out: - free(nva); + curlx_free(nva); Curl_dynhds_free(&h2_headers); return result; } static CURLcode cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, + const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) { struct cf_quiche_ctx *ctx = cf->ctx; @@ -1087,7 +1102,7 @@ static CURLcode cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode result; *pnwritten = 0; - vquic_ctx_update_time(&ctx->q); + vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); result = cf_process_ingress(cf, data); if(result) @@ -1099,23 +1114,27 @@ static CURLcode cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; stream = H3_STREAM_CTX(ctx, data); } + else if(stream->xfer_result) { + cf_quiche_stream_close(cf, data, stream); + result = stream->xfer_result; + } else if(stream->closed) { if(stream->resp_hds_complete) { /* sending request body on a stream that has been closed by the * 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- */ - CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] discarding data" + CURL_TRC_CF(data, cf, "[%" PRIu64 "] discarding data" "on closed stream with response", stream->id); result = CURLE_OK; *pnwritten = len; goto out; } - CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " + CURL_TRC_CF(data, cf, "[%" PRIu64 "] send_body(len=%zu) " "-> stream closed", stream->id, len); result = CURLE_HTTP3; goto out; @@ -1125,22 +1144,22 @@ static CURLcode cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, } out: - result = Curl_1st_err(result, cf_flush_egress(cf, data)); + result = Curl_1st_fatal(result, cf_flush_egress(cf, data)); - CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_send(len=%zu) -> %d, %zu", - stream ? stream->id : (curl_uint64_t)~0, len, + CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_send(len=%zu) -> %d, %zu", + stream ? stream->id : (uint64_t)~0, len, result, *pnwritten); return result; } -static bool stream_is_writeable(struct Curl_cfilter *cf, - struct Curl_easy *data) +static bool stream_is_writable(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); return stream && (quiche_conn_stream_writable( - ctx->qconn, (curl_uint64_t)stream->id, 1) > 0); + ctx->qconn, stream->id, 1) > 0); } static CURLcode cf_quiche_adjust_pollset(struct Curl_cfilter *cf, @@ -1162,7 +1181,7 @@ static CURLcode cf_quiche_adjust_pollset(struct Curl_cfilter *cf, c_exhaust = FALSE; /* Have not found any call in quiche that tells us if the connection itself is blocked */ s_exhaust = want_send && stream && stream->opened && - (stream->quic_flow_blocked || !stream_is_writeable(cf, data)); + (stream->quic_flow_blocked || !stream_is_writable(cf, data)); want_recv = (want_recv || c_exhaust || s_exhaust); want_send = (!s_exhaust && want_send) || !Curl_bufq_is_empty(&ctx->q.sendbuf); @@ -1172,19 +1191,6 @@ static CURLcode cf_quiche_adjust_pollset(struct Curl_cfilter *cf, 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_quiche_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - struct cf_quiche_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 h3_data_pause(struct Curl_cfilter *cf, struct Curl_easy *data, bool pause) @@ -1225,23 +1231,16 @@ static CURLcode cf_quiche_cntrl(struct Curl_cfilter *cf, stream->send_closed = TRUE; body[0] = 'X'; result = cf_quiche_send(cf, data, body, 0, TRUE, &sent); - CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] DONE_SEND -> %d, %zu", + CURL_TRC_CF(data, cf, "[%" PRIu64 "] DONE_SEND -> %d, %zu", stream->id, result, sent); } break; } - case CF_CTRL_DATA_IDLE: { - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - if(stream && !stream->closed) { - result = cf_flush_egress(cf, data); - if(result) - CURL_TRC_CF(data, cf, "data idle, flush egress -> %d", result); - } - break; - } case CF_CTRL_CONN_INFO_UPDATE: - if(!cf->sockindex && cf->connected) + if(!cf->sockindex && cf->connected) { cf->conn->httpversion_seen = 30; + Curl_conn_set_multiplex(cf->conn); + } break; default: break; @@ -1256,14 +1255,12 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf, int rv; CURLcode result; const struct Curl_sockaddr_ex *sockaddr; -static const struct alpn_spec ALPN_SPEC_H3 = { - { "h3" }, 1 -}; + static const struct alpn_spec ALPN_SPEC_H3 = { { "h3" }, 1 }; DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD); DEBUGASSERT(ctx->initialized); - result = vquic_ctx_init(&ctx->q); + result = vquic_ctx_init(data, &ctx->q); if(result) return result; @@ -1302,7 +1299,9 @@ static const struct alpn_spec ALPN_SPEC_H3 = { if(result) return result; - Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL); + if(Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL)) + return CURLE_QUIC_CONNECT_ERROR; + ctx->q.local_addrlen = sizeof(ctx->q.local_addr); rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, &ctx->q.local_addrlen); @@ -1327,8 +1326,7 @@ static const struct alpn_spec ALPN_SPEC_H3 = { int qfd; (void)Curl_qlogdir(data, ctx->scid, sizeof(ctx->scid), &qfd); if(qfd != -1) - quiche_conn_set_qlog_fd(ctx->qconn, qfd, - "qlog title", "curl qlog"); + quiche_conn_set_qlog_fd(ctx->qconn, qfd, "qlog title", "curl qlog"); } #endif @@ -1358,9 +1356,6 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; - - cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ - return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); } @@ -1384,7 +1379,13 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, } *done = FALSE; - vquic_ctx_update_time(&ctx->q); + if(Curl_ossl_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); + return CURLE_OK; + } + + vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); if(!ctx->qconn) { result = cf_quiche_ctx_open(cf, data); @@ -1406,8 +1407,8 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, if(quiche_conn_is_established(ctx->qconn)) { ctx->handshake_at = ctx->q.last_op; - CURL_TRC_CF(data, cf, "handshake complete after %dms", - (int)curlx_timediff(ctx->handshake_at, ctx->started_at)); + CURL_TRC_CF(data, cf, "handshake complete after %" FMT_TIMEDIFF_T "ms", + curlx_ptimediff_ms(&ctx->handshake_at, &ctx->started_at)); result = cf_quiche_verify_peer(cf, data); if(!result) { CURL_TRC_CF(data, cf, "peer verified"); @@ -1425,7 +1426,6 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, } cf->connected = TRUE; *done = TRUE; - connkeep(cf->conn, "HTTP/3 default"); } } else if(quiche_conn_is_draining(ctx->qconn)) { @@ -1437,13 +1437,15 @@ 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; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); - infof(data, "connect to %s port %u failed: %s", - ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); + if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) + infof(data, "connect to %s port %u failed: %s", + ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); + else + infof(data, "connect failed: %s", curl_easy_strerror(result)); } #endif return result; @@ -1465,7 +1467,7 @@ static CURLcode cf_quiche_shutdown(struct Curl_cfilter *cf, int err; ctx->shutdown_started = TRUE; - vquic_ctx_update_time(&ctx->q); + vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); err = quiche_conn_close(ctx->qconn, TRUE, 0, NULL, 0); if(err) { CURL_TRC_CF(data, cf, "error %d adding shutdown packet, " @@ -1502,6 +1504,7 @@ static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data) bool done; (void)cf_quiche_shutdown(cf, data, &done); cf_quiche_ctx_close(cf->ctx); + cf->connected = FALSE; } } @@ -1509,6 +1512,7 @@ static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { (void)data; if(cf->ctx) { + cf_quiche_ctx_close(cf->ctx); cf_quiche_ctx_free(cf->ctx); cf->ctx = NULL; } @@ -1522,19 +1526,20 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf, switch(query) { case CF_QUERY_MAX_CONCURRENT: { - curl_uint64_t max_streams = CONN_ATTACHED(cf->conn); + uint64_t max_streams = cf->conn->attached_xfers; if(!ctx->goaway && ctx->qconn) { max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn); } *pres1 = (max_streams > INT_MAX) ? INT_MAX : (int)max_streams; CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: " "MAX_CONCURRENT -> %d (%u in use)", - cf->conn->connection_id, *pres1, CONN_ATTACHED(cf->conn)); + cf->conn->connection_id, *pres1, cf->conn->attached_xfers); return CURLE_OK; } case CF_QUERY_CONNECT_REPLY_MS: if(ctx->q.got_first_byte) { - timediff_t ms = curlx_timediff(ctx->q.first_byte_at, ctx->started_at); + timediff_t ms = curlx_ptimediff_ms(&ctx->q.first_byte_at, + &ctx->started_at); *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX; } else @@ -1623,7 +1628,7 @@ struct Curl_cftype Curl_cft_http3 = { cf_quiche_close, cf_quiche_shutdown, cf_quiche_adjust_pollset, - cf_quiche_data_pending, + Curl_cf_def_data_pending, cf_quiche_send, cf_quiche_recv, cf_quiche_cntrl, @@ -1635,15 +1640,13 @@ struct Curl_cftype Curl_cft_http3 = { CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai) + struct Curl_sockaddr_ex *addr) { struct cf_quiche_ctx *ctx = NULL; - struct Curl_cfilter *cf = NULL, *udp_cf = NULL; + struct Curl_cfilter *cf = NULL; CURLcode result; - (void)data; - (void)conn; - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1653,22 +1656,21 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); if(result) goto out; + cf->conn = conn; - result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); + result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC); if(result) goto out; - - udp_cf->conn = cf->conn; - udp_cf->sockindex = cf->sockindex; - cf->next = udp_cf; + cf->next->conn = cf->conn; + cf->next->sockindex = cf->sockindex; out: *pcf = (!result) ? cf : NULL; if(result) { - if(udp_cf) - Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); - Curl_safefree(cf); - cf_quiche_ctx_free(ctx); + if(cf) + Curl_conn_cf_discard_chain(&cf, data); + else if(ctx) + cf_quiche_ctx_free(ctx); } return result; diff --git a/lib/vquic/curl_quiche.h b/lib/vquic/curl_quiche.h index bee966eeb7..c2c88ddeaf 100644 --- a/lib/vquic/curl_quiche.h +++ b/lib/vquic/curl_quiche.h @@ -23,8 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_QUICHE) @@ -39,7 +38,7 @@ void Curl_quiche_ver(char *p, size_t len); CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai); + struct Curl_sockaddr_ex *addr); #endif diff --git a/lib/vquic/vquic-tls.c b/lib/vquic/vquic-tls.c index 4bdd23c981..d81f2a9e6b 100644 --- a/lib/vquic/vquic-tls.c +++ b/lib/vquic/vquic-tls.c @@ -21,42 +21,33 @@ * 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 "../curl_trc.h" -#include "../cfilters.h" -#include "../multiif.h" -#include "../vtls/keylog.h" -#include "../vtls/vtls.h" -#include "../vtls/vtls_scache.h" -#include "vquic-tls.h" - -/* The last 3 #include files should be in this order */ -#include "../curl_printf.h" -#include "../curl_memory.h" -#include "../memdebug.h" +#include "urldata.h" +#include "cfilters.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, @@ -130,7 +121,7 @@ CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx, { #ifdef USE_OPENSSL if(!ctx->ossl.x509_store_setup) { - CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ossl.ssl_ctx); + CURLcode result = Curl_ssl_setup_x509_store(cf, data, &ctx->ossl); if(result) return result; ctx->ossl.x509_store_setup = TRUE; @@ -148,7 +139,9 @@ CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx, return result; } #else - (void)ctx; (void)cf; (void)data; + (void)ctx; + (void)cf; + (void)data; #endif return CURLE_OK; } @@ -169,22 +162,25 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, (void)conn_config; result = Curl_ossl_check_peer_cert(cf, data, &ctx->ossl, peer); #elif defined(USE_GNUTLS) - if(conn_config->verifyhost) { - result = Curl_gtls_verifyserver(data, ctx->gtls.session, - conn_config, &data->set.ssl, peer, - data->set.str[STRING_SSL_PINNEDPUBLICKEY]); - if(result) - return result; - } + result = Curl_gtls_verifyserver(cf, data, ctx->gtls.session, + conn_config, &data->set.ssl, peer, + data->set.str[STRING_SSL_PINNEDPUBLICKEY]); + if(result) + return result; #elif defined(USE_WOLFSSL) (void)data; if(conn_config->verifyhost) { - char *snihost = peer->sni ? peer->sni : peer->hostname; - WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->wssl.ssl); - if(wolfSSL_X509_check_host(cert, snihost, strlen(snihost), 0, NULL) - == WOLFSSL_FAILURE) { + WOLFSSL_X509 *cert = wolfSSL_get_peer_certificate(ctx->wssl.ssl); + if(!cert) + result = CURLE_OUT_OF_MEMORY; + else if(peer->sni && + (wolfSSL_X509_check_host(cert, peer->sni, strlen(peer->sni), 0, + NULL) == WOLFSSL_FAILURE)) + result = CURLE_PEER_FAILED_VERIFICATION; + else if(!peer->sni && + (wolfSSL_X509_check_ip_asc(cert, peer->hostname, + 0) == WOLFSSL_FAILURE)) result = CURLE_PEER_FAILED_VERIFICATION; - } wolfSSL_X509_free(cert); } if(!result) @@ -196,7 +192,6 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, return result; } - bool Curl_vquic_tls_get_ssl_info(struct curl_tls_ctx *ctx, bool give_ssl_ctx, struct curl_tlssessioninfo *info) diff --git a/lib/vquic/vquic-tls.h b/lib/vquic/vquic-tls.h index c694e23e4e..c461c1548b 100644 --- a/lib/vquic/vquic-tls.h +++ b/lib/vquic/vquic-tls.h @@ -23,17 +23,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" -#include "../bufq.h" -#include "../vtls/vtls.h" -#include "../vtls/vtls_int.h" -#include "../vtls/openssl.h" +#include "curl_setup.h" #if defined(USE_HTTP3) && \ (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL)) -#include "../vtls/wolfssl.h" +#include "bufq.h" +#include "vtls/vtls.h" +#include "vtls/vtls_int.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, @@ -69,14 +69,14 @@ typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf, /** * Initialize the QUIC TLS instances based of the SSL configurations * for the connection filter, transfer and peer. - * @param ctx the TLS context to initialize - * @param cf the connection filter involved - * @param data the transfer involved - * @param peer the peer that will be connected to - * @param alpns the ALPN specifications to negotiate, may be NULL - * @param cb_setup optional callback for early TLS config - * @param cb_user_data user_data param for callback - * @param ssl_user_data optional pointer to set in TLS application context + * @param ctx the TLS context to initialize + * @param cf the connection filter involved + * @param data the transfer involved + * @param peer the peer to be connected to + * @param alpns the ALPN specifications to negotiate, may be NULL + * @param cb_setup optional callback for early TLS config + * @param cb_user_data user_data param for callback + * @param ssl_user_data optional pointer to set in TLS application context * @param session_reuse_cb callback to handle session reuse, signal early data */ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 647450a29b..0040ad671f 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -21,44 +21,38 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" +#include "urldata.h" +#include "vquic/vquic.h" -#include "../curl_setup.h" +#include "curl_trc.h" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) #ifdef HAVE_NETINET_UDP_H #include #endif -#ifdef HAVE_FCNTL_H -#include -#endif + #ifdef USE_NGHTTP3 #include #endif -#include "../urldata.h" -#include "../bufq.h" -#include "../curlx/dynbuf.h" -#include "../cfilters.h" -#include "../curl_trc.h" -#include "curl_ngtcp2.h" -#include "curl_osslq.h" -#include "curl_quiche.h" -#include "../multiif.h" -#include "../rand.h" -#include "vquic.h" -#include "vquic_int.h" -#include "../strerror.h" -#include "../curlx/strparse.h" -/* The last 3 #include files should be in this order */ -#include "../curl_printf.h" -#include "../curl_memory.h" -#include "../memdebug.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 2 - +#define NW_SEND_CHUNKS 1 int Curl_vquic_init(void) { @@ -74,14 +68,13 @@ 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 } -CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx) +CURLcode vquic_ctx_init(struct Curl_easy *data, + struct cf_quic_ctx *qctx) { Curl_bufq_init2(&qctx->sendbuf, NW_CHUNK_SIZE, NW_SEND_CHUNKS, BUFQ_OPT_SOFT_LIMIT); @@ -100,7 +93,7 @@ CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx) } } #endif - vquic_ctx_update_time(qctx); + vquic_ctx_set_time(qctx, Curl_pgrs_now(data)); return CURLE_OK; } @@ -110,9 +103,16 @@ void vquic_ctx_free(struct cf_quic_ctx *qctx) Curl_bufq_free(&qctx->sendbuf); } -void vquic_ctx_update_time(struct cf_quic_ctx *qctx) +void vquic_ctx_set_time(struct cf_quic_ctx *qctx, + const struct curltime *pnow) { - qctx->last_op = curlx_now(); + qctx->last_op = *pnow; +} + +void vquic_ctx_update_time(struct cf_quic_ctx *qctx, + const struct curltime *pnow) +{ + qctx->last_op = *pnow; } static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, @@ -127,10 +127,11 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, const uint8_t *pkt, size_t pktlen, size_t gsolen, size_t *psent) { + CURLcode result = CURLE_OK; #ifdef HAVE_SENDMSG struct iovec msg_iov; - struct msghdr msg = {0}; - ssize_t sent; + struct msghdr msg = { 0 }; + ssize_t rv; #if defined(__linux__) && defined(UDP_SEGMENT) uint8_t msg_ctrl[32]; struct cmsghdr *cm; @@ -146,6 +147,7 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, if(pktlen > gsolen) { /* Only set this, when we need it. macOS, for example, * does not seem to like a msg_control of length 0. */ + memset(msg_ctrl, 0, sizeof(msg_ctrl)); msg.msg_control = msg_ctrl; assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(int))); msg.msg_controllen = CMSG_SPACE(sizeof(int)); @@ -157,12 +159,10 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, } #endif - - while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && - SOCKERRNO == SOCKEINTR) + while((rv = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == SOCKEINTR) ; - if(sent == -1) { + if(!curlx_sztouz(rv, psent)) { switch(SOCKERRNO) { case EAGAIN: #if EAGAIN != SOCKEWOULDBLOCK @@ -170,57 +170,69 @@ 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) { /* GSO failure */ - infof(data, "sendmsg() returned %zd (errno %d); disable GSO", sent, + infof(data, "sendmsg() returned %zd (errno %d); disable GSO", rv, SOCKERRNO); qctx->no_gso = TRUE; return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); } FALLTHROUGH(); default: - failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO); - return CURLE_SEND_ERROR; + failf(data, "sendmsg() returned %zd (errno %d)", rv, SOCKERRNO); + result = CURLE_SEND_ERROR; + goto out; } } - else if(pktlen != (size_t)sent) { - failf(data, "sendmsg() sent only %zd/%zu bytes", sent, pktlen); - return CURLE_SEND_ERROR; + else if(pktlen != *psent) { + failf(data, "sendmsg() sent only %zu/%zu bytes", *psent, pktlen); + result = CURLE_SEND_ERROR; + goto out; } #else - ssize_t sent; + ssize_t rv; (void)gsolen; *psent = 0; - while((sent = send(qctx->sockfd, - (const char *)pkt, (SEND_TYPE_ARG3)pktlen, 0)) == -1 && + while((rv = swrite(qctx->sockfd, pkt, pktlen)) == -1 && SOCKERRNO == SOCKEINTR) ; - if(sent == -1) { + if(!curlx_sztouz(rv, psent)) { if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) { - return CURLE_AGAIN; + result = CURLE_AGAIN; + goto out; } else { - failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO); if(SOCKERRNO != SOCKEMSGSIZE) { - return CURLE_SEND_ERROR; + 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 (void)cf; - *psent = pktlen; - return CURLE_OK; +out: + 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, struct cf_quic_ctx *qctx, @@ -228,20 +240,25 @@ 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; + size_t sent, len; + CURLcode result = CURLE_OK; + VERBOSE(size_t calls = 0); *psent = 0; for(p = pkt; p < end; p += gsolen) { - size_t len = CURLMIN(gsolen, (size_t)(end - p)); - CURLcode curlcode = do_sendmsg(cf, data, qctx, p, len, len, &sent); - if(curlcode != CURLE_OK) { - return curlcode; - } + len = CURLMIN(gsolen, (size_t)(end - p)); + result = do_sendmsg(cf, data, qctx, p, len, len, &sent); + if(result) + goto out; *psent += sent; + VERBOSE(++calls); } - - return CURLE_OK; +out: + CURL_TRC_CF(data, cf, "vquic_%s(len=%zu, gso=%zu, calls=%zu)" + " -> %d, sent=%zu", + VQUIC_SEND_METHOD, pktlen, gsolen, calls, result, *psent); + return result; } static CURLcode vquic_send_packets(struct Curl_cfilter *cf, @@ -257,7 +274,7 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, unsigned char c; *psent = 0; Curl_rand(data, &c, 1); - if(c >= ((100-qctx->wblock_percent)*256/100)) { + if(c >= ((100 - qctx->wblock_percent) * 256 / 100)) { CURL_TRC_CF(data, cf, "vquic_flush() simulate EWOULDBLOCK"); return CURLE_AGAIN; } @@ -268,6 +285,9 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf, } else { result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); + CURL_TRC_CF(data, cf, "vquic_%s(len=%zu, gso=%zu, calls=1)" + " -> %d, sent=%zu", + VQUIC_SEND_METHOD, pktlen, gsolen, result, *psent); } if(!result) qctx->last_io = qctx->last_op; @@ -291,8 +311,6 @@ CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, } result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent); - CURL_TRC_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu", - blen, gsolen, result, sent); if(result) { if(result == CURLE_AGAIN) { Curl_bufq_skip(&qctx->sendbuf, sent); @@ -324,8 +342,7 @@ CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, qctx->split_gsolen = gsolen; qctx->gsolen = tail_gsolen; CURL_TRC_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]", - qctx->split_len, qctx->split_gsolen, - tail_len, qctx->gsolen); + qctx->split_len, qctx->split_gsolen, tail_len, qctx->gsolen); return vquic_flush(cf, data, qctx); } @@ -364,29 +381,37 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, size_t max_pkts, - vquic_recv_pkt_cb *recv_cb, void *userp) + vquic_recv_pkts_cb *recv_cb, void *userp) { +#if defined(__linux__) && defined(UDP_GRO) #define MMSG_NUM 16 +#define UDP_GRO_CNT_MAX 64 +#else +#define MMSG_NUM 64 +#define UDP_GRO_CNT_MAX 1 +#endif +#define MSG_BUF_SIZE (UDP_GRO_CNT_MAX * 1500) struct iovec msg_iov[MMSG_NUM]; 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; +#ifdef CURLVERBOSE + size_t calls = 0; +#endif int mcount, i, n; char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; size_t gso_size; - size_t pktlen; - size_t offset, to; char *sockbuf = NULL; - uint8_t (*bufs)[64*1024] = NULL; + uint8_t (*bufs)[MSG_BUF_SIZE] = NULL; DEBUGASSERT(max_pkts > 0); - result = Curl_multi_xfer_sockbuf_borrow(data, MMSG_NUM * sizeof(bufs[0]), + result = Curl_multi_xfer_sockbuf_borrow(data, MMSG_NUM * MSG_BUF_SIZE, &sockbuf); if(result) goto out; - bufs = (uint8_t (*)[64*1024])sockbuf; + bufs = (uint8_t (*)[MSG_BUF_SIZE])sockbuf; total_nread = 0; while(pkts < max_pkts) { @@ -404,7 +429,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, } while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 && - SOCKERRNO == SOCKEINTR) + (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) ; if(mcount == -1) { if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) { @@ -413,51 +438,43 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, } if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) { struct ip_quadruple ip; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); - failf(data, "QUIC: connection to %s port %u refused", - ip.remote_ip, ip.remote_port); + if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) + failf(data, "QUIC: connection to %s port %u refused", + ip.remote_ip, ip.remote_port); result = CURLE_COULDNT_CONNECT; goto out; } - Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); - failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d; %s)", + curlx_strerror(SOCKERRNO, errstr, sizeof(errstr)); + failf(data, "QUIC: recvmmsg() unexpectedly returned %d (errno=%d; %s)", mcount, SOCKERRNO, errstr); result = CURLE_RECV_ERROR; goto out; } - CURL_TRC_CF(data, cf, "recvmmsg() -> %d packets", mcount); + VERBOSE(++calls); for(i = 0; i < mcount; ++i) { + /* A zero-length UDP packet is no QUIC packet. Ignore. */ + if(!mmsg[i].msg_len) + continue; total_nread += mmsg[i].msg_len; gso_size = vquic_msghdr_get_udp_gro(&mmsg[i].msg_hdr); - if(gso_size == 0) { + if(gso_size == 0) gso_size = mmsg[i].msg_len; - } - for(offset = 0; offset < mmsg[i].msg_len; offset = to) { - ++pkts; - - to = offset + gso_size; - if(to > mmsg[i].msg_len) { - pktlen = mmsg[i].msg_len - offset; - } - else { - pktlen = gso_size; - } - - result = recv_cb(bufs[i] + offset, pktlen, mmsg[i].msg_hdr.msg_name, - mmsg[i].msg_hdr.msg_namelen, 0, userp); - if(result) - goto out; - } + result = recv_cb(bufs[i], mmsg[i].msg_len, gso_size, + mmsg[i].msg_hdr.msg_name, + mmsg[i].msg_hdr.msg_namelen, 0, userp); + if(result) + goto out; + pkts += (mmsg[i].msg_len + gso_size - 1) / gso_size; } } out: if(total_nread || result) - CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", - pkts, total_nread, result); + CURL_TRC_CF(data, cf, "vquic_recvmmsg(len=%zu, packets=%zu, calls=%zu)" + " -> %d", total_nread, pkts, calls, result); Curl_multi_xfer_sockbuf_release(data, sockbuf); return result; } @@ -467,23 +484,22 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, size_t max_pkts, - vquic_recv_pkt_cb *recv_cb, void *userp) + vquic_recv_pkts_cb *recv_cb, void *userp) { struct iovec msg_iov; struct msghdr msg; - uint8_t buf[64*1024]; + uint8_t buf[64 * 1024]; struct sockaddr_storage remote_addr; - size_t total_nread, pkts; - ssize_t nread; + size_t total_nread, pkts, calls; + ssize_t rc; + size_t nread; char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; uint8_t msg_ctrl[CMSG_SPACE(sizeof(int))]; size_t gso_size; - size_t pktlen; - size_t offset, to; DEBUGASSERT(max_pkts > 0); - for(pkts = 0, total_nread = 0; pkts < max_pkts;) { + for(pkts = 0, total_nread = 0, calls = 0; pkts < max_pkts;) { /* fully initialise this on each call to `recvmsg()`. There seem to * operating systems out there that mess with `msg_iov.iov_len`. */ memset(&msg, 0, sizeof(msg)); @@ -496,57 +512,50 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, msg.msg_namelen = sizeof(remote_addr); msg.msg_controllen = sizeof(msg_ctrl); - while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 && - SOCKERRNO == SOCKEINTR) + while((rc = recvmsg(qctx->sockfd, &msg, 0)) == -1 && + (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) ; - if(nread == -1) { + if(!curlx_sztouz(rc, &nread)) { if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) { goto out; } if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) { struct ip_quadruple ip; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); - failf(data, "QUIC: connection to %s port %u refused", - ip.remote_ip, ip.remote_port); + if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) + failf(data, "QUIC: connection to %s port %u refused", + ip.remote_ip, ip.remote_port); result = CURLE_COULDNT_CONNECT; goto out; } - Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); + curlx_strerror(SOCKERRNO, errstr, sizeof(errstr)); failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d; %s)", - nread, SOCKERRNO, errstr); + rc, SOCKERRNO, errstr); result = CURLE_RECV_ERROR; goto out; } - total_nread += (size_t)nread; + total_nread += nread; + ++calls; + + /* A 0-length UDP packet is no QUIC packet */ + if(!nread) + continue; gso_size = vquic_msghdr_get_udp_gro(&msg); - if(gso_size == 0) { - gso_size = (size_t)nread; - } + if(gso_size == 0) + gso_size = nread; - for(offset = 0; offset < (size_t)nread; offset = to) { - ++pkts; - - to = offset + gso_size; - if(to > (size_t)nread) { - pktlen = (size_t)nread - offset; - } - else { - pktlen = gso_size; - } - - result = - recv_cb(buf + offset, pktlen, msg.msg_name, msg.msg_namelen, 0, userp); - if(result) - goto out; - } + result = recv_cb(buf, nread, gso_size, + msg.msg_name, msg.msg_namelen, 0, userp); + if(result) + goto out; + pkts += (nread + gso_size - 1) / gso_size; } out: if(total_nread || result) - CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", - pkts, total_nread, result); + CURL_TRC_CF(data, cf, "vquic_recvmsg(len=%zu, packets=%zu, calls=%zu)" + " -> %d", total_nread, pkts, calls, result); return result; } @@ -555,47 +564,53 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, size_t max_pkts, - vquic_recv_pkt_cb *recv_cb, void *userp) + vquic_recv_pkts_cb *recv_cb, void *userp) { - uint8_t buf[64*1024]; + uint8_t buf[64 * 1024]; int bufsize = (int)sizeof(buf); struct sockaddr_storage remote_addr; socklen_t remote_addrlen = sizeof(remote_addr); - size_t total_nread, pkts; - ssize_t nread; + size_t total_nread, pkts, calls = 0, nread; + ssize_t rv; char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; DEBUGASSERT(max_pkts > 0); for(pkts = 0, total_nread = 0; pkts < max_pkts;) { - while((nread = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0, - (struct sockaddr *)&remote_addr, - &remote_addrlen)) == -1 && - SOCKERRNO == SOCKEINTR) + while((rv = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0, + (struct sockaddr *)&remote_addr, + &remote_addrlen)) == -1 && + (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) ; - if(nread == -1) { + if(!curlx_sztouz(rv, &nread)) { if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) { CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN"); goto out; } if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) { struct ip_quadruple ip; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); - failf(data, "QUIC: connection to %s port %u refused", - ip.remote_ip, ip.remote_port); + if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip)) + failf(data, "QUIC: connection to %s port %u refused", + ip.remote_ip, ip.remote_port); result = CURLE_COULDNT_CONNECT; goto out; } - Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); + curlx_strerror(SOCKERRNO, errstr, sizeof(errstr)); failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d; %s)", - nread, SOCKERRNO, errstr); + rv, SOCKERRNO, errstr); result = CURLE_RECV_ERROR; goto out; } ++pkts; - total_nread += (size_t)nread; - result = recv_cb(buf, (size_t)nread, &remote_addr, remote_addrlen, + ++calls; + + /* A 0-length UDP packet is no QUIC packet */ + if(!nread) + continue; + + total_nread += nread; + result = recv_cb(buf, nread, nread, &remote_addr, remote_addrlen, 0, userp); if(result) goto out; @@ -603,8 +618,8 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, out: if(total_nread || result) - CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", - pkts, total_nread, result); + CURL_TRC_CF(data, cf, "vquic_recvfrom(len=%zu, packets=%zu, calls=%zu)" + " -> %d", total_nread, pkts, calls, result); return result; } #endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ @@ -613,7 +628,7 @@ CURLcode vquic_recv_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, size_t max_pkts, - vquic_recv_pkt_cb *recv_cb, void *userp) + vquic_recv_pkts_cb *recv_cb, void *userp) { CURLcode result; #ifdef HAVE_SENDMMSG @@ -658,20 +673,25 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, result = curlx_dyn_add(&fname, "/"); for(i = 0; (i < scidlen) && !result; i++) { char hex[3]; - msnprintf(hex, 3, "%02x", scid[i]); + curl_msnprintf(hex, 3, "%02x", scid[i]); result = curlx_dyn_add(&fname, hex); } if(!result) result = curlx_dyn_add(&fname, ".sqlog"); if(!result) { - int qlogfd = open(curlx_dyn_ptr(&fname), O_WRONLY|O_CREAT|CURL_O_BINARY, - data->set.new_file_perms); + int qlogfd = curlx_open(curlx_dyn_ptr(&fname), + O_WRONLY | O_CREAT | CURL_O_BINARY, + data->set.new_file_perms +#ifdef _WIN32 + & (_S_IREAD | _S_IWRITE) +#endif + ); if(qlogfd != -1) *qlogfdp = qlogfd; } curlx_dyn_free(&fname); - free(qlog_dir); + curlx_free(qlog_dir); if(result) return result; } @@ -682,22 +702,20 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport) + struct Curl_sockaddr_ex *addr, + uint8_t transport) { (void)transport; 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); + return Curl_cf_ngtcp2_create(pcf, data, conn, addr); #elif defined(USE_QUICHE) - return Curl_cf_quiche_create(pcf, data, conn, ai); + return Curl_cf_quiche_create(pcf, data, conn, addr); #else *pcf = NULL; (void)data; (void)conn; - (void)ai; + (void)addr; return CURLE_NOT_BUILT_IN; #endif } @@ -707,10 +725,10 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, unsigned char transport) { if(transport == TRNSPRT_UNIX) { - /* cannot do QUIC over a Unix domain socket */ + failf(data, "HTTP/3 cannot be used over UNIX domain sockets"); 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; } @@ -728,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) @@ -790,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/lib/vquic/vquic.h b/lib/vquic/vquic.h index 0f81334f29..1f0a1ab5e5 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -23,8 +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; @@ -40,12 +39,11 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, size_t scidlen, int *qlogfdp); - CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, - int transport); + struct Curl_sockaddr_ex *addr, + uint8_t transport); extern struct Curl_cftype Curl_cft_http3; diff --git a/lib/vquic/vquic_int.h b/lib/vquic/vquic_int.h index 38189beb70..82bd5b0358 100644 --- a/lib/vquic/vquic_int.h +++ b/lib/vquic/vquic_int.h @@ -23,41 +23,72 @@ * 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 -struct cf_quic_ctx { - curl_socket_t sockfd; /* connected UDP socket */ - struct sockaddr_storage local_addr; /* address socket is bound to */ - socklen_t local_addrlen; /* length of local address */ +/* 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; - struct bufq sendbuf; /* buffer for sending one or more packets */ - struct curltime first_byte_at; /* when first byte was recvd */ - struct curltime last_op; /* last (attempted) send/recv operation */ - struct curltime last_io; /* last successful socket IO */ - size_t gsolen; /* length of individual packets in send buf */ - size_t split_len; /* if != 0, buffer length after which GSO differs */ +#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 */ + socklen_t local_addrlen; /* length of local address */ + + struct bufq sendbuf; /* buffer for sending one or more packets */ + struct curltime first_byte_at; /* when first byte was recvd */ + struct curltime last_op; /* last (attempted) send/recv operation */ + struct curltime last_io; /* last successful socket IO */ + size_t gsolen; /* length of individual packets in send buf */ + size_t split_len; /* if != 0, buffer length after which GSO differs */ size_t split_gsolen; /* length of individual packets after split_len */ #ifdef DEBUGBUILD - int wblock_percent; /* percent of writes doing EAGAIN */ + int wblock_percent; /* percent of writes doing EAGAIN */ #endif BIT(got_first_byte); /* if first byte was received */ - BIT(no_gso); /* do not use gso on sending */ + BIT(no_gso); /* do not use gso on sending */ }; -#define H3_STREAM_CTX(ctx,data) \ - (data ? Curl_uint_hash_get(&(ctx)->streams, (data)->mid) : NULL) +#define H3_STREAM_CTX(ctx, data) \ + ((data) ? Curl_uint32_hash_get(&(ctx)->streams, (data)->mid) : NULL) -CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx); +CURLcode vquic_ctx_init(struct Curl_easy *data, + struct cf_quic_ctx *qctx); void vquic_ctx_free(struct cf_quic_ctx *qctx); -void vquic_ctx_update_time(struct cf_quic_ctx *qctx); +void vquic_ctx_set_time(struct cf_quic_ctx *qctx, + const struct curltime *pnow); + +void vquic_ctx_update_time(struct cf_quic_ctx *qctx, + const struct curltime *pnow); void vquic_push_blocked_pkt(struct Curl_cfilter *cf, struct cf_quic_ctx *qctx, @@ -77,19 +108,17 @@ CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx); - -typedef CURLcode vquic_recv_pkt_cb(const unsigned char *pkt, size_t pktlen, - struct sockaddr_storage *remote_addr, - socklen_t remote_addrlen, int ecn, - void *userp); +typedef CURLcode vquic_recv_pkts_cb(const unsigned char *buf, size_t buflen, + size_t gso_size, + struct sockaddr_storage *remote_addr, + socklen_t remote_addrlen, int ecn, + void *userp); CURLcode vquic_recv_packets(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, size_t max_pkts, - vquic_recv_pkt_cb *recv_cb, void *userp); - -#endif /* !USE_HTTP3 */ + vquic_recv_pkts_cb *recv_cb, void *userp); #ifdef USE_NGTCP2 struct ngtcp2_mem; @@ -100,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/lib/vssh/.checksrc b/lib/vssh/.checksrc deleted file mode 100644 index 22ca8e0b53..0000000000 --- a/lib/vssh/.checksrc +++ /dev/null @@ -1,5 +0,0 @@ -banfunc snprintf -banfunc sscanf -banfunc strerror -banfunc strtol -banfunc vsnprint diff --git a/lib/vssh/curl_path.c b/lib/vssh/curl_path.c deleted file mode 100644 index 021e85faf8..0000000000 --- a/lib/vssh/curl_path.c +++ /dev/null @@ -1,209 +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 AND ISC - * - ***************************************************************************/ - -#include "../curl_setup.h" - -#ifdef USE_SSH - -#include "curl_path.h" -#include -#include "../curl_memory.h" -#include "../escape.h" -#include "../memdebug.h" - -#define MAX_SSHPATH_LEN 100000 /* arbitrary */ - -/* figure out the path to work with in this particular request */ -CURLcode Curl_getworkingpath(struct Curl_easy *data, - char *homedir, /* when SFTP is used */ - char **path) /* returns the allocated - real path to work with */ -{ - char *working_path; - size_t working_path_len; - struct dynbuf npath; - CURLcode result = - Curl_urldecode(data->state.up.path, 0, &working_path, - &working_path_len, REJECT_ZERO); - if(result) - return result; - - /* new path to switch to in case we need to */ - curlx_dyn_init(&npath, MAX_SSHPATH_LEN); - - /* Check for /~/, indicating relative to the user's home directory */ - if((data->conn->handler->protocol & CURLPROTO_SCP) && - (working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) { - /* It is referenced to the home directory, so strip the leading '/~/' */ - if(curlx_dyn_addn(&npath, &working_path[3], working_path_len - 3)) { - free(working_path); - return CURLE_OUT_OF_MEMORY; - } - } - else if((data->conn->handler->protocol & CURLPROTO_SFTP) && - (!strcmp("/~", working_path) || - ((working_path_len > 2) && !memcmp(working_path, "/~/", 3)))) { - if(curlx_dyn_add(&npath, homedir)) { - free(working_path); - return CURLE_OUT_OF_MEMORY; - } - if(working_path_len > 2) { - size_t len; - const char *p; - int copyfrom = 3; - /* Copy a separating '/' if homedir does not end with one */ - len = curlx_dyn_len(&npath); - p = curlx_dyn_ptr(&npath); - if(len && (p[len-1] != '/')) - copyfrom = 2; - - if(curlx_dyn_addn(&npath, &working_path[copyfrom], - working_path_len - copyfrom)) { - free(working_path); - return CURLE_OUT_OF_MEMORY; - } - } - else { - if(curlx_dyn_add(&npath, "/")) { - free(working_path); - return CURLE_OUT_OF_MEMORY; - } - } - } - - if(curlx_dyn_len(&npath)) { - free(working_path); - - /* store the pointer for the caller to receive */ - *path = curlx_dyn_ptr(&npath); - } - else - *path = working_path; - - return CURLE_OK; -} - -/* The original get_pathname() function came from OpenSSH sftp.c version - 4.6p1. */ -/* - * Copyright (c) 2001-2004 Damien Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#define MAX_PATHLENGTH 65535 /* arbitrary long */ - -CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir) -{ - const char *cp = *cpp, *end; - char quot; - unsigned int i; - static const char WHITESPACE[] = " \t\r\n"; - struct dynbuf out; - CURLcode result; - - DEBUGASSERT(homedir); - *path = NULL; - *cpp = NULL; - if(!*cp || !homedir) - return CURLE_QUOTE_ERROR; - - curlx_dyn_init(&out, MAX_PATHLENGTH); - - /* Ignore leading whitespace */ - cp += strspn(cp, WHITESPACE); - - /* Check for quoted filenames */ - if(*cp == '\"' || *cp == '\'') { - quot = *cp++; - - /* Search for terminating quote, unescape some chars */ - for(i = 0; i <= strlen(cp); i++) { - if(cp[i] == quot) { /* Found quote */ - i++; - break; - } - if(cp[i] == '\0') { /* End of string */ - goto fail; - } - if(cp[i] == '\\') { /* Escaped characters */ - i++; - if(cp[i] != '\'' && cp[i] != '\"' && - cp[i] != '\\') { - goto fail; - } - } - result = curlx_dyn_addn(&out, &cp[i], 1); - if(result) - return result; - } - - if(!curlx_dyn_len(&out)) - goto fail; - - /* return pointer to second parameter if it exists */ - *cpp = &cp[i] + strspn(&cp[i], WHITESPACE); - } - else { - /* Read to end of filename - either to whitespace or terminator */ - end = strpbrk(cp, WHITESPACE); - if(!end) - end = strchr(cp, '\0'); - - /* return pointer to second parameter if it exists */ - *cpp = end + strspn(end, WHITESPACE); - - /* Handling for relative path - prepend home directory */ - if(cp[0] == '/' && cp[1] == '~' && cp[2] == '/') { - result = curlx_dyn_add(&out, homedir); - if(!result) - result = curlx_dyn_addn(&out, "/", 1); - if(result) - return result; - cp += 3; - } - /* Copy path name up until first "whitespace" */ - result = curlx_dyn_addn(&out, cp, (end - cp)); - if(result) - return result; - } - *path = curlx_dyn_ptr(&out); - return CURLE_OK; - -fail: - curlx_dyn_free(&out); - return CURLE_QUOTE_ERROR; -} - -#endif /* if SSH is used */ diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 695532ff85..44094e466c 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -24,13 +24,10 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_LIBSSH -#include - #ifdef HAVE_NETINET_IN_H #include #endif @@ -45,29 +42,21 @@ #include #endif -#include -#include "../urldata.h" -#include "../sendf.h" -#include "../hostip.h" -#include "../progress.h" -#include "../transfer.h" -#include "../escape.h" -#include "../http.h" /* for HTTP proxy tunnel stuff */ -#include "ssh.h" -#include "../url.h" -#include "../speedcheck.h" -#include "../getinfo.h" -#include "../strdup.h" -#include "../vtls/vtls.h" -#include "../cfilters.h" -#include "../connect.h" -#include "../parsedate.h" /* for the week day and month names */ -#include "../sockaddr.h" /* required for Curl_sockaddr_storage */ -#include "../curlx/strparse.h" -#include "../multiif.h" -#include "../select.h" -#include "../curlx/warnless.h" -#include "curl_path.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 @@ -76,19 +65,14 @@ #include #endif -/* The last 3 #include files should be in this order */ -#include "../curl_printf.h" -#include "../curl_memory.h" -#include "../memdebug.h" - /* A recent macro provided by libssh. Or make our own. */ #ifndef SSH_STRING_FREE_CHAR -#define SSH_STRING_FREE_CHAR(x) \ - do { \ - if(x) { \ - ssh_string_free_char(x); \ - x = NULL; \ - } \ +#define SSH_STRING_FREE_CHAR(x) \ + do { \ + if(x) { \ + ssh_string_free_char(x); \ + x = NULL; \ + } \ } while(0) #endif @@ -100,218 +84,30 @@ #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 | PROTOPT_NOURLQUERY /* flags */ -}; - -/* - * 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 - | PROTOPT_NOURLQUERY /* flags */ -}; - static CURLcode sftp_error_to_CURLE(int err) { switch(err) { - case SSH_FX_OK: - return CURLE_OK; + case SSH_FX_OK: + return CURLE_OK; - case SSH_FX_NO_SUCH_FILE: - case SSH_FX_NO_SUCH_PATH: - return CURLE_REMOTE_FILE_NOT_FOUND; + case SSH_FX_NO_SUCH_FILE: + case SSH_FX_NO_SUCH_PATH: + return CURLE_REMOTE_FILE_NOT_FOUND; - case SSH_FX_PERMISSION_DENIED: - case SSH_FX_WRITE_PROTECT: - return CURLE_REMOTE_ACCESS_DENIED; + case SSH_FX_PERMISSION_DENIED: + case SSH_FX_WRITE_PROTECT: + return CURLE_REMOTE_ACCESS_DENIED; - case SSH_FX_FILE_ALREADY_EXISTS: - return CURLE_REMOTE_FILE_EXISTS; + case SSH_FX_FILE_ALREADY_EXISTS: + return CURLE_REMOTE_FILE_EXISTS; - default: - break; + default: + break; } return CURLE_SSH; } -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) -#define myssh_to(x,y,z) myssh_set_state(x,y,z, __LINE__) -#else -#define myssh_to(x,y,z) myssh_set_state(x,y,z) -#endif - -/* - * 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 -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - , int lineno -#endif - ) -{ -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ - 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" - }; - - - if(sshc->state != nowstate) { - infof(data, "SSH %p state change from %s to %s (line %d)", - (void *) sshc, names[sshc->state], names[nowstate], - lineno); - } -#endif - (void)data; - 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) @@ -334,8 +130,7 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) enum curl_khmatch keymatch; struct curl_khkey foundkey; struct curl_khkey *knownkeyp = NULL; - curl_sshkeycallback func = - data->set.ssh_keyfunc; + curl_sshkeycallback func = data->set.ssh_keyfunc; struct ssh_knownhosts_entry *knownhostsentry = NULL; struct curl_khkey knownkey; @@ -349,22 +144,21 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) char md5buffer[33]; const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; - rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, - &hash, &hlen); + rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hlen); if(rc != SSH_OK || hlen != 16) { failf(data, - "Denied establishing ssh session: md5 fingerprint not available"); + "Denied establishing ssh session: MD5 fingerprint not available"); goto cleanup; } for(i = 0; i < 16; i++) - 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); if(!curl_strequal(md5buffer, pubkey_md5)) { failf(data, - "Denied establishing ssh session: mismatch md5 fingerprint. " + "Denied establishing ssh session: mismatch MD5 fingerprint. " "Remote %s is not equal to %s", md5buffer, pubkey_md5); rc = SSH_ERROR; goto cleanup; @@ -467,7 +261,7 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) Curl_set_in_callback(data, TRUE); rc = func(data, knownkeyp, /* from the knownhosts file */ - &foundkey, /* from the remote host */ + &foundkey, /* from the remote host */ keymatch, data->set.ssh_keyfunc_userp); Curl_set_in_callback(data, FALSE); @@ -496,10 +290,12 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) cleanup: if(found_base64) { - (free)(found_base64); + /* !checksrc! disable BANNEDFUNC 1 */ + free(found_base64); /* allocated by libssh, deallocate with system free */ } if(known_base64) { - (free)(known_base64); + /* !checksrc! disable BANNEDFUNC 1 */ + free(known_base64); /* allocated by libssh, deallocate with system free */ } if(hash) ssh_clean_pubkey_hash(&hash); @@ -523,8 +319,7 @@ static int myssh_to_SFTP_CLOSE(struct Curl_easy *data, struct ssh_conn *sshc) { myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->actualcode = - sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); + sshc->actualcode = sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); return SSH_ERROR; } @@ -562,6 +357,8 @@ static int myssh_in_SFTP_READDIR_INIT(struct Curl_easy *data, struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); Curl_pgrsSetDownloadSize(data, -1); if(data->req.no_body) { myssh_to(data, sshc, SSH_STOP); @@ -589,6 +386,8 @@ static int myssh_in_SFTP_READDIR(struct Curl_easy *data, { CURLcode result = CURLE_OK; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); curlx_dyn_reset(&sshc->readdir_buf); if(sshc->readdir_attrs) sftp_attributes_free(sshc->readdir_attrs); @@ -602,7 +401,7 @@ static int myssh_in_SFTP_READDIR(struct Curl_easy *data, if(data->set.list_only) { char *tmpLine; - tmpLine = aprintf("%s\n", sshc->readdir_filename); + tmpLine = curl_maprintf("%s\n", sshc->readdir_filename); if(!tmpLine) { myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->actualcode = CURLE_OUT_OF_MEMORY; @@ -610,13 +409,13 @@ static int myssh_in_SFTP_READDIR(struct Curl_easy *data, } result = Curl_client_write(data, CLIENTWRITE_BODY, tmpLine, sshc->readdir_len + 1); - free(tmpLine); + curlx_free(tmpLine); if(result) { myssh_to(data, sshc, SSH_STOP); + sshc->actualcode = result; return SSH_NO_ERROR; } - } else { if(curlx_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) { @@ -628,8 +427,8 @@ static int myssh_in_SFTP_READDIR(struct Curl_easy *data, if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) && ((sshc->readdir_attrs->permissions & SSH_S_IFMT) == SSH_S_IFLNK)) { - sshc->readdir_linkPath = aprintf("%s%s", sshp->path, - sshc->readdir_filename); + sshc->readdir_linkPath = curl_maprintf("%s%s", sshp->path, + sshc->readdir_filename); if(!sshc->readdir_linkPath) { myssh_to(data, sshc, SSH_SFTP_CLOSE); @@ -685,10 +484,9 @@ static int myssh_in_SFTP_READDIR_LINK(struct Curl_easy *data, sshc->readdir_longentry = sshc->readdir_link_attrs->longname; } - Curl_safefree(sshc->readdir_linkPath); + curlx_safefree(sshc->readdir_linkPath); - if(curlx_dyn_addf(&sshc->readdir_buf, " -> %s", - sshc->readdir_filename)) { + if(curlx_dyn_addf(&sshc->readdir_buf, " -> %s", sshc->readdir_filename)) { /* Not using: * return myssh_to_SFTP_CLOSE(data, sshc); * @@ -725,8 +523,10 @@ static int myssh_in_SFTP_READDIR_BOTTOM(struct Curl_easy *data, ssh_string_free_char(sshc->readdir_tmp); sshc->readdir_tmp = NULL; - if(result) + if(result) { myssh_to(data, sshc, SSH_STOP); + sshc->actualcode = result; + } else myssh_to(data, sshc, SSH_SFTP_READDIR); return SSH_NO_ERROR; @@ -744,6 +544,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)); + curlx_safefree(sshc->quote_path1); + curlx_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) { @@ -751,49 +564,45 @@ 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 - CURLcode result; - char *tmp = aprintf("statvfs:\n" - "f_bsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_frsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_blocks: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_bfree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_bavail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_files: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_ffree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_favail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_fsid: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_flag: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_namemax: %" CURL_LIBSSH_VFS_SIZE_MASK "\n", - statvfs->f_bsize, statvfs->f_frsize, - statvfs->f_blocks, statvfs->f_bfree, - statvfs->f_bavail, statvfs->f_files, - statvfs->f_ffree, statvfs->f_favail, - statvfs->f_fsid, statvfs->f_flag, - statvfs->f_namemax); +#ifdef _MSC_VER +#define CURL_LIBSSH_VFS_SIZE_MASK "I64u" +#elif defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 6) +#define CURL_LIBSSH_VFS_SIZE_MASK "llu" +#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" + "f_frsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_blocks: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_bfree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_bavail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_files: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_ffree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_favail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_fsid: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_flag: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_namemax: %" CURL_LIBSSH_VFS_SIZE_MASK "\n", + statvfs->f_bsize, statvfs->f_frsize, + statvfs->f_blocks, statvfs->f_bfree, + statvfs->f_bavail, statvfs->f_files, + statvfs->f_ffree, statvfs->f_favail, + statvfs->f_fsid, statvfs->f_flag, + statvfs->f_namemax); sftp_statvfs_free(statvfs); - if(!tmp) { - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - return SSH_OK; - } + if(!tmp) + result = CURLE_OUT_OF_MEMORY; - result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); - free(tmp); + if(!result) { + result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); + curlx_free(tmp); + } if(result) { myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; @@ -812,56 +621,56 @@ static int myssh_auth_interactive(struct connectdata *conn, restart: switch(sshc->kbd_state) { - case 0: - rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); - if(rc == SSH_AUTH_AGAIN) - return SSH_AGAIN; + case 0: + rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); + if(rc == SSH_AUTH_AGAIN) + return SSH_AGAIN; - if(rc != SSH_AUTH_INFO) - return SSH_ERROR; - - nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session); - if(nprompts != 1) - return SSH_ERROR; - - rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd); - if(rc < 0) - return SSH_ERROR; - - FALLTHROUGH(); - case 1: - sshc->kbd_state = 1; - - rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); - if(rc == SSH_AUTH_AGAIN) - return SSH_AGAIN; - else if(rc == SSH_AUTH_SUCCESS) - rc = SSH_OK; - else if(rc == SSH_AUTH_INFO) { - nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session); - if(nprompts) - return SSH_ERROR; - - sshc->kbd_state = 2; - goto restart; - } - else - rc = SSH_ERROR; - break; - case 2: - sshc->kbd_state = 2; - - rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); - if(rc == SSH_AUTH_AGAIN) - return SSH_AGAIN; - else if(rc == SSH_AUTH_SUCCESS) - rc = SSH_OK; - else - rc = SSH_ERROR; - - break; - default: + if(rc != SSH_AUTH_INFO) return SSH_ERROR; + + nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session); + if(nprompts != 1) + return SSH_ERROR; + + rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd); + if(rc < 0) + return SSH_ERROR; + + FALLTHROUGH(); + case 1: + sshc->kbd_state = 1; + + rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); + if(rc == SSH_AUTH_AGAIN) + return SSH_AGAIN; + else if(rc == SSH_AUTH_SUCCESS) + rc = SSH_OK; + else if(rc == SSH_AUTH_INFO) { + nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session); + if(nprompts) + return SSH_ERROR; + + sshc->kbd_state = 2; + goto restart; + } + else + rc = SSH_ERROR; + break; + case 2: + sshc->kbd_state = 2; + + rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); + if(rc == SSH_AUTH_AGAIN) + return SSH_AGAIN; + else if(rc == SSH_AUTH_SUCCESS) + rc = SSH_OK; + else + rc = SSH_ERROR; + + break; + default: + return SSH_ERROR; } sshc->kbd_state = 0; @@ -886,6 +695,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) ? REQ_IO_RECV : 0) | + ((dir & SSH_WRITE_PENDING) ? REQ_IO_SEND : 0); + } + else + sshc->waitfor = 0; +} + static int myssh_in_S_STARTUP(struct Curl_easy *data, struct ssh_conn *sshc) { @@ -894,7 +719,7 @@ static int myssh_in_S_STARTUP(struct Curl_easy *data, myssh_block2waitfor(conn, sshc, (rc == SSH_AGAIN)); if(rc == SSH_AGAIN) { - DEBUGF(infof(data, "ssh_connect -> EAGAIN")); + CURL_TRC_SSH(data, "connect -> EAGAIN"); } else if(rc != SSH_OK) { failf(data, "Failure establishing ssh session"); @@ -932,14 +757,18 @@ 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": ""); - if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { + "password" : ""); + /* For public key auth we need either the private key or + CURLSSH_AUTH_AGENT. */ + if((sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) && + (data->set.str[STRING_SSH_PRIVATE_KEY] || + (data->set.ssh_auth_types & CURLSSH_AUTH_AGENT))) { myssh_to(data, sshc, SSH_AUTH_PKEY_INIT); infof(data, "Authentication using SSH public key file"); } @@ -971,8 +800,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, * (2) use the "default" keys. */ if(data->set.str[STRING_SSH_PRIVATE_KEY]) { if(sshc->pubkey && !data->set.ssl.key_passwd) { - rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL, - sshc->pubkey); + rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL, sshc->pubkey); if(rc == SSH_AUTH_AGAIN) return SSH_AGAIN; @@ -997,7 +825,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, } else { rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL, - data->set.ssl.key_passwd); + data->set.ssl.key_passwd); if(rc == SSH_AUTH_AGAIN) return SSH_AGAIN; @@ -1107,6 +935,7 @@ static int myssh_in_AUTH_PASS(struct Curl_easy *data, static int myssh_in_AUTH_DONE(struct Curl_easy *data, struct ssh_conn *sshc) { + struct connectdata *conn = data->conn; if(!sshc->authed) { failf(data, "Authentication failure"); return myssh_to_ERROR(data, sshc, CURLE_LOGIN_DENIED); @@ -1115,10 +944,10 @@ static int myssh_in_AUTH_DONE(struct Curl_easy *data, /* At this point we have an authenticated ssh session. */ infof(data, "Authentication complete"); Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */ - data->conn->recv_idx = FIRSTSOCKET; - data->conn->send_idx = -1; + conn->recv_idx = FIRSTSOCKET; + conn->send_idx = -1; - if(data->conn->handler->protocol == CURLPROTO_SFTP) { + if(conn->scheme->protocol == CURLPROTO_SFTP) { myssh_to(data, sshc, SSH_SFTP_INIT); return SSH_NO_ERROR; } @@ -1134,6 +963,9 @@ static int myssh_in_UPLOAD_INIT(struct Curl_easy *data, int flags; int rc = 0; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + if(data->state.resume_from) { sftp_attributes attrs; @@ -1141,14 +973,13 @@ static int myssh_in_UPLOAD_INIT(struct Curl_easy *data, attrs = sftp_stat(sshc->sftp_session, sshp->path); if(attrs) { curl_off_t size = attrs->size; + sftp_attributes_free(attrs); if(size < 0) { failf(data, "Bad file size (%" FMT_OFF_T ")", size); rc = myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); return rc; } - data->state.resume_from = attrs->size; - - sftp_attributes_free(attrs); + data->state.resume_from = size; } else { data->state.resume_from = 0; @@ -1156,15 +987,21 @@ static int myssh_in_UPLOAD_INIT(struct Curl_easy *data, } } - if(data->set.remote_append) - /* Try to open for append, but create if nonexisting */ - flags = O_WRONLY|O_CREAT|O_APPEND; - else if(data->state.resume_from > 0) - /* If we have restart position then open for append */ - flags = O_WRONLY|O_APPEND; + if(data->set.remote_append) { + /* True append mode: create if nonexisting */ + flags = O_WRONLY | O_CREAT | O_APPEND; + } + else if(data->state.resume_from > 0) { + /* + * Resume MUST NOT use O_APPEND. Many SFTP servers/impls force all + * writes to EOF when O_APPEND is set, ignoring a prior seek(). + * Open write-only and seek to the resume offset instead. + */ + flags = O_WRONLY; + } else /* Clear file before writing (normal behavior) */ - flags = O_WRONLY|O_CREAT|O_TRUNC; + flags = O_WRONLY | O_CREAT | O_TRUNC; if(sshc->sftp_file) sftp_close(sshc->sftp_file); @@ -1174,10 +1011,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; @@ -1191,8 +1029,8 @@ static int myssh_in_UPLOAD_INIT(struct Curl_easy *data, } /* If we have a restart point then we need to seek to the correct - position. */ - if(data->state.resume_from > 0) { + position. Skip if in explicit remote append mode. */ + if(data->state.resume_from > 0 && !data->set.remote_append) { int seekerr = CURL_SEEKFUNC_OK; /* Let's read off the proper amount of bytes from the input. */ if(data->set.seek_func) { @@ -1212,7 +1050,7 @@ static int myssh_in_UPLOAD_INIT(struct Curl_easy *data, } /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ do { - char scratch[4*1024]; + char scratch[4 * 1024]; size_t readthisamountnow = (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ? @@ -1235,6 +1073,10 @@ static int myssh_in_UPLOAD_INIT(struct Curl_easy *data, /* now, decrease the size of the read */ if(data->state.infilesize > 0) { + if(data->state.resume_from > data->state.infilesize) { + failf(data, "Resume point beyond size"); + return myssh_to_ERROR(data, sshc, CURLE_BAD_FUNCTION_ARGUMENT); + } data->state.infilesize -= data->state.resume_from; data->req.size = data->state.infilesize; Curl_pgrsSetUploadSize(data, data->state.infilesize); @@ -1253,13 +1095,9 @@ static int myssh_in_UPLOAD_INIT(struct Curl_easy *data, /* upload data */ Curl_xfer_setup_send(data, FIRSTSOCKET); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ 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 so we mark this as dirty */ Curl_multi_mark_dirty(data); @@ -1274,6 +1112,8 @@ static int myssh_in_SFTP_DOWNLOAD_INIT(struct Curl_easy *data, struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); /* Work on getting the specified file */ if(sshc->sftp_file) sftp_close(sshc->sftp_file); @@ -1302,7 +1142,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 */ @@ -1320,60 +1160,18 @@ static int myssh_in_SFTP_DOWNLOAD_STAT(struct Curl_easy *data, if(size < 0) { failf(data, "Bad file size (%" FMT_OFF_T ")", size); - rc = myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); - return rc; + return myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); } if(data->state.use_range) { - curl_off_t from, to; - const char *p = data->state.range; - int from_t, to_t; - - from_t = curlx_str_number(&p, &from, CURL_OFF_T_MAX); - if(from_t == STRE_OVERFLOW) { - rc = myssh_to_ERROR(data, sshc, CURLE_RANGE_ERROR); - return rc; - } - curlx_str_passblanks(&p); - (void)curlx_str_single(&p, '-'); - - to_t = curlx_str_numblanks(&p, &to); - if(to_t == STRE_OVERFLOW) - return CURLE_RANGE_ERROR; - - if((to_t == STRE_NO_NUM) || (to >= size)) { - to = size - 1; - to_t = STRE_OK; - } - - if(from_t == STRE_NO_NUM) { - /* from is relative to end of file */ - from = size - to; - to = size - 1; - from_t = STRE_OK; - } - if(from > size) { - failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" - FMT_OFF_T ")", from, size); - rc = myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); - return rc; - } - if(from > to) { - from = to; - size = 0; - } - else { - if((to - from) == CURL_OFF_T_MAX) { - rc = myssh_to_ERROR(data, sshc, CURLE_RANGE_ERROR); - return rc; - } - size = to - from + 1; - } + curl_off_t from; + CURLcode result = Curl_ssh_range(data, data->state.range, size, + &from, &size); + if(result) + return myssh_to_ERROR(data, sshc, result); rc = sftp_seek64(sshc->sftp_file, from); - if(rc) { - rc = myssh_to_SFTP_CLOSE(data, sshc); - return rc; - } + if(rc) + return myssh_to_SFTP_CLOSE(data, sshc); } data->req.size = size; data->req.maxdownload = size; @@ -1384,35 +1182,30 @@ 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); - rc = myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); - return rc; + return myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); } /* download from where? */ 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); - rc = myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); - return rc; + return myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); } } /* Now store the number of bytes we are expected to download */ data->req.size = size - data->state.resume_from; data->req.maxdownload = size - data->state.resume_from; - Curl_pgrsSetDownloadSize(data, - size - data->state.resume_from); + Curl_pgrsSetDownloadSize(data, size - data->state.resume_from); rc = sftp_seek64(sshc->sftp_file, data->state.resume_from); - if(rc) { - rc = myssh_to_SFTP_CLOSE(data, sshc); - return rc; - } + if(rc) + return myssh_to_SFTP_CLOSE(data, sshc); } /* Setup the actual download */ @@ -1425,7 +1218,7 @@ static int myssh_in_SFTP_DOWNLOAD_STAT(struct Curl_easy *data, } Curl_xfer_setup_recv(data, FIRSTSOCKET, data->req.size); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->send_idx = 0; sshc->sftp_recv_state = 0; @@ -1438,17 +1231,19 @@ static int myssh_in_SFTP_CLOSE(struct Curl_easy *data, struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); if(sshc->sftp_file) { sftp_close(sshc->sftp_file); sshc->sftp_file = NULL; } - Curl_safefree(sshp->path); + curlx_safefree(sshp->path); - DEBUGF(infof(data, "SFTP DONE done")); + CURL_TRC_SSH(data, "SFTP DONE done"); /* 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); @@ -1519,16 +1314,16 @@ static int myssh_in_SFTP_REALPATH(struct Curl_easy *data, if(!sshc->homedir) return myssh_to_ERROR(data, sshc, CURLE_COULDNT_CONNECT); - free(data->state.most_recent_ftp_entrypath); - data->state.most_recent_ftp_entrypath = strdup(sshc->homedir); + curlx_free(data->state.most_recent_ftp_entrypath); + data->state.most_recent_ftp_entrypath = curlx_strdup(sshc->homedir); if(!data->state.most_recent_ftp_entrypath) return myssh_to_ERROR(data, sshc, CURLE_OUT_OF_MEMORY); /* This is the last step in the SFTP connect phase. Do note that while we get the homedir here, we get the "workingpath" in the DO action - since the homedir will remain the same between request but the - working path will not. */ - DEBUGF(infof(data, "SSH CONNECT phase done")); + since the homedir remains the same between request but the + working path does not. */ + CURL_TRC_SSH(data, "CONNECT phase done"); myssh_to(data, sshc, SSH_STOP); return SSH_NO_ERROR; } @@ -1537,7 +1332,12 @@ static int myssh_in_SFTP_QUOTE_INIT(struct Curl_easy *data, struct ssh_conn *sshc, struct SSHPROTO *sshp) { - CURLcode result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); + CURLcode result; + + 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); @@ -1566,6 +1366,18 @@ static int myssh_in_SFTP_POSTQUOTE_INIT(struct Curl_easy *data, return SSH_NO_ERROR; } +static int quote_error(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + failf(data, "Suspicious data after the command line"); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return SSH_NO_ERROR; +} + static int myssh_in_SFTP_QUOTE(struct Curl_easy *data, struct ssh_conn *sshc, struct SSHPROTO *sshp) @@ -1576,12 +1388,15 @@ 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(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + /* if a command starts with an asterisk, which a legal SFTP 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 + can, the command is allowed to fail without it causing any + aborts or cancels etc. It causes libcurl to act as if the command is successful, whatever the server responds. */ if(cmd[0] == '*') { @@ -1591,7 +1406,8 @@ static int myssh_in_SFTP_QUOTE(struct Curl_easy *data, if(curl_strequal("pwd", cmd)) { /* output debug output if that is requested */ - char *tmp = aprintf("257 \"%s\" is current directory.\n", sshp->path); + char *tmp = curl_maprintf("257 \"%s\" is current directory.\n", + sshp->path); if(!tmp) { sshc->actualcode = CURLE_OUT_OF_MEMORY; myssh_to(data, sshc, SSH_SFTP_CLOSE); @@ -1601,11 +1417,11 @@ static int myssh_in_SFTP_QUOTE(struct Curl_easy *data, Curl_debug(data, CURLINFO_HEADER_OUT, "PWD\n", 4); Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp)); - /* this sends an FTP-like "header" to the header callback so that the - current directory can be read very similar to how it is read when + /* this sends an FTP-like "header" to the header callback so that + the current directory can be read similar to how it is read when using ordinary FTP. */ result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); - free(tmp); + curlx_free(tmp); if(result) { myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; @@ -1667,12 +1483,14 @@ static int myssh_in_SFTP_QUOTE(struct Curl_easy *data, else failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: " "Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; return SSH_NO_ERROR; } + if(*cp) + return quote_error(data, sshc); sshc->quote_attrs = NULL; myssh_to(data, sshc, SSH_SFTP_QUOTE_STAT); return SSH_NO_ERROR; @@ -1688,17 +1506,21 @@ static int myssh_in_SFTP_QUOTE(struct Curl_easy *data, failf(data, "Out of memory"); else failf(data, "Syntax error in ln/symlink: Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; return SSH_NO_ERROR; } + if(*cp) + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_SYMLINK); return SSH_NO_ERROR; } else if(!strncmp(cmd, "mkdir ", 6)) { - /* create dir */ + if(*cp) + return quote_error(data, sshc); + /* create directory */ myssh_to(data, sshc, SSH_SFTP_QUOTE_MKDIR); return SSH_NO_ERROR; } @@ -1712,34 +1534,42 @@ static int myssh_in_SFTP_QUOTE(struct Curl_easy *data, failf(data, "Out of memory"); else failf(data, "Syntax error in rename: Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; return SSH_NO_ERROR; } + if(*cp) + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_RENAME); return SSH_NO_ERROR; } else if(!strncmp(cmd, "rmdir ", 6)) { - /* delete dir */ + /* delete directory */ + if(*cp) + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_RMDIR); return SSH_NO_ERROR; } else if(!strncmp(cmd, "rm ", 3)) { + if(*cp) + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_UNLINK); return SSH_NO_ERROR; } #ifdef HAS_STATVFS_SUPPORT else if(!strncmp(cmd, "statvfs ", 8)) { + if(*cp) + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_STATVFS); return SSH_NO_ERROR; } #endif failf(data, "Unknown SFTP command"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1749,8 +1579,8 @@ static int myssh_in_SFTP_QUOTE(struct Curl_easy *data, static int myssh_in_SFTP_NEXT_QUOTE(struct Curl_easy *data, struct ssh_conn *sshc) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); sshc->quote_item = sshc->quote_item->next; @@ -1772,12 +1602,12 @@ 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 - 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 + can, the command is allowed to fail without it causing any + aborts or cancels etc. It causes libcurl to act as if the command is successful, whatever the server responds. */ if(cmd[0] == '*') { @@ -1794,13 +1624,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; } @@ -1808,30 +1634,20 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, if(!strncmp(cmd, "chgrp", 5)) { const char *p = sshc->quote_path1; curl_off_t gid; - (void)curlx_str_number(&p, &gid, UINT_MAX); - sshc->quote_attrs->gid = (uint32_t)gid; - if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) && - !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + if(curlx_str_number(&p, &gid, UINT_MAX)) { 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; sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; } else if(!strncmp(cmd, "chmod", 5)) { 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; @@ -1840,17 +1656,12 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, else if(!strncmp(cmd, "chown", 5)) { const char *p = sshc->quote_path1; curl_off_t uid; - (void)curlx_str_number(&p, &uid, UINT_MAX); - if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) && - !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + if(curlx_str_number(&p, &uid, UINT_MAX)) { 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; sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; } else if(!strncmp(cmd, "atime", 5) || @@ -1868,11 +1679,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(!strncmp(cmd, "atime", 5)) @@ -1888,21 +1695,503 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, return SSH_NO_ERROR; } +static void conn_forget_socket(struct Curl_easy *data, int sockindex) +{ + struct connectdata *conn = data->conn; + if(conn && CONN_SOCK_IDX_VALID(sockindex)) { + struct Curl_cfilter *cf = conn->cfilter[sockindex]; + if(cf) + (void)Curl_conn_cf_cntrl(cf, data, TRUE, CF_CTRL_FORGET_SOCKET, 0, NULL); + fake_sclose(conn->sock[sockindex]); + conn->sock[sockindex] = CURL_SOCKET_BAD; + } +} + +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 io_flags */ + 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; + } + + curlx_safefree(sshc->rsa_pub); + curlx_safefree(sshc->rsa); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + curlx_dyn_free(&sshc->readdir_buf); + curlx_safefree(sshc->readdir_linkPath); + SSH_STRING_FREE_CHAR(sshc->homedir); + sshc->initialised = FALSE; + } +} + +static int myssh_in_SFTP_QUOTE_SETSTAT(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2, + sshc->quote_attrs); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "setstat"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_SYMLINK(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2, + sshc->quote_path1); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "symlink"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_MKDIR(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1, + (mode_t)data->set.new_directory_perms); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "mkdir"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_RENAME(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_rename(sshc->sftp_session, sshc->quote_path1, + sshc->quote_path2); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "rename"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_RMDIR(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "rmdir"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_UNLINK(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "rm"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_GETINFO(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->set.get_filetime) + myssh_to(data, sshc, SSH_SFTP_FILETIME); + else + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_FILETIME(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + sftp_attributes attrs; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + attrs = sftp_stat(sshc->sftp_session, sshp->path); + if(attrs) { + data->info.filetime = attrs->mtime; + sftp_attributes_free(attrs); + } + + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_TRANS_INIT(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + if(data->state.upload) + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + else { + size_t path_len = strlen(sshp->path); + + if(path_len && sshp->path[path_len - 1] == '/') + myssh_to(data, sshc, SSH_SFTP_READDIR_INIT); + else + myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT); + } + + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_CREATE_DIRS_INIT(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + if(strlen(sshp->path) > 1) { + sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); + } + else { + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + } + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_CREATE_DIRS(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + sshc->slash_pos = strchr(sshc->slash_pos, '/'); + if(sshc->slash_pos) { + *sshc->slash_pos = 0; + + infof(data, "Creating directory '%s'", sshp->path); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); + return SSH_NO_ERROR; + } + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_CREATE_DIRS_MKDIR(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + int rc; + int err; + /* 'mode' - parameter is preliminary - default to 0644 */ + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + rc = sftp_mkdir(sshc->sftp_session, sshp->path, + (mode_t)data->set.new_directory_perms); + if(rc == SSH_AGAIN) + return rc; + *sshc->slash_pos = '/'; + ++sshc->slash_pos; + if(rc < 0) { + /* + * Abort if failure was not that the directory already + * exists or the permission was denied (creation might + * succeed further down the path) - retry on unspecific + * FAILURE also + */ + err = sftp_get_error(sshc->sftp_session); + if((err != SSH_FX_FILE_ALREADY_EXISTS) && + (err != SSH_FX_FAILURE) && + (err != SSH_FX_PERMISSION_DENIED)) { + return myssh_to_SFTP_CLOSE(data, sshc); + } + rc = 0; /* clear rc and continue */ + } + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); + return rc; +} + +static int myssh_in_SCP_UPLOAD_INIT(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + int rc; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + rc = ssh_scp_init(sshc->scp_session); + if(rc != SSH_OK) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + return myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); + } + + rc = ssh_scp_push_file64(sshc->scp_session, sshp->path, + (uint64_t)data->state.infilesize, + (int)data->set.new_file_perms); + + if(rc != SSH_OK) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + return myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); + } + + /* upload data */ + Curl_xfer_setup_send(data, FIRSTSOCKET); + + /* not set by Curl_xfer_setup to preserve io_flags */ + data->conn->recv_idx = FIRSTSOCKET; + + myssh_to(data, sshc, SSH_STOP); + return SSH_NO_ERROR; +} + +static int myssh_in_SCP_DOWNLOAD_INIT(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = ssh_scp_init(sshc->scp_session); + if(rc != SSH_OK) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + return myssh_to_ERROR(data, sshc, + CURLE_COULDNT_CONNECT); + } + myssh_to(data, sshc, SSH_SCP_DOWNLOAD); + return SSH_NO_ERROR; +} + +static int myssh_in_SCP_DONE(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->state.upload) + myssh_to(data, sshc, SSH_SCP_SEND_EOF); + else + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return SSH_NO_ERROR; +} + +static int myssh_in_SCP_SEND_EOF(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->scp_session) { + int rc = ssh_scp_close(sshc->scp_session); + if(rc == SSH_AGAIN) { + /* Currently the ssh_scp_close handles waiting for + * EOF in blocking way. + */ + return SSH_AGAIN; + } + if(rc != SSH_OK) { + infof(data, + "Failed to close libssh scp channel: %s", + ssh_get_error(sshc->ssh_session)); + } + } + + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return SSH_NO_ERROR; +} + +static int myssh_in_SCP_CHANNEL_FREE(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->scp_session) { + ssh_scp_free(sshc->scp_session); + sshc->scp_session = NULL; + } + CURL_TRC_SSH(data, "SCP DONE phase complete"); + + ssh_set_blocking(sshc->ssh_session, 0); + + myssh_to(data, sshc, SSH_SESSION_DISCONNECT); + return SSH_NO_ERROR; +} + +static CURLcode myssh_in_SESSION_FREE(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + CURLcode result; + sshc_cleanup(sshc); + /* the code we are about to return */ + result = sshc->actualcode; + memset(sshc, 0, sizeof(struct ssh_conn)); + connclose(data->conn, "SSH session free"); + sshc->state = SSH_SESSION_FREE; /* current */ + sshc->nextstate = SSH_NO_STATE; + myssh_to(data, sshc, SSH_STOP); + return result; +} + /* - * ssh_statemach_act() runs the SSH state machine as far as it can without + * myssh_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 libssh function returns SSH_AGAIN + * to is set to TRUE if the libssh function returns SSH_AGAIN * meaning it wants to be called again when the socket is ready */ -static CURLcode myssh_statemach_act(struct Curl_easy *data, - struct ssh_conn *sshc, - struct SSHPROTO *sshp, - bool *block) +static CURLcode myssh_statemachine(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *block) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - int rc = SSH_NO_ERROR, err; - const char *err_msg; + int rc = SSH_NO_ERROR; *block = FALSE; /* we are not blocking by default */ do { @@ -1972,188 +2261,48 @@ 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 && !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 */ - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_SETSTAT(data, sshc); break; - case SSH_SFTP_QUOTE_SYMLINK: - rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2, - sshc->quote_path1); - 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; - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_SYMLINK(data, sshc); break; - case SSH_SFTP_QUOTE_MKDIR: - rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1, - (mode_t)data->set.new_directory_perms); - 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; - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_MKDIR(data, sshc); break; - case SSH_SFTP_QUOTE_RENAME: - rc = sftp_rename(sshc->sftp_session, sshc->quote_path1, - sshc->quote_path2); - 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; - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_RENAME(data, sshc); break; - case SSH_SFTP_QUOTE_RMDIR: - rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1); - 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; - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_RMDIR(data, sshc); break; - case SSH_SFTP_QUOTE_UNLINK: - rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1); - 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; - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_UNLINK(data, sshc); break; - case SSH_SFTP_QUOTE_STATVFS: rc = myssh_in_SFTP_QUOTE_STATVFS(data, sshc); break; - case SSH_SFTP_GETINFO: - if(data->set.get_filetime) { - myssh_to(data, sshc, SSH_SFTP_FILETIME); - } - else { - myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); - } + rc = myssh_in_SFTP_GETINFO(data, sshc); break; - - case SSH_SFTP_FILETIME: { - sftp_attributes attrs; - - attrs = sftp_stat(sshc->sftp_session, sshp->path); - if(attrs) { - data->info.filetime = attrs->mtime; - sftp_attributes_free(attrs); - } - - myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + case SSH_SFTP_FILETIME: + rc = myssh_in_SFTP_FILETIME(data, sshc, sshp); break; - } - case SSH_SFTP_TRANS_INIT: - if(data->state.upload) - myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); - else { - if(sshp->path[strlen(sshp->path)-1] == '/') - myssh_to(data, sshc, SSH_SFTP_READDIR_INIT); - else - myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT); - } + rc = myssh_in_SFTP_TRANS_INIT(data, sshc, sshp); break; - case SSH_SFTP_UPLOAD_INIT: rc = myssh_in_UPLOAD_INIT(data, sshc, sshp); break; - case SSH_SFTP_CREATE_DIRS_INIT: - if(strlen(sshp->path) > 1) { - sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ - myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); - } - else { - myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); - } + rc = myssh_in_SFTP_CREATE_DIRS_INIT(data, sshc, sshp); break; - case SSH_SFTP_CREATE_DIRS: - sshc->slash_pos = strchr(sshc->slash_pos, '/'); - if(sshc->slash_pos) { - *sshc->slash_pos = 0; - - infof(data, "Creating directory '%s'", sshp->path); - myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); - break; - } - myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + rc = myssh_in_SFTP_CREATE_DIRS(data, sshc, sshp); break; - case SSH_SFTP_CREATE_DIRS_MKDIR: - /* 'mode' - parameter is preliminary - default to 0644 */ - rc = sftp_mkdir(sshc->sftp_session, sshp->path, - (mode_t)data->set.new_directory_perms); - *sshc->slash_pos = '/'; - ++sshc->slash_pos; - if(rc < 0) { - /* - * Abort if failure was not that the dir already exists or the - * permission was denied (creation might succeed further down the - * path) - retry on unspecific FAILURE also - */ - err = sftp_get_error(sshc->sftp_session); - if((err != SSH_FX_FILE_ALREADY_EXISTS) && - (err != SSH_FX_FAILURE) && - (err != SSH_FX_PERMISSION_DENIED)) { - rc = myssh_to_SFTP_CLOSE(data, sshc); - break; - } - rc = 0; /* clear rc and continue */ - } - myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); + rc = myssh_in_SFTP_CREATE_DIRS_MKDIR(data, sshc, sshp); break; - case SSH_SFTP_READDIR_INIT: rc = myssh_in_SFTP_READDIR_INIT(data, sshc, sshp); break; @@ -2181,200 +2330,45 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, case SSH_SFTP_SHUTDOWN: rc = myssh_in_SFTP_SHUTDOWN(data, sshc); break; - case SSH_SCP_TRANS_INIT: - 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: - - rc = ssh_scp_init(sshc->scp_session); - if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); - break; - } - - rc = ssh_scp_push_file64(sshc->scp_session, sshp->path, - (uint64_t)data->state.infilesize, - (int)data->set.new_file_perms); - - if(rc != SSH_OK) { - err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); - break; - } - - /* 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; - - myssh_to(data, sshc, SSH_STOP); - + rc = myssh_in_SCP_UPLOAD_INIT(data, sshc, sshp); 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); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_COULDNT_CONNECT); + rc = myssh_in_SCP_DOWNLOAD_INIT(data, sshc); + if(rc) break; - } - 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); - break; - } + 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); - else - myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + rc = myssh_in_SCP_DONE(data, sshc); break; - case SSH_SCP_SEND_EOF: - if(sshc->scp_session) { - rc = ssh_scp_close(sshc->scp_session); - if(rc == SSH_AGAIN) { - /* Currently the ssh_scp_close handles waiting for EOF in - * blocking way. - */ - break; - } - if(rc != SSH_OK) { - infof(data, "Failed to close libssh scp channel: %s", - ssh_get_error(sshc->ssh_session)); - } - } - - myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + rc = myssh_in_SCP_SEND_EOF(data, sshc); break; - case SSH_SCP_CHANNEL_FREE: - if(sshc->scp_session) { - ssh_scp_free(sshc->scp_session); - sshc->scp_session = NULL; - } - DEBUGF(infof(data, "SCP DONE phase complete")); - - ssh_set_blocking(sshc->ssh_session, 0); - - myssh_to(data, sshc, SSH_SESSION_DISCONNECT); + myssh_in_SCP_CHANNEL_FREE(data, sshc); 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. */ - Curl_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"); - sshc->state = SSH_SESSION_FREE; /* current */ - sshc->nextstate = SSH_NO_STATE; - myssh_to(data, sshc, SSH_STOP); + result = myssh_in_SESSION_FREE(data, sshc); break; - case SSH_QUIT: default: /* internal error */ sshc->nextstate = SSH_NO_STATE; myssh_to(data, sshc, SSH_STOP); break; - } - } while(!rc && (sshc->state != SSH_STOP)); - + /* break the loop only on STOP or SSH_AGAIN. If `rc` is some + * other error code, we will have progressed the state accordingly. */ + } while((rc != SSH_AGAIN) && (sshc->state != SSH_STOP)); if(rc == SSH_AGAIN) { /* we would block, we need to wait for the socket to be ready (in the @@ -2383,45 +2377,39 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, } if(!result && (sshc->state == SSH_STOP)) result = sshc->actualcode; - DEBUGF(infof(data, "SSH: myssh_statemach_act -> %d", result)); + CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d", + Curl_ssh_statename(sshc->state), result, *block); return result; } - /* called by the multi interface to figure out what socket(s) to wait for and for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ static CURLcode myssh_pollset(struct Curl_easy *data, struct easy_pollset *ps) { - int flags = 0; - if(data->conn->waitfor & KEEP_RECV) - flags |= CURL_POLL_IN; - if(data->conn->waitfor & KEEP_SEND) - flags |= CURL_POLL_OUT; - if(!data->conn->waitfor) - flags |= CURL_POLL_OUT; - return flags ? - Curl_pollset_change(data, ps, data->conn->sock[FIRSTSOCKET], flags, 0) : - CURLE_OK; -} + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + int waitfor; -static void myssh_block2waitfor(struct connectdata *conn, - struct ssh_conn *sshc, - bool block) -{ - /* If it did not block, or nothing was returned by ssh_get_poll_flags - * have the original set */ - conn->waitfor = sshc->orig_waitfor; + if(!sshc || (sock == CURL_SOCKET_BAD)) + return CURLE_FAILED_INIT; - if(block) { - int dir = ssh_get_poll_flags(sshc->ssh_session); - conn->waitfor = 0; - /* translate the libssh define bits into our own bit defines */ - if(dir & SSH_READ_PENDING) - conn->waitfor |= KEEP_RECV; - if(dir & SSH_WRITE_PENDING) - conn->waitfor |= KEEP_SEND; + waitfor = sshc->waitfor ? sshc->waitfor : data->req.io_flags; + if(waitfor) { + int flags = 0; + if(waitfor & REQ_IO_RECV) + flags |= CURL_POLL_IN; + if(waitfor & REQ_IO_SEND) + flags |= CURL_POLL_OUT; + DEBUGASSERT(flags); + CURL_TRC_SSH(data, "pollset, flags=%x", flags); + return Curl_pollset_change(data, ps, sock, flags, 0); } + /* While we still have a session, we listen incoming data. */ + if(sshc->ssh_session) + return Curl_pollset_change(data, ps, sock, CURL_POLL_IN, 0); + return CURLE_OK; } /* called repeatedly until done from multi.c */ @@ -2437,7 +2425,7 @@ static CURLcode myssh_multi_statemach(struct Curl_easy *data, if(!sshc || !sshp) return CURLE_FAILED_INIT; - result = myssh_statemach_act(data, sshc, sshp, &block); + result = myssh_statemachine(data, sshc, sshp, &block); *done = (sshc->state == SSH_STOP); myssh_block2waitfor(conn, sshc, block); @@ -2454,23 +2442,19 @@ static CURLcode myssh_block_statemach(struct Curl_easy *data, while((sshc->state != SSH_STOP) && !result) { bool block; - timediff_t left = 1000; - struct curltime now = curlx_now(); + timediff_t left_ms = 1000; - result = myssh_statemach_act(data, sshc, sshp, &block); + result = myssh_statemachine(data, sshc, sshp, &block); if(result) break; if(!disconnect) { - if(Curl_pgrsUpdate(data)) - return CURLE_ABORTED_BY_CALLBACK; - - result = Curl_speedcheck(data, now); + result = Curl_pgrsCheck(data); if(result) break; - left = Curl_timeleft(data, NULL, FALSE); - if(left < 0) { + left_ms = Curl_timeleft_ms(data); + if(left_ms < 0) { failf(data, "Operation timed out"); return CURLE_OPERATION_TIMEDOUT; } @@ -2479,10 +2463,8 @@ static CURLcode myssh_block_statemach(struct Curl_easy *data, if(block) { curl_socket_t fd_read = conn->sock[FIRSTSOCKET]; /* wait for the socket to become ready */ - (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, - CURL_SOCKET_BAD, left > 1000 ? 1000 : left); + (void)SOCKET_READABLE(fd_read, left_ms > 1000 ? 1000 : left_ms); } - } return result; @@ -2493,8 +2475,8 @@ static void myssh_easy_dtor(void *key, size_t klen, void *entry) struct SSHPROTO *sshp = entry; (void)key; (void)klen; - Curl_safefree(sshp->path); - free(sshp); + curlx_safefree(sshp->path); + curlx_free(sshp); } static void myssh_conn_dtor(void *key, size_t klen, void *entry) @@ -2503,7 +2485,7 @@ static void myssh_conn_dtor(void *key, size_t klen, void *entry) (void)key; (void)klen; sshc_cleanup(sshc); - free(sshc); + curlx_free(sshc); } /* @@ -2515,7 +2497,7 @@ static CURLcode myssh_setup_connection(struct Curl_easy *data, struct SSHPROTO *sshp; struct ssh_conn *sshc; - sshc = calloc(1, sizeof(*sshc)); + sshc = curlx_calloc(1, sizeof(*sshc)); if(!sshc) return CURLE_OUT_OF_MEMORY; @@ -2524,7 +2506,7 @@ static CURLcode myssh_setup_connection(struct Curl_easy *data, if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, myssh_conn_dtor)) return CURLE_OUT_OF_MEMORY; - sshp = calloc(1, sizeof(*sshp)); + sshp = curlx_calloc(1, sizeof(*sshp)); if(!sshp || Curl_meta_set(data, CURL_META_SSH_EASY, sshp, myssh_easy_dtor)) return CURLE_OUT_OF_MEMORY; @@ -2551,11 +2533,8 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) if(!sshc || !ssh) return CURLE_FAILED_INIT; - /* We default to persistent connections. We set this already in this connect - function to make the reuse checks properly be able to check this bit. */ - connkeep(conn, "SSH default"); - - if(conn->handler->protocol & CURLPROTO_SCP) { + CURL_TRC_SSH(data, "myssh_connect"); + if(conn->scheme->protocol & CURLPROTO_SCP) { conn->recv[FIRSTSOCKET] = scp_recv; conn->send[FIRSTSOCKET] = scp_send; } @@ -2570,13 +2549,9 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) return CURLE_FAILED_INIT; } - if(conn->bits.ipv6_ip) { - char ipv6[MAX_IPADR_LEN]; - msnprintf(ipv6, sizeof(ipv6), "[%s]", conn->host.name); - rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, ipv6); - } - else - rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, conn->host.name); + rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, + (data->state.up.hostname[0] == '[') ? + data->state.up.hostname : conn->host.name); if(rc != SSH_OK) { failf(data, "Could not set remote host"); @@ -2589,6 +2564,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) /* ignore */ } + CURL_TRC_SSH(data, "myssh_connect, set socket=%" FMT_SOCKET_T, sock); rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_FD, &sock); if(rc != SSH_OK) { failf(data, "Could not set socket"); @@ -2608,6 +2584,11 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) infof(data, "Known hosts: %s", data->set.str[STRING_SSH_KNOWNHOSTS]); rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_KNOWNHOSTS, data->set.str[STRING_SSH_KNOWNHOSTS]); + if(rc == SSH_OK) + /* libssh has two separate options for this. Set both to the same file + to avoid surprises */ + rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, + data->set.str[STRING_SSH_KNOWNHOSTS]); if(rc != SSH_OK) { failf(data, "Could not set known hosts file path"); return CURLE_FAILED_INIT; @@ -2662,7 +2643,7 @@ static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done) result = myssh_multi_statemach(data, dophase_done); if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); + CURL_TRC_SSH(data, "DO phase is complete"); } return result; } @@ -2676,14 +2657,13 @@ static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done) * the options previously setup. */ -static -CURLcode scp_perform(struct Curl_easy *data, - bool *connected, bool *dophase_done) +static CURLcode scp_perform(struct Curl_easy *data, + bool *connected, bool *dophase_done) { CURLcode result = CURLE_OK; struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN); - DEBUGF(infof(data, "DO phase starts")); + CURL_TRC_SSH(data, "DO phase starts"); *dophase_done = FALSE; /* not done yet */ if(!sshc) @@ -2697,98 +2677,12 @@ CURLcode scp_perform(struct Curl_easy *data, *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); + CURL_TRC_SSH(data, "DO phase is complete"); } 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 dir attempt state - variable */ - - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - 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 */ @@ -2801,11 +2695,9 @@ static CURLcode scp_disconnect(struct Curl_easy *data, struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY); (void)dead_connection; - if(sshc && sshc->ssh_session && sshp) { + if(sshc && sshc->ssh_session) { /* only if there is a session still around to use! */ - myssh_to(data, sshc, SSH_SESSION_DISCONNECT); - result = myssh_block_statemach(data, sshc, sshp, TRUE); } @@ -2831,11 +2723,10 @@ static CURLcode myssh_done(struct Curl_easy *data, if(Curl_pgrsDone(data)) return CURLE_ABORTED_BY_CALLBACK; - data->req.keepon = 0; /* clear all bits */ + CURL_REQ_CLEAR_IO(data); return result; } - static CURLcode scp_done(struct Curl_easy *data, CURLcode status, bool premature) { @@ -2851,7 +2742,7 @@ static CURLcode scp_done(struct Curl_easy *data, CURLcode status, } static CURLcode scp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, bool eos, + const uint8_t *mem, size_t len, bool eos, size_t *pnwritten) { int rc; @@ -2869,7 +2760,7 @@ static CURLcode scp_send(struct Curl_easy *data, int sockindex, #if 0 /* The following code is misleading, mostly added as wishful thinking - * that libssh at some point will implement non-blocking ssh_scp_write/read. + * that libssh at some point would implement non-blocking ssh_scp_write/read. * Currently rc can only be number of bytes read or SSH_ERROR. */ myssh_block2waitfor(conn, sshc, (rc == SSH_AGAIN)); @@ -2889,7 +2780,7 @@ static CURLcode scp_recv(struct Curl_easy *data, int sockindex, { struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - ssize_t nread; + int nread; (void)sockindex; /* we only support SCP on the fixed known primary socket */ *pnread = 0; @@ -2899,10 +2790,11 @@ static CURLcode scp_recv(struct Curl_easy *data, int sockindex, /* libssh returns int */ nread = ssh_scp_read(sshc->scp_session, mem, len); - + if(nread == SSH_ERROR) + return CURLE_SSH; #if 0 /* The following code is misleading, mostly added as wishful thinking - * that libssh at some point will implement non-blocking ssh_scp_write/read. + * that libssh at some point would implement non-blocking ssh_scp_write/read. * Currently rc can only be SSH_OK or SSH_ERROR. */ myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN)); @@ -2926,15 +2818,14 @@ static CURLcode scp_recv(struct Curl_easy *data, int sockindex, * the options previously setup. */ -static -CURLcode sftp_perform(struct Curl_easy *data, - bool *connected, - bool *dophase_done) +static CURLcode sftp_perform(struct Curl_easy *data, + bool *connected, + bool *dophase_done) { struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN); CURLcode result = CURLE_OK; - DEBUGF(infof(data, "DO phase starts")); + CURL_TRC_SSH(data, "DO phase starts"); *dophase_done = FALSE; /* not done yet */ if(!sshc) @@ -2949,7 +2840,7 @@ CURLcode sftp_perform(struct Curl_easy *data, *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); + CURL_TRC_SSH(data, "DO phase is complete"); } return result; @@ -2961,7 +2852,7 @@ static CURLcode sftp_doing(struct Curl_easy *data, { CURLcode result = myssh_multi_statemach(data, dophase_done); if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); + CURL_TRC_SSH(data, "DO phase is complete"); } return result; } @@ -2978,15 +2869,15 @@ static CURLcode sftp_disconnect(struct Curl_easy *data, CURLcode result = CURLE_OK; (void)dead_connection; - DEBUGF(infof(data, "SSH DISCONNECT starts now")); + CURL_TRC_SSH(data, "DISCONNECT starts now"); - if(sshc && sshc->ssh_session && sshp) { + if(sshc && sshc->ssh_session) { /* only if there is a session still around to use! */ myssh_to(data, sshc, SSH_SFTP_SHUTDOWN); result = myssh_block_statemach(data, sshc, sshp, TRUE); } - DEBUGF(infof(data, "SSH DISCONNECT is done")); + CURL_TRC_SSH(data, "DISCONNECT is done"); return result; } @@ -3011,7 +2902,7 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, /* return number of sent bytes */ static CURLcode sftp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, bool eos, + const uint8_t *mem, size_t len, bool eos, size_t *pnwritten) { struct connectdata *conn = data->conn; @@ -3027,34 +2918,34 @@ static CURLcode sftp_send(struct Curl_easy *data, int sockindex, #if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0) switch(sshc->sftp_send_state) { - case 0: - sftp_file_set_nonblocking(sshc->sftp_file); - if(sftp_aio_begin_write(sshc->sftp_file, mem, len, - &sshc->sftp_send_aio) == SSH_ERROR) { - return CURLE_SEND_ERROR; - } - sshc->sftp_send_state = 1; - FALLTHROUGH(); - case 1: - nwrite = sftp_aio_wait_write(&sshc->sftp_send_aio); - myssh_block2waitfor(conn, sshc, (nwrite == SSH_AGAIN) ? TRUE : FALSE); - if(nwrite == SSH_AGAIN) - return CURLE_AGAIN; - else if(nwrite < 0) - return CURLE_SEND_ERROR; - - /* - * sftp_aio_wait_write() would free sftp_send_aio and - * assign it NULL in all cases except when it returns - * SSH_AGAIN. - */ - - sshc->sftp_send_state = 0; - *pnwritten = (size_t)nwrite; - return CURLE_OK; - default: - /* we never reach here */ + case 0: + sftp_file_set_nonblocking(sshc->sftp_file); + if(sftp_aio_begin_write(sshc->sftp_file, mem, len, + &sshc->sftp_send_aio) == SSH_ERROR) { return CURLE_SEND_ERROR; + } + sshc->sftp_send_state = 1; + FALLTHROUGH(); + case 1: + nwrite = sftp_aio_wait_write(&sshc->sftp_send_aio); + myssh_block2waitfor(conn, sshc, (nwrite == SSH_AGAIN) ? TRUE : FALSE); + if(nwrite == SSH_AGAIN) + return CURLE_AGAIN; + else if(nwrite < 0) + return CURLE_SEND_ERROR; + + /* + * sftp_aio_wait_write() would free sftp_send_aio and + * assign it NULL in all cases except when it returns + * SSH_AGAIN. + */ + + sshc->sftp_send_state = 0; + *pnwritten = (size_t)nwrite; + return CURLE_OK; + default: + /* we never reach here */ + return CURLE_SEND_ERROR; } #else /* @@ -3106,58 +2997,84 @@ static CURLcode sftp_recv(struct Curl_easy *data, int sockindex, return CURLE_FAILED_INIT; switch(sshc->sftp_recv_state) { - case 0: + case 0: #if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0) - if(sftp_aio_begin_read(sshc->sftp_file, len, - &sshc->sftp_recv_aio) == SSH_ERROR) { - return CURLE_RECV_ERROR; - } -#else - sshc->sftp_file_index = - sftp_async_read_begin(sshc->sftp_file, (uint32_t)len); - if(sshc->sftp_file_index < 0) - return CURLE_RECV_ERROR; -#endif - - FALLTHROUGH(); - case 1: - sshc->sftp_recv_state = 1; - -#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0) - nread = sftp_aio_wait_read(&sshc->sftp_recv_aio, mem, len); -#else - nread = sftp_async_read(sshc->sftp_file, mem, (uint32_t)len, - (uint32_t)sshc->sftp_file_index); -#endif - - myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN)); - - if(nread == SSH_AGAIN) - return CURLE_AGAIN; - else if(nread < 0) - return CURLE_RECV_ERROR; - - /* - * sftp_aio_wait_read() would free sftp_recv_aio and - * assign it NULL in all cases except when it returns - * SSH_AGAIN. - */ - - sshc->sftp_recv_state = 0; - *pnread = (size_t)nread; - return CURLE_OK; - - default: - /* we never reach here */ + if(sftp_aio_begin_read(sshc->sftp_file, len, + &sshc->sftp_recv_aio) == SSH_ERROR) { return CURLE_RECV_ERROR; + } +#else + sshc->sftp_file_index = + sftp_async_read_begin(sshc->sftp_file, (uint32_t)len); + if(sshc->sftp_file_index < 0) + return CURLE_RECV_ERROR; +#endif + + FALLTHROUGH(); + case 1: + sshc->sftp_recv_state = 1; + +#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0) + nread = sftp_aio_wait_read(&sshc->sftp_recv_aio, mem, len); +#else + nread = sftp_async_read(sshc->sftp_file, mem, (uint32_t)len, + (uint32_t)sshc->sftp_file_index); +#endif + + myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN)); + + if(nread == SSH_AGAIN) + return CURLE_AGAIN; + else if(nread < 0) + return CURLE_RECV_ERROR; + + /* + * sftp_aio_wait_read() would free sftp_recv_aio and + * assign it NULL in all cases except when it returns + * SSH_AGAIN. + */ + + sshc->sftp_recv_state = 0; + *pnread = (size_t)nread; + return CURLE_OK; + + default: + /* we never reach here */ + return CURLE_RECV_ERROR; } } +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()) { - DEBUGF(fprintf(stderr, "Error: libssh_init failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: libssh_init failed\n")); return CURLE_FAILED_INIT; } return CURLE_OK; @@ -3170,7 +3087,53 @@ void Curl_ssh_cleanup(void) void Curl_ssh_version(char *buffer, size_t buflen) { - (void)msnprintf(buffer, buflen, "libssh/%s", ssh_version(0)); + (void)curl_msnprintf(buffer, buflen, "libssh/%s", ssh_version(0)); } -#endif /* USE_LIBSSH */ +/* + * 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_is_dead */ + 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_is_dead */ + ZERO_NULL, /* attach connection */ + ZERO_NULL, /* follow */ +}; + +#endif /* USE_LIBSSH */ diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 5dfc377ec1..e4d35bbdcf 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -21,18 +21,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -/* #define CURL_LIBSSH2_DEBUG */ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_LIBSSH2 -#include - -#ifdef HAVE_FCNTL_H -#include -#endif +/* #define CURL_LIBSSH2_DEBUG */ #ifdef HAVE_NETINET_IN_H #include @@ -48,149 +41,114 @@ #include #endif -#include -#include "../urldata.h" -#include "../sendf.h" -#include "../hostip.h" -#include "../progress.h" -#include "../transfer.h" -#include "../escape.h" -#include "../http.h" /* for HTTP proxy tunnel stuff */ -#include "ssh.h" -#include "../url.h" -#include "../speedcheck.h" -#include "../getinfo.h" -#include "../strdup.h" -#include "../vtls/vtls.h" -#include "../cfilters.h" -#include "../connect.h" -#include "../parsedate.h" /* for the week day and month names */ -#include "../sockaddr.h" /* required for Curl_sockaddr_storage */ -#include "../multiif.h" -#include "../select.h" -#include "../curlx/warnless.h" -#include "curl_path.h" -#include "../curlx/strparse.h" -#include "../curlx/base64.h" /* for base64 encoding/decoding */ -#include "../curl_sha256.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 "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 */ -/* The last 3 #include files should be in this order */ -#include "../curl_printf.h" -#include "../curl_memory.h" -#include "../memdebug.h" +static const char *sftp_libssh2_strerror(unsigned long err) +{ + switch(err) { + case LIBSSH2_FX_NO_SUCH_FILE: + return "No such file or directory"; -/* 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. - */ + case LIBSSH2_FX_PERMISSION_DENIED: + return "Permission denied"; -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 - | PROTOPT_NOURLQUERY /* flags */ -}; + case LIBSSH2_FX_FAILURE: + return "Operation failed"; + case LIBSSH2_FX_BAD_MESSAGE: + return "Bad message from SFTP server"; -/* - * SFTP protocol handler. - */ + case LIBSSH2_FX_NO_CONNECTION: + return "Not connected to SFTP server"; -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 - | PROTOPT_NOURLQUERY /* flags */ -}; + case LIBSSH2_FX_CONNECTION_LOST: + return "Connection to SFTP server lost"; -static void -kbd_callback(const char *name, int name_len, const char *instruction, - int instruction_len, int num_prompts, - const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, - LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, - void **abstract) + 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, + int num_prompts, + const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, + LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, + void **abstract) { struct Curl_easy *data = (struct Curl_easy *)*abstract; #ifdef CURL_LIBSSH2_DEBUG - fprintf(stderr, "name=%s\n", name); - fprintf(stderr, "name_len=%d\n", name_len); - fprintf(stderr, "instruction=%s\n", instruction); - fprintf(stderr, "instruction_len=%d\n", instruction_len); - fprintf(stderr, "num_prompts=%d\n", num_prompts); + curl_mfprintf(stderr, "name=%s\n", name); + curl_mfprintf(stderr, "name_len=%d\n", name_len); + curl_mfprintf(stderr, "instruction=%s\n", instruction); + curl_mfprintf(stderr, "instruction_len=%d\n", instruction_len); + curl_mfprintf(stderr, "num_prompts=%d\n", num_prompts); #else (void)name; (void)name_len; (void)instruction; (void)instruction_len; -#endif /* CURL_LIBSSH2_DEBUG */ +#endif /* CURL_LIBSSH2_DEBUG */ if(num_prompts == 1) { struct connectdata *conn = data->conn; - responses[0].text = strdup(conn->passwd); + responses[0].text = curlx_strdup(conn->passwd); responses[0].length = responses[0].text == NULL ? 0 : curlx_uztoui(strlen(conn->passwd)); } @@ -200,30 +158,30 @@ kbd_callback(const char *name, int name_len, const char *instruction, static CURLcode sftp_libssh2_error_to_CURLE(unsigned long err) { switch(err) { - case LIBSSH2_FX_OK: - return CURLE_OK; + case LIBSSH2_FX_OK: + return CURLE_OK; - case LIBSSH2_FX_NO_SUCH_FILE: - case LIBSSH2_FX_NO_SUCH_PATH: - return CURLE_REMOTE_FILE_NOT_FOUND; + case LIBSSH2_FX_NO_SUCH_FILE: + case LIBSSH2_FX_NO_SUCH_PATH: + return CURLE_REMOTE_FILE_NOT_FOUND; - case LIBSSH2_FX_PERMISSION_DENIED: - case LIBSSH2_FX_WRITE_PROTECT: - case LIBSSH2_FX_LOCK_CONFlICT: - return CURLE_REMOTE_ACCESS_DENIED; + case LIBSSH2_FX_PERMISSION_DENIED: + case LIBSSH2_FX_WRITE_PROTECT: + case LIBSSH2_FX_LOCK_CONFlICT: + return CURLE_REMOTE_ACCESS_DENIED; - case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: - case LIBSSH2_FX_QUOTA_EXCEEDED: - return CURLE_REMOTE_DISK_FULL; + case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: + case LIBSSH2_FX_QUOTA_EXCEEDED: + return CURLE_REMOTE_DISK_FULL; - case LIBSSH2_FX_FILE_ALREADY_EXISTS: - return CURLE_REMOTE_FILE_EXISTS; + case LIBSSH2_FX_FILE_ALREADY_EXISTS: + return CURLE_REMOTE_FILE_EXISTS; - case LIBSSH2_FX_DIR_NOT_EMPTY: - return CURLE_QUOTE_ERROR; + case LIBSSH2_FX_DIR_NOT_EMPTY: + return CURLE_QUOTE_ERROR; - default: - break; + default: + break; } return CURLE_SSH; @@ -232,39 +190,39 @@ static CURLcode sftp_libssh2_error_to_CURLE(unsigned long err) static CURLcode libssh2_session_error_to_CURLE(int err) { switch(err) { - /* Ordered by order of appearance in libssh2.h */ - case LIBSSH2_ERROR_NONE: - return CURLE_OK; + /* Ordered by order of appearance in libssh2.h */ + case LIBSSH2_ERROR_NONE: + return CURLE_OK; - /* This is the error returned by libssh2_scp_recv2 - * on unknown file */ - case LIBSSH2_ERROR_SCP_PROTOCOL: - return CURLE_REMOTE_FILE_NOT_FOUND; + /* This is the error returned by libssh2_scp_recv2 + * on unknown file */ + case LIBSSH2_ERROR_SCP_PROTOCOL: + return CURLE_REMOTE_FILE_NOT_FOUND; - case LIBSSH2_ERROR_SOCKET_NONE: - return CURLE_COULDNT_CONNECT; + case LIBSSH2_ERROR_SOCKET_NONE: + return CURLE_COULDNT_CONNECT; - case LIBSSH2_ERROR_ALLOC: - return CURLE_OUT_OF_MEMORY; + case LIBSSH2_ERROR_ALLOC: + return CURLE_OUT_OF_MEMORY; - case LIBSSH2_ERROR_SOCKET_SEND: - return CURLE_SEND_ERROR; + case LIBSSH2_ERROR_SOCKET_SEND: + return CURLE_SEND_ERROR; - case LIBSSH2_ERROR_HOSTKEY_INIT: - case LIBSSH2_ERROR_HOSTKEY_SIGN: - case LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED: - case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED: - return CURLE_PEER_FAILED_VERIFICATION; + case LIBSSH2_ERROR_HOSTKEY_INIT: + case LIBSSH2_ERROR_HOSTKEY_SIGN: + case LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED: + case LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED: + return CURLE_PEER_FAILED_VERIFICATION; - case LIBSSH2_ERROR_PASSWORD_EXPIRED: - return CURLE_LOGIN_DENIED; + case LIBSSH2_ERROR_PASSWORD_EXPIRED: + return CURLE_LOGIN_DENIED; - case LIBSSH2_ERROR_SOCKET_TIMEOUT: - case LIBSSH2_ERROR_TIMEOUT: - return CURLE_OPERATION_TIMEDOUT; + case LIBSSH2_ERROR_SOCKET_TIMEOUT: + case LIBSSH2_ERROR_TIMEOUT: + return CURLE_OPERATION_TIMEDOUT; - case LIBSSH2_ERROR_EAGAIN: - return CURLE_AGAIN; + case LIBSSH2_ERROR_EAGAIN: + return CURLE_AGAIN; } return CURLE_SSH; @@ -293,91 +251,6 @@ static LIBSSH2_FREE_FUNC(my_libssh2_free) Curl_cfree(ptr); } -/* - * SSH State machine related code - */ -/* This is the ONLY way to change SSH state! */ -static void myssh_state(struct Curl_easy *data, - struct ssh_conn *sshc, - sshstate nowstate) -{ -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ - 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); - - if(sshc->state != nowstate) { - infof(data, "SFTP %p state change from %s to %s", - (void *)sshc, names[sshc->state], names[nowstate]); - } -#endif - (void)data; - sshc->state = nowstate; -} - static int sshkeycallback(CURL *easy, const struct curl_khkey *knownkey, /* known */ const struct curl_khkey *foundkey, /* found */ @@ -464,26 +337,18 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, case LIBSSH2_HOSTKEY_TYPE_DSS: keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS; break; -#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256 case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256; break; -#endif -#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_384 case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384; break; -#endif -#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_521 case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521; break; -#endif -#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519 case LIBSSH2_HOSTKEY_TYPE_ED25519: keybit = LIBSSH2_KNOWNHOST_KEY_ED25519; break; -#endif default: infof(data, "unsupported key type, cannot check knownhosts"); keybit = 0; @@ -540,9 +405,9 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, rc = CURLKHSTAT_REJECT; switch(rc) { - default: /* unknown return codes will equal reject */ + default: /* unknown return codes is the same as 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 */ @@ -606,41 +471,28 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, size_t pub_pos = 0; size_t b64_pos = 0; -#ifdef LIBSSH2_HOSTKEY_HASH_SHA256 /* The fingerprint points to static storage (!), do not free() it. */ fingerprint = libssh2_hostkey_hash(sshc->ssh_session, LIBSSH2_HOSTKEY_HASH_SHA256); -#else - const char *hostkey; - size_t len = 0; - unsigned char hash[32]; - - hostkey = libssh2_session_hostkey(sshc->ssh_session, &len, NULL); - if(hostkey) { - if(!Curl_sha256it(hash, (const unsigned char *) hostkey, len)) - fingerprint = (char *) hash; - } -#endif - if(!fingerprint) { failf(data, - "Denied establishing ssh session: sha256 fingerprint " + "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; } /* The length of fingerprint is 32 bytes for SHA256. * See libssh2_hostkey_hash documentation. */ - if(curlx_base64_encode(fingerprint, 32, &fingerprint_b64, + 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); + failf(data, "SHA256 fingerprint could not be encoded"); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } @@ -657,20 +509,20 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, b64_pos++; } - /* Before we authenticate we check the hostkey's sha256 fingerprint + /* Before we authenticate we check the hostkey's SHA256 fingerprint * against a known fingerprint, if available. */ if((pub_pos != b64_pos) || strncmp(fingerprint_b64, pubkey_sha256, pub_pos)) { failf(data, - "Denied establishing ssh session: mismatch sha256 fingerprint. " + "Denied establishing ssh session: mismatch SHA256 fingerprint. " "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256); - free(fingerprint_b64); - myssh_state(data, sshc, SSH_SESSION_FREE); + curlx_free(fingerprint_b64); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } - free(fingerprint_b64); + curlx_free(fingerprint_b64); infof(data, "SHA256 checksum match"); } @@ -686,26 +538,27 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, /* The fingerprint points to static storage (!), do not free() it. */ int i; for(i = 0; i < 16; i++) { - msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]); + curl_msnprintf(&md5buffer[i * 2], 3, "%02x", + (unsigned char)fingerprint[i]); } infof(data, "SSH MD5 fingerprint: %s", md5buffer); } - /* This does NOT verify the length of 'pubkey_md5' separately, which will - make the comparison below fail unless it is exactly 32 characters */ + /* This does NOT verify the length of 'pubkey_md5' separately, which + makes the comparison below fail unless it is exactly 32 characters */ if(!fingerprint || !curl_strequal(md5buffer, pubkey_md5)) { if(fingerprint) { failf(data, - "Denied establishing ssh session: mismatch md5 fingerprint. " + "Denied establishing ssh session: mismatch MD5 fingerprint. " "Remote %s is not equal to %s", md5buffer, pubkey_md5); } else { failf(data, - "Denied establishing ssh session: md5 fingerprint " + "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"); @@ -725,13 +578,13 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, rc = data->set.ssh_hostkeyfunc(data->set.ssh_hostkeyfunc_userp, (int)keytype, remotekey, keylen); Curl_set_in_callback(data, FALSE); - if(rc!= CURLKHMATCH_OK) { - myssh_state(data, sshc, SSH_SESSION_FREE); + if(rc != CURLKHMATCH_OK) { + 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; @@ -747,7 +600,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, } /* - * ssh_force_knownhost_key_type() will check the known hosts file and try to + * ssh_force_knownhost_key_type() checks the known hosts file and try to * force a specific public key type from the server if an entry is found. */ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, @@ -755,58 +608,42 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, { CURLcode result = CURLE_OK; -#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 - static const char * const hostkey_method_ssh_ed25519 - = "ssh-ed25519"; -#endif -#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 - static const char * const hostkey_method_ssh_ecdsa_521 - = "ecdsa-sha2-nistp521"; -#endif -#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 - static const char * const hostkey_method_ssh_ecdsa_384 - = "ecdsa-sha2-nistp384"; -#endif -#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 - static const char * const hostkey_method_ssh_ecdsa_256 - = "ecdsa-sha2-nistp256"; -#endif - static const char * const hostkey_method_ssh_rsa - = "ssh-rsa"; - static const char * const hostkey_method_ssh_rsa_all - = "rsa-sha2-256,rsa-sha2-512,ssh-rsa"; - static const char * const hostkey_method_ssh_dss - = "ssh-dss"; - - const char *hostkey_method = NULL; - struct connectdata *conn = data->conn; - struct libssh2_knownhost* store = NULL; - const char *kh_name_end = NULL; - size_t kh_name_size = 0; - int port = 0; + static const char hostkey_method_ssh_ed25519[] = "ssh-ed25519"; + static const char hostkey_method_ssh_ecdsa_521[] = "ecdsa-sha2-nistp521"; + static const char hostkey_method_ssh_ecdsa_384[] = "ecdsa-sha2-nistp384"; + static const char hostkey_method_ssh_ecdsa_256[] = "ecdsa-sha2-nistp256"; + static const char hostkey_method_ssh_rsa_all[] = + "rsa-sha2-256,rsa-sha2-512,ssh-rsa"; + static const char hostkey_method_ssh_dss[] = "ssh-dss"; bool found = FALSE; if(sshc->kh && !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] && !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]) { + struct libssh2_knownhost *store = NULL; + struct connectdata *conn = data->conn; /* lets try to find our host in the known hosts file */ while(!libssh2_knownhost_get(sshc->kh, &store, store)) { - /* For non-standard ports, the name will be enclosed in */ + /* For non-standard ports, the name is enclosed in */ /* square brackets, followed by a colon and the port */ if(store) { if(store->name) { if(store->name[0] == '[') { - kh_name_end = strstr(store->name, "]:"); + curl_off_t port; + size_t kh_name_size = 0; + const char *p; + const char *kh_name_end = strstr(store->name, "]:"); if(!kh_name_end) { infof(data, "Invalid host pattern %s in %s", store->name, data->set.str[STRING_SSH_KNOWNHOSTS]); continue; } - port = atoi(kh_name_end + 2); - if(kh_name_end && (port == conn->remote_port)) { + p = kh_name_end + 2; /* start of port number */ + if(!curlx_str_number(&p, &port, 0xffff) && + (kh_name_end && (port == conn->remote_port))) { kh_name_size = strlen(store->name) - 1 - strlen(kh_name_end); if(strncmp(store->name + 1, - conn->host.name, kh_name_size) == 0) { + conn->host.name, kh_name_size) == 0) { found = TRUE; break; } @@ -826,39 +663,25 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, if(found) { int rc; + const char *hostkey_method = NULL; infof(data, "Found host %s in %s", conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) { -#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 case LIBSSH2_KNOWNHOST_KEY_ED25519: hostkey_method = hostkey_method_ssh_ed25519; break; -#endif -#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_521 case LIBSSH2_KNOWNHOST_KEY_ECDSA_521: hostkey_method = hostkey_method_ssh_ecdsa_521; break; -#endif -#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_384 case LIBSSH2_KNOWNHOST_KEY_ECDSA_384: hostkey_method = hostkey_method_ssh_ecdsa_384; break; -#endif -#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 case LIBSSH2_KNOWNHOST_KEY_ECDSA_256: hostkey_method = hostkey_method_ssh_ecdsa_256; break; -#endif case LIBSSH2_KNOWNHOST_KEY_SSHRSA: - if(libssh2_version(0x010900)) - /* since 1.9.0 libssh2_session_method_pref() works as expected */ - hostkey_method = hostkey_method_ssh_rsa_all; - else - /* old libssh2 which cannot correctly remove unsupported methods due - * to bug in src/kex.c or does not support the new methods anyways. - */ - hostkey_method = hostkey_method_ssh_rsa; + hostkey_method = hostkey_method_ssh_rsa_all; break; case LIBSSH2_KNOWNHOST_KEY_SSHDSS: hostkey_method = hostkey_method_ssh_dss; @@ -876,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: %s", 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); } } @@ -892,6 +715,15 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, return result; } +static CURLcode quote_error(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + failf(data, "Suspicious data after the command line"); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + return CURLE_QUOTE_ERROR; +} + static CURLcode sftp_quote(struct Curl_easy *data, struct ssh_conn *sshc, struct SSHPROTO *sshp) @@ -905,12 +737,12 @@ 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 - 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 + can, the command is allowed to fail without it causing any + aborts or cancels etc. It causes libcurl to act as if the command is successful, whatever the server responds. */ if(cmd[0] == '*') { @@ -920,19 +752,20 @@ static CURLcode sftp_quote(struct Curl_easy *data, if(curl_strequal("pwd", cmd)) { /* output debug output if that is requested */ - char *tmp = aprintf("257 \"%s\" is current directory.\n", sshp->path); + char *tmp = curl_maprintf("257 \"%s\" is current directory.\n", + sshp->path); if(!tmp) return CURLE_OUT_OF_MEMORY; Curl_debug(data, CURLINFO_HEADER_OUT, "PWD\n", 4); Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp)); - /* this sends an FTP-like "header" to the header callback so that the - current directory can be read very similar to how it is read when + /* this sends an FTP-like "header" to the header callback so that + the current directory can be read similar to how it is read when using ordinary FTP. */ result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); - free(tmp); + curlx_free(tmp); if(!result) - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); return result; } @@ -975,11 +808,14 @@ static CURLcode sftp_quote(struct Curl_easy *data, if(result) { if(result != CURLE_OUT_OF_MEMORY) failf(data, "Syntax error in %s: Bad second parameter", cmd); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); return result; } + if(*cp) + 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) || @@ -991,15 +827,19 @@ static CURLcode sftp_quote(struct Curl_easy *data, if(result) { if(result != CURLE_OUT_OF_MEMORY) failf(data, "Syntax error in ln/symlink: Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); return result; } - myssh_state(data, sshc, SSH_SFTP_QUOTE_SYMLINK); + if(*cp) + return quote_error(data, sshc); + myssh_to(data, sshc, SSH_SFTP_QUOTE_SYMLINK); return result; } else if(!strncmp(cmd, "mkdir ", 6)) { - /* create dir */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_MKDIR); + if(*cp) + return quote_error(data, sshc); + /* create directory */ + myssh_to(data, sshc, SSH_SFTP_QUOTE_MKDIR); return result; } else if(!strncmp(cmd, "rename ", 7)) { @@ -1010,44 +850,51 @@ static CURLcode sftp_quote(struct Curl_easy *data, if(result) { if(result != CURLE_OUT_OF_MEMORY) failf(data, "Syntax error in rename: Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); return result; } - myssh_state(data, sshc, SSH_SFTP_QUOTE_RENAME); + if(*cp) + return quote_error(data, sshc); + myssh_to(data, sshc, SSH_SFTP_QUOTE_RENAME); return result; } else if(!strncmp(cmd, "rmdir ", 6)) { - /* delete dir */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_RMDIR); + if(*cp) + return quote_error(data, sshc); + /* delete directory */ + myssh_to(data, sshc, SSH_SFTP_QUOTE_RMDIR); return result; } else if(!strncmp(cmd, "rm ", 3)) { - myssh_state(data, sshc, SSH_SFTP_QUOTE_UNLINK); + if(*cp) + return quote_error(data, sshc); + myssh_to(data, sshc, SSH_SFTP_QUOTE_UNLINK); return result; } else if(!strncmp(cmd, "statvfs ", 8)) { - myssh_state(data, sshc, SSH_SFTP_QUOTE_STATVFS); + if(*cp) + return quote_error(data, sshc); + myssh_to(data, sshc, SSH_SFTP_QUOTE_STATVFS); return result; } failf(data, "Unknown SFTP command"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); return CURLE_QUOTE_ERROR; } -static CURLcode -sftp_upload_init(struct Curl_easy *data, - struct ssh_conn *sshc, - struct SSHPROTO *sshp, - bool *blockp) +static CURLcode sftp_upload_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *blockp) { unsigned long flags; /* * NOTE!!! libssh2 requires that the destination path is a full path * that includes the destination file and name OR ends in a "/" - * If this is not done the destination file will be named the + * If this is not done the destination file is named the * same name as the last directory in the path. */ @@ -1075,15 +922,21 @@ sftp_upload_init(struct Curl_easy *data, } } - if(data->set.remote_append) - /* Try to open for append, but create if nonexisting */ - flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND; - else if(data->state.resume_from > 0) - /* If we have restart position then open for append */ - flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND; - else + if(data->set.remote_append) { + /* True append mode: create if nonexisting */ + flags = LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_APPEND; + } + else if(data->state.resume_from > 0) { + /* + * Resume MUST NOT use APPEND; some servers force writes to EOF when + * APPEND is set, ignoring a prior seek(). + */ + flags = LIBSSH2_FXF_WRITE; + } + else { /* Clear file before writing (normal behavior) */ - flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC; + flags = LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC; + } sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session, sshp->path, @@ -1109,22 +962,22 @@ 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 @@ -1141,8 +994,8 @@ sftp_upload_init(struct Curl_easy *data, } /* If we have a restart point then we need to seek to the correct - position. */ - if(data->state.resume_from > 0) { + Skip if in explicit remote append mode. */ + if(data->state.resume_from > 0 && !data->set.remote_append) { int seekerr = CURL_SEEKFUNC_OK; /* Let's read off the proper amount of bytes from the input. */ if(data->set.seek_func) { @@ -1161,7 +1014,7 @@ sftp_upload_init(struct Curl_easy *data, } /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ do { - char scratch[4*1024]; + char scratch[4 * 1024]; size_t readthisamountnow = (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ? @@ -1201,24 +1054,17 @@ sftp_upload_init(struct Curl_easy *data, /* upload data */ Curl_xfer_setup_send(data, FIRSTSOCKET); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ 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 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; } -/* make sure that this does not collide with an actual libssh2 error code */ -#define ERROR_LIBBSH2 1 - static CURLcode ssh_state_pkey_init(struct Curl_easy *data, struct ssh_conn *sshc) { @@ -1234,42 +1080,45 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, sshc->rsa_pub = sshc->rsa = NULL; - if(data->set.str[STRING_SSH_PRIVATE_KEY]) - sshc->rsa = strdup(data->set.str[STRING_SSH_PRIVATE_KEY]); + if(data->set.str[STRING_SSH_PRIVATE_KEY]) { + sshc->rsa = curlx_strdup(data->set.str[STRING_SSH_PRIVATE_KEY]); + if(!sshc->rsa) + out_of_memory = TRUE; + } else { /* 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) { /* Try ~/.ssh first. */ - sshc->rsa = aprintf("%s/.ssh/id_rsa", home); + sshc->rsa = curl_maprintf("%s/.ssh/id_rsa", home); if(!sshc->rsa) out_of_memory = TRUE; - else if(stat(sshc->rsa, &sbuf)) { - free(sshc->rsa); - sshc->rsa = aprintf("%s/.ssh/id_dsa", home); + else if(curlx_stat(sshc->rsa, &sbuf)) { + curlx_free(sshc->rsa); + sshc->rsa = curl_maprintf("%s/.ssh/id_dsa", home); if(!sshc->rsa) out_of_memory = TRUE; - else if(stat(sshc->rsa, &sbuf)) { - Curl_safefree(sshc->rsa); + else if(curlx_stat(sshc->rsa, &sbuf)) { + curlx_safefree(sshc->rsa); } } - free(home); + curlx_free(home); } if(!out_of_memory && !sshc->rsa) { /* Nothing found; try the current dir. */ - sshc->rsa = strdup("id_rsa"); - if(sshc->rsa && stat(sshc->rsa, &sbuf)) { - free(sshc->rsa); - sshc->rsa = strdup("id_dsa"); - if(sshc->rsa && stat(sshc->rsa, &sbuf)) { - free(sshc->rsa); + sshc->rsa = curlx_strdup("id_rsa"); + if(sshc->rsa && curlx_stat(sshc->rsa, &sbuf)) { + curlx_free(sshc->rsa); + sshc->rsa = curlx_strdup("id_dsa"); + if(sshc->rsa && curlx_stat(sshc->rsa, &sbuf)) { + curlx_free(sshc->rsa); /* Out of guesses. Set to the empty string to avoid * surprising info messages. */ - sshc->rsa = strdup(""); + sshc->rsa = curlx_strdup(""); } } } @@ -1278,20 +1127,20 @@ 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(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]) { - sshc->rsa_pub = strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); + 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; } if(out_of_memory || !sshc->rsa) { - Curl_safefree(sshc->rsa); - Curl_safefree(sshc->rsa_pub); - myssh_state(data, sshc, SSH_SESSION_FREE); + curlx_safefree(sshc->rsa); + curlx_safefree(sshc->rsa_pub); + myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_OUT_OF_MEMORY; } @@ -1303,26 +1152,25 @@ 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; + return CURLE_OK; } -static CURLcode -sftp_quote_stat(struct Curl_easy *data, - struct ssh_conn *sshc, - struct SSHPROTO *sshp, - bool *blockp) +static CURLcode sftp_quote_stat(struct Curl_easy *data, + struct ssh_conn *sshc, + 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 - 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 + can, the command is allowed to fail without it causing any aborts or + cancels etc. It causes libcurl to act as if the command is successful, whatever the server responds. */ if(cmd[0] == '*') { @@ -1355,11 +1203,11 @@ sftp_quote_stat(struct Curl_easy *data, if(!strncmp(cmd, "chgrp", 5)) { const char *p = sshc->quote_path1; curl_off_t gid; - (void)curlx_str_number(&p, &gid, ULONG_MAX); - sshp->quote_attrs.gid = (unsigned long)gid; - sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; - if(sshp->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) && - !sshc->acceptfail) { + if(!curlx_str_number(&p, &gid, ULONG_MAX)) { + sshp->quote_attrs.gid = (unsigned long)gid; + sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; + } + else if(!sshc->acceptfail) { failf(data, "Syntax error: chgrp gid not a number"); goto fail; } @@ -1379,11 +1227,11 @@ sftp_quote_stat(struct Curl_easy *data, else if(!strncmp(cmd, "chown", 5)) { const char *p = sshc->quote_path1; curl_off_t uid; - (void)curlx_str_number(&p, &uid, ULONG_MAX); - sshp->quote_attrs.uid = (unsigned long)uid; - sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; - if(sshp->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) && - !sshc->acceptfail) { + if(!curlx_str_number(&p, &uid, ULONG_MAX)) { + sshp->quote_attrs.uid = (unsigned long)uid; + sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; + } + else if(!sshc->acceptfail) { failf(data, "Syntax error: chown uid not a number"); goto fail; } @@ -1415,19 +1263,18 @@ 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); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); return CURLE_QUOTE_ERROR; } -static CURLcode -sftp_download_stat(struct Curl_easy *data, - struct ssh_conn *sshc, - struct SSHPROTO *sshp, - bool *blockp) +static CURLcode sftp_download_stat(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *blockp) { LIBSSH2_SFTP_ATTRIBUTES attrs; int rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, @@ -1442,13 +1289,14 @@ 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 */ data->req.size = -1; data->req.maxdownload = -1; Curl_pgrsSetDownloadSize(data, -1); + attrs.filesize = 0; /* might be uninitialized but is read below */ } else { curl_off_t size = attrs.filesize; @@ -1458,42 +1306,11 @@ sftp_download_stat(struct Curl_easy *data, return CURLE_BAD_DOWNLOAD_RESUME; } if(data->state.use_range) { - curl_off_t from, to; - const char *p = data->state.range; - int to_t, from_t; - - from_t = curlx_str_number(&p, &from, CURL_OFF_T_MAX); - if(from_t == STRE_OVERFLOW) - return CURLE_RANGE_ERROR; - curlx_str_passblanks(&p); - (void)curlx_str_single(&p, '-'); - - to_t = curlx_str_numblanks(&p, &to); - if(to_t == STRE_OVERFLOW) - return CURLE_RANGE_ERROR; - if((to_t == STRE_NO_NUM) /* no "to" value given */ - || (to >= size)) { - to = size - 1; - } - if(from_t) { - /* from is relative to end of file */ - from = size - to; - to = size - 1; - } - if(from > size) { - failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" - FMT_OFF_T ")", from, (curl_off_t)attrs.filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - if(from > to) { - from = to; - size = 0; - } - else { - if((to - from) == CURL_OFF_T_MAX) - return CURLE_RANGE_ERROR; - size = to - from + 1; - } + curl_off_t from; + CURLcode result = Curl_ssh_range(data, data->state.range, size, + &from, &size); + if(result) + return result; libssh2_sftp_seek64(sshc->sftp_handle, (libssh2_uint64_t)from); } @@ -1526,8 +1343,7 @@ sftp_download_stat(struct Curl_easy *data, /* Now store the number of bytes we are expected to download */ data->req.size = attrs.filesize - data->state.resume_from; data->req.maxdownload = attrs.filesize - data->state.resume_from; - Curl_pgrsSetDownloadSize(data, - attrs.filesize - data->state.resume_from); + Curl_pgrsSetDownloadSize(data, attrs.filesize - data->state.resume_from); libssh2_sftp_seek64(sshc->sftp_handle, (libssh2_uint64_t)data->state.resume_from); } @@ -1537,15 +1353,15 @@ 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); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->send_idx = 0; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -1565,7 +1381,7 @@ static CURLcode sftp_readdir(struct Curl_easy *data, return result; } if(rc > 0) { - size_t readdir_len = (size_t) rc; + size_t readdir_len = (size_t)rc; sshp->readdir_filename[readdir_len] = '\0'; if(data->set.list_only) { @@ -1586,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); @@ -1604,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; } @@ -1622,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; } @@ -1642,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; } @@ -1660,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; } @@ -1673,9 +1489,9 @@ static CURLcode ssh_state_authlist(struct Curl_easy *data, * must never change it later. Thus, always specify the correct username * here, even though the libssh2 docs kind of indicate that it should be * possible to get a 'generic' list (not user-specific) of authentication - * methods, presumably with a blank username. That will not work in my + * methods, presumably with a blank username. That does not work in my * experience. - * So always specify it here. + * Therefore always specify it here. */ struct connectdata *conn = data->conn; sshc->authlist = libssh2_userauth_list(sshc->ssh_session, @@ -1687,20 +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); + 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; } @@ -1720,13 +1535,13 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; - Curl_safefree(sshc->rsa_pub); - Curl_safefree(sshc->rsa); + curlx_safefree(sshc->rsa_pub); + curlx_safefree(sshc->rsa); 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; @@ -1737,11 +1552,10 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, err_msg = unknown; } else { - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); + (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; } @@ -1751,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; } @@ -1775,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; } @@ -1788,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; } @@ -1800,8 +1614,8 @@ static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data, struct ssh_conn *sshc) { int rc = 0; - if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT) - && (strstr(sshc->authlist, "publickey") != NULL)) { + if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT) && + (strstr(sshc->authlist, "publickey") != NULL)) { /* Connect to the ssh-agent */ /* The agent could be shared by a curl thread i believe @@ -1811,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; } } @@ -1821,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; } @@ -1841,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; @@ -1871,8 +1685,9 @@ static CURLcode ssh_state_auth_agent(struct Curl_easy *data, if(rc != LIBSSH2_ERROR_EAGAIN) { /* tried and failed? go to next identity */ sshc->sshagent_prev_identity = sshc->sshagent_identity; + return CURLE_OK; } - return CURLE_OK; + return CURLE_AGAIN; } } @@ -1884,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; } @@ -1895,12 +1710,12 @@ static CURLcode ssh_state_auth_agent(struct Curl_easy *data, static CURLcode ssh_state_auth_key_init(struct Curl_easy *data, struct ssh_conn *sshc) { - if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) - && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) { - myssh_state(data, sshc, SSH_AUTH_KEY); + if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) && + (strstr(sshc->authlist, "keyboard-interactive") != NULL)) { + 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; } @@ -1922,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; @@ -1934,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; } @@ -1948,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; } @@ -1970,13 +1785,12 @@ static CURLcode ssh_state_sftp_init(struct Curl_easy *data, LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); + (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; } @@ -1987,24 +1801,26 @@ static CURLcode ssh_state_sftp_realpath(struct Curl_easy *data, /* * Get the "home" directory */ - int rc = libssh2_sftp_symlink_ex(sshc->sftp_session, - ".", curlx_uztoui(strlen(".")), - sshp->readdir_filename, CURL_PATH_MAX, - LIBSSH2_SFTP_REALPATH); + int rc; + + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_symlink_ex(sshc->sftp_session, ".", + curlx_uztoui(strlen(".")), + sshp->readdir_filename, CURL_PATH_MAX, + LIBSSH2_SFTP_REALPATH); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; + myssh_to(data, sshc, SSH_STOP); if(rc > 0) { - /* It seems that this string is not always null-terminated */ - sshp->readdir_filename[rc] = '\0'; - free(sshc->homedir); - sshc->homedir = strdup(sshp->readdir_filename); - if(!sshc->homedir) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); + curlx_free(sshc->homedir); + sshc->homedir = curlx_strdup(sshp->readdir_filename); + if(!sshc->homedir) return CURLE_OUT_OF_MEMORY; - } - free(data->state.most_recent_ftp_entrypath); - data->state.most_recent_ftp_entrypath = strdup(sshc->homedir); + curlx_free(data->state.most_recent_ftp_entrypath); + data->state.most_recent_ftp_entrypath = curlx_strdup(sshc->homedir); if(!data->state.most_recent_ftp_entrypath) return CURLE_OUT_OF_MEMORY; } @@ -2015,21 +1831,17 @@ static CURLcode ssh_state_sftp_realpath(struct Curl_easy *data, if(sftperr) result = sftp_libssh2_error_to_CURLE(sftperr); else - /* in this case, the error was not in the SFTP level but for example - a time-out or similar */ + /* in this case, the error was not in the SFTP level but for example a + time-out or similar */ result = CURLE_SSH; - DEBUGF(infof(data, "error = %lu makes libcurl = %d", - sftperr, (int)result)); - myssh_state(data, sshc, SSH_STOP); + CURL_TRC_SSH(data, "error = %lu makes libcurl = %d", sftperr, (int)result); return result; } - /* This is the last step in the SFTP connect phase. Do note that while - we get the homedir here, we get the "workingpath" in the DO action - since the homedir will remain the same between request but the - working path will not. */ - DEBUGF(infof(data, "SSH CONNECT phase done")); - myssh_state(data, sshc, SSH_STOP); + /* This is the last step in the SFTP connect phase. Do note that while we + get the homedir here, we get the "workingpath" in the DO action since the + homedir remains the same between request but the working path does not. */ + CURL_TRC_SSH(data, "CONNECT phase done"); return CURLE_OK; } @@ -2037,19 +1849,24 @@ static CURLcode ssh_state_sftp_quote_init(struct Curl_easy *data, struct ssh_conn *sshc, struct SSHPROTO *sshp) { - CURLcode result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); + CURLcode result; + + if(!sshp) + return CURLE_FAILED_INIT; + + 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; } @@ -2060,10 +1877,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; } @@ -2073,9 +1890,14 @@ static CURLcode ssh_state_sftp_quote(struct Curl_easy *data, struct SSHPROTO *sshp) { /* Send quote commands */ - CURLcode result = sftp_quote(data, sshc, sshp); + CURLcode result; + + if(!sshp) + return CURLE_FAILED_INIT; + + 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; @@ -2084,21 +1906,21 @@ static CURLcode ssh_state_sftp_quote(struct Curl_easy *data, static CURLcode ssh_state_sftp_next_quote(struct Curl_easy *data, struct ssh_conn *sshc) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); 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; @@ -2109,9 +1931,14 @@ static CURLcode ssh_state_sftp_quote_stat(struct Curl_easy *data, struct SSHPROTO *sshp, bool *blockp) { - CURLcode result = sftp_quote_stat(data, sshc, sshp, blockp); + CURLcode result; + + if(!sshp) + return CURLE_FAILED_INIT; + + 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; @@ -2121,25 +1948,29 @@ static CURLcode ssh_state_sftp_quote_setstat(struct Curl_easy *data, struct ssh_conn *sshc, struct SSHPROTO *sshp) { - int rc = - libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, - curlx_uztoui(strlen(sshc->quote_path2)), - LIBSSH2_SFTP_SETSTAT, - &sshp->quote_attrs); + int rc; + + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, + curlx_uztoui(strlen(sshc->quote_path2)), + LIBSSH2_SFTP_SETSTAT, + &sshp->quote_attrs); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; if(rc && !sshc->acceptfail) { unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Attempt to set SFTP stats failed: %s", - sftp_libssh2_strerror(sftperr)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + failf(data, "Attempt to set SFTP stats for \"%s\" failed: %s", + sshc->quote_path2, sftp_libssh2_strerror(sftperr)); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + 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; } @@ -2157,15 +1988,16 @@ static CURLcode ssh_state_sftp_quote_symlink(struct Curl_easy *data, if(rc && !sshc->acceptfail) { unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "symlink command failed: %s", + failf(data, "symlink \"%s\" to \"%s\" failed: %s", + sshc->quote_path1, sshc->quote_path2, sftp_libssh2_strerror(sftperr)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + 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,14 +2012,14 @@ static CURLcode ssh_state_sftp_quote_mkdir(struct Curl_easy *data, if(rc && !sshc->acceptfail) { unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - failf(data, "mkdir command failed: %s", - sftp_libssh2_strerror(sftperr)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + failf(data, "mkdir \"%s\" failed: %s", + sshc->quote_path1, sftp_libssh2_strerror(sftperr)); + curlx_safefree(sshc->quote_path1); + 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; } @@ -2208,15 +2040,16 @@ static CURLcode ssh_state_sftp_quote_rename(struct Curl_easy *data, if(rc && !sshc->acceptfail) { unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "rename command failed: %s", + failf(data, "rename \"%s\" to \"%s\" failed: %s", + sshc->quote_path1, sshc->quote_path2, sftp_libssh2_strerror(sftperr)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + 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; } @@ -2230,14 +2063,14 @@ static CURLcode ssh_state_sftp_quote_rmdir(struct Curl_easy *data, if(rc && !sshc->acceptfail) { unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - failf(data, "rmdir command failed: %s", - sftp_libssh2_strerror(sftperr)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + failf(data, "rmdir \"%s\" failed: %s", + sshc->quote_path1, sftp_libssh2_strerror(sftperr)); + curlx_safefree(sshc->quote_path1); + 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; } @@ -2251,13 +2084,14 @@ static CURLcode ssh_state_sftp_quote_unlink(struct Curl_easy *data, if(rc && !sshc->acceptfail) { unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - failf(data, "rm command failed: %s", sftp_libssh2_strerror(sftperr)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + failf(data, "rm \"%s\" failed: %s", + sshc->quote_path1, sftp_libssh2_strerror(sftperr)); + curlx_safefree(sshc->quote_path1); + 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; } @@ -2274,10 +2108,10 @@ static CURLcode ssh_state_sftp_quote_statvfs(struct Curl_easy *data, if(rc && !sshc->acceptfail) { unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - failf(data, "statvfs command failed: %s", - sftp_libssh2_strerror(sftperr)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + failf(data, "statvfs \"%s\" failed: %s", + sshc->quote_path1, sftp_libssh2_strerror(sftperr)); + curlx_safefree(sshc->quote_path1); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; } @@ -2288,39 +2122,39 @@ static CURLcode ssh_state_sftp_quote_statvfs(struct Curl_easy *data, #define CURL_LIBSSH2_VFS_SIZE_MASK "llu" #endif CURLcode result; - char *tmp = aprintf("statvfs:\n" - "f_bsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" - "f_frsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" - "f_blocks: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" - "f_bfree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" - "f_bavail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" - "f_files: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" - "f_ffree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" - "f_favail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" - "f_fsid: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" - "f_flag: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" - "f_namemax: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n", - statvfs.f_bsize, statvfs.f_frsize, - statvfs.f_blocks, statvfs.f_bfree, - statvfs.f_bavail, statvfs.f_files, - statvfs.f_ffree, statvfs.f_favail, - statvfs.f_fsid, statvfs.f_flag, - statvfs.f_namemax); + char *tmp = curl_maprintf("statvfs:\n" + "f_bsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_frsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_blocks: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_bfree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_bavail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_files: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_ffree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_favail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_fsid: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_flag: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" + "f_namemax: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n", + statvfs.f_bsize, statvfs.f_frsize, + statvfs.f_blocks, statvfs.f_bfree, + statvfs.f_bavail, statvfs.f_files, + statvfs.f_ffree, statvfs.f_favail, + 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; } result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); - free(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; } @@ -2329,9 +2163,14 @@ static CURLcode ssh_state_sftp_create_dirs_mkdir(struct Curl_easy *data, struct SSHPROTO *sshp) { /* 'mode' - parameter is preliminary - default to 0644 */ - int rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - (long)data->set.new_directory_perms); + int rc; + + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + (long)data->set.new_directory_perms); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; @@ -2339,19 +2178,19 @@ static CURLcode ssh_state_sftp_create_dirs_mkdir(struct Curl_easy *data, ++sshc->slash_pos; if(rc < 0) { /* - * Abort if failure was not that the dir already exists or the - * permission was denied (creation might succeed further down the - * path) - retry on unspecific FAILURE also + * Abort if failure was not that the directory already exists or + * the permission was denied (creation might succeed further down + * the path) - retry on unspecific FAILURE also */ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); 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; } @@ -2359,9 +2198,12 @@ static CURLcode ssh_state_sftp_readdir_init(struct Curl_easy *data, struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return CURLE_FAILED_INIT; + Curl_pgrsSetDownloadSize(data, -1); if(data->req.no_body) { - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -2381,10 +2223,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; } @@ -2393,24 +2235,30 @@ static CURLcode ssh_state_sftp_readdir_link(struct Curl_easy *data, struct SSHPROTO *sshp) { CURLcode result; - int rc = - libssh2_sftp_symlink_ex(sshc->sftp_session, - curlx_dyn_ptr(&sshp->readdir_link), - (unsigned int) - curlx_dyn_len(&sshp->readdir_link), - sshp->readdir_filename, - CURL_PATH_MAX, LIBSSH2_SFTP_READLINK); + int rc; + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_symlink_ex(sshc->sftp_session, + curlx_dyn_ptr(&sshp->readdir_link), + (unsigned int) + curlx_dyn_len(&sshp->readdir_link), + sshp->readdir_filename, + CURL_PATH_MAX, LIBSSH2_SFTP_READLINK); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; curlx_dyn_free(&sshp->readdir_link); + if(rc < 0) + return CURLE_OUT_OF_MEMORY; + /* 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; } @@ -2419,9 +2267,12 @@ static CURLcode ssh_state_scp_download_init(struct Curl_easy *data, struct SSHPROTO *sshp) { curl_off_t bytecount; + libssh2_struct_stat sb; + if(!sshp) + return CURLE_FAILED_INIT; /* - * We must check the remote file; if it is a directory no values will + * We must check the remote file; if it is a directory no values are * be set in sb */ @@ -2430,18 +2281,8 @@ static CURLcode ssh_state_scp_download_init(struct Curl_easy *data, */ /* get a fresh new channel from the ssh layer */ -#if LIBSSH2_VERSION_NUM < 0x010700 - struct stat sb; - memset(&sb, 0, sizeof(struct stat)); - sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session, - sshp->path, &sb); -#else - libssh2_struct_stat sb; memset(&sb, 0, sizeof(libssh2_struct_stat)); - sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session, - sshp->path, &sb); -#endif - + sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session, sshp->path, &sb); if(!sshc->ssh_channel) { int ssh_err; char *err_msg = NULL; @@ -2450,10 +2291,9 @@ static CURLcode ssh_state_scp_download_init(struct Curl_easy *data, LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; - ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0)); + 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); } @@ -2462,10 +2302,10 @@ static CURLcode ssh_state_scp_download_init(struct Curl_easy *data, data->req.maxdownload = (curl_off_t)sb.st_size; Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->send_idx = 0; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -2474,6 +2314,8 @@ static CURLcode ssh_state_sftp_close(struct Curl_easy *data, struct SSHPROTO *sshp) { int rc = 0; + if(!sshp) + return CURLE_FAILED_INIT; if(sshc->sftp_handle) { rc = libssh2_sftp_close(sshc->sftp_handle); if(rc == LIBSSH2_ERROR_EAGAIN) @@ -2481,27 +2323,26 @@ static CURLcode ssh_state_sftp_close(struct Curl_easy *data, if(rc < 0) { char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg); } sshc->sftp_handle = NULL; } - Curl_safefree(sshp->path); + curlx_safefree(sshp->path); - DEBUGF(infof(data, "SFTP DONE done")); + CURL_TRC_SSH(data, "SFTP DONE done"); /* 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; } @@ -2520,8 +2361,7 @@ static CURLcode ssh_state_sftp_shutdown(struct Curl_easy *data, if(rc < 0) { char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, - NULL, 0); + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg); } sshc->sftp_handle = NULL; @@ -2537,9 +2377,9 @@ static CURLcode ssh_state_sftp_shutdown(struct Curl_easy *data, sshc->sftp_session = NULL; } - Curl_safefree(sshc->homedir); + curlx_safefree(sshc->homedir); - myssh_state(data, sshc, SSH_SESSION_DISCONNECT); + myssh_to(data, sshc, SSH_SESSION_DISCONNECT); return CURLE_OK; } @@ -2547,6 +2387,8 @@ static CURLcode ssh_state_sftp_download_init(struct Curl_easy *data, struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return CURLE_FAILED_INIT; /* * Work on getting the specified file */ @@ -2564,10 +2406,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; } @@ -2575,10 +2417,12 @@ static CURLcode ssh_state_scp_upload_init(struct Curl_easy *data, struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return CURLE_FAILED_INIT; /* * libssh2 requires that the destination path is a full path that * includes the destination file and name OR ends in a "/" . If this is - * not done the destination file will be named the same name as the last + * not done the destination file is named the same name as the last * directory in the path. */ sshc->ssh_channel = @@ -2593,10 +2437,9 @@ static CURLcode ssh_state_scp_upload_init(struct Curl_easy *data, LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; - ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0)); + 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 */ @@ -2611,14 +2454,10 @@ static CURLcode ssh_state_scp_upload_init(struct Curl_easy *data, Curl_pgrsSetUploadSize(data, data->state.infilesize); Curl_xfer_setup_send(data, FIRSTSOCKET); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ 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; - - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -2633,14 +2472,12 @@ static CURLcode ssh_state_session_disconnect(struct Curl_easy *data, if(sshc->ssh_channel) { rc = libssh2_channel_free(sshc->ssh_channel); if(rc == LIBSSH2_ERROR_EAGAIN) - return CURLE_OK; + return CURLE_AGAIN; if(rc < 0) { 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); + (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); } sshc->ssh_channel = NULL; } @@ -2652,32 +2489,429 @@ static CURLcode ssh_state_session_disconnect(struct Curl_easy *data, if(rc < 0) { char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to disconnect libssh2 session: %d %s", - rc, err_msg); + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); + infof(data, "Failed to disconnect libssh2 session: %d %s", rc, err_msg); } } - Curl_safefree(sshc->homedir); + curlx_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); + + curlx_safefree(sshc->rsa_pub); + curlx_safefree(sshc->rsa); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + curlx_safefree(sshc->homedir); + + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_getinfo(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->set.get_filetime) + myssh_to(data, sshc, SSH_SFTP_FILETIME); + else + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_filetime(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + LIBSSH2_SFTP_ATTRIBUTES attrs; + int rc; + + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + LIBSSH2_SFTP_STAT, &attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + + if(rc == 0) + data->info.filetime = (time_t)attrs.mtime; + + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_trans_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(data->state.upload) + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + else if(sshp) { + size_t plen = strlen(sshp->path); + if(plen && (sshp->path[plen - 1] == '/')) + myssh_to(data, sshc, SSH_SFTP_READDIR_INIT); + else + myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT); + } + else + return CURLE_FAILED_INIT; + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_upload_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *block) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + result = sftp_upload_init(data, sshc, sshp, block); + if(result) { + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + } + return result; +} + +static CURLcode ssh_state_sftp_create_dirs_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return CURLE_FAILED_INIT; + if(strlen(sshp->path) > 1) { + sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); + } + else + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_create_dirs(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return CURLE_FAILED_INIT; + sshc->slash_pos = strchr(sshc->slash_pos, '/'); + if(sshc->slash_pos) { + *sshc->slash_pos = 0; + infof(data, "Creating directory '%s'", sshp->path); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); + return CURLE_OK; + } + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_readdir(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *block) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + result = sftp_readdir(data, sshc, sshp, block); + if(result) + myssh_to(data, sshc, SSH_SFTP_CLOSE); + return result; +} + +static CURLcode ssh_state_sftp_readdir_bottom(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + + result = curlx_dyn_addn(&sshp->readdir, "\n", 1); + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, + curlx_dyn_ptr(&sshp->readdir), + curlx_dyn_len(&sshp->readdir)); + if(result) { + curlx_dyn_free(&sshp->readdir); + myssh_to(data, sshc, SSH_STOP); + } + else { + curlx_dyn_reset(&sshp->readdir); + myssh_to(data, sshc, SSH_SFTP_READDIR); + } + return result; +} + +static CURLcode ssh_state_sftp_readdir_done(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(libssh2_sftp_closedir(sshc->sftp_handle) == + LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + + sshc->sftp_handle = NULL; + + /* no data to transfer */ + Curl_xfer_setup_nop(data); + myssh_to(data, sshc, SSH_STOP); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_download_stat(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *block) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + result = sftp_download_stat(data, sshc, sshp, block); + if(result) { + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + } + return result; +} + +static CURLcode ssh_state_scp_trans_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + result = Curl_getworkingpath(data, sshc->homedir, + &sshp->path); + if(result) { + myssh_to(data, sshc, SSH_STOP); + return result; + } + + if(data->state.upload) { + if(data->state.infilesize < 0) { + failf(data, "SCP requires a known file size for upload"); + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return CURLE_UPLOAD_FAILED; + } + myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT); + } + else + myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_done(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->state.upload) + myssh_to(data, sshc, SSH_SCP_SEND_EOF); + else + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_send_eof(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->ssh_channel) { + int rc = libssh2_channel_send_eof(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + if(rc) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, + "Failed to send libssh2 channel EOF: %d %s", + rc, err_msg); + } + } + myssh_to(data, sshc, SSH_SCP_WAIT_EOF); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_wait_eof(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->ssh_channel) { + int rc = libssh2_channel_wait_eof(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + if(rc) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to get channel EOF: %d %s", + rc, err_msg); + } + } + myssh_to(data, sshc, SSH_SCP_WAIT_CLOSE); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_wait_close(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->ssh_channel) { + int rc = + libssh2_channel_wait_closed(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + if(rc) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Channel failed to close: %d %s", + rc, err_msg); + } + } + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_channel_free( + struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->ssh_channel) { + int rc = libssh2_channel_free(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + if(rc < 0) { + 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); + } + sshc->ssh_channel = NULL; + } + CURL_TRC_SSH(data, "SCP DONE phase complete"); + myssh_to(data, sshc, SSH_STOP); + return CURLE_OK; +} + +static CURLcode ssh_state_session_free(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + CURLcode result = sshc_cleanup(sshc, data, FALSE); + struct connectdata *conn = data->conn; + if(result) + return result; + memset(sshc, 0, sizeof(struct ssh_conn)); + connclose(conn, "SSH session free"); + sshc->state = SSH_SESSION_FREE; /* current */ + myssh_to(data, sshc, SSH_STOP); + 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 + * to is 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, bool *block) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; *block = 0; /* we are not blocking by default */ do { @@ -2723,7 +2957,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: @@ -2807,72 +3041,27 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, break; case SSH_SFTP_GETINFO: - if(data->set.get_filetime) { - myssh_state(data, sshc, SSH_SFTP_FILETIME); - } - else { - myssh_state(data, sshc, SSH_SFTP_TRANS_INIT); - } + result = ssh_state_sftp_getinfo(data, sshc); break; case SSH_SFTP_FILETIME: - { - LIBSSH2_SFTP_ATTRIBUTES attrs; - - int rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - LIBSSH2_SFTP_STAT, &attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc == 0) { - data->info.filetime = (time_t)attrs.mtime; - } - - myssh_state(data, sshc, SSH_SFTP_TRANS_INIT); + result = ssh_state_sftp_filetime(data, sshc, sshp); break; - } case SSH_SFTP_TRANS_INIT: - if(data->state.upload) - myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT); - else { - if(sshp->path[strlen(sshp->path)-1] == '/') - myssh_state(data, sshc, SSH_SFTP_READDIR_INIT); - else - myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT); - } + result = ssh_state_sftp_trans_init(data, sshc, sshp); break; case SSH_SFTP_UPLOAD_INIT: - result = sftp_upload_init(data, sshc, sshp, block); - if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - } + result = ssh_state_sftp_upload_init(data, sshc, sshp, block); break; case SSH_SFTP_CREATE_DIRS_INIT: - if(strlen(sshp->path) > 1) { - sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS); - } - else { - myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT); - } + result = ssh_state_sftp_create_dirs_init(data, sshc, sshp); break; case SSH_SFTP_CREATE_DIRS: - sshc->slash_pos = strchr(sshc->slash_pos, '/'); - if(sshc->slash_pos) { - *sshc->slash_pos = 0; - - infof(data, "Creating directory '%s'", sshp->path); - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); - break; - } - myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT); + result = ssh_state_sftp_create_dirs(data, sshc, sshp); break; case SSH_SFTP_CREATE_DIRS_MKDIR: @@ -2884,10 +3073,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, break; case SSH_SFTP_READDIR: - result = sftp_readdir(data, sshc, sshp, block); - if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); - } + result = ssh_state_sftp_readdir(data, sshc, sshp, block); break; case SSH_SFTP_READDIR_LINK: @@ -2895,32 +3081,11 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, break; case SSH_SFTP_READDIR_BOTTOM: - result = curlx_dyn_addn(&sshp->readdir, "\n", 1); - if(!result) - result = Curl_client_write(data, CLIENTWRITE_BODY, - curlx_dyn_ptr(&sshp->readdir), - curlx_dyn_len(&sshp->readdir)); - - if(result) { - curlx_dyn_free(&sshp->readdir); - myssh_state(data, sshc, SSH_STOP); - } - else { - curlx_dyn_reset(&sshp->readdir); - myssh_state(data, sshc, SSH_SFTP_READDIR); - } + result = ssh_state_sftp_readdir_bottom(data, sshc, sshp); break; case SSH_SFTP_READDIR_DONE: - if(libssh2_sftp_closedir(sshc->sftp_handle) == LIBSSH2_ERROR_EAGAIN) - result = CURLE_AGAIN; - else { - sshc->sftp_handle = NULL; - - /* no data to transfer */ - Curl_xfer_setup_nop(data); - myssh_state(data, sshc, SSH_STOP); - } + result = ssh_state_sftp_readdir_done(data, sshc); break; case SSH_SFTP_DOWNLOAD_INIT: @@ -2928,11 +3093,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, break; case SSH_SFTP_DOWNLOAD_STAT: - result = sftp_download_stat(data, sshc, sshp, block); - if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - } + result = ssh_state_sftp_download_stat(data, sshc, sshp, block); break; case SSH_SFTP_CLOSE: @@ -2944,24 +3105,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, break; case SSH_SCP_TRANS_INIT: - result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); - if(result) { - myssh_state(data, sshc, SSH_STOP); - break; - } - - if(data->state.upload) { - 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); - break; - } - myssh_state(data, sshc, SSH_SCP_UPLOAD_INIT); - } - else { - myssh_state(data, sshc, SSH_SCP_DOWNLOAD_INIT); - } + result = ssh_state_scp_trans_init(data, sshc, sshp); break; case SSH_SCP_UPLOAD_INIT: @@ -2973,82 +3117,23 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, break; case SSH_SCP_DONE: - if(data->state.upload) - myssh_state(data, sshc, SSH_SCP_SEND_EOF); - else - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); + result = ssh_state_scp_done(data, sshc); break; case SSH_SCP_SEND_EOF: - if(sshc->ssh_channel) { - int rc = libssh2_channel_send_eof(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to send libssh2 channel EOF: %d %s", - rc, err_msg); - } - } - myssh_state(data, sshc, SSH_SCP_WAIT_EOF); + result = ssh_state_scp_send_eof(data, sshc); break; case SSH_SCP_WAIT_EOF: - if(sshc->ssh_channel) { - int rc = libssh2_channel_wait_eof(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to get channel EOF: %d %s", rc, err_msg); - } - } - myssh_state(data, sshc, SSH_SCP_WAIT_CLOSE); + result = ssh_state_scp_wait_eof(data, sshc); break; case SSH_SCP_WAIT_CLOSE: - if(sshc->ssh_channel) { - int rc = libssh2_channel_wait_closed(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Channel failed to close: %d %s", rc, err_msg); - } - } - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); + result = ssh_state_scp_wait_close(data, sshc); break; case SSH_SCP_CHANNEL_FREE: - if(sshc->ssh_channel) { - int rc = libssh2_channel_free(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc < 0) { - 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); - } - sshc->ssh_channel = NULL; - } - DEBUGF(infof(data, "SCP DONE phase complete")); - myssh_state(data, sshc, SSH_STOP); + result = ssh_state_scp_channel_free(data, sshc); break; case SSH_SESSION_DISCONNECT: @@ -3056,20 +3141,13 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, break; case SSH_SESSION_FREE: - result = sshc_cleanup(sshc, data, FALSE); - if(result) - break; - /* the code we are about to return */ - memset(sshc, 0, sizeof(struct ssh_conn)); - connclose(conn, "SSH session free"); - sshc->state = SSH_SESSION_FREE; /* current */ - myssh_state(data, sshc, SSH_STOP); + result = ssh_state_session_free(data, sshc); break; case SSH_QUIT: default: /* internal error */ - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); break; } @@ -3081,6 +3159,8 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, *block = TRUE; result = CURLE_OK; } + CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d", + Curl_ssh_statename(sshc->state), result, *block); return result; } @@ -3090,14 +3170,29 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, static CURLcode ssh_pollset(struct Curl_easy *data, struct easy_pollset *ps) { - int flags = 0; - if(data->conn->waitfor & KEEP_RECV) - flags |= CURL_POLL_IN; - if(data->conn->waitfor & KEEP_SEND) - flags |= CURL_POLL_OUT; - return flags ? - Curl_pollset_change(data, ps, data->conn->sock[FIRSTSOCKET], flags, 0) : - CURLE_OK; + struct connectdata *conn = data->conn; + struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); + curl_socket_t sock = conn->sock[FIRSTSOCKET]; + int waitfor; + + if(!sshc || (sock == CURL_SOCKET_BAD)) + return CURLE_FAILED_INIT; + + waitfor = sshc->waitfor ? sshc->waitfor : data->req.io_flags; + if(waitfor) { + int flags = 0; + if(waitfor & REQ_IO_RECV) + flags |= CURL_POLL_IN; + if(waitfor & REQ_IO_SEND) + flags |= CURL_POLL_OUT; + DEBUGASSERT(flags); + CURL_TRC_SSH(data, "pollset, flags=%x", flags); + return Curl_pollset_change(data, ps, sock, flags, 0); + } + /* While we still have a session, we listen incoming data. */ + if(sshc->ssh_session) + return Curl_pollset_change(data, ps, sock, CURL_POLL_IN, 0); + return CURLE_OK; } /* @@ -3111,20 +3206,19 @@ static void ssh_block2waitfor(struct Curl_easy *data, struct ssh_conn *sshc, bool block) { - struct connectdata *conn = data->conn; int dir = 0; + (void)data; if(block) { dir = libssh2_session_block_directions(sshc->ssh_session); if(dir) { /* translate the libssh2 define bits into our own bit defines */ - conn->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND) ? KEEP_RECV : 0) | - ((dir&LIBSSH2_SESSION_BLOCK_OUTBOUND) ? KEEP_SEND : 0); + sshc->waitfor = + ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) ? REQ_IO_RECV : 0) | + ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) ? REQ_IO_SEND : 0); } } if(!dir) - /* It did not block or libssh2 did not reveal in which direction, put back - the original set */ - conn->waitfor = sshc->orig_waitfor; + sshc->waitfor = 0; } /* called repeatedly until done from multi.c */ @@ -3156,32 +3250,28 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data, bool disconnect) { CURLcode result = CURLE_OK; - struct curltime dis = curlx_now(); + struct curltime start = *Curl_pgrs_now(data); while((sshc->state != SSH_STOP) && !result) { bool block; - timediff_t left = 1000; - struct curltime now = curlx_now(); + timediff_t left_ms = 1000; result = ssh_statemachine(data, sshc, sshp, &block); if(result) break; if(!disconnect) { - if(Curl_pgrsUpdate(data)) - return CURLE_ABORTED_BY_CALLBACK; - - result = Curl_speedcheck(data, now); + result = Curl_pgrsCheck(data); if(result) break; - left = Curl_timeleft(data, NULL, FALSE); - if(left < 0) { + left_ms = Curl_timeleft_ms(data); + if(left_ms < 0) { failf(data, "Operation timed out"); return CURLE_OPERATION_TIMEDOUT; } } - else if(curlx_timediff(now, dis) > 1000) { + else if(curlx_ptimediff_ms(Curl_pgrs_now(data), &start) > 1000) { /* disconnect timeout */ failf(data, "Disconnect timed out"); result = CURLE_OK; @@ -3199,7 +3289,7 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data, fd_write = sock; /* wait for the socket to become ready */ (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, - left > 1000 ? 1000 : left); + left_ms > 1000 ? 1000 : left_ms); } } @@ -3211,10 +3301,10 @@ static void myssh_easy_dtor(void *key, size_t klen, void *entry) struct SSHPROTO *sshp = entry; (void)key; (void)klen; - Curl_safefree(sshp->path); + curlx_safefree(sshp->path); curlx_dyn_free(&sshp->readdir); curlx_dyn_free(&sshp->readdir_link); - free(sshp); + curlx_free(sshp); } static void myssh_conn_dtor(void *key, size_t klen, void *entry) @@ -3223,7 +3313,7 @@ static void myssh_conn_dtor(void *key, size_t klen, void *entry) (void)key; (void)klen; sshc_cleanup(sshc, NULL, TRUE); - free(sshc); + curlx_free(sshc); } /* @@ -3236,14 +3326,14 @@ static CURLcode ssh_setup_connection(struct Curl_easy *data, struct SSHPROTO *sshp; (void)conn; - sshc = calloc(1, sizeof(*sshc)); + sshc = curlx_calloc(1, sizeof(*sshc)); if(!sshc) return CURLE_OUT_OF_MEMORY; if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, myssh_conn_dtor)) return CURLE_OUT_OF_MEMORY; - sshp = calloc(1, sizeof(*sshp)); + sshp = curlx_calloc(1, sizeof(*sshp)); if(!sshp) return CURLE_OUT_OF_MEMORY; @@ -3263,12 +3353,12 @@ static ssize_t ssh_tls_recv(libssh2_socket_t sock, void *buffer, size_t length, int flags, void **abstract) { struct Curl_easy *data = (struct Curl_easy *)*abstract; + int sockindex = Curl_conn_sockindex(data, sock); size_t nread; CURLcode result; struct connectdata *conn = data->conn; - Curl_recv *backup = conn->recv[0]; + Curl_recv *backup = conn->recv[sockindex]; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - int socknum = Curl_conn_sockindex(data, sock); (void)flags; if(!sshc) @@ -3276,14 +3366,14 @@ static ssize_t ssh_tls_recv(libssh2_socket_t sock, void *buffer, /* swap in the TLS reader function for this call only, and then swap back the SSH one again */ - conn->recv[0] = sshc->tls_recv; - result = Curl_conn_recv(data, socknum, buffer, length, &nread); - conn->recv[0] = backup; + conn->recv[sockindex] = sshc->tls_recv; + result = Curl_conn_recv(data, sockindex, buffer, length, &nread); + conn->recv[sockindex] = backup; if(result == CURLE_AGAIN) 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; } @@ -3291,12 +3381,12 @@ static ssize_t ssh_tls_send(libssh2_socket_t sock, const void *buffer, size_t length, int flags, void **abstract) { struct Curl_easy *data = (struct Curl_easy *)*abstract; + int sockindex = Curl_conn_sockindex(data, sock); size_t nwrite; CURLcode result; struct connectdata *conn = data->conn; - Curl_send *backup = conn->send[0]; + Curl_send *backup = conn->send[sockindex]; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - int socknum = Curl_conn_sockindex(data, sock); (void)flags; if(!sshc) @@ -3304,9 +3394,9 @@ static ssize_t ssh_tls_send(libssh2_socket_t sock, const void *buffer, /* swap in the TLS writer function for this call only, and then swap back the SSH one again */ - conn->send[0] = sshc->tls_send; - result = Curl_conn_send(data, socknum, buffer, length, FALSE, &nwrite); - conn->send[0] = backup; + conn->send[sockindex] = sshc->tls_send; + result = Curl_conn_send(data, sockindex, buffer, length, FALSE, &nwrite); + conn->send[sockindex] = backup; if(result == CURLE_AGAIN) return -EAGAIN; /* magic return code for libssh2 */ else if(result) @@ -3333,24 +3423,24 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) { const char *crypto_str; switch(libssh2_crypto_engine()) { - case libssh2_gcrypt: - crypto_str = "libgcrypt"; - break; - case libssh2_mbedtls: - crypto_str = "mbedTLS"; - break; - case libssh2_openssl: - crypto_str = "openssl compatible"; - break; - case libssh2_os400qc3: - crypto_str = "OS400QC3"; - break; - case libssh2_wincng: - crypto_str = "WinCNG"; - break; - default: - crypto_str = NULL; - break; + case libssh2_gcrypt: + crypto_str = "libgcrypt"; + break; + case libssh2_mbedtls: + crypto_str = "mbedTLS"; + break; + case libssh2_openssl: + crypto_str = "openssl compatible"; + break; + case libssh2_os400qc3: + crypto_str = "OS400QC3"; + break; + case libssh2_wincng: + crypto_str = "WinCNG"; + break; + default: + crypto_str = NULL; + break; } if(crypto_str) infof(data, "libssh2 cryptography backend: %s", crypto_str); @@ -3360,18 +3450,9 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) if(!sshc) return CURLE_FAILED_INIT; - /* We default to persistent connections. We set this already in this connect - function to make the reuse checks properly be able to check this bit. */ - connkeep(conn, "SSH default"); - - if(conn->user) - infof(data, "User: '%s'", conn->user); - else - infof(data, "User: NULL"); + infof(data, "User: '%s'", conn->user); #ifdef CURL_LIBSSH2_DEBUG - if(conn->passwd) { - infof(data, "Password: %s", conn->passwd); - } + infof(data, "Password: %s", conn->passwd); sock = conn->sock[FIRSTSOCKET]; #endif /* CURL_LIBSSH2_DEBUG */ @@ -3410,12 +3491,19 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) */ #if LIBSSH2_VERSION_NUM >= 0x010b01 infof(data, "Uses HTTPS proxy"); +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +#endif libssh2_session_callback_set2(sshc->ssh_session, LIBSSH2_CALLBACK_RECV, (libssh2_cb_generic *)ssh_tls_recv); libssh2_session_callback_set2(sshc->ssh_session, LIBSSH2_CALLBACK_SEND, (libssh2_cb_generic *)ssh_tls_send); +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic pop +#endif #else /* * This crazy union dance is here to avoid assigning a void pointer a @@ -3450,7 +3538,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; } @@ -3487,7 +3575,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); @@ -3503,22 +3591,21 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) * the options previously setup. */ -static -CURLcode scp_perform(struct Curl_easy *data, - bool *connected, - bool *dophase_done) +static CURLcode scp_perform(struct Curl_easy *data, + bool *connected, + bool *dophase_done) { struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN); CURLcode result = CURLE_OK; - DEBUGF(infof(data, "DO phase starts")); + CURL_TRC_SSH(data, "DO phase starts"); *dophase_done = FALSE; /* not done yet */ if(!sshc) 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); @@ -3526,7 +3613,7 @@ CURLcode scp_perform(struct Curl_easy *data, *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); + CURL_TRC_SSH(data, "DO phase is complete"); } return result; @@ -3540,146 +3627,11 @@ static CURLcode scp_doing(struct Curl_easy *data, result = ssh_multi_statemach(data, dophase_done); if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); + CURL_TRC_SSH(data, "DO phase is complete"); } 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 dir attempt state - variable */ - - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - 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(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - 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); - } - 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(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - 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); - } - sshc->sftp_handle = NULL; - } - - if(sshc->ssh_channel) { - rc = libssh2_channel_free(sshc->ssh_channel); - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - 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); - } - sshc->ssh_channel = NULL; - } - - if(sshc->sftp_session) { - rc = libssh2_sftp_shutdown(sshc->sftp_session); - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - if((rc < 0) && data) - infof(data, "Failed to stop libssh2 sftp subsystem"); - sshc->sftp_session = NULL; - } - - if(sshc->ssh_session) { - rc = libssh2_session_free(sshc->ssh_session); - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - 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); - } - 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 */ @@ -3692,9 +3644,9 @@ static CURLcode scp_disconnect(struct Curl_easy *data, struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY); (void)dead_connection; - if(sshc && sshc->ssh_session && sshp) { + 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); } @@ -3723,11 +3675,10 @@ static CURLcode ssh_done(struct Curl_easy *data, CURLcode status) if(Curl_pgrsDone(data)) return CURLE_ABORTED_BY_CALLBACK; - data->req.keepon = 0; /* clear all bits */ + CURL_REQ_CLEAR_IO(data); return result; } - static CURLcode scp_done(struct Curl_easy *data, CURLcode status, bool premature) { @@ -3735,13 +3686,13 @@ 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); } static CURLcode scp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, bool eos, + const uint8_t *mem, size_t len, bool eos, size_t *pnwritten) { struct connectdata *conn = data->conn; @@ -3757,7 +3708,8 @@ static CURLcode scp_send(struct Curl_easy *data, int sockindex, return CURLE_FAILED_INIT; /* libssh2_channel_write() returns int! */ - nwritten = (ssize_t) libssh2_channel_write(sshc->ssh_channel, mem, len); + nwritten = (ssize_t)libssh2_channel_write(sshc->ssh_channel, + (const char *)mem, len); ssh_block2waitfor(data, sshc, (nwritten == LIBSSH2_ERROR_EAGAIN)); @@ -3786,7 +3738,7 @@ static CURLcode scp_recv(struct Curl_easy *data, int sockindex, return CURLE_FAILED_INIT; /* libssh2_channel_read() returns int */ - nread = (ssize_t) libssh2_channel_read(sshc->ssh_channel, mem, len); + nread = (ssize_t)libssh2_channel_read(sshc->ssh_channel, mem, len); ssh_block2waitfor(data, sshc, (nread == LIBSSH2_ERROR_EAGAIN)); if(nread == LIBSSH2_ERROR_EAGAIN) @@ -3812,22 +3764,21 @@ static CURLcode scp_recv(struct Curl_easy *data, int sockindex, * the options previously setup. */ -static -CURLcode sftp_perform(struct Curl_easy *data, - bool *connected, - bool *dophase_done) +static CURLcode sftp_perform(struct Curl_easy *data, + bool *connected, + bool *dophase_done) { struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN); CURLcode result = CURLE_OK; - DEBUGF(infof(data, "DO phase starts")); + CURL_TRC_SSH(data, "DO phase starts"); *dophase_done = FALSE; /* not done yet */ if(!sshc) 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); @@ -3835,7 +3786,7 @@ CURLcode sftp_perform(struct Curl_easy *data, *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); + CURL_TRC_SSH(data, "DO phase is complete"); } return result; @@ -3848,7 +3799,7 @@ static CURLcode sftp_doing(struct Curl_easy *data, CURLcode result = ssh_multi_statemach(data, dophase_done); if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); + CURL_TRC_SSH(data, "DO phase is complete"); } return result; } @@ -3864,20 +3815,17 @@ static CURLcode sftp_disconnect(struct Curl_easy *data, struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY); (void)dead_connection; - DEBUGF(infof(data, "SSH DISCONNECT starts now")); - - if(sshc && sshc->ssh_session && sshp) { - /* only if there is a session still around to use! */ - myssh_state(data, sshc, SSH_SFTP_SHUTDOWN); - result = ssh_block_statemach(data, sshc, sshp, TRUE); - } - - DEBUGF(infof(data, "SSH DISCONNECT is done")); - if(sshc) + if(sshc) { + if(sshc->ssh_session) { + /* only if there is a session still around to use! */ + CURL_TRC_SSH(data, "DISCONNECT starts now"); + myssh_to(data, sshc, SSH_SFTP_SHUTDOWN); + result = ssh_block_statemach(data, sshc, sshp, TRUE); + CURL_TRC_SSH(data, "DISCONNECT is done -> %d", result); + } sshc_cleanup(sshc, data, TRUE); - + } return result; - } static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, @@ -3895,14 +3843,14 @@ 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); } /* return number of sent bytes */ static CURLcode sftp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, bool eos, + const uint8_t *mem, size_t len, bool eos, size_t *pnwritten) { struct connectdata *conn = data->conn; @@ -3916,7 +3864,7 @@ static CURLcode sftp_send(struct Curl_easy *data, int sockindex, if(!sshc) return CURLE_FAILED_INIT; - nwrite = libssh2_sftp_write(sshc->sftp_handle, mem, len); + nwrite = libssh2_sftp_write(sshc->sftp_handle, (const char *)mem, len); ssh_block2waitfor(data, sshc, (nwrite == LIBSSH2_ERROR_EAGAIN)); @@ -3958,76 +3906,39 @@ 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"; + 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_PERMISSION_DENIED: - return "Permission denied"; + *done = FALSE; /* default to false */ + if(!sshc) + return CURLE_FAILED_INIT; - case LIBSSH2_FX_FAILURE: - return "Operation failed"; + 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_BAD_MESSAGE: - return "Bad message from SFTP server"; + Curl_pgrsReset(data); - case LIBSSH2_FX_NO_CONNECTION: - return "Not connected to SFTP server"; + if(conn->scheme->protocol & CURLPROTO_SCP) + result = scp_perform(data, &connected, done); + else + result = sftp_perform(data, &connected, done); - 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"; + return result; } CURLcode Curl_ssh_init(void) { if(libssh2_init(0)) { - DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n")); + DEBUGF(curl_mfprintf(stderr, "Error: libssh2_init failed\n")); return CURLE_FAILED_INIT; } return CURLE_OK; @@ -4035,12 +3946,12 @@ CURLcode Curl_ssh_init(void) void Curl_ssh_cleanup(void) { - (void)libssh2_exit(); + libssh2_exit(); } void Curl_ssh_version(char *buffer, size_t buflen) { - (void)msnprintf(buffer, buflen, "libssh2/%s", libssh2_version(0)); + (void)curl_msnprintf(buffer, buflen, "libssh2/%s", libssh2_version(0)); } /* The SSH session is associated with the *CONNECTION* but the callback user @@ -4051,7 +3962,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 */ @@ -4060,4 +3971,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_is_dead */ + 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_is_dead */ + ssh_attach, /* attach */ + ZERO_NULL, /* follow */ +}; + #endif /* USE_LIBSSH2 */ diff --git a/lib/vssh/ssh.h b/lib/vssh/ssh.h index 75b31bd931..44e72ab802 100644 --- a/lib/vssh/ssh.h +++ b/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,8 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" +#include "urldata.h" -#include "../curl_setup.h" +extern const struct Curl_protocol Curl_protocol_sftp; +extern const struct Curl_protocol Curl_protocol_scp; + +#ifdef USE_SSH #ifdef USE_LIBSSH2 #include @@ -34,13 +39,8 @@ #define SSH_SUPPRESS_DEPRECATED #include #include -#elif defined(USE_WOLFSSH) -#include -#include #endif -#include "curl_path.h" - /* meta key for storing protocol meta at easy handle */ #define CURL_META_SSH_EASY "meta:proto:ssh:easy" /* meta key for storing protocol meta at connection */ @@ -123,7 +123,7 @@ typedef enum { Everything that is strictly related to a connection is banned from this struct. */ struct SSHPROTO { - char *path; /* the path we operate on */ + char *path; /* the path we operate on, at least one byte long */ #ifdef USE_LIBSSH2 struct dynbuf readdir_link; struct dynbuf readdir; @@ -152,14 +152,15 @@ struct ssh_conn { char *quote_path1; /* two generic pointers for the QUOTE stuff */ char *quote_path2; - char *homedir; /* when doing SFTP we figure out home dir in the - connect phase */ + char *homedir; /* when doing SFTP we figure out home directory + in the connect phase */ /* end of READDIR stuff */ int secondCreateDirs; /* counter use by the code to see if the second attempt has been made to change to/create a directory */ - int orig_waitfor; /* default READ/WRITE bits wait for */ + int waitfor; /* REQ_IO_RECV/REQ_IO_SEND bits overriding + pollset given flags */ char *slash_pos; /* used by the SFTP_CREATE_DIRS state */ #ifdef USE_LIBSSH @@ -211,14 +212,6 @@ struct ssh_conn { struct libssh2_agent_publickey *sshagent_identity; struct libssh2_agent_publickey *sshagent_prev_identity; LIBSSH2_KNOWNHOSTS *kh; -#elif defined(USE_WOLFSSH) - CURLcode actualcode; /* the actual error code */ - WOLFSSH *ssh_session; - WOLFSSH_CTX *ctx; - word32 handleSz; - byte handle[WOLFSSH_MAX_HANDLE]; - curl_off_t offset; - BIT(initialised); #endif /* USE_LIBSSH */ BIT(authed); /* the connection has been authenticated fine */ BIT(acceptfail); /* used by the SFTP_QUOTE (continue if @@ -227,7 +220,7 @@ struct ssh_conn { #ifdef USE_LIBSSH #if LIBSSH_VERSION_INT < SSH_VERSION_INT(0, 9, 0) -# error "SCP/SFTP protocols require libssh 0.9.0 or later" +#error "SCP/SFTP protocols require libssh 0.9.0 or later" #endif #endif @@ -237,16 +230,22 @@ struct ssh_conn { non-configure platforms */ #if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x010208) -# error "SCP/SFTP protocols require libssh2 1.2.8 or later" +#error "SCP/SFTP protocols require libssh2 1.2.8 or later" /* 1.2.8 was released on April 5 2011 */ #endif #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); @@ -254,11 +253,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_attach(x, y) #define Curl_ssh_init() 0 -#endif +#endif /* USE_SSH */ #endif /* HEADER_CURL_SSH_H */ diff --git a/lib/vssh/vssh.c b/lib/vssh/vssh.c new file mode 100644 index 0000000000..0ba4a9e690 --- /dev/null +++ b/lib/vssh/vssh.c @@ -0,0 +1,333 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 AND ISC + * + ***************************************************************************/ +#include "curl_setup.h" +#include "vssh/ssh.h" + +#ifdef USE_SSH + +#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, + const char *homedir, /* when SFTP is used */ + char **path) /* returns the allocated + real path to work with */ +{ + char *working_path; + size_t working_path_len; + struct dynbuf npath; + CURLcode result = + Curl_urldecode(data->state.up.path, 0, &working_path, + &working_path_len, REJECT_ZERO); + if(result) + return result; + + /* new path to switch to in case we need to */ + curlx_dyn_init(&npath, MAX_SSHPATH_LEN); + + /* Check for /~/, indicating relative to the user's home directory */ + 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)) { + curlx_free(working_path); + return CURLE_OUT_OF_MEMORY; + } + } + 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)) { + curlx_free(working_path); + return CURLE_OUT_OF_MEMORY; + } + if(working_path_len > 2) { + size_t len; + const char *p; + int copyfrom = 3; + /* Copy a separating '/' if homedir does not end with one */ + len = curlx_dyn_len(&npath); + p = curlx_dyn_ptr(&npath); + if(len && (p[len - 1] != '/')) + copyfrom = 2; + + if(curlx_dyn_addn(&npath, &working_path[copyfrom], + working_path_len - copyfrom)) { + curlx_free(working_path); + return CURLE_OUT_OF_MEMORY; + } + } + else { + if(curlx_dyn_add(&npath, "/")) { + curlx_free(working_path); + return CURLE_OUT_OF_MEMORY; + } + } + } + + if(curlx_dyn_len(&npath)) { + curlx_free(working_path); + + /* store the pointer for the caller to receive */ + *path = curlx_dyn_ptr(&npath); + } + else + *path = working_path; + DEBUGASSERT(*path && (*path)[0]); + + return CURLE_OK; +} + +#define MAX_PATHLENGTH 65535 /* arbitrary long */ + +CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir) +{ + const char *cp = *cpp; + struct dynbuf out; + CURLcode result; + + DEBUGASSERT(homedir); + *path = NULL; + *cpp = NULL; + if(!*cp || !homedir) + return CURLE_QUOTE_ERROR; + + curlx_dyn_init(&out, MAX_PATHLENGTH); + + /* Ignore leading whitespace */ + curlx_str_passblanks(&cp); + + /* Check for quoted filenames */ + if(*cp == '\"' || *cp == '\'') { + char quot = *cp++; + + /* Search for terminating quote, unescape some chars */ + while(*cp != quot) { + if(!*cp) /* End of string */ + goto fail; + + if(*cp == '\\') { /* Escaped characters */ + cp++; + if(*cp != '\'' && *cp != '\"' && *cp != '\\') + goto fail; + } + result = curlx_dyn_addn(&out, cp, 1); + if(result) + return result; + cp++; + } + cp++; /* pass the end quote */ + + if(!curlx_dyn_len(&out)) + goto fail; + } + else { + struct Curl_str word; + bool content = FALSE; + int rc; + /* Handling for relative path - prepend home directory */ + if(cp[0] == '/' && cp[1] == '~' && cp[2] == '/') { + result = curlx_dyn_add(&out, homedir); + if(!result) + result = curlx_dyn_addn(&out, "/", 1); + if(result) + return result; + cp += 3; + content = TRUE; + } + /* Read to end of filename - either to whitespace or terminator */ + rc = curlx_str_word(&cp, &word, MAX_PATHLENGTH); + if(rc) { + if(rc == STRE_BIG) { + curlx_dyn_free(&out); + return CURLE_TOO_LARGE; + } + else if(!content) + /* no path, no word, this is incorrect */ + goto fail; + } + else { + /* append the word */ + result = curlx_dyn_addn(&out, curlx_str(&word), curlx_strlen(&word)); + if(result) + return result; + } + } + /* skip whitespace */ + curlx_str_passblanks(&cp); + + /* return pointer to second parameter if it exists */ + *cpp = cp; + + *path = curlx_dyn_ptr(&out); + return CURLE_OK; + +fail: + curlx_dyn_free(&out); + return CURLE_QUOTE_ERROR; +} + +CURLcode Curl_ssh_range(struct Curl_easy *data, + 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(&range, &from, CURL_OFF_T_MAX); + if(from_t == STRE_OVERFLOW) + return CURLE_RANGE_ERROR; + curlx_str_passblanks(&range); + (void)curlx_str_single(&range, '-'); + + to_t = curlx_str_numblanks(&range, &to); + if((to_t == STRE_OVERFLOW) || (to_t && from_t) || *range) + return CURLE_RANGE_ERROR; + + if(from_t) { + /* no start point given, set from relative to end of file */ + if(!to) + /* "-0" is not a fine range */ + return CURLE_RANGE_ERROR; + else if(to > filesize) + to = filesize; + from = filesize - to; + to = filesize - 1; + } + else if(from > filesize) { + failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" + FMT_OFF_T ")", from, filesize); + return CURLE_RANGE_ERROR; + } + else if((to_t == STRE_NO_NUM) || (to >= filesize)) + to = filesize - 1; + + if(from > to) { + failf(data, "Bad range: start offset larger than end offset"); + return CURLE_RANGE_ERROR; + } + if((to - from) == CURL_OFF_T_MAX) + return CURLE_RANGE_ERROR; + + *startp = from; + *sizep = to - from + 1; + return CURLE_OK; +} + +#endif /* USE_SSH */ diff --git a/lib/vssh/curl_path.h b/lib/vssh/vssh.h similarity index 74% rename from lib/vssh/curl_path.h rename to lib/vssh/vssh.h index 1c167f9660..492108fbd7 100644 --- a/lib/vssh/curl_path.h +++ b/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,14 +23,20 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" -#include "../curl_setup.h" -#include -#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); -#endif /* HEADER_CURL_PATH_H */ + +CURLcode Curl_ssh_range(struct Curl_easy *data, + const char *range, curl_off_t filesize, + curl_off_t *startp, curl_off_t *sizep); +#endif /* USE_SSH */ +#endif /* HEADER_CURL_VSSH_VSSH_H */ diff --git a/lib/vssh/wolfssh.c b/lib/vssh/wolfssh.c deleted file mode 100644 index 7cd0402a99..0000000000 --- a/lib/vssh/wolfssh.c +++ /dev/null @@ -1,1225 +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" - -#ifdef USE_WOLFSSH - -#include - -#include "../urldata.h" -#include "../url.h" -#include "../cfilters.h" -#include "../connect.h" -#include "../sendf.h" -#include "../progress.h" -#include "curl_path.h" -#include "../transfer.h" -#include "../speedcheck.h" -#include "../select.h" -#include "../multiif.h" -#include "../curlx/warnless.h" -#include "../strdup.h" - -/* The last 3 #include files should be in this order */ -#include "../curl_printf.h" -#include "../curl_memory.h" -#include "../memdebug.h" - -static CURLcode wssh_connect(struct Curl_easy *data, bool *done); -static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done); -static CURLcode wssh_do(struct Curl_easy *data, bool *done); -#if 0 -static CURLcode wscp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode wscp_doing(struct Curl_easy *data, - bool *dophase_done); -static CURLcode wscp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection); -#endif -static CURLcode wsftp_done(struct Curl_easy *data, - CURLcode, bool premature); -static CURLcode wsftp_doing(struct Curl_easy *data, - bool *dophase_done); -static CURLcode wsftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead); -static CURLcode wssh_pollset(struct Curl_easy *data, - struct easy_pollset *ps); -static CURLcode wssh_setup_connection(struct Curl_easy *data, - struct connectdata *conn); -static void wssh_sshc_cleanup(struct ssh_conn *sshc); - -#if 0 -/* - * SCP protocol handler. - */ - -const struct Curl_handler Curl_handler_scp = { - "SCP", /* scheme */ - wssh_setup_connection, /* setup_connection */ - wssh_do, /* do_it */ - wscp_done, /* done */ - ZERO_NULL, /* do_more */ - wssh_connect, /* connect_it */ - wssh_multi_statemach, /* connecting */ - wscp_doing, /* doing */ - wssh_pollset, /* proto_pollset */ - wssh_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - wssh_pollset, /* perform_pollset */ - wscp_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 */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION - | PROTOPT_NOURLQUERY /* flags */ -}; - -#endif - -/* - * SFTP protocol handler. - */ - -const struct Curl_handler Curl_handler_sftp = { - "SFTP", /* scheme */ - wssh_setup_connection, /* setup_connection */ - wssh_do, /* do_it */ - wsftp_done, /* done */ - ZERO_NULL, /* do_more */ - wssh_connect, /* connect_it */ - wssh_multi_statemach, /* connecting */ - wsftp_doing, /* doing */ - wssh_pollset, /* proto_pollset */ - wssh_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - wssh_pollset, /* perform_pollset */ - wsftp_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 - | PROTOPT_NOURLQUERY /* flags */ -}; - -/* - * SSH State machine related code - */ -/* This is the ONLY way to change SSH state! */ -static void wssh_state(struct Curl_easy *data, - struct ssh_conn *sshc, - sshstate nowstate) -{ -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - /* for debug purposes */ - 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); - - if(sshc->state != nowstate) { - infof(data, "wolfssh %p state change from %s to %s", - (void *)sshc, names[sshc->state], names[nowstate]); - } -#endif - (void)data; - sshc->state = nowstate; -} - -static CURLcode wscp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, bool eos, - size_t *pnwritten) -{ - (void)data; - (void)sockindex; /* we only support SCP on the fixed known primary socket */ - (void)mem; - (void)len; - (void)eos; - *pnwritten = 0; - return CURLE_OK; -} - -static CURLcode wscp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, size_t *pnread) -{ - (void)data; - (void)sockindex; /* we only support SCP on the fixed known primary socket */ - (void)mem; - (void)len; - *pnread = 0; - - return CURLE_OK; -} - -/* return number of sent bytes */ -static CURLcode wsftp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, bool eos, - size_t *pnwritten) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - word32 offset[2]; - int rc; - (void)sockindex; - (void)eos; - - *pnwritten = 0; - if(!sshc) - return CURLE_FAILED_INIT; - - offset[0] = (word32)sshc->offset & 0xFFFFFFFF; - offset[1] = (word32)(sshc->offset >> 32) & 0xFFFFFFFF; - - rc = wolfSSH_SFTP_SendWritePacket(sshc->ssh_session, sshc->handle, - sshc->handleSz, - &offset[0], - (byte *)CURL_UNCONST(mem), (word32)len); - - if(rc == WS_FATAL_ERROR) - rc = wolfSSH_get_error(sshc->ssh_session); - if(rc == WS_WANT_READ) { - conn->waitfor = KEEP_RECV; - return CURLE_AGAIN; - } - else if(rc == WS_WANT_WRITE) { - conn->waitfor = KEEP_SEND; - return CURLE_AGAIN; - } - if(rc < 0) { - failf(data, "wolfSSH_SFTP_SendWritePacket returned %d", rc); - return CURLE_SEND_ERROR; - } - DEBUGASSERT(rc == (int)len); - *pnwritten = (size_t)rc; - sshc->offset += *pnwritten; - infof(data, "sent %zu bytes SFTP from offset %" FMT_OFF_T, - *pnwritten, sshc->offset); - return CURLE_OK; -} - -/* - * Return number of received (decrypted) bytes - * or <0 on error - */ -static CURLcode wsftp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, size_t *pnread) -{ - int rc; - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - word32 offset[2]; - (void)sockindex; - - *pnread = 0; - if(!sshc) - return CURLE_FAILED_INIT; - - offset[0] = (word32)sshc->offset & 0xFFFFFFFF; - offset[1] = (word32)(sshc->offset >> 32) & 0xFFFFFFFF; - - rc = wolfSSH_SFTP_SendReadPacket(sshc->ssh_session, sshc->handle, - sshc->handleSz, - &offset[0], - (byte *)mem, (word32)len); - if(rc == WS_FATAL_ERROR) - rc = wolfSSH_get_error(sshc->ssh_session); - if(rc == WS_WANT_READ) { - conn->waitfor = KEEP_RECV; - return CURLE_AGAIN; - } - else if(rc == WS_WANT_WRITE) { - conn->waitfor = KEEP_SEND; - return CURLE_AGAIN; - } - - DEBUGASSERT(rc <= (int)len); - - if(rc < 0) { - failf(data, "wolfSSH_SFTP_SendReadPacket returned %d", rc); - return CURLE_RECV_ERROR; - } - *pnread = (size_t)rc; - sshc->offset += *pnread; - return CURLE_OK; -} - -static void wssh_easy_dtor(void *key, size_t klen, void *entry) -{ - struct SSHPROTO *sshp = entry; - (void)key; - (void)klen; - Curl_safefree(sshp->path); - free(sshp); -} - -static void wssh_conn_dtor(void *key, size_t klen, void *entry) -{ - struct ssh_conn *sshc = entry; - (void)key; - (void)klen; - wssh_sshc_cleanup(sshc); - free(sshc); -} - -/* - * SSH setup and connection - */ -static CURLcode wssh_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - struct ssh_conn *sshc; - struct SSHPROTO *sshp; - (void)conn; - - sshc = calloc(1, sizeof(*sshc)); - if(!sshc) - return CURLE_OUT_OF_MEMORY; - - sshc->initialised = TRUE; - if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, wssh_conn_dtor)) - return CURLE_OUT_OF_MEMORY; - - sshp = calloc(1, sizeof(*sshp)); - if(!sshp || - Curl_meta_set(data, CURL_META_SSH_EASY, sshp, wssh_easy_dtor)) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} - -static int userauth(byte authtype, - WS_UserAuthData* authdata, - void *ctx) -{ - struct Curl_easy *data = ctx; - DEBUGF(infof(data, "wolfssh callback: type %s", - authtype == WOLFSSH_USERAUTH_PASSWORD ? "PASSWORD" : - "PUBLICCKEY")); - if(authtype == WOLFSSH_USERAUTH_PASSWORD) { - authdata->sf.password.password = (byte *)data->conn->passwd; - authdata->sf.password.passwordSz = (word32) strlen(data->conn->passwd); - } - - return 0; -} - -static CURLcode wssh_connect(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY); - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - int rc; - - if(!sshc || !sshp) - return CURLE_FAILED_INIT; - - /* We default to persistent connections. We set this already in this connect - function to make the reuse checks properly be able to check this bit. */ - connkeep(conn, "SSH default"); - - if(conn->handler->protocol & CURLPROTO_SCP) { - conn->recv[FIRSTSOCKET] = wscp_recv; - conn->send[FIRSTSOCKET] = wscp_send; - } - else { - conn->recv[FIRSTSOCKET] = wsftp_recv; - conn->send[FIRSTSOCKET] = wsftp_send; - } - sshc->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL); - if(!sshc->ctx) { - failf(data, "No wolfSSH context"); - goto error; - } - - sshc->ssh_session = wolfSSH_new(sshc->ctx); - if(!sshc->ssh_session) { - failf(data, "No wolfSSH session"); - goto error; - } - - rc = wolfSSH_SetUsername(sshc->ssh_session, conn->user); - if(rc != WS_SUCCESS) { - failf(data, "wolfSSH failed to set username"); - goto error; - } - - /* set callback for authentication */ - wolfSSH_SetUserAuth(sshc->ctx, userauth); - wolfSSH_SetUserAuthCtx(sshc->ssh_session, data); - - rc = wolfSSH_set_fd(sshc->ssh_session, (int)sock); - if(rc) { - failf(data, "wolfSSH failed to set socket"); - goto error; - } - -#if 0 - wolfSSH_Debugging_ON(); -#endif - - *done = TRUE; - if(conn->handler->protocol & CURLPROTO_SCP) - wssh_state(data, sshc, SSH_INIT); - else - wssh_state(data, sshc, SSH_SFTP_INIT); - - return wssh_multi_statemach(data, done); -error: - wssh_sshc_cleanup(sshc); - 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 - * to will be set to TRUE if the wolfssh function returns EAGAIN meaning it - * wants to be called again when the socket is ready - */ - -static CURLcode wssh_statemach_act(struct Curl_easy *data, - struct ssh_conn *sshc, - bool *block) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - struct SSHPROTO *sftp_scp = Curl_meta_get(data, CURL_META_SSH_EASY); - WS_SFTPNAME *name; - int rc = 0; - *block = FALSE; /* we are not blocking by default */ - - if(!sftp_scp) - return CURLE_FAILED_INIT; - - do { - switch(sshc->state) { - case SSH_INIT: - wssh_state(data, sshc, SSH_S_STARTUP); - break; - - case SSH_S_STARTUP: - rc = wolfSSH_connect(sshc->ssh_session); - if(rc != WS_SUCCESS) - 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) { - wssh_state(data, sshc, SSH_STOP); - return CURLE_SSH; - } - infof(data, "wolfssh connected"); - wssh_state(data, sshc, SSH_STOP); - break; - case SSH_STOP: - break; - - case SSH_SFTP_INIT: - rc = wolfSSH_SFTP_connect(sshc->ssh_session); - if(rc != WS_SUCCESS) - 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 connected"); - wssh_state(data, sshc, SSH_SFTP_REALPATH); - } - else { - failf(data, "wolfssh SFTP connect error %d", rc); - return CURLE_SSH; - } - break; - case SSH_SFTP_REALPATH: - name = wolfSSH_SFTP_RealPath(sshc->ssh_session, - (char *)CURL_UNCONST(".")); - 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(name && (rc == WS_SUCCESS)) { - sshc->homedir = Curl_memdup0(name->fName, name->fSz); - if(!sshc->homedir) - sshc->actualcode = CURLE_OUT_OF_MEMORY; - wolfSSH_SFTPNAME_list_free(name); - wssh_state(data, sshc, SSH_STOP); - return CURLE_OK; - } - failf(data, "wolfssh SFTP realpath %d", rc); - return CURLE_SSH; - - case SSH_SFTP_QUOTE_INIT: - result = Curl_getworkingpath(data, sshc->homedir, &sftp_scp->path); - if(result) { - sshc->actualcode = result; - wssh_state(data, sshc, SSH_STOP); - break; - } - - if(data->set.quote) { - infof(data, "Sending quote commands"); - sshc->quote_item = data->set.quote; - wssh_state(data, sshc, SSH_SFTP_QUOTE); - } - else { - wssh_state(data, sshc, SSH_SFTP_GETINFO); - } - break; - case SSH_SFTP_GETINFO: - if(data->set.get_filetime) { - wssh_state(data, sshc, SSH_SFTP_FILETIME); - } - else { - wssh_state(data, sshc, SSH_SFTP_TRANS_INIT); - } - break; - case SSH_SFTP_TRANS_INIT: - if(data->state.upload) - wssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT); - else { - if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/') - wssh_state(data, sshc, SSH_SFTP_READDIR_INIT); - else - wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT); - } - break; - 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, - WOLFSSH_FXF_READ, NULL, - 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"); - wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT); - return CURLE_OK; - } - - failf(data, "wolfssh SFTP open failed: %d", rc); - return CURLE_SSH; - - case SSH_SFTP_DOWNLOAD_STAT: { - WS_SFTP_FILEATRB attrs; - curl_off_t size; - - rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, &attrs); - 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 STAT succeeded"); - } - else { - failf(data, "wolfssh SFTP open failed: %d", rc); - data->req.size = -1; - data->req.maxdownload = -1; - Curl_pgrsSetDownloadSize(data, -1); - return CURLE_SSH; - } - - size = ((curl_off_t)attrs.sz[1] << 32) | attrs.sz[0]; - - data->req.size = size; - data->req.maxdownload = size; - Curl_pgrsSetDownloadSize(data, size); - - infof(data, "SFTP download %" FMT_OFF_T " bytes", size); - - /* We cannot seek with wolfSSH so resuming and range requests are not - possible */ - if(data->state.use_range || data->state.resume_from) { - infof(data, "wolfSSH cannot do range/seek on SFTP"); - return CURLE_BAD_DOWNLOAD_RESUME; - } - - /* Setup the actual download */ - if(data->req.size == 0) { - /* no data to transfer */ - Curl_xfer_setup_nop(data); - infof(data, "File already completely downloaded"); - wssh_state(data, sshc, SSH_STOP); - break; - } - Curl_xfer_setup_recv(data, FIRSTSOCKET, data->req.size); - - /* not set by Curl_xfer_setup to preserve keepon bits */ - conn->send_idx = 0; - - if(result) { - /* this should never occur; the close state should be entered - at the time the error occurs */ - wssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->actualcode = result; - } - else { - wssh_state(data, sshc, SSH_STOP); - } - break; - } - case SSH_SFTP_CLOSE: - if(sshc->handleSz) { - rc = wolfSSH_SFTP_Close(sshc->ssh_session, sshc->handle, - sshc->handleSz); - if(rc != WS_SUCCESS) - rc = wolfSSH_get_error(sshc->ssh_session); - } - else { - rc = WS_SUCCESS; /* directory listing */ - } - 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) { - wssh_state(data, sshc, SSH_STOP); - return CURLE_OK; - } - - failf(data, "wolfssh SFTP CLOSE failed: %d", rc); - return CURLE_SSH; - - case SSH_SFTP_READDIR_INIT: - Curl_pgrsSetDownloadSize(data, -1); - if(data->req.no_body) { - wssh_state(data, sshc, SSH_STOP); - break; - } - wssh_state(data, sshc, SSH_SFTP_READDIR); - break; - - case SSH_SFTP_READDIR: - name = wolfSSH_SFTP_LS(sshc->ssh_session, sftp_scp->path); - if(!name) - rc = wolfSSH_get_error(sshc->ssh_session); - else - rc = WS_SUCCESS; - - 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(name && (rc == WS_SUCCESS)) { - WS_SFTPNAME *origname = name; - result = CURLE_OK; - while(name) { - char *line = aprintf("%s\n", - data->set.list_only ? - name->fName : name->lName); - if(!line) { - wssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - result = Curl_client_write(data, CLIENTWRITE_BODY, - line, strlen(line)); - free(line); - if(result) { - sshc->actualcode = result; - break; - } - name = name->next; - } - wolfSSH_SFTPNAME_list_free(origname); - wssh_state(data, sshc, SSH_STOP); - return result; - } - failf(data, "wolfssh SFTP ls failed: %d", rc); - return CURLE_SSH; - - case SSH_SFTP_SHUTDOWN: - wssh_sshc_cleanup(sshc); - wssh_state(data, sshc, SSH_STOP); - return CURLE_OK; - default: - break; - } - } while(!rc && (sshc->state != SSH_STOP)); - return result; -} - -/* called repeatedly until done from multi.c */ -static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - CURLcode result = CURLE_OK; - bool block; /* we store the status and use that to provide a ssh_pollset() - implementation */ - if(!sshc) - return CURLE_FAILED_INIT; - - do { - result = wssh_statemach_act(data, sshc, &block); - *done = (sshc->state == SSH_STOP); - /* if there is no error, it is not done and it did not EWOULDBLOCK, then - try again */ - if(*done) { - DEBUGF(infof(data, "wssh_statemach_act says DONE")); - } - } while(!result && !*done && !block); - - return result; -} - -static -CURLcode wscp_perform(struct Curl_easy *data, - bool *connected, - bool *dophase_done) -{ - (void)data; - (void)connected; - (void)dophase_done; - return CURLE_OK; -} - -static -CURLcode wsftp_perform(struct Curl_easy *data, - struct ssh_conn *sshc, - bool *connected, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - - DEBUGF(infof(data, "DO phase starts")); - - *dophase_done = FALSE; /* not done yet */ - - /* start the first command in the DO phase */ - wssh_state(data, sshc, SSH_SFTP_QUOTE_INIT); - - /* run the state-machine */ - result = wssh_multi_statemach(data, dophase_done); - - *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); - - if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); - } - - return result; -} - -/* - * The DO function is generic for both protocols. - */ -static CURLcode wssh_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->actualcode = CURLE_OK; /* reset error code */ - sshc->secondCreateDirs = 0; /* reset the create dir attempt state - variable */ - - Curl_pgrsSetUploadCounter(data, 0); - Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, -1); - Curl_pgrsSetDownloadSize(data, -1); - - if(conn->handler->protocol & CURLPROTO_SCP) - result = wscp_perform(data, &connected, done); - else - result = wsftp_perform(data, sshc, &connected, done); - - return result; -} - -static CURLcode wssh_block_statemach(struct Curl_easy *data, - struct ssh_conn *sshc, - bool disconnect) -{ - struct connectdata *conn = data->conn; - CURLcode result = CURLE_OK; - - while((sshc->state != SSH_STOP) && !result) { - bool block; - timediff_t left = 1000; - struct curltime now = curlx_now(); - - result = wssh_statemach_act(data, sshc, &block); - if(result) - break; - - if(!disconnect) { - if(Curl_pgrsUpdate(data)) - return CURLE_ABORTED_BY_CALLBACK; - - result = Curl_speedcheck(data, now); - if(result) - break; - - left = Curl_timeleft(data, NULL, FALSE); - if(left < 0) { - failf(data, "Operation timed out"); - return CURLE_OPERATION_TIMEDOUT; - } - } - - if(!result) { - int dir = conn->waitfor; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; - curl_socket_t fd_read = CURL_SOCKET_BAD; - curl_socket_t fd_write = CURL_SOCKET_BAD; - if(dir == KEEP_RECV) - fd_read = sock; - else if(dir == KEEP_SEND) - fd_write = sock; - - /* wait for the socket to become ready */ - (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, - left > 1000 ? 1000 : left); /* ignore result */ - } - } - - return result; -} - -/* generic done function for both SCP and SFTP called from their specific - done functions */ -static CURLcode wssh_done(struct Curl_easy *data, - struct ssh_conn *sshc, - CURLcode status) -{ - CURLcode result = CURLE_OK; - - if(!status) { - /* run the state-machine */ - result = wssh_block_statemach(data, sshc, FALSE); - } - else - result = status; - - if(Curl_pgrsDone(data)) - return CURLE_ABORTED_BY_CALLBACK; - - data->req.keepon = 0; /* clear all bits */ - return result; -} - -static void wssh_sshc_cleanup(struct ssh_conn *sshc) -{ - if(sshc->ssh_session) { - wolfSSH_free(sshc->ssh_session); - sshc->ssh_session = NULL; - } - if(sshc->ctx) { - wolfSSH_CTX_free(sshc->ctx); - sshc->ctx = NULL; - } - Curl_safefree(sshc->homedir); -} - -#if 0 -static CURLcode wscp_done(struct Curl_easy *data, - CURLcode code, bool premature) -{ - CURLcode result = CURLE_OK; - (void)conn; - (void)code; - (void)premature; - - return result; -} - -static CURLcode wscp_doing(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result = CURLE_OK; - (void)conn; - (void)dophase_done; - - return result; -} - -static CURLcode wscp_disconnect(struct Curl_easy *data, - struct connectdata *conn, bool dead_connection) -{ - struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - CURLcode result = CURLE_OK; - (void)dead_connection; - if(sshc) - wssh_sshc_cleanup(sshc); - return result; -} -#endif - -static CURLcode wsftp_done(struct Curl_easy *data, - CURLcode code, bool premature) -{ - struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN); - (void)premature; - if(!sshc) - return CURLE_FAILED_INIT; - - wssh_state(data, sshc, SSH_SFTP_CLOSE); - - return wssh_done(data, sshc, code); -} - -static CURLcode wsftp_doing(struct Curl_easy *data, - bool *dophase_done) -{ - CURLcode result = wssh_multi_statemach(data, dophase_done); - - if(*dophase_done) { - DEBUGF(infof(data, "DO phase is complete")); - } - return result; -} - -static CURLcode wsftp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead) -{ - struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); - CURLcode result = CURLE_OK; - (void)dead; - - DEBUGF(infof(data, "SSH DISCONNECT starts now")); - - if(sshc && sshc->ssh_session) { - /* only if there is a session still around to use! */ - wssh_state(data, sshc, SSH_SFTP_SHUTDOWN); - result = wssh_block_statemach(data, sshc, TRUE); - } - - if(sshc) - wssh_sshc_cleanup(sshc); - DEBUGF(infof(data, "SSH DISCONNECT is done")); - return result; -} - -static CURLcode wssh_pollset(struct Curl_easy *data, - struct easy_pollset *ps) -{ - int flags = 0; - if(data->conn->waitfor & KEEP_RECV) - flags |= CURL_POLL_IN; - if(data->conn->waitfor & KEEP_SEND) - flags |= CURL_POLL_OUT; - return flags ? - Curl_pollset_change(data, ps, data->conn->sock[FIRSTSOCKET], flags, 0) : - CURLE_OK; -} - -void Curl_ssh_version(char *buffer, size_t buflen) -{ - (void)msnprintf(buffer, buflen, "wolfssh/%s", LIBWOLFSSH_VERSION_STRING); -} - -CURLcode Curl_ssh_init(void) -{ - if(WS_SUCCESS != wolfSSH_Init()) { - DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n")); - return CURLE_FAILED_INIT; - } - - return CURLE_OK; -} -void Curl_ssh_cleanup(void) -{ - (void)wolfSSH_Cleanup(); -} - -#endif /* USE_WOLFSSH */ diff --git a/lib/vtls/.checksrc b/lib/vtls/.checksrc deleted file mode 100644 index 22ca8e0b53..0000000000 --- a/lib/vtls/.checksrc +++ /dev/null @@ -1,5 +0,0 @@ -banfunc snprintf -banfunc sscanf -banfunc strerror -banfunc strtol -banfunc vsnprint diff --git a/lib/vtls/apple.c b/lib/vtls/apple.c new file mode 100644 index 0000000000..e28a40cc0e --- /dev/null +++ b/lib/vtls/apple.c @@ -0,0 +1,293 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 + * + ***************************************************************************/ + +/* This file is for implementing all "generic" SSL functions that all libcurl + internals should use. It is then responsible for calling the proper + "backend" function. + + SSL-functions in libcurl should call functions in this source file, and not + to any specific SSL-layer. + + Curl_ssl_ - prefix for generic ones + + Note that this source code uses the functions of the configured SSL + backend via the global Curl_ssl instance. + + "SSL/TLS Strong Encryption: An Introduction" + https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html +*/ + +#include "curl_setup.h" + +#ifdef USE_APPLE_SECTRUST + +#include "urldata.h" +#include "cfilters.h" +#include "curl_trc.h" +#include "vtls/vtls.h" +#include "vtls/apple.h" + +#include + +#if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= 101400) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + __IPHONE_OS_VERSION_MAX_ALLOWED >= 120000) +#define SUPPORTS_SecTrustEvaluateWithError 1 +#endif + +#if defined(SUPPORTS_SecTrustEvaluateWithError) && \ + ((defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ + MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || \ + (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \ + __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)) +#define REQUIRES_SecTrustEvaluateWithError 1 +#endif + +#if defined(SUPPORTS_SecTrustEvaluateWithError) && \ + !defined(HAVE_BUILTIN_AVAILABLE) && \ + !defined(REQUIRES_SecTrustEvaluateWithError) +#undef SUPPORTS_SecTrustEvaluateWithError +#endif + +#if (defined(MAC_OS_X_VERSION_MAX_ALLOWED) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= 100900) || \ + (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ + __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) +#define SUPPORTS_SecOCSP 1 +#endif + +CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + size_t num_certs, + Curl_vtls_get_cert_der *der_cb, + void *cb_user_data, + const unsigned char *ocsp_buf, + size_t ocsp_len) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + CURLcode result = CURLE_OK; + SecTrustRef trust = NULL; + SecPolicyRef policy = NULL; + CFMutableArrayRef policies = NULL; + CFMutableArrayRef cert_array = NULL; + CFStringRef host_str = NULL; + CFErrorRef error = NULL; + OSStatus status = noErr; + CFStringRef error_ref = NULL; + char *err_desc = NULL; + size_t i; + + if(conn_config->verifyhost) { + host_str = CFStringCreateWithCString(NULL, + peer->sni ? peer->sni : peer->hostname, kCFStringEncodingUTF8); + if(!host_str) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + + policies = CFArrayCreateMutable(NULL, 2, &kCFTypeArrayCallBacks); + if(!policies) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + policy = SecPolicyCreateSSL(true, host_str); + if(!policy) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + CFArrayAppendValue(policies, policy); + CFRelease(policy); + policy = NULL; + +#if defined(HAVE_BUILTIN_AVAILABLE) && defined(SUPPORTS_SecOCSP) + { + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + if(!ssl_config->no_revoke) { + if(__builtin_available(macOS 10.9, iOS 7, tvOS 9, watchOS 2, *)) { + /* Even without this set, validation seemingly-unavoidably fails + * for certificates that trustd already knows to be revoked. + * This policy further allows trustd to consult CRLs and OCSP data + * to determine revocation status (which it may then cache). */ + CFOptionFlags revocation_flags = kSecRevocationUseAnyAvailableMethod; +#if 0 + /* `revoke_best_effort` is off by default in libcurl. When we + * add `kSecRevocationRequirePositiveResponse` to the Apple + * Trust policies, it interprets this as it NEEDs a confirmation + * of a cert being NOT REVOKED. Which not in general available for + * certificates on the Internet. + * It seems that applications using this policy are expected to PIN + * their certificate public keys or verification fails. + * This does not seem to be what we want here. */ + if(!ssl_config->revoke_best_effort) { + revocation_flags |= kSecRevocationRequirePositiveResponse; + } +#endif + policy = SecPolicyCreateRevocation(revocation_flags); + if(!policy) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + CFArrayAppendValue(policies, policy); + } + } + } +#endif + + cert_array = CFArrayCreateMutable(NULL, num_certs, &kCFTypeArrayCallBacks); + if(!cert_array) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + for(i = 0; i < num_certs; i++) { + SecCertificateRef cert; + CFDataRef certdata; + unsigned char *der; + size_t der_len; + + result = der_cb(cf, data, cb_user_data, i, &der, &der_len); + if(result) + goto out; + + certdata = CFDataCreate(NULL, der, (CFIndex)der_len); + if(!certdata) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + cert = SecCertificateCreateWithData(NULL, certdata); + CFRelease(certdata); + if(!cert) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + CFArrayAppendValue(cert_array, cert); + CFRelease(cert); + } + + status = SecTrustCreateWithCertificates(cert_array, policies, &trust); + if(status != noErr || !trust) { + failf(data, "Apple SecTrust: failed to create validation trust"); + result = CURLE_PEER_FAILED_VERIFICATION; + goto out; + } + +#if defined(HAVE_BUILTIN_AVAILABLE) && defined(SUPPORTS_SecOCSP) + if(ocsp_len > 0) { + if(__builtin_available(macOS 10.9, iOS 7, tvOS 9, watchOS 2, *)) { + CFDataRef ocspdata = CFDataCreate(NULL, ocsp_buf, (CFIndex)ocsp_len); + + status = SecTrustSetOCSPResponse(trust, ocspdata); + CFRelease(ocspdata); + if(status != noErr) { + failf(data, "Apple SecTrust: failed to set OCSP response: %i", + (int)status); + result = CURLE_PEER_FAILED_VERIFICATION; + goto out; + } + } + } +#else + (void)ocsp_buf; + (void)ocsp_len; +#endif + +#ifdef SUPPORTS_SecTrustEvaluateWithError +#ifdef HAVE_BUILTIN_AVAILABLE + if(__builtin_available(macOS 10.14, iOS 12, tvOS 12, watchOS 5, *)) { +#else + if(1) { +#endif + result = SecTrustEvaluateWithError(trust, &error) ? + CURLE_OK : CURLE_PEER_FAILED_VERIFICATION; + if(error) { + VERBOSE(CFIndex code = CFErrorGetCode(error)); + error_ref = CFErrorCopyDescription(error); + + if(error_ref) { + CFIndex size = CFStringGetMaximumSizeForEncoding( + CFStringGetLength(error_ref), kCFStringEncodingUTF8); + err_desc = curlx_malloc(size + 1); + if(err_desc) { + if(!CFStringGetCString(error_ref, err_desc, size, + kCFStringEncodingUTF8)) { + curlx_free(err_desc); + err_desc = NULL; + } + } + } + infof(data, "Apple SecTrust failure %ld%s%s", code, + err_desc ? ": " : "", err_desc ? err_desc : ""); + } + } + else +#endif /* SUPPORTS_SecTrustEvaluateWithError */ + { +#ifndef REQUIRES_SecTrustEvaluateWithError + SecTrustResultType sec_result; + status = SecTrustEvaluate(trust, &sec_result); + + if(status != noErr) { + failf(data, "Apple SecTrust verification failed: error %i", (int)status); + result = CURLE_PEER_FAILED_VERIFICATION; + } + else if((sec_result == kSecTrustResultUnspecified) || + (sec_result == kSecTrustResultProceed)) { + /* "unspecified" means system-trusted with no explicit user setting */ + result = CURLE_OK; + } + else { + /* Any other trust result is a verification failure in this context */ + result = CURLE_PEER_FAILED_VERIFICATION; + } +#endif /* REQUIRES_SecTrustEvaluateWithError */ + } + +out: + curlx_free(err_desc); + if(error_ref) + CFRelease(error_ref); + if(error) + CFRelease(error); + if(host_str) + CFRelease(host_str); + if(policies) + CFRelease(policies); + if(policy) + CFRelease(policy); + if(cert_array) + CFRelease(cert_array); + if(trust) + CFRelease(trust); + return result; +} + +#endif /* USE_APPLE_SECTRUST */ diff --git a/lib/vtls/apple.h b/lib/vtls/apple.h new file mode 100644 index 0000000000..d86a56b1f9 --- /dev/null +++ b/lib/vtls/apple.h @@ -0,0 +1,54 @@ +#ifndef HEADER_CURL_VTLS_APPLE_H +#define HEADER_CURL_VTLS_APPLE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Jan Venekamp, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_APPLE_SECTRUST +struct Curl_cfilter; +struct Curl_easy; +struct ssl_peer; + +/* Get the DER encoded i-th certificate in the server handshake */ +typedef CURLcode Curl_vtls_get_cert_der(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data, + size_t i, + unsigned char **pder, + size_t *pder_len); + +/* Ask Apple's Security framework to verify the certificate chain + * send by the peer. On CURLE_OK it has been verified. + */ +CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + size_t num_certs, + Curl_vtls_get_cert_der *der_cb, + void *cb_user_data, + const unsigned char *ocsp_buf, + size_t ocsp_len); +#endif /* USE_APPLE_SECTRUST */ + +#endif /* HEADER_CURL_VTLS_APPLE_H */ diff --git a/lib/vtls/cipher_suite.c b/lib/vtls/cipher_suite.c index 4bf60c1a4a..1fc6e9f8c6 100644 --- a/lib/vtls/cipher_suite.c +++ b/lib/vtls/cipher_suite.c @@ -21,12 +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 "../curl_printf.h" -#include + +#include "vtls/cipher_suite.h" /* * To support the CURLOPT_SSL_CIPHER_LIST option on SSL backends @@ -132,24 +131,24 @@ enum { CS_TXT_LEN, }; -#define CS_ZIP_IDX(a, b, c, d, e, f, g, h) \ -{ \ - (uint8_t) ((((a) << 2) & 0xFF) | ((b) & 0x3F) >> 4), \ - (uint8_t) ((((b) << 4) & 0xFF) | ((c) & 0x3F) >> 2), \ - (uint8_t) ((((c) << 6) & 0xFF) | ((d) & 0x3F)), \ - (uint8_t) ((((e) << 2) & 0xFF) | ((f) & 0x3F) >> 4), \ - (uint8_t) ((((f) << 4) & 0xFF) | ((g) & 0x3F) >> 2), \ - (uint8_t) ((((g) << 6) & 0xFF) | ((h) & 0x3F)) \ +#define CS_ZIP_IDX(a, b, c, d, e, f, g, h) \ +{ \ + (uint8_t)((((a) << 2) & 0xFF) | ((b) & 0x3F) >> 4), \ + (uint8_t)((((b) << 4) & 0xFF) | ((c) & 0x3F) >> 2), \ + (uint8_t)((((c) << 6) & 0xFF) | ((d) & 0x3F)), \ + (uint8_t)((((e) << 2) & 0xFF) | ((f) & 0x3F) >> 4), \ + (uint8_t)((((f) << 4) & 0xFF) | ((g) & 0x3F) >> 2), \ + (uint8_t)((((g) << 6) & 0xFF) | ((h) & 0x3F)) \ } -#define CS_ENTRY(id, a, b, c, d, e, f, g, h) \ -{ \ - id, \ - CS_ZIP_IDX( \ - CS_TXT_IDX_ ## a, CS_TXT_IDX_ ## b, \ - CS_TXT_IDX_ ## c, CS_TXT_IDX_ ## d, \ - CS_TXT_IDX_ ## e, CS_TXT_IDX_ ## f, \ - CS_TXT_IDX_ ## g, CS_TXT_IDX_ ## h \ - ) \ +#define CS_ENTRY(id, a, b, c, d, e, f, g, h) \ +{ \ + id, \ + CS_ZIP_IDX( \ + CS_TXT_IDX_ ## a, CS_TXT_IDX_ ## b, \ + CS_TXT_IDX_ ## c, CS_TXT_IDX_ ## d, \ + CS_TXT_IDX_ ## e, CS_TXT_IDX_ ## f, \ + CS_TXT_IDX_ ## g, CS_TXT_IDX_ ## h \ + ) \ } struct cs_entry { @@ -158,15 +157,13 @@ struct cs_entry { }; /* !checksrc! disable COMMANOSPACE all */ -static const struct cs_entry cs_list [] = { +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,,,), @@ -233,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,,,), @@ -321,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,,), @@ -339,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,,,,,,), @@ -548,10 +539,9 @@ static const struct cs_entry cs_list [] = { }; #define CS_LIST_LEN CURL_ARRAYSIZE(cs_list) -static int cs_str_to_zip(const char *cs_str, size_t cs_len, - uint8_t zip[6]) +static int cs_str_to_zip(const char *cs_str, size_t cs_len, uint8_t zip[6]) { - uint8_t indexes[8] = {0}; + uint8_t indexes[8] = { 0 }; const char *entry, *cur; const char *nxt = cs_str; const char *end = cs_str + cs_len; @@ -569,7 +559,8 @@ static int cs_str_to_zip(const char *cs_str, size_t cs_len, /* determine the length of the part */ cur = nxt; - for(; nxt < end && *nxt != '\0' && *nxt != separator; nxt++); + for(; nxt < end && *nxt != '\0' && *nxt != separator; nxt++) + ; len = nxt - cur; /* lookup index for the part (skip empty string at 0) */ @@ -582,24 +573,23 @@ static int cs_str_to_zip(const char *cs_str, size_t cs_len, if(idx == CS_TXT_LEN) return -1; - indexes[i++] = (uint8_t) idx; + indexes[i++] = (uint8_t)idx; } while(nxt < end && *(nxt++) != '\0'); /* zip the 8 indexes into 48 bits */ - zip[0] = (uint8_t) (indexes[0] << 2 | (indexes[1] & 0x3F) >> 4); - zip[1] = (uint8_t) (indexes[1] << 4 | (indexes[2] & 0x3F) >> 2); - zip[2] = (uint8_t) (indexes[2] << 6 | (indexes[3] & 0x3F)); - zip[3] = (uint8_t) (indexes[4] << 2 | (indexes[5] & 0x3F) >> 4); - zip[4] = (uint8_t) (indexes[5] << 4 | (indexes[6] & 0x3F) >> 2); - zip[5] = (uint8_t) (indexes[6] << 6 | (indexes[7] & 0x3F)); + zip[0] = (uint8_t)(indexes[0] << 2 | (indexes[1] & 0x3F) >> 4); + zip[1] = (uint8_t)(indexes[1] << 4 | (indexes[2] & 0x3F) >> 2); + zip[2] = (uint8_t)(indexes[2] << 6 | (indexes[3] & 0x3F)); + zip[3] = (uint8_t)(indexes[4] << 2 | (indexes[5] & 0x3F) >> 4); + zip[4] = (uint8_t)(indexes[5] << 4 | (indexes[6] & 0x3F) >> 2); + zip[5] = (uint8_t)(indexes[6] << 6 | (indexes[7] & 0x3F)); return 0; } -static int cs_zip_to_str(const uint8_t zip[6], - char *buf, size_t buf_size) +static int cs_zip_to_str(const uint8_t zip[6], char *buf, size_t buf_size) { - uint8_t indexes[8] = {0}; + uint8_t indexes[8] = { 0 }; const char *entry; char separator = '-'; int idx, i, r; @@ -630,9 +620,9 @@ static int cs_zip_to_str(const uint8_t zip[6], /* append the part string to the buffer */ if(i > 0) - r = msnprintf(&buf[len], buf_size - len, "%c%s", separator, entry); + r = curl_msnprintf(&buf[len], buf_size - len, "%c%s", separator, entry); else - r = msnprintf(&buf[len], buf_size - len, "%s", entry); + r = curl_msnprintf(&buf[len], buf_size - len, "%s", entry); if(r < 0) return -1; @@ -660,13 +650,12 @@ uint16_t Curl_cipher_suite_lookup_id(const char *cs_str, size_t cs_len) static bool cs_is_separator(char c) { switch(c) { - case ' ': - case '\t': - case ':': - case ',': - case ';': - return TRUE; - default:; + case ' ': + case '\t': + case ':': + case ',': + case ';': + return TRUE; } return FALSE; } @@ -674,10 +663,12 @@ static bool cs_is_separator(char c) uint16_t Curl_cipher_suite_walk_str(const char **str, const char **end) { /* move string pointer to first non-separator or end of string */ - for(; cs_is_separator(*str[0]); (*str)++); + for(; cs_is_separator(*str[0]); (*str)++) + ; /* move end pointer to next separator or end of string */ - for(*end = *str; *end[0] != '\0' && !cs_is_separator(*end[0]); (*end)++); + for(*end = *str; *end[0] != '\0' && !cs_is_separator(*end[0]); (*end)++) + ; return Curl_cipher_suite_lookup_id(*str, *end - *str); } @@ -703,7 +694,7 @@ int Curl_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, r = cs_zip_to_str(cs_list[j].zip, buf, buf_size); if(r < 0) - msnprintf(buf, buf_size, "TLS_UNKNOWN_0x%04x", id); + curl_msnprintf(buf, buf_size, "TLS_UNKNOWN_0x%04x", id); return r; } diff --git a/lib/vtls/cipher_suite.h b/lib/vtls/cipher_suite.h index a1dcbc524f..0e185e351e 100644 --- a/lib/vtls/cipher_suite.h +++ b/lib/vtls/cipher_suite.h @@ -23,11 +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/lib/vtls/gtls.c b/lib/vtls/gtls.c index f38c90e66c..1b581cda88 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Source file for all GnuTLS-specific code for the TLS/SSL layer. No code * but vtls.c should ever call or use these functions. @@ -29,8 +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 @@ -40,40 +38,35 @@ #include #include -#include "../urldata.h" -#include "../sendf.h" -#include "../curlx/inet_pton.h" -#include "keylog.h" -#include "gtls.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "../vauth/vauth.h" -#include "../parsedate.h" -#include "../connect.h" /* for the connect timeout */ -#include "../progress.h" -#include "../select.h" -#include "../strdup.h" -#include "../curlx/warnless.h" -#include "x509asn1.h" -#include "../multiif.h" -#include "../curl_printf.h" -#include "../curl_memory.h" -/* The last #include file should be: */ -#include "../memdebug.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) { - fprintf(stderr, "|<%d>| %s", level, str); + curl_mfprintf(stderr, "|<%d>| %s", level, str); } #endif -static bool gtls_inited = FALSE; -#if !defined(GNUTLS_VERSION_NUMBER) || (GNUTLS_VERSION_NUMBER < 0x03010a) +#if !defined(GNUTLS_VERSION_NUMBER) || (GNUTLS_VERSION_NUMBER < 0x030605) #error "too old GnuTLS version" #endif @@ -106,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; @@ -134,13 +127,12 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen) } result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); - CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %d, %zd", - blen, result, nread); + CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %d, %zd", blen, result, nread); backend->gtls.io_result = result; 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) @@ -148,58 +140,55 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen) return (ssize_t)nread; } -/* gtls_init() +/** + * gtls_init() * * Global GnuTLS init, called from Curl_ssl_init(). This calls functions that - * are not thread-safe and thus this function itself is not thread-safe and - * must only be called from within curl_global_init() to keep the thread - * situation under control! + * are not thread-safe (It is thread-safe since GnuTLS 3.3.0) and thus this + * function itself is not thread-safe and must only be called from within + * curl_global_init() to keep the thread situation under control! + * + * @retval 0 error initializing SSL + * @retval 1 SSL initialized successfully */ static int gtls_init(void) { int ret = 1; - if(!gtls_inited) { - ret = gnutls_global_init() ? 0 : 1; + ret = gnutls_global_init() ? 0 : 1; #ifdef GTLSDEBUG - gnutls_global_set_log_function(tls_log_func); - gnutls_global_set_log_level(2); + gnutls_global_set_log_function(tls_log_func); + gnutls_global_set_log_level(2); #endif - gtls_inited = TRUE; - } return ret; } static void gtls_cleanup(void) { - if(gtls_inited) { - gnutls_global_deinit(); - gtls_inited = FALSE; - } + gnutls_global_deinit(); + Curl_tls_keylog_close(); } -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void showtime(struct Curl_easy *data, - const char *text, - time_t stamp) +#ifdef CURLVERBOSE +static void showtime(struct Curl_easy *data, const char *text, time_t stamp) { struct tm buffer; const struct tm *tm = &buffer; char str[96]; - CURLcode result = Curl_gmtime(stamp, &buffer); + CURLcode result = curlx_gmtime(stamp, &buffer); if(result) return; - msnprintf(str, - sizeof(str), - " %s: %s, %02d %s %4d %02d:%02d:%02d GMT", - text, - 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); + curl_msnprintf(str, + sizeof(str), + " %s: %s, %02d %s %4d %02d:%02d:%02d GMT", + text, + 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); infof(data, "%s", str); } #endif @@ -211,35 +200,39 @@ static gnutls_datum_t load_file(const char *file) long filelen; void *ptr; - f = fopen(file, "rb"); + f = curlx_fopen(file, "rb"); if(!f) return loaded_file; - if(fseek(f, 0, SEEK_END) != 0 - || (filelen = ftell(f)) < 0 - || fseek(f, 0, SEEK_SET) != 0 - || !(ptr = malloc((size_t)filelen))) + if(fseek(f, 0, SEEK_END) != 0) + goto out; + filelen = ftell(f); + if(filelen < 0 || filelen > CURL_MAX_INPUT_LENGTH) + goto out; + if(fseek(f, 0, SEEK_SET) != 0) + goto out; + ptr = curlx_malloc((size_t)filelen); + if(!ptr) goto out; if(fread(ptr, 1, (size_t)filelen, f) < (size_t)filelen) { - free(ptr); + curlx_free(ptr); goto out; } loaded_file.data = ptr; loaded_file.size = (unsigned int)filelen; out: - fclose(f); + curlx_fclose(f); return loaded_file; } static void unload_file(gnutls_datum_t data) { - free(data.data); + curlx_free(data.data); } - /* this function does an SSL/TLS (re-)handshake */ -static CURLcode handshake(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode cf_gtls_handshake(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; struct gtls_ssl_backend_data *backend = @@ -269,6 +262,7 @@ static CURLcode handshake(struct Curl_cfilter *cf, return CURLE_AGAIN; } else if((rc < 0) && !gnutls_error_is_fatal(rc)) { +#ifdef CURLVERBOSE const char *strerr = NULL; if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) { @@ -280,6 +274,7 @@ static CURLcode handshake(struct Curl_cfilter *cf, strerr = gnutls_strerror(rc); infof(data, "gnutls_handshake() warning: %s", strerr); +#endif return CURLE_AGAIN; } else if((rc < 0) && backend->gtls.io_result) { @@ -314,16 +309,17 @@ static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type) return GNUTLS_X509_FMT_PEM; /* default to PEM */ } -#define GNUTLS_CIPHERS "NORMAL:-ARCFOUR-128:-CTYPE-ALL:+CTYPE-X509" -/* If GnuTLS was compiled without support for SRP it will error out if SRP is +#define GNUTLS_CIPHERS "NORMAL:%PROFILE_MEDIUM:-ARCFOUR-128:" \ + "-CTYPE-ALL:+CTYPE-X509" +/* If GnuTLS was compiled without support for SRP it errors out if SRP is requested in the priority string, so treat it specially */ #define GNUTLS_SRP "+SRP" #define QUIC_PRIORITY \ - "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \ - "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \ - "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ + "NORMAL:%PROFILE_MEDIUM:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:" \ + "+AES-256-GCM:+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:" \ + "+GROUP-SECP256R1:+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ "%DISABLE_TLS13_COMPAT_MODE" static CURLcode @@ -336,8 +332,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)) @@ -348,7 +344,7 @@ gnutls_set_ssl_version_min_max(struct Curl_easy *data, if(ssl_version_max < CURL_SSLVERSION_MAX_TLSv1_3) { failf(data, "QUIC needs at least TLS version 1.3"); return CURLE_SSL_CONNECT_ERROR; - } + } *prioritylist = QUIC_PRIORITY; return CURLE_OK; } @@ -395,7 +391,7 @@ gnutls_set_ssl_version_min_max(struct Curl_easy *data, return CURLE_OK; } - failf(data, "GnuTLS: cannot set ssl protocol"); + failf(data, "GnuTLS: cannot set TLS protocol"); return CURLE_SSL_CONNECT_ERROR; } @@ -406,19 +402,19 @@ CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data, int rc; *pcreds = NULL; - shared = calloc(1, sizeof(*shared)); + shared = curlx_calloc(1, sizeof(*shared)); if(!shared) return CURLE_OUT_OF_MEMORY; rc = gnutls_certificate_allocate_credentials(&shared->creds); if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc)); - free(shared); + curlx_free(shared); return CURLE_SSL_CONNECT_ERROR; } shared->refcount = 1; - shared->time = curlx_now(); + shared->time = *Curl_pgrs_now(data); *pcreds = shared; return CURLE_OK; } @@ -441,8 +437,8 @@ void Curl_gtls_shared_creds_free(struct gtls_shared_creds **pcreds) --shared->refcount; if(!shared->refcount) { gnutls_certificate_free_credentials(shared->creds); - free(shared->CAfile); - free(shared); + curlx_free(shared->CAfile); + curlx_free(shared); } } } @@ -453,73 +449,110 @@ static CURLcode gtls_populate_creds(struct Curl_cfilter *cf, { struct ssl_primary_config *config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + bool creds_are_empty = TRUE; int rc; - if(config->verifypeer) { - bool imported_native_ca = FALSE; - - if(ssl_config->native_ca_store) { - rc = gnutls_certificate_set_x509_system_trust(creds); - if(rc < 0) - infof(data, "error reading native ca store (%s), continuing anyway", - gnutls_strerror(rc)); - else { - infof(data, "found %d certificates in native ca store", rc); - if(rc > 0) - imported_native_ca = TRUE; - } - } - - if(config->CAfile) { - /* set the trusted CA cert bundle file */ - gnutls_certificate_set_verify_flags(creds, - GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); - - rc = gnutls_certificate_set_x509_trust_file(creds, - config->CAfile, - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - infof(data, "error reading ca cert file %s (%s)%s", - config->CAfile, gnutls_strerror(rc), - (imported_native_ca ? ", continuing anyway" : "")); - if(!imported_native_ca) { - ssl_config->certverifyresult = rc; - return CURLE_SSL_CACERT_BADFILE; - } - } - else - infof(data, "found %d certificates in %s", rc, config->CAfile); - } - - if(config->CApath) { - /* set the trusted CA cert directory */ - rc = gnutls_certificate_set_x509_trust_dir(creds, config->CApath, - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - infof(data, "error reading ca cert file %s (%s)%s", - config->CApath, gnutls_strerror(rc), - (imported_native_ca ? ", continuing anyway" : "")); - if(!imported_native_ca) { - ssl_config->certverifyresult = rc; - return CURLE_SSL_CACERT_BADFILE; - } - } - else - infof(data, "found %d certificates in %s", rc, config->CApath); - } + if(!config->verifypeer) { + infof(data, "SSL Trust: peer verification disabled"); + return CURLE_OK; } + infof(data, "SSL Trust Anchors:"); + if(ssl_config->native_ca_store) { +#ifdef USE_APPLE_SECTRUST + infof(data, " Native: Apple SecTrust"); + creds_are_empty = FALSE; +#else + rc = gnutls_certificate_set_x509_system_trust(creds); + if(rc < 0) + infof(data, "error reading native CA store (%s), continuing anyway", + gnutls_strerror(rc)); + else { + infof(data, " Native: %d certificates from system trust", rc); + if(rc > 0) + creds_are_empty = FALSE; + } +#endif + } + + if(config->ca_info_blob) { + gnutls_datum_t ca_info_datum; + if(config->ca_info_blob->len > (size_t)UINT_MAX) { + failf(data, "certificate blob too long: %zu bytes", + config->ca_info_blob->len); + return CURLE_SSL_CACERT_BADFILE; + } + ca_info_datum.data = config->ca_info_blob->data; + ca_info_datum.size = (unsigned int)config->ca_info_blob->len; + rc = gnutls_certificate_set_x509_trust_mem(creds, &ca_info_datum, + GNUTLS_X509_FMT_PEM); + creds_are_empty = creds_are_empty && (rc <= 0); + if(rc < 0) { + infof(data, "error reading CA cert blob (%s)%s", gnutls_strerror(rc), + (creds_are_empty ? "" : ", continuing anyway")); + if(creds_are_empty) { + ssl_config->certverifyresult = rc; + return CURLE_SSL_CACERT_BADFILE; + } + } + else + infof(data, " CA Blob: %d certificates", rc); + } + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + else if(config->CAfile) { + /* set the trusted CA cert bundle file */ + gnutls_certificate_set_verify_flags(creds, + GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); + + rc = gnutls_certificate_set_x509_trust_file(creds, + config->CAfile, + GNUTLS_X509_FMT_PEM); + creds_are_empty = creds_are_empty && (rc <= 0); + if(rc < 0) { + infof(data, "error reading CA cert file %s (%s)%s", + config->CAfile, gnutls_strerror(rc), + (creds_are_empty ? "" : ", continuing anyway")); + if(creds_are_empty) { + ssl_config->certverifyresult = rc; + return CURLE_SSL_CACERT_BADFILE; + } + } + else + infof(data, " CAfile: %d certificates in %s", rc, config->CAfile); + } + + if(config->CApath) { + /* set the trusted CA cert directory */ + rc = gnutls_certificate_set_x509_trust_dir(creds, config->CApath, + GNUTLS_X509_FMT_PEM); + creds_are_empty = creds_are_empty && (rc <= 0); + if(rc < 0) { + infof(data, "error reading CA cert file %s (%s)%s", + config->CApath, gnutls_strerror(rc), + (creds_are_empty ? "" : ", continuing anyway")); + if(creds_are_empty) { + ssl_config->certverifyresult = rc; + return CURLE_SSL_CACERT_BADFILE; + } + } + else + infof(data, " CApath: %d certificates in %s", rc, config->CApath); + } + + if(creds_are_empty) + infof(data, " no trust anchors configured"); + if(config->CRLfile) { /* set the CRL list file */ rc = gnutls_certificate_set_x509_crl_file(creds, config->CRLfile, GNUTLS_X509_FMT_PEM); if(rc < 0) { - failf(data, "error reading crl file %s (%s)", + failf(data, "error reading CRL file %s (%s)", config->CRLfile, gnutls_strerror(rc)); return CURLE_SSL_CRL_BADFILE; } else - infof(data, "found %d CRL in %s", rc, config->CRLfile); + infof(data, " CRLfile: %d CRL in %s", rc, config->CRLfile); } return CURLE_OK; @@ -528,12 +561,11 @@ static CURLcode gtls_populate_creds(struct Curl_cfilter *cf, /* key to use at `multi->proto_hash` */ #define MPROTO_GTLS_X509_KEY "tls:gtls:x509:share" -static bool gtls_shared_creds_expired(const struct Curl_easy *data, +static bool gtls_shared_creds_expired(struct Curl_easy *data, const struct gtls_shared_creds *sc) { const struct ssl_general_config *cfg = &data->set.general_ssl; - struct curltime now = curlx_now(); - timediff_t elapsed_ms = curlx_timediff(now, sc->time); + timediff_t elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &sc->time); timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; if(timeout_ms < 0) @@ -552,20 +584,20 @@ static bool gtls_shared_creds_different(struct Curl_cfilter *cf, return strcmp(sc->CAfile, conn_config->CAfile); } -static struct gtls_shared_creds* -gtls_get_cached_creds(struct Curl_cfilter *cf, struct Curl_easy *data) +static struct gtls_shared_creds *gtls_get_cached_creds(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct gtls_shared_creds *shared_creds; if(data->multi) { shared_creds = Curl_hash_pick(&data->multi->proto_hash, CURL_UNCONST(MPROTO_GTLS_X509_KEY), - sizeof(MPROTO_GTLS_X509_KEY)-1); - if(shared_creds && shared_creds->creds && - !gtls_shared_creds_expired(data, shared_creds) && - !gtls_shared_creds_different(cf, shared_creds)) { - return shared_creds; - } + sizeof(MPROTO_GTLS_X509_KEY) - 1); + if(shared_creds && shared_creds->creds && + !gtls_shared_creds_expired(data, shared_creds) && + !gtls_shared_creds_different(cf, shared_creds)) { + return shared_creds; + } } return NULL; } @@ -573,7 +605,7 @@ gtls_get_cached_creds(struct Curl_cfilter *cf, struct Curl_easy *data) static void gtls_shared_creds_hash_free(void *key, size_t key_len, void *p) { struct gtls_shared_creds *sc = p; - DEBUGASSERT(key_len == (sizeof(MPROTO_GTLS_X509_KEY)-1)); + DEBUGASSERT(key_len == (sizeof(MPROTO_GTLS_X509_KEY) - 1)); DEBUGASSERT(!memcmp(MPROTO_GTLS_X509_KEY, key, key_len)); (void)key; (void)key_len; @@ -594,7 +626,7 @@ static void gtls_set_cached_creds(struct Curl_cfilter *cf, return; if(conn_config->CAfile) { - sc->CAfile = strdup(conn_config->CAfile); + sc->CAfile = curlx_strdup(conn_config->CAfile); if(!sc->CAfile) return; } @@ -603,9 +635,9 @@ static void gtls_set_cached_creds(struct Curl_cfilter *cf, return; if(!Curl_hash_add2(&data->multi->proto_hash, - CURL_UNCONST(MPROTO_GTLS_X509_KEY), - sizeof(MPROTO_GTLS_X509_KEY)-1, - sc, gtls_shared_creds_hash_free)) { + CURL_UNCONST(MPROTO_GTLS_X509_KEY), + sizeof(MPROTO_GTLS_X509_KEY) - 1, + sc, gtls_shared_creds_hash_free)) { Curl_gtls_shared_creds_free(&sc); /* down reference again */ return; } @@ -622,7 +654,6 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, CURLcode result; int rc; - /* Consider the X509 store cacheable if it comes exclusively from a CAfile, or no source is provided and we are falling back to OpenSSL's built-in default. */ @@ -670,27 +701,26 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, unsigned char *quic_tp, size_t quic_tp_len) { - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct Curl_ssl_session *sc_session; unsigned char *sdata, *qtp_clone = NULL; size_t sdata_len = 0; size_t earlydata_max = 0; CURLcode result = CURLE_OK; - if(!ssl_config->primary.cache_session) + if(!Curl_ssl_scache_use(cf, data)) return CURLE_OK; /* we always unconditionally get the session id here, as even if we already got it from the cache and asked to use it in the connection, it - might've been rejected and then a new one is in use now and we need to + might have been rejected and then a new one is in use now and we need to detect that. */ /* get the session ID data size */ gnutls_session_get_data(session, NULL, &sdata_len); - if(!sdata_len) /* gnutls does this for some version combinations */ + if(!sdata_len) /* GnuTLS does this for some version combinations */ return CURLE_OK; - sdata = malloc(sdata_len); /* get a buffer for it */ + sdata = curlx_malloc(sdata_len); /* get a buffer for it */ if(!sdata) return CURLE_OUT_OF_MEMORY; @@ -702,9 +732,9 @@ 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) { - free(sdata); + curlx_free(sdata); return CURLE_OUT_OF_MEMORY; } } @@ -734,10 +764,8 @@ int Curl_glts_get_ietf_proto(gnutls_session_t session) return CURL_IETF_PROTO_TLS1_1; case GNUTLS_TLS1_2: return CURL_IETF_PROTO_TLS1_2; -#if GNUTLS_VERSION_NUMBER >= 0x030603 case GNUTLS_TLS1_3: return CURL_IETF_PROTO_TLS1_3; -#endif default: return CURL_IETF_PROTO_UNKNOWN; } @@ -797,7 +825,7 @@ static CURLcode gtls_set_priority(struct Curl_cfilter *cf, #ifdef USE_GNUTLS_SRP if(conn_config->username) { /* Only add SRP to the cipher list if SRP is requested. Otherwise - * GnuTLS will disable TLS 1.3 support. */ + * GnuTLS disables TLS 1.3 support. */ result = curlx_dyn_add(&buf, priority); if(!result) result = curlx_dyn_add(&buf, ":" GNUTLS_SRP); @@ -811,7 +839,7 @@ static CURLcode gtls_set_priority(struct Curl_cfilter *cf, if((conn_config->cipher_list[0] == '+') || (conn_config->cipher_list[0] == '-') || (conn_config->cipher_list[0] == '!')) { - /* add it to out own */ + /* add it to out own */ if(!curlx_dyn_len(&buf)) { /* not added yet */ result = curlx_dyn_add(&buf, priority); if(result) @@ -848,20 +876,15 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); unsigned int init_flags; int rc; - bool sni = TRUE; /* default is SNI enabled */ const char *prioritylist; bool tls13support; CURLcode result; - if(!gtls_inited) - gtls_init(); - - if(config->version == CURL_SSLVERSION_SSLv2) { - failf(data, "GnuTLS does not support SSLv2"); + if(config->version == CURL_SSLVERSION_SSLv2 || + config->version == CURL_SSLVERSION_SSLv3) { + failf(data, "GnuTLS does not support SSLv2 or SSLv3"); return CURLE_SSL_CONNECT_ERROR; } - else if(config->version == CURL_SSLVERSION_SSLv3) - sni = FALSE; /* SSLv3 has no SNI */ /* allocate a shared creds struct */ result = Curl_gtls_shared_creds_create(data, >ls->shared_creds); @@ -873,7 +896,12 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, infof(data, "Using TLS-SRP username: %s", config->username); rc = gnutls_srp_allocate_client_credentials(>ls->srp_client_cred); - if(rc != GNUTLS_E_SUCCESS) { + if(rc == GNUTLS_E_UNIMPLEMENTED_FEATURE) { + failf(data, "GnuTLS: TLS-SRP support not built in: %s", + gnutls_strerror(rc)); + return CURLE_NOT_BUILT_IN; + } + else if(rc != GNUTLS_E_SUCCESS) { failf(data, "gnutls_srp_allocate_client_cred() failed: %s", gnutls_strerror(rc)); return CURLE_OUT_OF_MEMORY; @@ -928,7 +956,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - if(sni && peer->sni) { + if(peer->sni) { if(gnutls_server_name_set(gtls->session, GNUTLS_NAME_DNS, peer->sni, strlen(peer->sni)) < 0) { failf(data, "Failed to set SNI"); @@ -944,16 +972,6 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, /* "In GnuTLS 3.6.5, TLS 1.3 is enabled by default" */ tls13support = !!gnutls_check_version("3.6.5"); - /* Ensure +SRP comes at the *end* of all relevant strings so that it can be - * removed if a runtime error indicates that SRP is not supported by this - * GnuTLS version */ - - if(config->version == CURL_SSLVERSION_SSLv2 || - config->version == CURL_SSLVERSION_SSLv3) { - failf(data, "GnuTLS does not support SSLv2 or SSLv3"); - return CURLE_SSL_CONNECT_ERROR; - } - if(config->version == CURL_SSLVERSION_TLSv1_3) { if(!tls13support) { failf(data, "This GnuTLS installation does not support TLS 1.3"); @@ -1070,29 +1088,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 @@ -1120,9 +1123,9 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, Curl_alpn_copy(&alpns, alpns_requested); /* This might be a reconnect, so we check for a session ID in the cache - to speed up things. We need to do this before constructing the gnutls + to speed up things. We need to do this before constructing the GnuTLS session since we need to set flags depending on the kind of reuse. */ - if(conn_config->cache_session) { + if(conn_config->cache_session && !conn_config->verifystatus) { result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs); if(result) goto out; @@ -1142,12 +1145,13 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, else { infof(data, "SSL reusing session with ALPN '%s'", scs->alpn ? scs->alpn : "-"); - if(ssl_config->earlydata && scs->alpn && !cf->conn->connect_only) { + if(ssl_config->earlydata && scs->alpn && + !cf->conn->bits.connect_only) { bool do_early_data = FALSE; if(sess_reuse_cb) { result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data); if(result) - goto out; + goto out; } if(do_early_data) { /* We only try the ALPN protocol the session used before, @@ -1182,7 +1186,7 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, #endif /* convert the ALPN string from our arguments to a list of strings that - * gnutls wants and will convert internally back to this string for sending + * GnuTLS wants and does convert internally back to this string for sending * to the server. nice. */ if(!gtls_alpns_count && alpns.count) { size_t i; @@ -1207,8 +1211,8 @@ out: return result; } -static CURLcode -gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) +static CURLcode gtls_connect_step1(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; struct gtls_ssl_backend_data *backend = @@ -1278,8 +1282,10 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, do { int ret; - /* Begin Gyrations to get the public key */ - gnutls_pubkey_init(&key); + /* Begin Gyrations to get the public key */ + ret = gnutls_pubkey_init(&key); + if(ret < 0) + break; /* failed */ ret = gnutls_pubkey_import_x509(key, cert, 0); if(ret < 0) @@ -1289,7 +1295,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, if(ret != GNUTLS_E_SHORT_MEMORY_BUFFER || len1 == 0) break; /* failed */ - buff1 = malloc(len1); + buff1 = curlx_malloc(len1); if(!buff1) break; /* failed */ @@ -1308,15 +1314,14 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, if(key) gnutls_pubkey_deinit(key); - Curl_safefree(buff1); + curlx_safefree(buff1); return result; } -void Curl_gtls_report_handshake(struct Curl_easy *data, - struct gtls_ctx *gctx) +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); @@ -1369,7 +1374,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; @@ -1438,7 +1443,12 @@ static CURLcode gtls_verify_ocsp_status(struct Curl_easy *data, goto out; } - gnutls_ocsp_resp_init(&ocsp_resp); + rc = gnutls_ocsp_resp_init(&ocsp_resp); + if(rc < 0) { + failf(data, "Failed to initialize OCSP response object"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; + } rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request); if(rc < 0) { @@ -1519,31 +1529,72 @@ out: return result; } -CURLcode -Curl_gtls_verifyserver(struct Curl_easy *data, - gnutls_session_t session, - struct ssl_primary_config *config, - struct ssl_config_data *ssl_config, - struct ssl_peer *peer, - const char *pinned_key) +struct gtls_cert_chain { + const gnutls_datum_t *certs; + unsigned int num_certs; +}; + +#ifdef USE_APPLE_SECTRUST +static CURLcode gtls_chain_get_der(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data, + size_t i, + unsigned char **pder, + size_t *pder_len) { - unsigned int cert_list_size; - const gnutls_datum_t *chainp; - unsigned int verify_status = 0; + struct gtls_cert_chain *chain = user_data; + + (void)cf; + (void)data; + *pder_len = 0; + *pder = NULL; + + if(i >= chain->num_certs) + return CURLE_TOO_LARGE; + *pder = chain->certs[i].data; + *pder_len = (size_t)chain->certs[i].size; + return CURLE_OK; +} + +static CURLcode glts_apple_verify(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + struct gtls_cert_chain *chain, + bool *pverified) +{ + CURLcode result; + + result = Curl_vtls_apple_verify(cf, data, peer, chain->num_certs, + gtls_chain_get_der, chain, NULL, 0); + *pverified = !result; + return result; +} +#endif /* USE_APPLE_SECTRUST */ + +CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, + struct Curl_easy *data, + gnutls_session_t session, + struct ssl_primary_config *config, + struct ssl_config_data *ssl_config, + struct ssl_peer *peer, + const char *pinned_key) +{ + struct gtls_cert_chain chain; gnutls_x509_crt_t x509_cert = NULL, x509_issuer = NULL; time_t certclock; int rc; CURLcode result = CURLE_OK; long * const certverifyresult = &ssl_config->certverifyresult; - /* This function will return the peer's raw certificate (chain) as sent by + (void)cf; + /* This function returns the peer's raw certificate (chain) as sent by the peer. These certificates are in raw format (DER encoded for X.509). In case of a X.509 then a certificate list may be present. The first certificate in the list is the peer's certificate, following the issuer's certificate, then the issuer's issuer etc. */ - chainp = gnutls_certificate_get_peers(session, &cert_list_size); - if(!chainp) { + chain.certs = gnutls_certificate_get_peers(session, &chain.num_certs); + if(!chain.certs) { if(config->verifypeer || config->verifyhost || config->issuercert) { @@ -1566,31 +1617,41 @@ Curl_gtls_verifyserver(struct Curl_easy *data, infof(data, " common name: WARNING could not obtain"); } - if(data->set.ssl.certinfo && chainp) { - unsigned int i; - - result = Curl_ssl_init_certinfo(data, (int)cert_list_size); - if(result) + if(data->set.ssl.certinfo && chain.certs) { + if(chain.num_certs > MAX_ALLOWED_CERT_AMOUNT) { + failf(data, "%u certificates is more than allowed (%u)", + chain.num_certs, MAX_ALLOWED_CERT_AMOUNT); + result = CURLE_SSL_CONNECT_ERROR; goto out; + } + else { + unsigned int i; - for(i = 0; i < cert_list_size; i++) { - const char *beg = (const char *) chainp[i].data; - const char *end = beg + chainp[i].size; - - result = Curl_extract_certinfo(data, (int)i, beg, end); + result = Curl_ssl_init_certinfo(data, (int)chain.num_certs); if(result) goto out; + + for(i = 0; i < chain.num_certs; i++) { + const char *beg = (const char *)chain.certs[i].data; + const char *end = beg + chain.certs[i].size; + + result = Curl_extract_certinfo(data, (int)i, beg, end); + if(result) + goto out; + } } } if(config->verifypeer) { - /* This function will try to verify the peer's certificate and return its - status (trusted, invalid etc.). The value of status should be one or - more of the gnutls_certificate_status_t enumerated elements bitwise - or'd. To avoid denial of service attacks some default upper limits - regarding the certificate key size and chain size are set. To override - them use gnutls_certificate_set_verify_limits(). */ - + bool verified = FALSE; + unsigned int verify_status = 0; + /* This function tries to verify the peer's certificate and return + its status (trusted, invalid etc.). The value of status should be + one or more of the gnutls_certificate_status_t enumerated elements + bitwise or'd. To avoid denial of service attacks some default + upper limits regarding the certificate key size and chain size + are set. To override them use + gnutls_certificate_set_verify_limits(). */ rc = gnutls_certificate_verify_peers2(session, &verify_status); if(rc < 0) { failf(data, "server cert verify failed: %d", rc); @@ -1598,132 +1659,66 @@ Curl_gtls_verifyserver(struct Curl_easy *data, result = CURLE_SSL_CONNECT_ERROR; goto out; } - *certverifyresult = verify_status; + verified = !(verify_status & GNUTLS_CERT_INVALID); + if(verified) + infof(data, " SSL certificate verified by GnuTLS"); - /* verify_status is a bitmask of gnutls_certificate_status bits */ - if(verify_status & GNUTLS_CERT_INVALID) { - if(config->verifypeer) { - const char *cause = "certificate error, no details available"; - if(verify_status & GNUTLS_CERT_EXPIRED) - cause = "certificate has expired"; - else if(verify_status & GNUTLS_CERT_SIGNER_NOT_FOUND) - cause = "certificate signer not trusted"; - else if(verify_status & GNUTLS_CERT_INSECURE_ALGORITHM) - cause = "certificate uses insecure algorithm"; - else if(verify_status & GNUTLS_CERT_INVALID_OCSP_STATUS) - cause = "attached OCSP status response is invalid"; - failf(data, "server verification failed: %s. (CAfile: %s " - "CRLfile: %s)", cause, - config->CAfile ? config->CAfile : "none", - ssl_config->primary.CRLfile ? - ssl_config->primary.CRLfile : "none"); - result = CURLE_PEER_FAILED_VERIFICATION; - goto out; +#ifdef USE_APPLE_SECTRUST + if(!verified && ssl_config->native_ca_store) { + result = glts_apple_verify(cf, data, peer, &chain, &verified); + if(result && (result != CURLE_PEER_FAILED_VERIFICATION)) + goto out; /* unexpected error */ + if(verified) { + infof(data, "SSL certificate verified via Apple SecTrust."); + *certverifyresult = 0; } - else - infof(data, " server certificate verification FAILED"); } - else - infof(data, " server certificate verification OK"); - } - else - infof(data, " server certificate verification SKIPPED"); +#endif - if(config->verifystatus) { - result = gtls_verify_ocsp_status(data, session); - if(result) + if(!verified) { + /* verify_status is a bitmask of gnutls_certificate_status bits */ + const char *cause = "certificate error, no details available"; + if(verify_status & GNUTLS_CERT_EXPIRED) + cause = "certificate has expired"; + else if(verify_status & GNUTLS_CERT_SIGNER_NOT_FOUND) + cause = "certificate signer not trusted"; + else if(verify_status & GNUTLS_CERT_INSECURE_ALGORITHM) + cause = "certificate uses insecure algorithm"; + else if(verify_status & GNUTLS_CERT_INVALID_OCSP_STATUS) + cause = "attached OCSP status response is invalid"; + failf(data, "SSL certificate verification failed: %s. (CAfile: %s " + "CRLfile: %s)", cause, + config->CAfile ? config->CAfile : "none", + ssl_config->primary.CRLfile ? + ssl_config->primary.CRLfile : "none"); + result = CURLE_PEER_FAILED_VERIFICATION; goto out; + } } else - infof(data, " server certificate status verification SKIPPED"); + infof(data, " SSL certificate verification SKIPPED"); /* initialize an X.509 certificate structure. */ - gnutls_x509_crt_init(&x509_cert); + if(gnutls_x509_crt_init(&x509_cert)) { + failf(data, "failed to init GnuTLS x509_crt"); + *certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND; + result = CURLE_SSL_CONNECT_ERROR; + goto out; + } - if(chainp) + if(chain.certs) { /* convert the given DER or PEM encoded Certificate to the native gnutls_x509_crt_t format */ - gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER); - - if(config->issuercert) { - gnutls_datum_t issuerp; - gnutls_x509_crt_init(&x509_issuer); - issuerp = load_file(config->issuercert); - gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM); - rc = (int)gnutls_x509_crt_check_issuer(x509_cert, x509_issuer); - unload_file(issuerp); - if(rc <= 0) { - failf(data, "server certificate issuer check failed (IssuerCert: %s)", - config->issuercert ? config->issuercert : "none"); - result = CURLE_SSL_ISSUER_ERROR; + rc = gnutls_x509_crt_import(x509_cert, chain.certs, GNUTLS_X509_FMT_DER); + if(rc) { + failf(data, "error parsing server's certificate chain"); + *certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND; + result = CURLE_SSL_CONNECT_ERROR; goto out; } - infof(data, " server certificate issuer check OK (Issuer Cert: %s)", - config->issuercert ? config->issuercert : "none"); } - /* This function will check if the given certificate's subject matches the - given hostname. This is a basic implementation of the matching described - in RFC2818 (HTTPS), which takes into account wildcards, and the subject - alternative name PKIX extension. Returns non zero on success, and zero on - failure. */ - - /* This function does not handle trailing dots, so if we have an SNI name - use that and fallback to the hostname only if there is no SNI (like for - IP addresses) */ - rc = (int)gnutls_x509_crt_check_hostname(x509_cert, - peer->sni ? peer->sni : - peer->hostname); -#if GNUTLS_VERSION_NUMBER < 0x030306 - /* Before 3.3.6, gnutls_x509_crt_check_hostname() did not check IP - addresses. */ - if(!rc) { -#ifdef USE_IPV6 - #define use_addr in6_addr -#else - #define use_addr in_addr -#endif - unsigned char addrbuf[sizeof(struct use_addr)]; - size_t addrlen = 0; - - if(curlx_inet_pton(AF_INET, peer->hostname, addrbuf) > 0) - addrlen = 4; -#ifdef USE_IPV6 - else if(curlx_inet_pton(AF_INET6, peer->hostname, addrbuf) > 0) - addrlen = 16; -#endif - - if(addrlen) { - unsigned char certaddr[sizeof(struct use_addr)]; - int i; - - for(i = 0; ; i++) { - size_t certaddrlen = sizeof(certaddr); - int ret = gnutls_x509_crt_get_subject_alt_name(x509_cert, i, certaddr, - &certaddrlen, NULL); - /* If this happens, it was not an IP address. */ - if(ret == GNUTLS_E_SHORT_MEMORY_BUFFER) - continue; - if(ret < 0) - break; - if(ret != GNUTLS_SAN_IPADDRESS) - continue; - if(certaddrlen == addrlen && !memcmp(addrbuf, certaddr, addrlen)) { - rc = 1; - break; - } - } - } - } -#endif - - result = (!rc && config->verifyhost) ? - CURLE_PEER_FAILED_VERIFICATION : CURLE_OK; - gtls_msg_verify_result(data, peer, x509_cert, rc, config->verifyhost); - if(result) - goto out; - /* Check for time-based validity */ certclock = gnutls_x509_crt_get_expiration_time(x509_cert); @@ -1735,7 +1730,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, goto out; } else - infof(data, " server certificate expiration date verify FAILED"); + infof(data, " SSL certificate expiration date verify FAILED"); } else { if(certclock < time(NULL)) { @@ -1746,10 +1741,10 @@ Curl_gtls_verifyserver(struct Curl_easy *data, goto out; } else - infof(data, " server certificate expiration date FAILED"); + infof(data, " SSL certificate expiration date FAILED"); } else - infof(data, " server certificate expiration date OK"); + infof(data, " SSL certificate expiration date OK"); } certclock = gnutls_x509_crt_get_activation_time(x509_cert); @@ -1762,7 +1757,7 @@ Curl_gtls_verifyserver(struct Curl_easy *data, goto out; } else - infof(data, " server certificate activation date verify FAILED"); + infof(data, " SSL certificate activation date verify FAILED"); } else { if(certclock > time(NULL)) { @@ -1773,12 +1768,60 @@ Curl_gtls_verifyserver(struct Curl_easy *data, goto out; } else - infof(data, " server certificate activation date FAILED"); + infof(data, " SSL certificate activation date FAILED"); } else - infof(data, " server certificate activation date OK"); + infof(data, " SSL certificate activation date OK"); } + if(config->verifystatus) { + result = gtls_verify_ocsp_status(data, session); + if(result) + goto out; + } + else + infof(data, " SSL certificate status verification SKIPPED"); + + if(config->issuercert) { + gnutls_datum_t issuerp; + if(gnutls_x509_crt_init(&x509_issuer)) { + failf(data, "failed to init GnuTLS x509_crt for issuer"); + result = CURLE_SSL_ISSUER_ERROR; + goto out; + } + issuerp = load_file(config->issuercert); + rc = gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM); + if(!rc) + rc = (int)gnutls_x509_crt_check_issuer(x509_cert, x509_issuer); + unload_file(issuerp); + if(rc <= 0) { + failf(data, "server certificate issuer check failed (IssuerCert: %s)", + config->issuercert ? config->issuercert : "none"); + result = CURLE_SSL_ISSUER_ERROR; + goto out; + } + infof(data, " SSL certificate issuer check OK (Issuer Cert: %s)", + config->issuercert ? config->issuercert : "none"); + } + + /* This function checks if the given certificate's subject matches the + given hostname. This is a basic implementation of the matching described + in RFC2818 (HTTPS), which takes into account wildcards, and the subject + alternative name PKIX extension. Returns non zero on success, and zero on + failure. */ + + /* This function does not handle trailing dots, so if we have an SNI name + use that and fallback to the hostname only if there is no SNI (like for + IP addresses) */ + rc = (int)gnutls_x509_crt_check_hostname(x509_cert, + peer->sni ? peer->sni : + peer->hostname); + result = (!rc && config->verifyhost) ? + CURLE_PEER_FAILED_VERIFICATION : CURLE_OK; + gtls_msg_verify_result(data, peer, x509_cert, rc, config->verifyhost); + if(result) + goto out; + if(pinned_key) { result = pkp_pin_peer_pubkey(data, x509_cert, pinned_key); if(result != CURLE_OK) { @@ -1813,7 +1856,7 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf, #endif CURLcode result; - result = Curl_gtls_verifyserver(data, session, conn_config, ssl_config, + result = Curl_gtls_verifyserver(cf, data, session, conn_config, ssl_config, &connssl->peer, pinned_key); if(result) goto out; @@ -1835,7 +1878,7 @@ static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf, { struct ssl_connect_data *connssl = cf->ctx; struct gtls_ssl_backend_data *backend = - (struct gtls_ssl_backend_data *)connssl->backend; + (struct gtls_ssl_backend_data *)connssl->backend; CURLcode result = CURLE_OK; const unsigned char *buf; size_t blen; @@ -1845,8 +1888,7 @@ static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf, backend->gtls.io_result = CURLE_OK; while(Curl_bufq_peek(&connssl->earlydata, &buf, &blen)) { n = gnutls_record_send_early_data(backend->gtls.session, buf, blen); - CURL_TRC_CF(data, cf, "gtls_send_earlydata(len=%zu) -> %zd", - blen, n); + CURL_TRC_CF(data, cf, "gtls_send_earlydata(len=%zu) -> %zd", blen, n); if(n < 0) { if(n == GNUTLS_E_AGAIN) result = CURLE_AGAIN; @@ -1856,7 +1898,7 @@ static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf, goto out; } else if(!n) { - /* gnutls is buggy, it *SHOULD* return the amount of bytes it took in. + /* GnuTLS is buggy, it *SHOULD* return the amount of bytes it took in. * Instead it returns 0 if everything was written. */ n = (ssize_t)blen; } @@ -1881,10 +1923,11 @@ out: */ static CURLcode gtls_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, - bool *done) { + bool *done) +{ struct ssl_connect_data *connssl = cf->ctx; struct gtls_ssl_backend_data *backend = - (struct gtls_ssl_backend_data *)connssl->backend; + (struct gtls_ssl_backend_data *)connssl->backend; CURLcode result = CURLE_OK; DEBUGASSERT(backend); @@ -1918,7 +1961,7 @@ static CURLcode gtls_connect_common(struct Curl_cfilter *cf, DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) || (connssl->earlydata_state == ssl_earlydata_sent)); #endif - result = handshake(cf, data); + result = cf_gtls_handshake(cf, data); if(result) goto out; connssl->connecting_state = ssl_connect_3; @@ -2019,7 +2062,6 @@ static CURLcode gtls_send(struct Curl_cfilter *cf, ssize_t nwritten; size_t remain = blen; - (void)data; DEBUGASSERT(backend); *pnwritten = 0; @@ -2064,11 +2106,11 @@ static CURLcode gtls_shutdown(struct Curl_cfilter *cf, (struct gtls_ssl_backend_data *)connssl->backend; char buf[1024]; CURLcode result = CURLE_OK; - ssize_t nread; + ssize_t nread = 0; size_t i; DEBUGASSERT(backend); - /* If we have no handshaked connection or already shut down */ + /* If we have no handshaked connection or already shut down */ if(!backend->gtls.session || cf->shutdown || connssl->state != ssl_connection_complete) { *done = TRUE; @@ -2136,7 +2178,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) { @@ -2165,7 +2206,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); @@ -2178,11 +2218,10 @@ static CURLcode gtls_recv(struct Curl_cfilter *cf, goto out; } else if(nread == GNUTLS_E_REHANDSHAKE) { - /* BLOCKING call, this is bad but a work-around for now. Fixing this "the - proper way" takes a whole lot of work. */ - result = handshake(cf, data); + /* Either TLSv1.2 renegotiate or a TLSv1.3 session key update. */ + result = cf_gtls_handshake(cf, data); if(!result) - result = CURLE_AGAIN; /* then return as if this was a wouldblock */ + result = CURLE_AGAIN; /* make us get called again. */ goto out; } else { @@ -2195,13 +2234,13 @@ static CURLcode gtls_recv(struct Curl_cfilter *cf, } out: - CURL_TRC_CF(data, cf, "gtls_recv(len=%zu) -> 0, %zu", blen, *pnread); + CURL_TRC_CF(data, cf, "gtls_recv(len=%zu) -> 0, %zd", blen, nread); return result; } size_t Curl_gtls_version(char *buffer, size_t size) { - return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL)); + return curl_msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL)); } /* data might be NULL! */ @@ -2248,8 +2287,11 @@ const struct Curl_ssl Curl_ssl_gnutls = { SSLSUPP_CERTINFO | SSLSUPP_PINNEDPUBKEY | 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/lib/vtls/gtls.h b/lib/vtls/gtls.h index 01f8b43ac8..49106dd869 100644 --- a/lib/vtls/gtls.h +++ b/lib/vtls/gtls.h @@ -23,14 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" -#include +#include "curl_setup.h" #ifdef USE_GNUTLS #include -#include "../curlx/timeval.h" + +#include "curlx/timeval.h" #ifdef HAVE_GNUTLS_SRP /* the function exists */ @@ -90,7 +89,7 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, - const struct alpn_spec *alpns, + const struct alpn_spec *alpns_requested, Curl_gtls_ctx_setup_cb *cb_setup, void *cb_user_data, void *ssl_user_data, @@ -100,7 +99,8 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, struct Curl_easy *data, struct gtls_ctx *gtls); -CURLcode Curl_gtls_verifyserver(struct Curl_easy *data, +CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, + struct Curl_easy *data, gnutls_session_t session, struct ssl_primary_config *config, struct ssl_config_data *ssl_config, @@ -118,8 +118,7 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, size_t quic_tp_len); /* Report properties of a successful handshake */ -void Curl_gtls_report_handshake(struct Curl_easy *data, - struct gtls_ctx *gctx); +void Curl_gtls_report_handshake(struct Curl_easy *data, struct gtls_ctx *gctx); extern const struct Curl_ssl Curl_ssl_gnutls; diff --git a/lib/vtls/hostcheck.c b/lib/vtls/hostcheck.c index 23ba33951f..ad09b83194 100644 --- a/lib/vtls/hostcheck.c +++ b/lib/vtls/hostcheck.c @@ -21,8 +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 */ @@ -33,13 +32,10 @@ #ifdef HAVE_NETINET_IN6_H #include #endif -#include "../curl_memrchr.h" -#include "hostcheck.h" -#include "../hostip.h" -#include "../curl_memory.h" -/* The last #include file should be: */ -#include "../memdebug.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 */ @@ -88,16 +84,16 @@ static bool hostmatch(const char *hostname, DEBUGASSERT(hostlen); /* normalize pattern and hostname by stripping off trailing dots */ - if(hostname[hostlen-1]=='.') + if(hostname[hostlen - 1] == '.') hostlen--; - if(pattern[patternlen-1]=='.') + if(pattern[patternlen - 1] == '.') patternlen--; if(strncmp(pattern, "*.", 2)) return pmatch(hostname, hostlen, pattern, patternlen); - /* detect IP address as hostname and fail the match if so */ - else if(Curl_host_is_ipnum(hostname)) + /* detect host as IP address or starting with a dot and fail if so */ + else if(Curl_host_is_ipnum(hostname) || (hostname[0] == '.')) return FALSE; /* We require at least 2 dots in the pattern to avoid too wide wildcard @@ -129,4 +125,4 @@ bool Curl_cert_hostcheck(const char *match, size_t matchlen, return FALSE; } -#endif /* OPENSSL or SCHANNEL */ +#endif /* USE_OPENSSL || USE_SCHANNEL */ diff --git a/lib/vtls/hostcheck.h b/lib/vtls/hostcheck.h index b843d09c65..d8e1a52377 100644 --- a/lib/vtls/hostcheck.h +++ b/lib/vtls/hostcheck.h @@ -23,13 +23,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include +#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/lib/vtls/keylog.c b/lib/vtls/keylog.c index 2fd25089d9..aa37814e06 100644 --- a/lib/vtls/keylog.c +++ b/lib/vtls/keylog.c @@ -21,35 +21,24 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" -#if defined(USE_OPENSSL) || \ - defined(USE_GNUTLS) || \ - defined(USE_WOLFSSL) || \ - (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \ - defined(USE_QUICHE) || \ +#if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ defined(USE_RUSTLS) -#include "keylog.h" -#include -#include "../escape.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.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; -void -Curl_tls_keylog_open(void) +void Curl_tls_keylog_open(void) { - char *keylog_file_name; - if(!keylog_file_fp) { - keylog_file_name = curl_getenv("SSLKEYLOGFILE"); + char *keylog_file_name = curl_getenv("SSLKEYLOGFILE"); if(keylog_file_name) { - keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT); + keylog_file_fp = curlx_fopen(keylog_file_name, FOPEN_APPENDTEXT); if(keylog_file_fp) { #ifdef _WIN32 if(setvbuf(keylog_file_fp, NULL, _IONBF, 0)) @@ -57,32 +46,29 @@ Curl_tls_keylog_open(void) if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096)) #endif { - fclose(keylog_file_fp); + curlx_fclose(keylog_file_fp); keylog_file_fp = NULL; } } - Curl_safefree(keylog_file_name); + curlx_safefree(keylog_file_name); } } } -void -Curl_tls_keylog_close(void) +void Curl_tls_keylog_close(void) { if(keylog_file_fp) { - fclose(keylog_file_fp); + curlx_fclose(keylog_file_fp); keylog_file_fp = NULL; } } -bool -Curl_tls_keylog_enabled(void) +bool Curl_tls_keylog_enabled(void) { return keylog_file_fp != NULL; } -bool -Curl_tls_keylog_write_line(const char *line) +bool Curl_tls_keylog_write_line(const char *line) { /* The current maximum valid keylog line length LF and NUL is 195. */ size_t linelen; @@ -110,14 +96,14 @@ Curl_tls_keylog_write_line(const char *line) return TRUE; } -bool -Curl_tls_keylog_write(const char *label, - const unsigned char client_random[CLIENT_RANDOM_SIZE], - const unsigned char *secret, size_t secretlen) +bool Curl_tls_keylog_write(const char *label, + const unsigned char client_random[CLIENT_RANDOM_SIZE], + 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; @@ -153,4 +139,4 @@ Curl_tls_keylog_write(const char *label, return TRUE; } -#endif /* TLS or QUIC backend */ +#endif /* TLS backend */ diff --git a/lib/vtls/keylog.h b/lib/vtls/keylog.h index ec82abf547..c1563243fb 100644 --- a/lib/vtls/keylog.h +++ b/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) @@ -57,8 +57,8 @@ bool Curl_tls_keylog_enabled(void); * Returns true iff the key log file is open and a valid entry was provided. */ bool Curl_tls_keylog_write(const char *label, - const unsigned char client_random[32], - const unsigned char *secret, size_t secretlen); + const unsigned char client_random[CLIENT_RANDOM_SIZE], + const unsigned char *secret, size_t secretlen); /* * Appends a line to the key log file, ensure it is terminated by an LF. diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 7b1a31e42f..1cac66041d 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -22,14 +22,12 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Source file for all mbedTLS-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_MBEDTLS @@ -38,48 +36,45 @@ #include #if MBEDTLS_VERSION_NUMBER < 0x03020000 - #error "mbedTLS 3.2.0 or later required" +#error "mbedTLS 3.2.0 or later required" #endif +#include #include #include #include +#include + +#if MBEDTLS_VERSION_NUMBER < 0x04000000 && !defined(MBEDTLS_CTR_DRBG_C) +#error "MBEDTLS_CTR_DRBG_C is required for mbedTLS 3.x." +#endif #include +#if MBEDTLS_VERSION_NUMBER < 0x04000000 #include #include -#include +#endif #ifdef MBEDTLS_DEBUG #include #endif -#include "cipher_suite.h" -#include "../urldata.h" -#include "../sendf.h" -#include "../curlx/inet_pton.h" -#include "mbedtls.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "x509asn1.h" -#include "../parsedate.h" -#include "../connect.h" /* for the connect timeout */ -#include "../select.h" -#include "../multiif.h" -#include "mbedtls_threadlock.h" -#include "../strdup.h" -/* The last 3 #include files should be in this order */ -#include "../curl_printf.h" -#include "../curl_memory.h" -#include "../memdebug.h" +#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) -# define HAS_ALPN_MBEDTLS +#define HAS_ALPN_MBEDTLS #endif struct mbed_ssl_backend_data { - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_entropy_context entropy; mbedtls_ssl_context ssl; mbedtls_x509_crt cacert; mbedtls_x509_crt clicert; @@ -98,55 +93,21 @@ struct mbed_ssl_backend_data { BIT(send_blocked); }; -/* apply threading? */ -#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || defined(_WIN32) -#define HAS_THREADING_SUPPORT +/** A context for random number generation (RNG). + */ +#if MBEDTLS_VERSION_NUMBER < 0x04000000 +struct rng_context_t { + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context drbg; +}; + +static struct rng_context_t rng; #endif #ifndef MBEDTLS_ERROR_C -#define mbedtls_strerror(a,b,c) b[0] = 0 +#define mbedtls_strerror(a, b, c) b[0] = 0 #endif -#ifdef HAS_THREADING_SUPPORT -static mbedtls_entropy_context ts_entropy; - -static int entropy_init_initialized = 0; - -static void entropy_init_mutex(mbedtls_entropy_context *ctx) -{ - /* lock 0 = entropy_init_mutex() */ - Curl_mbedtlsthreadlock_lock_function(0); - if(entropy_init_initialized == 0) { - mbedtls_entropy_init(ctx); - entropy_init_initialized = 1; - } - Curl_mbedtlsthreadlock_unlock_function(0); -} - -static void entropy_cleanup_mutex(mbedtls_entropy_context *ctx) -{ - /* lock 0 = use same lock as init */ - Curl_mbedtlsthreadlock_lock_function(0); - if(entropy_init_initialized == 1) { - mbedtls_entropy_free(ctx); - entropy_init_initialized = 0; - } - Curl_mbedtlsthreadlock_unlock_function(0); -} - -static int entropy_func_mutex(void *data, unsigned char *output, size_t len) -{ - int ret; - /* lock 1 = entropy_func_mutex() */ - Curl_mbedtlsthreadlock_lock_function(1); - ret = mbedtls_entropy_func(data, output, len); - Curl_mbedtlsthreadlock_unlock_function(1); - - return ret; -} - -#endif /* HAS_THREADING_SUPPORT */ - #ifdef MBEDTLS_DEBUG static void mbed_debug(void *context, int level, const char *f_name, int line_nb, const char *line) @@ -178,11 +139,10 @@ static int mbedtls_bio_cf_write(void *bio, if(!data) return 0; - result = Curl_conn_cf_send(cf->next, data, (const char *)buf, blen, FALSE, - &nwritten); + 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; } @@ -191,7 +151,7 @@ static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen) { struct Curl_cfilter *cf = bio; struct Curl_easy *data = CF_DATA_CURRENT(cf); - size_t nread; + size_t nread = 0; CURLcode result; DEBUGASSERT(data); @@ -204,33 +164,17 @@ 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; } -/* - * profile +/* See: + * https://web.archive.org/web/20200921194007/tls.mbed.org/discussions/generic/howto-determine-exact-buffer-len-for-mbedtls_pk_write_pubkey_der */ -static const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_fr = -{ - /* Hashes from SHA-1 and above */ - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA1) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_RIPEMD160) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA224) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA384) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA512), - 0xFFFFFFF, /* Any PK alg */ - 0xFFFFFFF, /* Any curve */ - 1024, /* RSA min key len */ -}; - -/* 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) @@ -240,12 +184,13 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, struct mbed_ssl_backend_data *backend, struct ssl_primary_config *conn_config) { - /* TLS 1.0 and TLS 1.1 were dropped with mbedTLS 3.0.0 (2021). So, since - * then, and before the introduction of TLS 1.3 in 3.6.0 (2024), this - * function basically always sets TLS 1.2 as min/max, unless given - * unsupported option values. */ - - mbedtls_ssl_protocol_version ver_min = MBEDTLS_SSL_VERSION_TLS1_2; + mbedtls_ssl_protocol_version ver_min = +#ifdef MBEDTLS_SSL_PROTO_TLS1_2 + MBEDTLS_SSL_VERSION_TLS1_2 +#else + MBEDTLS_SSL_VERSION_TLS1_3 +#endif + ; mbedtls_ssl_protocol_version ver_max = #ifdef MBEDTLS_SSL_PROTO_TLS1_3 MBEDTLS_SSL_VERSION_TLS1_3 @@ -254,14 +199,16 @@ 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: case CURL_SSLVERSION_TLSv1_2: +#ifdef MBEDTLS_SSL_PROTO_TLS1_2 ver_min = MBEDTLS_SSL_VERSION_TLS1_2; break; +#endif case CURL_SSLVERSION_TLSv1_3: #ifdef MBEDTLS_SSL_PROTO_TLS1_3 ver_min = MBEDTLS_SSL_VERSION_TLS1_3; @@ -281,9 +228,11 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, ver_max = MBEDTLS_SSL_VERSION_TLS1_3; break; #endif +#ifdef MBEDTLS_SSL_PROTO_TLS1_2 case CURL_SSLVERSION_MAX_TLSv1_2: ver_max = MBEDTLS_SSL_VERSION_TLS1_2; break; +#endif case CURL_SSLVERSION_MAX_TLSv1_1: case CURL_SSLVERSION_MAX_TLSv1_0: default: @@ -297,30 +246,30 @@ 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) +static int mbed_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, + bool prefer_rfc) { if(id == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8) - msnprintf(buf, buf_size, "%s", "TLS_ECJPAKE_WITH_AES_128_CCM_8"); + curl_msnprintf(buf, buf_size, "%s", "TLS_ECJPAKE_WITH_AES_128_CCM_8"); else return Curl_cipher_suite_get_str(id, buf, buf_size, prefer_rfc); return 0; } -static uint16_t -mbed_cipher_suite_walk_str(const char **str, const char **end) +static uint16_t mbed_cipher_suite_walk_str(const char **str, const char **end) { uint16_t id = Curl_cipher_suite_walk_str(str, end); size_t len = *end - *str; + static const char ecjpake_suite[] = "TLS_ECJPAKE_WITH_AES_128_CCM_8"; if(!id) { - if(curl_strnequal("TLS_ECJPAKE_WITH_AES_128_CCM_8", *str, len)) + if((len == sizeof(ecjpake_suite) - 1) && + curl_strnequal(ecjpake_suite, *str, len)) id = MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8; } return id; @@ -343,10 +292,11 @@ mbed_set_selected_ciphers(struct Curl_easy *data, const char *ptr, *end; supported = mbedtls_ssl_list_ciphersuites(); - for(i = 0; supported[i] != 0; i++); + for(i = 0; supported[i] != 0; i++) + ; supported_len = i; - selected = malloc(sizeof(int) * (supported_len + 1)); + selected = curlx_malloc(sizeof(int) * (supported_len + 1)); if(!selected) return CURLE_OUT_OF_MEMORY; @@ -356,7 +306,7 @@ mbed_set_selected_ciphers(struct Curl_easy *data, if(!ciphers13) { /* Add default TLSv1.3 ciphers to selection */ for(j = 0; j < supported_len; j++) { - uint16_t id = (uint16_t) supported[j]; + uint16_t id = (uint16_t)supported[j]; if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6) != 0) continue; @@ -375,23 +325,25 @@ add_ciphers: /* Check if cipher is supported */ if(id) { - for(i = 0; i < supported_len && supported[i] != id; i++); + for(i = 0; i < supported_len && supported[i] != id; i++) + ; if(i == supported_len) id = 0; } if(!id) { if(ptr[0] != '\0') infof(data, "mbedTLS: unknown cipher in list: \"%.*s\"", - (int) (end - ptr), ptr); + (int)(end - ptr), ptr); continue; } /* No duplicates allowed (so selected cannot overflow) */ - for(i = 0; i < count && selected[i] != id; i++); + for(i = 0; i < count && selected[i] != id; i++) + ; if(i < count) { if(i >= default13_count) infof(data, "mbedTLS: duplicate cipher in list: \"%.*s\"", - (int) (end - ptr), ptr); + (int)(end - ptr), ptr); continue; } @@ -407,12 +359,13 @@ add_ciphers: if(!ciphers12) { /* Add default TLSv1.2 ciphers to selection */ for(j = 0; j < supported_len; j++) { - uint16_t id = (uint16_t) supported[j]; + uint16_t id = (uint16_t)supported[j]; if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6) == 0) continue; /* No duplicates allowed (so selected cannot overflow) */ - for(i = 0; i < count && selected[i] != id; i++); + for(i = 0; i < count && selected[i] != id; i++) + ; if(i < count) continue; @@ -424,7 +377,7 @@ add_ciphers: selected[count] = 0; if(count == 0) { - free(selected); + curlx_free(selected); failf(data, "mbedTLS: no supported cipher in list"); return CURLE_SSL_CIPHER; } @@ -436,42 +389,51 @@ add_ciphers: return CURLE_OK; } -static void -mbed_dump_cert_info(struct Curl_easy *data, const mbedtls_x509_crt *crt) +static void mbed_dump_cert_info(struct Curl_easy *data, + const mbedtls_x509_crt *crt) { -#if defined(CURL_DISABLE_VERBOSE_STRINGS) || defined(MBEDTLS_X509_REMOVE_INFO) +#if !defined(CURLVERBOSE) || defined(MBEDTLS_X509_REMOVE_INFO) (void)data, (void)crt; #else const size_t bufsize = 16384; - char *p, *buffer = malloc(bufsize); + char *p, *buffer = curlx_malloc(bufsize); if(buffer && mbedtls_x509_crt_info(buffer, bufsize, " ", crt) > 0) { infof(data, "Server certificate:"); for(p = buffer; *p; p += *p != '\0') { size_t s = strcspn(p, "\n"); - infof(data, "%.*s", (int) s, p); + infof(data, "%.*s", (int)s, p); p += s; } } else infof(data, "Unable to dump certificate information"); - free(buffer); + curlx_free(buffer); #endif } -static void -mbed_extract_certinfo(struct Curl_easy *data, const mbedtls_x509_crt *crt) +static void mbed_extract_certinfo(struct Curl_easy *data, + const mbedtls_x509_crt *crt) { CURLcode result; const mbedtls_x509_crt *cur; + int cert_count = 0; int i; - for(i = 0, cur = crt; cur; ++i, cur = cur->next); - result = Curl_ssl_init_certinfo(data, i); + for(cur = crt; cur && cert_count <= MAX_ALLOWED_CERT_AMOUNT; cur = cur->next) + cert_count++; + + if(cert_count > MAX_ALLOWED_CERT_AMOUNT) { + infof(data, "Certificates is more than allowed (%u), skipping certinfo", + MAX_ALLOWED_CERT_AMOUNT); + return; + } + + result = Curl_ssl_init_certinfo(data, cert_count); for(i = 0, cur = crt; result == CURLE_OK && cur; ++i, cur = cur->next) { - const char *beg = (const char *) cur->raw.p; + const char *beg = (const char *)cur->raw.p; const char *end = beg + cur->raw.len; result = Curl_extract_certinfo(data, i, beg, end); } @@ -480,7 +442,7 @@ mbed_extract_certinfo(struct Curl_easy *data, const mbedtls_x509_crt *crt) static int mbed_verify_cb(void *ptr, mbedtls_x509_crt *crt, int depth, uint32_t *flags) { - struct Curl_cfilter *cf = (struct Curl_cfilter *) ptr; + struct Curl_cfilter *cf = (struct Curl_cfilter *)ptr; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct Curl_easy *data = CF_DATA_CURRENT(cf); @@ -509,78 +471,64 @@ 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; - const char * const ssl_crlfile = ssl_config->primary.CRLfile; - const char *hostname = connssl->peer.hostname; +#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 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; - } - -#ifdef HAS_THREADING_SUPPORT - mbedtls_ctr_drbg_init(&backend->ctr_drbg); - - ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, entropy_func_mutex, - &ts_entropy, NULL, 0); - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s", - -ret, errorbuf); - return CURLE_FAILED_INIT; - } -#else - mbedtls_entropy_init(&backend->entropy); - mbedtls_ctr_drbg_init(&backend->ctr_drbg); - - ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, mbedtls_entropy_func, - &backend->entropy, NULL, 0); - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s", - -ret, errorbuf); - return CURLE_FAILED_INIT; - } -#endif /* HAS_THREADING_SUPPORT */ - - /* Load the trusted CA */ mbedtls_x509_crt_init(&backend->cacert); if(ca_info_blob && verifypeer) { - /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null - terminated even when provided the exact length, forcing us to waste - extra memory here. */ - unsigned char *newblob = Curl_memdup0(ca_info_blob->data, - ca_info_blob->len); - if(!newblob) - return CURLE_OUT_OF_MEMORY; - ret = mbedtls_x509_crt_parse(&backend->cacert, newblob, - ca_info_blob->len + 1); - free(newblob); +#ifdef MBEDTLS_PEM_PARSE_C + /* 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, + 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 = curlx_memdup0(ca_info_blob->data, + ca_info_blob->len); + if(!newblob) + return CURLE_OUT_OF_MEMORY; + + ret = mbedtls_x509_crt_parse(&backend->cacert, newblob, + ca_info_blob->len + 1); + curlx_free(newblob); + } +#else + /* DER encoded certs do not need to be null-terminated because it is a + binary format. Thus, 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 + if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error importing ca cert blob - mbedTLS: (-0x%04X) %s", + failf(data, "mbedTLS: error importing CA cert blob: (-0x%04X) %s", -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } @@ -592,12 +540,12 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading ca cert file %s - mbedTLS: (-0x%04X) %s", + failf(data, "mbedTLS: error reading CA cert file %s: (-0x%04X) %s", ssl_cafile, -ret, errorbuf); return CURLE_SSL_CACERT_BADFILE; } #else - failf(data, "mbedtls: functions that use the file system not built in"); + failf(data, "mbedTLS: functions that use the file system not built in"); return CURLE_NOT_BUILT_IN; #endif } @@ -608,19 +556,36 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading ca cert path %s - mbedTLS: (-0x%04X) %s", + failf(data, "mbedTLS: error reading CA cert path %s: (-0x%04X) %s", ssl_capath, -ret, errorbuf); if(verifypeer) return CURLE_SSL_CACERT_BADFILE; } #else - failf(data, "mbedtls: functions that use the file system not built in"); + failf(data, "mbedTLS: functions that use the file system not built in"); return CURLE_NOT_BUILT_IN; #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) { @@ -629,59 +594,106 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading client cert file %s - mbedTLS: (-0x%04X) %s", + failf(data, "mbedTLS: error reading client cert file %s: (-0x%04X) %s", ssl_cert, -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } #else - failf(data, "mbedtls: functions that use the file system not built in"); + failf(data, "mbedTLS: functions that use the file system not built in"); return CURLE_NOT_BUILT_IN; #endif } if(ssl_cert_blob) { - /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null - terminated even when provided the exact length, forcing us to waste - extra memory here. */ - unsigned char *newblob = Curl_memdup0(ssl_cert_blob->data, - ssl_cert_blob->len); - if(!newblob) - return CURLE_OUT_OF_MEMORY; - ret = mbedtls_x509_crt_parse(&backend->clicert, newblob, - ssl_cert_blob->len + 1); - free(newblob); +#ifdef MBEDTLS_PEM_PARSE_C + /* 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') { + + ret = mbedtls_x509_crt_parse(&backend->clicert, + ssl_cert_blob->data, + ssl_cert_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 = 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, + ssl_cert_blob->len + 1); + curlx_free(newblob); + } +#else + /* DER encoded certs do not need to be null-terminated because it is a + binary format. Thus, 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, "Error reading client cert data %s - mbedTLS: (-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) { if(ssl_config->key) { #ifdef MBEDTLS_FS_IO +#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))) + ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; +#else ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key, ssl_config->key_passwd, mbedtls_ctr_drbg_random, - &backend->ctr_drbg); + &rng.drbg); if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) || mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; +#endif if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s", + failf(data, "mbedTLS: error reading private key %s: (-0x%04X) %s", ssl_config->key, -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } #else - failf(data, "mbedtls: functions that use the file system not built in"); + failf(data, "mbedTLS: functions that use the file system not built in"); return CURLE_NOT_BUILT_IN; #endif } @@ -690,52 +702,96 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) const unsigned char *key_data = (const unsigned char *)ssl_key_blob->data; const char *passwd = ssl_config->key_passwd; +#if MBEDTLS_VERSION_NUMBER >= 0x04000000 + 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))) + ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; +#else ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, (const unsigned char *)passwd, passwd ? strlen(passwd) : 0, mbedtls_ctr_drbg_random, - &backend->ctr_drbg); + &rng.drbg); if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) || mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; +#endif if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error parsing private key - mbedTLS: (-0x%04X) %s", + failf(data, "mbedTLS: error parsing private key: (-0x%04X) %s", -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } } - /* 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)); - failf(data, "Error reading CRL file %s - mbedTLS: (-0x%04X) %s", + failf(data, "mbedTLS: error reading CRL file %s: (-0x%04X) %s", ssl_crlfile, -ret, errorbuf); return CURLE_SSL_CRL_BADFILE; } #else - failf(data, "mbedtls: functions that use the file system not built in"); + (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"); + 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, @@ -747,10 +803,26 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } -#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); +#ifdef MBEDTLS_DEBUG + /* In order to make that work in mbedtls MBEDTLS_DEBUG_C must be defined. */ + mbedtls_ssl_conf_dbg(&backend->config, mbed_debug, data); + /* - 0 No debug + * - 1 Error + * - 2 State change + * - 3 Informational + * - 4 Verbose + */ + mbedtls_debug_set_threshold(4); +#endif + +#if defined(MBEDTLS_SSL_SESSION_TICKETS) && \ + 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 @@ -762,22 +834,25 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) mbedtls_ssl_init(&backend->ssl); backend->initialized = TRUE; - /* new profile with RSA min key len = 1024 ... */ + /* use the default secure profile baked into mbedTLS */ mbedtls_ssl_conf_cert_profile(&backend->config, - &mbedtls_x509_crt_profile_fr); + &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, - &backend->ctr_drbg); + &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, "ssl_setup failed - mbedTLS: (-0x%04X) %s", - -ret, errorbuf); + failf(data, "mbedTLS: ssl_setup failed: " + "(-0x%04X) %s", -ret, errorbuf); return CURLE_SSL_CONNECT_ERROR; } @@ -788,16 +863,15 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #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; } @@ -807,7 +881,6 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) mbedtls_ssl_list_ciphersuites()); } - #ifdef MBEDTLS_SSL_RENEGOTIATION mbedtls_ssl_conf_renegotiation(&backend->config, MBEDTLS_SSL_RENEGOTIATION_ENABLED); @@ -819,13 +892,11 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #endif /* Check if there is a cached ID we can/should use here! */ - if(ssl_config->primary.cache_session) { + 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); @@ -843,27 +914,28 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } 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 ? connssl->peer.sni : connssl->peer.hostname)) { /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and - the name to set in the SNI extension. So even if curl connects to a - host specified as an IP address, this function must be used. */ + the name to set in the SNI extension. Thus even if curl connects to + a host specified as an IP address, this function must be used. */ failf(data, "Failed to set SNI"); return CURLE_SSL_CONNECT_ERROR; } @@ -888,46 +960,68 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } #endif -#ifdef MBEDTLS_DEBUG - /* In order to make that work in mbedtls MBEDTLS_DEBUG_C must be defined. */ - mbedtls_ssl_conf_dbg(&backend->config, mbed_debug, data); - /* - 0 No debug - * - 1 Error - * - 2 State change - * - 3 Informational - * - 4 Verbose - */ - mbedtls_debug_set_threshold(4); -#endif - /* 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; } -static CURLcode -mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) +#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(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 HAVE_PINNED_PUBKEY #ifndef CURL_DISABLE_PROXY const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf) ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : data->set.str[STRING_SSL_PINNEDPUBLICKEY]; #else const char * const pinnedpubkey = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#endif #endif DEBUGASSERT(backend); @@ -959,16 +1053,16 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) { char cipher_str[64]; uint16_t cipher_id; - cipher_id = (uint16_t) - mbedtls_ssl_get_ciphersuite_id_from_ssl(&backend->ssl); + cipher_id = + (uint16_t)mbedtls_ssl_get_ciphersuite_id_from_ssl(&backend->ssl); mbed_cipher_suite_get_str(cipher_id, cipher_str, sizeof(cipher_str), TRUE); infof(data, "mbedTLS: %s Handshake complete, cipher is %s", mbedtls_ssl_get_version(&backend->ssl), cipher_str); } +#ifdef HAVE_PINNED_PUBKEY if(pinnedpubkey) { int size; - CURLcode result; const mbedtls_x509_crt *peercert; mbedtls_x509_crt *p = NULL; unsigned char *pubkey = NULL; @@ -979,12 +1073,12 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_PINNEDPUBKEYNOTMATCH; } - p = calloc(1, sizeof(*p)); + p = curlx_calloc(1, sizeof(*p)); if(!p) return CURLE_OUT_OF_MEMORY; - pubkey = malloc(PUB_DER_MAX_BYTES); + pubkey = curlx_malloc(PUB_DER_MAX_BYTES); if(!pubkey) { result = CURLE_OUT_OF_MEMORY; @@ -1016,19 +1110,22 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) &pubkey[PUB_DER_MAX_BYTES - size], size); pinnedpubkey_error: mbedtls_x509_crt_free(p); - free(p); - free(pubkey); - if(result) { + curlx_free(p); + curlx_free(pubkey); + if(result) return result; - } } +#endif #ifdef HAS_ALPN_MBEDTLS if(connssl->alpn) { const char *proto = mbedtls_ssl_get_alpn_protocol(&backend->ssl); - Curl_alpn_set_negotiated(cf, data, connssl, (const unsigned char *)proto, - proto ? strlen(proto) : 0); + result = Curl_alpn_set_negotiated(cf, data, connssl, + (const unsigned char *)proto, + proto ? strlen(proto) : 0); + if(result) + return result; } #endif @@ -1038,13 +1135,12 @@ pinnedpubkey_error: return CURLE_OK; } -static CURLcode -mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data) +static CURLcode mbed_new_session(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); mbedtls_ssl_session session; bool msession_alloced = FALSE; struct Curl_ssl_session *sc_session = NULL; @@ -1055,7 +1151,7 @@ mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data) int ret; DEBUGASSERT(backend); - if(!ssl_config->primary.cache_session) + if(!Curl_ssl_scache_use(cf, data)) return CURLE_OK; mbedtls_ssl_session_init(&session); @@ -1073,7 +1169,7 @@ mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data) goto out; } - sdata = malloc(slen); + sdata = curlx_malloc(slen); if(!sdata) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1098,13 +1194,12 @@ mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data) out: if(msession_alloced) mbedtls_ssl_session_free(&session); - free(sdata); + curlx_free(sdata); return result; } static CURLcode mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *mem, size_t len, - size_t *pnwritten) + const void *mem, size_t len, size_t *pnwritten) { struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = @@ -1112,12 +1207,12 @@ static CURLcode mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode result = CURLE_OK; int nwritten; - (void)data; - *pnwritten = 0; DEBUGASSERT(backend); - /* mbedtls is picky when a mbedtls_ssl_write) was previously blocked. + *pnwritten = 0; + connssl->io_need = CURL_SSL_IO_NEED_NONE; + /* mbedTLS is picky when a mbedtls_ssl_write() was previously blocked. * It requires to be called with the same amount of bytes again, or it - * will lose bytes, e.g. reporting all was sent but they were not. + * loses bytes, e.g. reporting all was sent but they were not. * Remember the blocked length and use that when set. */ if(backend->send_blocked) { DEBUGASSERT(backend->send_blocked_len <= len); @@ -1135,11 +1230,22 @@ static CURLcode mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, else { CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X", len, -nwritten); - result = ((nwritten == MBEDTLS_ERR_SSL_WANT_WRITE) + switch(nwritten) { #ifdef MBEDTLS_SSL_PROTO_TLS1_3 - || (nwritten == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) + case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: #endif - ) ? CURLE_AGAIN : CURLE_SEND_ERROR; + case MBEDTLS_ERR_SSL_WANT_READ: + connssl->io_need = CURL_SSL_IO_NEED_RECV; + result = CURLE_AGAIN; + break; + case MBEDTLS_ERR_SSL_WANT_WRITE: + connssl->io_need = CURL_SSL_IO_NEED_SEND; + result = CURLE_AGAIN; + break; + default: + result = CURLE_SEND_ERROR; + break; + } if((result == CURLE_AGAIN) && !backend->send_blocked) { backend->send_blocked = TRUE; backend->send_blocked_len = len; @@ -1160,7 +1266,7 @@ static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, (struct mbed_ssl_backend_data *)connssl->backend; unsigned char buf[1024]; CURLcode result = CURLE_OK; - int ret; + int ret = 0; size_t i; DEBUGASSERT(backend); @@ -1256,20 +1362,15 @@ static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data) #ifdef MBEDTLS_X509_CRL_PARSE_C mbedtls_x509_crl_free(&backend->crl); #endif - Curl_safefree(backend->ciphersuites); + curlx_safefree(backend->ciphersuites); mbedtls_ssl_config_free(&backend->config); mbedtls_ssl_free(&backend->ssl); - mbedtls_ctr_drbg_free(&backend->ctr_drbg); -#ifndef HAS_THREADING_SUPPORT - mbedtls_entropy_free(&backend->entropy); -#endif /* HAS_THREADING_SUPPORT */ backend->initialized = FALSE; } } static CURLcode mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t buffersize, - size_t *pnread) + char *buf, size_t buffersize, size_t *pnread) { struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = @@ -1277,14 +1378,15 @@ 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; nread = mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, buffersize); if(nread > 0) *pnread = (size_t)nread; else { + char errorbuf[128]; CURL_TRC_CF(data, cf, "mbedtls_ssl_read(len=%zu) -> -0x%04X", buffersize, -nread); switch(nread) { @@ -1294,19 +1396,22 @@ static CURLcode mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, FALLTHROUGH(); #endif case MBEDTLS_ERR_SSL_WANT_READ: + connssl->io_need = CURL_SSL_IO_NEED_RECV; + result = CURLE_AGAIN; + break; + case MBEDTLS_ERR_SSL_WANT_WRITE: + connssl->io_need = CURL_SSL_IO_NEED_SEND; result = CURLE_AGAIN; break; case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: result = CURLE_OK; break; - default: { - char errorbuf[128]; + default: mbedtls_strerror(nread, errorbuf, sizeof(errorbuf)); failf(data, "ssl_read returned: (-0x%04X) %s", -nread, errorbuf); result = CURLE_RECV_ERROR; break; } - } } return result; } @@ -1316,10 +1421,10 @@ static size_t mbedtls_version(char *buffer, size_t size) #ifdef MBEDTLS_VERSION_C /* if mbedtls_version_get_number() is available it is better */ unsigned int version = mbedtls_version_get_number(); - return msnprintf(buffer, size, "mbedTLS/%u.%u.%u", version >> 24, - (version >> 16) & 0xff, (version >> 8) & 0xff); + return curl_msnprintf(buffer, size, "mbedTLS/%u.%u.%u", version >> 24, + (version >> 16) & 0xff, (version >> 8) & 0xff); #else - return msnprintf(buffer, size, "mbedTLS/%s", MBEDTLS_VERSION_STRING); + return curl_msnprintf(buffer, size, "mbedTLS/%s", MBEDTLS_VERSION_STRING); #endif } @@ -1327,40 +1432,19 @@ static size_t mbedtls_version(char *buffer, size_t size) static CURLcode mbedtls_random(struct Curl_easy *data, unsigned char *entropy, size_t length) { -#ifdef MBEDTLS_CTR_DRBG_C - int ret; - mbedtls_entropy_context ctr_entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_entropy_init(&ctr_entropy); - mbedtls_ctr_drbg_init(&ctr_drbg); + psa_status_t status; (void)data; - ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, - &ctr_entropy, NULL, 0); + status = psa_generate_random(entropy, length); - if(!ret) - ret = mbedtls_ctr_drbg_random(&ctr_drbg, entropy, length); - - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&ctr_entropy); - - return ret == 0 ? CURLE_OK : CURLE_FAILED_INIT; -#elif defined(MBEDTLS_HAVEGE_C) - mbedtls_havege_state hs; - mbedtls_havege_init(&hs); - mbedtls_havege_random(&hs, entropy, length); - mbedtls_havege_free(&hs); - return CURLE_OK; -#else - return CURLE_NOT_BUILT_IN; -#endif + return status == PSA_SUCCESS ? CURLE_OK : CURLE_FAILED_INIT; } 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 */ @@ -1373,15 +1457,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) { @@ -1392,9 +1476,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; } @@ -1413,34 +1497,47 @@ static CURLcode mbedtls_connect(struct Curl_cfilter *cf, */ static int mbedtls_init(void) { - if(!Curl_mbedtlsthreadlock_thread_setup()) +#if MBEDTLS_VERSION_NUMBER < 0x04000000 + int ret = 0; +#endif + psa_status_t status; + status = psa_crypto_init(); + + if(status != PSA_SUCCESS) + return 0; + +#if MBEDTLS_VERSION_NUMBER < 0x04000000 + mbedtls_ctr_drbg_init(&rng.drbg); + mbedtls_entropy_init(&rng.entropy); + + ret = mbedtls_ctr_drbg_seed(&rng.drbg, mbedtls_entropy_func, &rng.entropy, + NULL, 0); + + if(ret) { + failf(NULL, "failed: mbedtls_ctr_drbg_seed returned -0x%x", + (unsigned int)-ret); return 0; -#ifdef HAS_THREADING_SUPPORT - entropy_init_mutex(&ts_entropy); -#endif -#ifdef MBEDTLS_USE_PSA_CRYPTO /* requires mbedTLS 3.6.0+ */ - { - int ret; -#ifdef HAS_THREADING_SUPPORT - Curl_mbedtlsthreadlock_lock_function(0); -#endif - ret = psa_crypto_init(); -#ifdef HAS_THREADING_SUPPORT - Curl_mbedtlsthreadlock_unlock_function(0); -#endif - if(ret != PSA_SUCCESS) - return 0; } -#endif /* MBEDTLS_USE_PSA_CRYPTO */ + + /* To prevent an adversary from reading your random data, + you can enable prediction resistance. + + Entropy is gathered before each mbedtls_ctr_drbg_random() call. + Only use this if you have ample supply of good entropy.*/ + mbedtls_ctr_drbg_set_prediction_resistance(&rng.drbg, + MBEDTLS_CTR_DRBG_PR_ON); +#endif return 1; } static void mbedtls_cleanup(void) { -#ifdef HAS_THREADING_SUPPORT - entropy_cleanup_mutex(&ts_entropy); + mbedtls_psa_crypto_free(); + +#if MBEDTLS_VERSION_NUMBER < 0x04000000 + mbedtls_ctr_drbg_free(&rng.drbg); + mbedtls_entropy_free(&rng.entropy); #endif - (void)Curl_mbedtlsthreadlock_thread_cleanup(); } static bool mbedtls_data_pending(struct Curl_cfilter *cf, @@ -1460,11 +1557,19 @@ static CURLcode mbedtls_sha256sum(const unsigned char *input, unsigned char *sha256sum, size_t sha256len) { - (void)sha256len; - /* returns 0 on success, otherwise failure */ - if(mbedtls_sha256(input, inputlen, sha256sum, 0) != 0) +#if defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 /* mbedTLS 4+ */ + psa_status_t status; + size_t sha256len_actual; + status = psa_hash_compute(PSA_ALG_SHA_256, input, inputlen, + sha256sum, sha256len, + &sha256len_actual); + if(status != PSA_SUCCESS) return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_OK; +#else + (void)sha256len; + return Curl_sha256it(sha256sum, input, inputlen); +#endif } static void *mbedtls_get_internals(struct ssl_connect_data *connssl, @@ -1483,13 +1588,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/lib/vtls/mbedtls.h b/lib/vtls/mbedtls.h index 3876fe36fc..d8a0a06eb6 100644 --- a/lib/vtls/mbedtls.h +++ b/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/lib/vtls/mbedtls_threadlock.c b/lib/vtls/mbedtls_threadlock.c deleted file mode 100644 index 682c221852..0000000000 --- a/lib/vtls/mbedtls_threadlock.c +++ /dev/null @@ -1,134 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * Copyright (C) Hoi-Ho Chan, - * - * 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(USE_MBEDTLS) && \ - ((defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ - defined(_WIN32)) - -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) -# include -# define MBEDTLS_MUTEX_T pthread_mutex_t -#elif defined(_WIN32) -# define MBEDTLS_MUTEX_T HANDLE -#endif - -#include "mbedtls_threadlock.h" -#include "../curl_printf.h" -#include "../curl_memory.h" -/* The last #include file should be: */ -#include "../memdebug.h" - -/* number of thread locks */ -#define NUMT 2 - -/* This array will store all of the mutexes available to Mbedtls. */ -static MBEDTLS_MUTEX_T *mutex_buf = NULL; - -int Curl_mbedtlsthreadlock_thread_setup(void) -{ - int i; - - mutex_buf = calloc(1, NUMT * sizeof(MBEDTLS_MUTEX_T)); - if(!mutex_buf) - return 0; /* error, no number of threads defined */ - - for(i = 0; i < NUMT; i++) { -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) - if(pthread_mutex_init(&mutex_buf[i], NULL)) - return 0; /* pthread_mutex_init failed */ -#elif defined(_WIN32) - mutex_buf[i] = CreateMutex(0, FALSE, 0); - if(mutex_buf[i] == 0) - return 0; /* CreateMutex failed */ -#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ - } - - return 1; /* OK */ -} - -int Curl_mbedtlsthreadlock_thread_cleanup(void) -{ - int i; - - if(!mutex_buf) - return 0; /* error, no threads locks defined */ - - for(i = 0; i < NUMT; i++) { -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) - if(pthread_mutex_destroy(&mutex_buf[i])) - return 0; /* pthread_mutex_destroy failed */ -#elif defined(_WIN32) - if(!CloseHandle(mutex_buf[i])) - return 0; /* CloseHandle failed */ -#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ - } - free(mutex_buf); - mutex_buf = NULL; - - return 1; /* OK */ -} - -int Curl_mbedtlsthreadlock_lock_function(int n) -{ - if(n < NUMT) { -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) - if(pthread_mutex_lock(&mutex_buf[n])) { - DEBUGF(fprintf(stderr, - "Error: mbedtlsthreadlock_lock_function failed\n")); - return 0; /* pthread_mutex_lock failed */ - } -#elif defined(_WIN32) - if(WaitForSingleObject(mutex_buf[n], INFINITE) == WAIT_FAILED) { - DEBUGF(fprintf(stderr, - "Error: mbedtlsthreadlock_lock_function failed\n")); - return 0; /* pthread_mutex_lock failed */ - } -#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ - } - return 1; /* OK */ -} - -int Curl_mbedtlsthreadlock_unlock_function(int n) -{ - if(n < NUMT) { -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) - if(pthread_mutex_unlock(&mutex_buf[n])) { - DEBUGF(fprintf(stderr, - "Error: mbedtlsthreadlock_unlock_function failed\n")); - return 0; /* pthread_mutex_unlock failed */ - } -#elif defined(_WIN32) - if(!ReleaseMutex(mutex_buf[n])) { - DEBUGF(fprintf(stderr, - "Error: mbedtlsthreadlock_unlock_function failed\n")); - return 0; /* pthread_mutex_lock failed */ - } -#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */ - } - return 1; /* OK */ -} - -#endif /* USE_MBEDTLS */ diff --git a/lib/vtls/mbedtls_threadlock.h b/lib/vtls/mbedtls_threadlock.h deleted file mode 100644 index 9402af6e41..0000000000 --- a/lib/vtls/mbedtls_threadlock.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef HEADER_CURL_MBEDTLS_THREADLOCK_H -#define HEADER_CURL_MBEDTLS_THREADLOCK_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * Copyright (C) Hoi-Ho Chan, - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "../curl_setup.h" - -#ifdef USE_MBEDTLS - -#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \ - defined(_WIN32) - -int Curl_mbedtlsthreadlock_thread_setup(void); -int Curl_mbedtlsthreadlock_thread_cleanup(void); -int Curl_mbedtlsthreadlock_lock_function(int n); -int Curl_mbedtlsthreadlock_unlock_function(int n); - -#else - -#define Curl_mbedtlsthreadlock_thread_setup() 1 -#define Curl_mbedtlsthreadlock_thread_cleanup() 1 -#define Curl_mbedtlsthreadlock_lock_function(x) 1 -#define Curl_mbedtlsthreadlock_unlock_function(x) 1 - -#endif /* (USE_THREADS_POSIX && HAVE_PTHREAD_H) || _WIN32 */ - -#endif /* USE_MBEDTLS */ - -#endif /* HEADER_CURL_MBEDTLS_THREADLOCK_H */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index a49203ab07..71f792962d 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -21,54 +21,41 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * 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" +#ifdef USE_OPENSSL -#if defined(USE_QUICHE) || defined(USE_OPENSSL) - -#include - -/* 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 +#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 "cf-dns.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" +#ifdef USE_ECH +#include "curlx/base64.h" #endif -#include "../urldata.h" -#include "../sendf.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 "../slist.h" -#include "../select.h" -#include "../curlx/wait.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/strparse.h" -#include "../strdup.h" -#include "../strerror.h" -#include "../curl_printf.h" - -#include #include #include #ifndef OPENSSL_NO_DSA @@ -76,21 +63,15 @@ #endif #include #include -#include #include #include #include #include -#include #include #include #include -#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST -#define USE_ECH_OPENSSL -#endif - -#if defined(USE_ECH_OPENSSL) && !defined(HAVE_BORINGSSL_LIKE) +#if defined(HAVE_SSL_SET1_ECH_CONFIG_LIST) && !defined(HAVE_BORINGSSL_LIKE) #include #endif @@ -108,8 +89,10 @@ # if LIBRESSL_VERSION_NUMBER < 0x2090100fL /* 2019-04-13 */ # error "LibreSSL 2.9.1 or later required" # endif -#elif OPENSSL_VERSION_NUMBER < 0x1000201fL /* 2015-03-19 */ -# error "OpenSSL 1.0.2a or later required" +#elif !defined(HAVE_BORINGSSL_LIKE) +# ifndef HAVE_OPENSSL3 /* 2021-09-07 */ +# error "OpenSSL 3.0.0 or later required" +# endif #endif #if defined(HAVE_OPENSSL3) && !defined(OPENSSL_NO_UI_CONSOLE) @@ -117,56 +100,19 @@ #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 has `SSL_CTX_set_default_read_buffer_len()?` but runs into - * decryption failures with large buffers. Sporadic failures in - * test_10_08 with h2 proxy uploads, increased frequency - * with CURL_DBG_SOCK_RBLOCK=50. Looks like a bug on their part. - */ -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ - !defined(LIBRESSL_VERSION_NUMBER) && !defined(HAVE_BORINGSSL_LIKE) +/* AWS-LC fixed a bug with large buffers in v1.61.0 which also introduced + * X509_V_ERR_EC_KEY_EXPLICIT_PARAMS. */ +#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL) && \ + (!defined(OPENSSL_IS_AWSLC) || defined(X509_V_ERR_EC_KEY_EXPLICIT_PARAMS)) #define HAVE_SSL_CTX_SET_DEFAULT_READ_BUFFER_LEN 1 #endif -#include "../curlx/warnless.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" - #if defined(USE_OPENSSL_ENGINE) || defined(OPENSSL_HAS_PROVIDERS) #include #endif -#if OPENSSL_VERSION_NUMBER >= 0x10100000L -#define OSSL_UI_METHOD_CAST(x) (x) -#else -#define OSSL_UI_METHOD_CAST(x) CURL_UNCONST(x) -#endif - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* OpenSSL 1.1.0+ and LibreSSL */ -#define HAVE_X509_GET0_EXTENSIONS 1 /* added in 1.1.0 -pre1 */ -#define HAVE_OPAQUE_EVP_PKEY 1 /* since 1.1.0 -pre3 */ -#define HAVE_OPAQUE_RSA_DSA_DH 1 /* since 1.1.0 -pre5 */ -#define HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED 1 -#else -/* For OpenSSL before 1.1.0 */ -#define ASN1_STRING_get0_data(x) ASN1_STRING_data(x) -#define X509_get0_notBefore(x) X509_get_notBefore(x) -#define X509_get0_notAfter(x) X509_get_notAfter(x) -#define OpenSSL_version_num() SSLeay() -#endif - -#if OPENSSL_VERSION_NUMBER >= 0x10002003L && \ - OPENSSL_VERSION_NUMBER <= 0x10002FFFL && \ - !defined(OPENSSL_NO_COMP) -#define HAVE_SSL_COMP_FREE_COMPRESSION_METHODS 1 -#endif - #ifdef HAVE_OPENSSL3 #define HAVE_EVP_PKEY_GET_PARAMS 1 #endif @@ -185,8 +131,7 @@ static void ossl_provider_cleanup(struct Curl_easy *data); * BoringSSL: no * LibreSSL: supported since 3.4.1 (released 2021-10-14) */ -#if ((OPENSSL_VERSION_NUMBER >= 0x10101000L && \ - !defined(LIBRESSL_VERSION_NUMBER)) || \ +#if (!defined(LIBRESSL_VERSION_NUMBER) || \ (defined(LIBRESSL_VERSION_NUMBER) && \ LIBRESSL_VERSION_NUMBER >= 0x3040100fL)) && \ !defined(OPENSSL_IS_BORINGSSL) @@ -201,7 +146,7 @@ static void ossl_provider_cleanup(struct Curl_easy *data); * BoringSSL: supported since 0.20240913.0 (commit 826ce15) * LibreSSL: no */ -#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER) +#ifndef LIBRESSL_VERSION_NUMBER #define HAVE_SSL_CTX_SET1_SIGALGS #endif @@ -211,7 +156,7 @@ static void ossl_provider_cleanup(struct Curl_easy *data); #define OSSL_PACKAGE "BoringSSL" #elif defined(OPENSSL_IS_AWSLC) #define OSSL_PACKAGE "AWS-LC" -#elif defined(USE_NGTCP2) && defined(USE_NGHTTP3) && \ +#elif defined(USE_NGTCP2) && defined(USE_NGHTTP3) && \ !defined(OPENSSL_QUIC_API2) #define OSSL_PACKAGE "quictls" #else @@ -227,32 +172,6 @@ typedef unsigned long sslerr_t; #endif #define ossl_valsize_t numcert_t -#if OPENSSL_VERSION_NUMBER >= 0x10100000L -/* up2date versions of OpenSSL maintain reasonably secure defaults without - * breaking compatibility, so it is better not to override the defaults in curl - */ -#define DEFAULT_CIPHER_SELECTION NULL -#else -/* not the case with old versions of OpenSSL */ -#define DEFAULT_CIPHER_SELECTION \ - "ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH" -#endif - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L -#define HAVE_RANDOM_INIT_BY_DEFAULT 1 -#endif - -/* - * Whether the OpenSSL version has the API needed to support sharing an - * X509_STORE between connections. The API is: - * * `X509_STORE_up_ref` -- Introduced: OpenSSL 1.1.0. - */ -#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* OpenSSL >= 1.1.0 */ -#define HAVE_SSL_X509_STORE_SHARE -#endif - -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; @@ -267,6 +186,13 @@ static CURLcode push_certinfo(struct Curl_easy *data, return result; } +static CURLcode pubkey_show(struct Curl_easy *data, + BIO *mem, + int num, + const char *type, + const char *name, + const BIGNUM *bn) WARN_UNUSED_RESULT; + static CURLcode pubkey_show(struct Curl_easy *data, BIO *mem, int num, @@ -276,40 +202,20 @@ static CURLcode pubkey_show(struct Curl_easy *data, { char namebuf[32]; - msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name); + curl_msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name); if(bn) BN_print(mem, bn); return push_certinfo(data, mem, namebuf, num); } -#ifdef HAVE_OPAQUE_RSA_DSA_DH -#define print_pubkey_BN(_type, _name, _num) \ +#define print_pubkey_BN(_type, _name, _num) \ pubkey_show(data, mem, _num, #_type, #_name, _name) -#else -#define print_pubkey_BN(_type, _name, _num) \ -do { \ - if(_type->_name) { \ - pubkey_show(data, mem, _num, #_type, #_name, _type->_name); \ - } \ -} while(0) -#endif - -static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len) +static int asn1_object_dump(const ASN1_OBJECT *a, char *buf, size_t len) { - int i, ilen; - - ilen = (int)len; - if(ilen < 0) - return 1; /* buffer too big */ - - i = i2t_ASN1_OBJECT(buf, ilen, a); - - if(i >= ilen) - return 1; /* buffer too small */ - - return 0; + int i = i2t_ASN1_OBJECT(buf, (int)len, a); + return (i >= (int)len); /* buffer too small */ } static CURLcode X509V3_ext(struct Curl_easy *data, @@ -318,10 +224,10 @@ static CURLcode X509V3_ext(struct Curl_easy *data, { int i; CURLcode result = CURLE_OK; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) - const STACK_OF(X509_EXTENSION) *exts = extsarg; -#else +#ifdef LIBRESSL_VERSION_NUMBER STACK_OF(X509_EXTENSION) *exts = CURL_UNCONST(extsarg); +#else + const STACK_OF(X509_EXTENSION) *exts = extsarg; #endif if((int)sk_X509_EXTENSION_num(exts) <= 0) @@ -329,7 +235,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]; @@ -340,10 +246,13 @@ static CURLcode X509V3_ext(struct Curl_easy *data, obj = X509_EXTENSION_get_object(ext); - asn1_object_dump(obj, namebuf, sizeof(namebuf)); + if(asn1_object_dump(obj, namebuf, sizeof(namebuf))) + /* make sure the name is null-terminated */ + 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, @@ -355,6 +264,110 @@ static CURLcode X509V3_ext(struct Curl_easy *data, return result; } +static CURLcode get_pkey_rsa(struct Curl_easy *data, + EVP_PKEY *pubkey, BIO *mem, int i) +{ + CURLcode result = CURLE_OK; +#ifndef HAVE_EVP_PKEY_GET_PARAMS + RSA *rsa = EVP_PKEY_get0_RSA(pubkey); +#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ + DECLARE_PKEY_PARAM_BIGNUM(n); + DECLARE_PKEY_PARAM_BIGNUM(e); +#ifdef HAVE_EVP_PKEY_GET_PARAMS + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_N, &n); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_E, &e); +#else + RSA_get0_key(rsa, &n, &e, NULL); +#endif /* HAVE_EVP_PKEY_GET_PARAMS */ + BIO_printf(mem, "%d", n ? BN_num_bits(n) : 0); + result = push_certinfo(data, mem, "RSA Public Key", i); + if(!result) { + result = print_pubkey_BN(rsa, n, i); + if(!result) + result = print_pubkey_BN(rsa, e, i); + } + FREE_PKEY_PARAM_BIGNUM(n); + FREE_PKEY_PARAM_BIGNUM(e); + return result; +} + +#ifndef OPENSSL_NO_DSA +static CURLcode get_pkey_dsa(struct Curl_easy *data, + EVP_PKEY *pubkey, BIO *mem, int i) +{ + CURLcode result = CURLE_OK; +#ifndef HAVE_EVP_PKEY_GET_PARAMS + DSA *dsa = EVP_PKEY_get0_DSA(pubkey); +#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ + DECLARE_PKEY_PARAM_BIGNUM(p); + DECLARE_PKEY_PARAM_BIGNUM(q); + DECLARE_PKEY_PARAM_BIGNUM(g); + DECLARE_PKEY_PARAM_BIGNUM(pub_key); +#ifdef HAVE_EVP_PKEY_GET_PARAMS + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key); +#else + DSA_get0_pqg(dsa, &p, &q, &g); + DSA_get0_key(dsa, &pub_key, NULL); +#endif /* HAVE_EVP_PKEY_GET_PARAMS */ + result = print_pubkey_BN(dsa, p, i); + if(!result) + result = print_pubkey_BN(dsa, q, i); + if(!result) + result = print_pubkey_BN(dsa, g, i); + if(!result) + result = print_pubkey_BN(dsa, pub_key, i); + FREE_PKEY_PARAM_BIGNUM(p); + FREE_PKEY_PARAM_BIGNUM(q); + FREE_PKEY_PARAM_BIGNUM(g); + FREE_PKEY_PARAM_BIGNUM(pub_key); + return result; +} +#endif /* !OPENSSL_NO_DSA */ + +static CURLcode get_pkey_dh(struct Curl_easy *data, + EVP_PKEY *pubkey, BIO *mem, int i) +{ + CURLcode result; +#ifndef HAVE_EVP_PKEY_GET_PARAMS + DH *dh = EVP_PKEY_get0_DH(pubkey); +#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ + DECLARE_PKEY_PARAM_BIGNUM(p); + DECLARE_PKEY_PARAM_BIGNUM(q); + DECLARE_PKEY_PARAM_BIGNUM(g); + DECLARE_PKEY_PARAM_BIGNUM(pub_key); +#ifdef HAVE_EVP_PKEY_GET_PARAMS + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g); + EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key); +#else + DH_get0_pqg(dh, &p, &q, &g); + DH_get0_key(dh, &pub_key, NULL); +#endif /* HAVE_EVP_PKEY_GET_PARAMS */ + result = print_pubkey_BN(dh, p, i); + if(!result) + result = print_pubkey_BN(dh, q, i); + if(!result) + result = print_pubkey_BN(dh, g, i); + if(!result) + result = print_pubkey_BN(dh, pub_key, i); + FREE_PKEY_PARAM_BIGNUM(p); + FREE_PKEY_PARAM_BIGNUM(q); + FREE_PKEY_PARAM_BIGNUM(g); + FREE_PKEY_PARAM_BIGNUM(pub_key); + 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; @@ -366,11 +379,15 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl) DEBUGASSERT(ssl); sk = SSL_get_peer_cert_chain(ssl); - if(!sk) { - return CURLE_OUT_OF_MEMORY; - } + if(!sk) + return CURLE_SSL_CONNECT_ERROR; numcerts = sk_X509_num(sk); + if(numcerts > MAX_ALLOWED_CERT_AMOUNT) { + failf(data, "%d certificates is more than allowed (%u)", (int)numcerts, + MAX_ALLOWED_CERT_AMOUNT); + return CURLE_SSL_CONNECT_ERROR; + } result = Curl_ssl_init_certinfo(data, (int)numcerts); if(result) @@ -382,6 +399,7 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl) for(i = 0; !result && (i < (int)numcerts); i++) { ASN1_INTEGER *num; + const unsigned char *numdata; X509 *x = sk_X509_value(sk, (ossl_valsize_t)i); EVP_PKEY *pubkey = NULL; int j; @@ -403,18 +421,18 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl) break; num = X509_get_serialNumber(x); - if(num->type == V_ASN1_NEG_INTEGER) + if(ASN1_STRING_type(num) == V_ASN1_NEG_INTEGER) BIO_puts(mem, "-"); - for(j = 0; j < num->length; j++) - BIO_printf(mem, "%02x", num->data[j]); + numdata = ASN1_STRING_get0_data(num); + for(j = 0; j < ASN1_STRING_length(num); j++) + BIO_printf(mem, "%02x", numdata[j]); result = push_certinfo(data, mem, "Serial Number", i); if(result) break; -#ifdef HAVE_X509_GET0_EXTENSIONS { const X509_ALGOR *sigalg = NULL; - X509_PUBKEY *xpubkey = NULL; + pubkeytype_t *xpubkey = NULL; ASN1_OBJECT *pubkeyoid = NULL; X509_get0_signature(&psig, &sigalg, x); @@ -442,28 +460,6 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl) if(result) break; } -#else - { - /* before OpenSSL 1.0.2 */ - X509_CINF *cinf = x->cert_info; - - i2a_ASN1_OBJECT(mem, cinf->signature->algorithm); - result = push_certinfo(data, mem, "Signature Algorithm", i); - - if(!result) { - i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm); - result = push_certinfo(data, mem, "Public Key Algorithm", i); - } - - if(!result) - result = X509V3_ext(data, i, cinf->extensions); - - if(result) - break; - - psig = x->signature; - } -#endif ASN1_TIME_print(mem, X509_get0_notBefore(x)); result = push_certinfo(data, mem, "Start date", i); @@ -479,133 +475,28 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl) if(!pubkey) infof(data, " Unable to load public key"); else { - int pktype; -#ifdef HAVE_OPAQUE_EVP_PKEY - pktype = EVP_PKEY_id(pubkey); -#else - pktype = pubkey->type; -#endif - switch(pktype) { - case EVP_PKEY_RSA: { -#ifndef HAVE_EVP_PKEY_GET_PARAMS - RSA *rsa; -#ifdef HAVE_OPAQUE_EVP_PKEY - rsa = EVP_PKEY_get0_RSA(pubkey); -#else - rsa = pubkey->pkey.rsa; -#endif /* HAVE_OPAQUE_EVP_PKEY */ -#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ - - { -#ifdef HAVE_OPAQUE_RSA_DSA_DH - DECLARE_PKEY_PARAM_BIGNUM(n); - DECLARE_PKEY_PARAM_BIGNUM(e); -#ifdef HAVE_EVP_PKEY_GET_PARAMS - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_N, &n); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_E, &e); -#else - RSA_get0_key(rsa, &n, &e, NULL); -#endif /* HAVE_EVP_PKEY_GET_PARAMS */ - BIO_printf(mem, "%d", n ? BN_num_bits(n) : 0); -#else - BIO_printf(mem, "%d", rsa->n ? BN_num_bits(rsa->n) : 0); -#endif /* HAVE_OPAQUE_RSA_DSA_DH */ - result = push_certinfo(data, mem, "RSA Public Key", i); - if(result) - break; - print_pubkey_BN(rsa, n, i); - print_pubkey_BN(rsa, e, i); - FREE_PKEY_PARAM_BIGNUM(n); - FREE_PKEY_PARAM_BIGNUM(e); - } - + switch(EVP_PKEY_id(pubkey)) { + case EVP_PKEY_RSA: + result = get_pkey_rsa(data, pubkey, mem, i); break; - } - case EVP_PKEY_DSA: - { + #ifndef OPENSSL_NO_DSA -#ifndef HAVE_EVP_PKEY_GET_PARAMS - DSA *dsa; -#ifdef HAVE_OPAQUE_EVP_PKEY - dsa = EVP_PKEY_get0_DSA(pubkey); -#else - dsa = pubkey->pkey.dsa; -#endif /* HAVE_OPAQUE_EVP_PKEY */ -#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ - { -#ifdef HAVE_OPAQUE_RSA_DSA_DH - DECLARE_PKEY_PARAM_BIGNUM(p); - DECLARE_PKEY_PARAM_BIGNUM(q); - DECLARE_PKEY_PARAM_BIGNUM(g); - DECLARE_PKEY_PARAM_BIGNUM(pub_key); -#ifdef HAVE_EVP_PKEY_GET_PARAMS - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key); -#else - DSA_get0_pqg(dsa, &p, &q, &g); - DSA_get0_key(dsa, &pub_key, NULL); -#endif /* HAVE_EVP_PKEY_GET_PARAMS */ -#endif /* HAVE_OPAQUE_RSA_DSA_DH */ - print_pubkey_BN(dsa, p, i); - print_pubkey_BN(dsa, q, i); - print_pubkey_BN(dsa, g, i); - print_pubkey_BN(dsa, pub_key, i); - FREE_PKEY_PARAM_BIGNUM(p); - FREE_PKEY_PARAM_BIGNUM(q); - FREE_PKEY_PARAM_BIGNUM(g); - FREE_PKEY_PARAM_BIGNUM(pub_key); - } -#endif /* !OPENSSL_NO_DSA */ + case EVP_PKEY_DSA: + result = get_pkey_dsa(data, pubkey, mem, i); break; - } - case EVP_PKEY_DH: { -#ifndef HAVE_EVP_PKEY_GET_PARAMS - DH *dh; -#ifdef HAVE_OPAQUE_EVP_PKEY - dh = EVP_PKEY_get0_DH(pubkey); -#else - dh = pubkey->pkey.dh; -#endif /* HAVE_OPAQUE_EVP_PKEY */ -#endif /* !HAVE_EVP_PKEY_GET_PARAMS */ - { -#ifdef HAVE_OPAQUE_RSA_DSA_DH - DECLARE_PKEY_PARAM_BIGNUM(p); - DECLARE_PKEY_PARAM_BIGNUM(q); - DECLARE_PKEY_PARAM_BIGNUM(g); - DECLARE_PKEY_PARAM_BIGNUM(pub_key); -#ifdef HAVE_EVP_PKEY_GET_PARAMS - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g); - EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key); -#else - DH_get0_pqg(dh, &p, &q, &g); - DH_get0_key(dh, &pub_key, NULL); -#endif /* HAVE_EVP_PKEY_GET_PARAMS */ - print_pubkey_BN(dh, p, i); - print_pubkey_BN(dh, q, i); - print_pubkey_BN(dh, g, i); -#else - print_pubkey_BN(dh, p, i); - print_pubkey_BN(dh, g, i); -#endif /* HAVE_OPAQUE_RSA_DSA_DH */ - print_pubkey_BN(dh, pub_key, i); - FREE_PKEY_PARAM_BIGNUM(p); - FREE_PKEY_PARAM_BIGNUM(q); - FREE_PKEY_PARAM_BIGNUM(g); - FREE_PKEY_PARAM_BIGNUM(pub_key); - } +#endif + + case EVP_PKEY_DH: + result = get_pkey_dh(data, pubkey, mem, i); break; } - } EVP_PKEY_free(pubkey); } if(!result && psig) { - for(j = 0; j < psig->length; j++) - BIO_printf(mem, "%02x:", psig->data[j]); + const unsigned char *psigdata = ASN1_STRING_get0_data(psig); + for(j = 0; j < ASN1_STRING_length(psig); j++) + BIO_printf(mem, "%02x:", psigdata[j]); result = push_certinfo(data, mem, "Signature", i); } @@ -624,25 +515,10 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl) return result; } -#endif /* quiche or OpenSSL */ - -#ifdef USE_OPENSSL - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#define BIO_set_init(x,v) ((x)->init=(v)) -#define BIO_get_data(x) ((x)->ptr) -#define BIO_set_data(x,v) ((x)->ptr=(v)) -#define BIO_get_shutdown(x) ((x)->shutdown) -#define BIO_set_shutdown(x,v) ((x)->shutdown=(v)) -#endif /* HAVE_PRE_1_1_API */ - static int ossl_bio_cf_create(BIO *bio) { BIO_set_shutdown(bio, 1); BIO_set_init(bio, 1); -#if OPENSSL_VERSION_NUMBER < 0x10100000L - bio->num = -1; -#endif BIO_set_data(bio, NULL); return 1; } @@ -701,14 +577,15 @@ static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen) if(blen < 0) return 0; - result = Curl_conn_cf_send(cf->next, data, buf, (size_t)blen, FALSE, + result = Curl_conn_cf_send(cf->next, data, + (const uint8_t *)buf, (size_t)blen, FALSE, &nwritten); CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, %zu", blen, result, nwritten); 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; } @@ -737,7 +614,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 { @@ -748,10 +625,11 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen) } /* Before returning server replies to the SSL instance, we need - * to have setup the x509 store or verification will fail. */ + * to have setup the x509 store or verification fails. */ if(!octx->x509_store_setup) { - r2 = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); + r2 = Curl_ssl_setup_x509_store(cf, data, octx); if(r2) { + BIO_clear_retry_flags(bio); octx->io_result = r2; return -1; } @@ -760,30 +638,6 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen) return result ? -1 : (int)nread; } -#if OPENSSL_VERSION_NUMBER < 0x10100000L - -static BIO_METHOD ossl_bio_cf_meth_1_0 = { - BIO_TYPE_MEM, - "OpenSSL CF BIO", - ossl_bio_cf_out_write, - ossl_bio_cf_in_read, - NULL, /* puts is never called */ - NULL, /* gets is never called */ - ossl_bio_cf_ctrl, - ossl_bio_cf_create, - ossl_bio_cf_destroy, - NULL -}; - -static BIO_METHOD *ossl_bio_cf_method_create(void) -{ - return &ossl_bio_cf_meth_1_0; -} - -#define ossl_bio_cf_method_free(m) Curl_nop_stmt - -#else - static BIO_METHOD *ossl_bio_cf_method_create(void) { BIO_METHOD *m = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO"); @@ -803,9 +657,7 @@ static void ossl_bio_cf_method_free(BIO_METHOD *m) BIO_meth_free(m); } -#endif - - +#ifndef HAVE_KEYLOG_UPSTREAM #ifdef HAVE_KEYLOG_CALLBACK static void ossl_keylog_callback(const SSL *ssl, const char *line) { @@ -818,8 +670,7 @@ static void ossl_keylog_callback(const SSL *ssl, const char *line) * ossl_log_tls12_secret is called by libcurl to make the CLIENT_RANDOMs if the * OpenSSL being used does not have native support for doing that. */ -static void -ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) +static void ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) { const SSL_SESSION *session; unsigned char client_random[SSL3_RANDOM_SIZE]; @@ -835,19 +686,9 @@ ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) return; } -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - /* ssl->s3 is not checked in OpenSSL 1.1.0-pre6, but let's assume that - * we have a valid SSL context if we have a non-NULL session. */ SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE); master_key_length = (int) SSL_SESSION_get_master_key(session, master_key, SSL_MAX_MASTER_KEY_LENGTH); -#else - if(ssl->s3 && session->master_key_length > 0) { - master_key_length = session->master_key_length; - memcpy(master_key, session->master_key, session->master_key_length); - memcpy(client_random, ssl->s3->client_random, SSL3_RANDOM_SIZE); - } -#endif ERR_pop_to_mark(); @@ -862,6 +703,7 @@ 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) { @@ -927,15 +769,13 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size) if(!*buf) { const char *msg = error ? "Unknown error" : "No error"; - if(strlen(msg) < size) - strcpy(buf, msg); + curlx_strcopy(buf, size, msg, strlen(msg)); } return buf; } -static int passwd_callback(char *buf, int num, int encrypting, - void *password) +static int passwd_callback(char *buf, int num, int encrypting, void *password) { DEBUGASSERT(encrypting == 0); @@ -969,58 +809,8 @@ static CURLcode ossl_seed(struct Curl_easy *data) data->multi->ssl_seeded = TRUE; return CURLE_OK; } -#ifdef HAVE_RANDOM_INIT_BY_DEFAULT - /* with OpenSSL 1.1.0+, a failed RAND_status is a showstopper */ failf(data, "Insufficient randomness"); return CURLE_SSL_CONNECT_ERROR; -#else - - /* fallback to a custom seeding of the PRNG using a hash based on a current - time */ - do { - unsigned char randb[64]; - size_t len = sizeof(randb); - size_t i, i_max; - for(i = 0, i_max = len / sizeof(struct curltime); i < i_max; ++i) { - struct curltime tv = curlx_now(); - curlx_wait_ms(1); - tv.tv_sec *= (time_t)i + 1; - tv.tv_usec *= (int)i + 2; - tv.tv_sec ^= ((curlx_now().tv_sec + (time_t)curlx_now().tv_usec) * - (time_t)(i + 3)) << 8; - tv.tv_usec ^= (int) ((curlx_now().tv_sec + (time_t)curlx_now().tv_usec) * - (time_t)(i + 4)) << 16; - memcpy(&randb[i * sizeof(struct curltime)], &tv, - sizeof(struct curltime)); - } - RAND_add(randb, (int)len, (double)len/2); - } while(!rand_enough()); - - /* - * Number of bytes to read from the random number seed file. This must be - * a finite value (because some entropy "files" like /dev/urandom have - * an infinite length), but must be large enough to provide enough - * entropy to properly seed OpenSSL's PRNG. - */ -# define RAND_LOAD_LENGTH 1024 - - { - /* generates a default path for the random seed file */ - char fname[256]; - fname[0] = 0; /* blank it first */ - RAND_file_name(fname, sizeof(fname)); - if(fname[0]) { - /* we got a filename to try */ - RAND_load_file(fname, RAND_LOAD_LENGTH); - if(rand_enough()) - return CURLE_OK; - } - } - - infof(data, "libcurl is now using a weak random seed"); - return rand_enough() ? CURLE_OK : - CURLE_SSL_CONNECT_ERROR; /* confusing error code */ -#endif } #ifndef SSL_FILETYPE_ENGINE @@ -1102,10 +892,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, @@ -1125,8 +914,7 @@ static int use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob, } else if(type == SSL_FILETYPE_PEM) { /* ERR_R_PEM_LIB; */ - x = PEM_read_bio_X509(in, NULL, - passwd_callback, CURL_UNCONST(key_passwd)); + x = PEM_read_bio_X509(in, NULL, passwd_callback, CURL_UNCONST(key_passwd)); } else { ret = 0; @@ -1172,9 +960,9 @@ end: return ret; } -static int -use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob, - const char *key_passwd) +static int use_certificate_chain_blob(SSL_CTX *ctx, + const struct curl_blob *blob, + const char *key_passwd) { int ret = 0; X509 *x = NULL; @@ -1204,8 +992,7 @@ use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob, } while((ca = PEM_read_bio_X509(in, NULL, passwd_callback, - CURL_UNCONST(key_passwd))) - != NULL) { + CURL_UNCONST(key_passwd))) != NULL) { if(!SSL_CTX_add0_chain_cert(ctx, ca)) { X509_free(ca); @@ -1232,8 +1019,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 @@ -1247,10 +1034,9 @@ static int enginecheck(struct Curl_easy *data, } if(data->state.engine) { - UI_METHOD *ui_method = - UI_create_method(OSSL_UI_METHOD_CAST("curl user interface")); + UI_METHOD *ui_method = UI_create_method("curl user interface"); if(!ui_method) { - failf(data, "unable do create " OSSL_PACKAGE " user-interface method"); + failf(data, "unable to create " OSSL_PACKAGE " user-interface method"); return 0; } UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL())); @@ -1277,22 +1063,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 */ @@ -1309,10 +1093,9 @@ static int providercheck(struct Curl_easy *data, EVP_PKEY *priv_key = NULL; OSSL_STORE_CTX *store = NULL; OSSL_STORE_INFO *info = NULL; - UI_METHOD *ui_method = - UI_create_method(OSSL_UI_METHOD_CAST("curl user interface")); + UI_METHOD *ui_method = UI_create_method("curl user interface"); if(!ui_method) { - failf(data, "unable do create " OSSL_PACKAGE " user-interface method"); + failf(data, "unable to create " OSSL_PACKAGE " user-interface method"); return 0; } UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL())); @@ -1327,6 +1110,7 @@ static int providercheck(struct Curl_easy *data, failf(data, "Failed to open OpenSSL store: %s", ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); + UI_destroy_method(ui_method); return 0; } if(OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY) != 1) { @@ -1366,22 +1150,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 */ @@ -1411,18 +1193,16 @@ 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)) { - failf(data, "ssl engine cannot load client cert with id" - " '%s' [%s]", cert_file, + 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, sizeof(error_buffer))); return 0; } if(!params.cert) { - failf(data, "ssl engine did not initialized the certificate " - "properly."); + failf(data, "ssl engine did not initialized the certificate properly."); return 0; } @@ -1430,6 +1210,7 @@ static int engineload(struct Curl_easy *data, failf(data, "unable to set client certificate [%s]", ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); + X509_free(params.cert); return 0; } X509_free(params.cert); /* we do not need the handle any more... */ @@ -1439,21 +1220,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 */ @@ -1472,6 +1251,8 @@ static int providerload(struct Curl_easy *data, OSSL_STORE_CTX *store = OSSL_STORE_open_ex(cert_file, data->state.libctx, NULL, NULL, NULL, NULL, NULL, NULL); + int rc; + if(!store) { failf(data, "Failed to open OpenSSL store: %s", ossl_strerror(ERR_get_error(), error_buffer, @@ -1500,28 +1281,28 @@ static int providerload(struct Curl_easy *data, return 0; } - if(SSL_CTX_use_certificate(ctx, cert) != 1) { + rc = SSL_CTX_use_certificate(ctx, cert); + X509_free(cert); /* we do not need the handle any more... */ + + if(rc != 1) { failf(data, "unable to set client certificate [%s]", ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); return 0; } - X509_free(cert); /* we do not need the handle any more... */ } else { failf(data, "crypto provider not set, cannot load certificate"); return 0; } 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, @@ -1539,20 +1320,18 @@ static int pkcs12load(struct Curl_easy *data, if(cert_blob) { cert_bio = BIO_new_mem_buf(cert_blob->data, (int)(cert_blob->len)); if(!cert_bio) { - failf(data, - "BIO_new_mem_buf NULL, " OSSL_PACKAGE " error %s", + 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; } } else { cert_bio = BIO_new(BIO_s_file()); if(!cert_bio) { - failf(data, - "BIO_new return NULL, " OSSL_PACKAGE " error %s", + 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; } @@ -1572,14 +1351,10 @@ static int pkcs12load(struct Curl_easy *data, return 0; } - PKCS12_PBE_add(); - if(!PKCS12_parse(p12, key_passwd, &pri, &x509, &ca)) { - failf(data, - "could not parse PKCS12 file, check password, " OSSL_PACKAGE + failf(data, "could not parse PKCS12 file, check password, " OSSL_PACKAGE " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); + ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); PKCS12_free(p12); return 0; } @@ -1587,17 +1362,14 @@ static int pkcs12load(struct Curl_easy *data, PKCS12_free(p12); if(SSL_CTX_use_certificate(ctx, x509) != 1) { - failf(data, - "could not load PKCS12 client certificate, " OSSL_PACKAGE + failf(data, "could not load PKCS12 client certificate, " OSSL_PACKAGE " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); + ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); goto fail; } if(SSL_CTX_use_PrivateKey(ctx, pri) != 1) { - failf(data, "unable to use private key from PKCS12 file '%s'", - cert_file); + failf(data, "unable to use private key from PKCS12 file '%s'", cert_file); goto fail; } @@ -1634,13 +1406,19 @@ static int pkcs12load(struct Curl_easy *data, fail: EVP_PKEY_free(pri); X509_free(x509); +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +#endif sk_X509_pop_free(ca, X509_free); +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic pop +#endif if(!cert_done) return 0; /* failure! */ return 1; } - static CURLcode client_cert(struct Curl_easy *data, SSL_CTX* ctx, char *cert_file, @@ -1679,10 +1457,10 @@ static CURLcode client_cert(struct Curl_easy *data, failf(data, "could not load PEM client certificate from %s, " OSSL_PACKAGE " error %s, " - "(no key found, wrong pass phrase, or wrong file format?)", + "(no key found, wrong passphrase, or wrong file format?)", (cert_blob ? "CURLOPT_SSLCERT_BLOB" : cert_file), ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); + sizeof(error_buffer))); return CURLE_SSL_CERTPROBLEM; } break; @@ -1699,21 +1477,21 @@ static CURLcode client_cert(struct Curl_easy *data, failf(data, "could not load ASN1 client certificate from %s, " OSSL_PACKAGE " error %s, " - "(no key found, wrong pass phrase, or wrong file format?)", + "(no key found, wrong passphrase, or wrong file format?)", (cert_blob ? "CURLOPT_SSLCERT_BLOB" : cert_file), ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); + sizeof(error_buffer))); return CURLE_SSL_CERTPROBLEM; } break; case SSL_FILETYPE_ENGINE: - if(!engineload(data, ctx, cert_file)) + if(!cert_file || !engineload(data, ctx, cert_file)) return CURLE_SSL_CERTPROBLEM; break; case SSL_FILETYPE_PROVIDER: - if(!providerload(data, ctx, cert_file)) + if(!cert_file || !providerload(data, ctx, cert_file)) return CURLE_SSL_CERTPROBLEM; break; @@ -1728,7 +1506,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; } @@ -1777,8 +1555,6 @@ static CURLcode client_cert(struct Curl_easy *data, x509 = SSL_get_certificate(ssl); - /* This version was provided by Evan Jordan and is supposed to not - leak memory as the previous version: */ if(x509) { EVP_PKEY *pktmp = X509_get_pubkey(x509); EVP_PKEY_copy_parameters(pktmp, SSL_get_privatekey(ssl)); @@ -1790,13 +1566,7 @@ static CURLcode client_cert(struct Curl_easy *data, /* If RSA is used, do not check the private key if its flags indicate * it does not support it. */ EVP_PKEY *priv_key = SSL_get_privatekey(ssl); - int pktype; -#ifdef HAVE_OPAQUE_EVP_PKEY - pktype = EVP_PKEY_id(priv_key); -#else - pktype = priv_key->type; -#endif - if(pktype == EVP_PKEY_RSA) { + if(EVP_PKEY_id(priv_key) == EVP_PKEY_RSA) { RSA *rsa = EVP_PKEY_get1_RSA(priv_key); if(RSA_flags(rsa) & RSA_METHOD_FLAG_NO_CHECK) check_privkey = FALSE; @@ -1822,8 +1592,9 @@ static CURLcode client_cert(struct Curl_easy *data, return CURLE_OK; } +#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; @@ -1838,11 +1609,12 @@ static CURLcode x509_name_oneline(X509_NAME *a, struct dynbuf *d) if(rc != -1) { BIO_get_mem_ptr(bio_out, &biomem); result = curlx_dyn_addn(d, biomem->data, biomem->length); - BIO_free(bio_out); } + BIO_free(bio_out); } return result; } +#endif /** * Global SSL init @@ -1852,7 +1624,6 @@ static CURLcode x509_name_oneline(X509_NAME *a, struct dynbuf *d) */ static int ossl_init(void) { -#if OPENSSL_VERSION_NUMBER >= 0x10100000L const uint64_t flags = #ifdef OPENSSL_INIT_ENGINE_ALL_BUILTIN /* not present in BoringSSL */ @@ -1865,30 +1636,10 @@ static int ossl_init(void) #endif 0; OPENSSL_init_ssl(flags, NULL); -#else - OPENSSL_load_builtin_modules(); - -#ifdef USE_OPENSSL_ENGINE - ENGINE_load_builtin_engines(); -#endif - -#ifndef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG - CONF_modules_load_file(NULL, NULL, - CONF_MFLAGS_DEFAULT_SECTION| - CONF_MFLAGS_IGNORE_MISSING_FILE); -#endif - - /* Let's get nice error messages */ - SSL_load_error_strings(); - - /* Init the global ciphers and digests */ - if(!SSLeay_add_ssl_algorithms()) - return 0; - - OpenSSL_add_all_algorithms(); -#endif +#ifndef HAVE_KEYLOG_UPSTREAM Curl_tls_keylog_open(); +#endif return 1; } @@ -1896,33 +1647,9 @@ static int ossl_init(void) /* Global cleanup */ static void ossl_cleanup(void) { -#if OPENSSL_VERSION_NUMBER >= 0x10100000L - /* OpenSSL 1.1 deprecates all these cleanup functions and - turns them into no-ops in OpenSSL 1.0 compatibility mode */ -#else - /* Free ciphers and digests lists */ - EVP_cleanup(); - -#ifdef USE_OPENSSL_ENGINE - /* Free engine list */ - ENGINE_cleanup(); -#endif - - /* Free OpenSSL error strings */ - ERR_free_strings(); - - /* Free thread local error state, destroying hash upon zero refcount */ - ERR_remove_thread_state(NULL); - - /* Free all memory allocated by all configuration modules */ - CONF_modules_free(); - -#ifdef HAVE_SSL_COMP_FREE_COMPRESSION_METHODS - SSL_COMP_free_compression_methods(); -#endif -#endif - +#ifndef HAVE_KEYLOG_UPSTREAM Curl_tls_keylog_close(); +#endif } /* Selects an OpenSSL crypto engine or provider. @@ -2023,7 +1750,7 @@ static void ossl_provider_cleanup(struct Curl_easy *data) } OSSL_LIB_CTX_free(data->state.libctx); data->state.libctx = NULL; - Curl_safefree(data->state.propq); + curlx_safefree(data->state.propq); data->state.provider_loaded = FALSE; } @@ -2064,7 +1791,7 @@ static CURLcode ossl_set_provider(struct Curl_easy *data, const char *iname) if(!libctx) return CURLE_OUT_OF_MEMORY; if(propq) { - data->state.propq = strdup(propq); + data->state.propq = curlx_strdup(propq); if(!data->state.propq) { OSSL_LIB_CTX_free(libctx); return CURLE_OUT_OF_MEMORY; @@ -2087,20 +1814,17 @@ static CURLcode ossl_set_provider(struct Curl_easy *data, const char *iname) return CURLE_OK; } - data->state.provider = - OSSL_PROVIDER_try_load(data->state.libctx, name, 1); + data->state.provider = OSSL_PROVIDER_try_load(data->state.libctx, name, 1); if(!data->state.provider) { char error_buffer[256]; failf(data, "Failed to initialize provider: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); + ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); ossl_provider_cleanup(data); return CURLE_SSL_ENGINE_NOTFOUND; } /* load the base provider as well */ - data->state.baseprov = - OSSL_PROVIDER_try_load(data->state.libctx, "base", 1); + data->state.baseprov = OSSL_PROVIDER_try_load(data->state.libctx, "base", 1); if(!data->state.baseprov) { ossl_provider_cleanup(data); failf(data, "Failed to load base"); @@ -2112,7 +1836,6 @@ static CURLcode ossl_set_provider(struct Curl_easy *data, const char *iname) } #endif - static CURLcode ossl_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool send_shutdown, bool *done) @@ -2122,7 +1845,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); @@ -2167,14 +1889,16 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf, /* SSL should now have started the shutdown from our side. Since it * was not complete, we are lacking the close notify from the server. */ if(send_shutdown && !(SSL_get_shutdown(octx->ssl) & SSL_SENT_SHUTDOWN)) { + int rc; ERR_clear_error(); CURL_TRC_CF(data, cf, "send SSL close notify"); - if(SSL_shutdown(octx->ssl) == 1) { + rc = SSL_shutdown(octx->ssl); + if(rc == 1) { CURL_TRC_CF(data, cf, "SSL shutdown finished"); *done = TRUE; goto out; } - if(SSL_ERROR_WANT_WRITE == SSL_get_error(octx->ssl, nread)) { + if(SSL_ERROR_WANT_WRITE == SSL_get_error(octx->ssl, rc)) { CURL_TRC_CF(data, cf, "SSL shutdown still wants to send"); connssl->io_need = CURL_SSL_IO_NEED_SEND; goto out; @@ -2199,7 +1923,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. */ @@ -2213,12 +1937,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; @@ -2273,13 +1999,6 @@ static void ossl_close_all(struct Curl_easy *data) #ifdef OPENSSL_HAS_PROVIDERS ossl_provider_cleanup(data); #endif -#ifndef HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED - /* OpenSSL 1.0.1 and 1.0.2 build an error queue that is stored per-thread - so we need to clean it here in case the thread will be killed. All OpenSSL - code should extract the error in association with the error so clearing - this queue here should be harmless at worst. */ - ERR_remove_thread_state(NULL); -#endif } /* ====================================================== */ @@ -2380,15 +2099,15 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, if(check->type == target) { /* get data and length */ const char *altptr = (const char *)ASN1_STRING_get0_data(check->d.ia5); - size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5); + size_t altlen = (size_t)ASN1_STRING_length(check->d.ia5); switch(target) { case GEN_DNS: /* name/pattern comparison */ - /* The OpenSSL manpage explicitly says: "In general it cannot be + /* The OpenSSL man page explicitly says: "In general it cannot be assumed that the data returned by ASN1_STRING_data() is null - terminated or does not contain embedded nulls." But also that - "The actual format of the data will depend on the actual string - type itself: for example for an IA5String the data will be ASCII" + terminated or does not contain embedded nulls.", but also that + "The actual format of the data depends on the actual string + type itself: for example for an IA5String the data is ASCII" It has been however verified that in 0.9.6 and 0.9.7, IA5String is always null-terminated. @@ -2398,7 +2117,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, string and we cannot match it. */ Curl_cert_hostcheck(altptr, altlen, peer->hostname, hostlen)) { matched = TRUE; - infof(data, " subjectAltName: host \"%s\" matched cert's \"%.*s\"", + infof(data, " subjectAltName: \"%s\" matches cert's \"%.*s\"", peer->dispname, (int)altlen, altptr); } break; @@ -2408,8 +2127,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, our server IP address is */ if((altlen == addrlen) && !memcmp(altptr, &addr, altlen)) { matched = TRUE; - infof(data, - " subjectAltName: host \"%s\" matched cert's IP address!", + infof(data, " subjectAltName: \"%s\" matches cert's IP address!", peer->dispname); } break; @@ -2440,19 +2158,19 @@ 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) i = j; } - /* we have the name entry and we will now convert this to a string + /* we have the name entry and we now convert this to a string that we can use for comparison. Doing this we support BMPstring, 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 @@ -2484,8 +2202,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, /* error already detected, pass through */ ; else if(!cn) { - failf(data, - "SSL: unable to obtain common name from peer certificate"); + failf(data, "SSL: unable to obtain common name from peer certificate"); result = CURLE_PEER_FAILED_VERIFICATION; } else if(!Curl_cert_hostcheck((const char *)cn, cnlen, @@ -2504,7 +2221,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, return result; } -#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP) +#ifndef OPENSSL_NO_OCSP static CURLcode verifystatus(struct Curl_cfilter *cf, struct Curl_easy *data, struct ossl_ctx *octx) @@ -2586,6 +2303,9 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, for(i = 0; i < (int)sk_X509_num(ch); i++) { X509 *issuer = sk_X509_value(ch, (ossl_valsize_t)i); if(X509_check_issued(issuer, cert) == X509_V_OK) { + /* Note to analysis tools: using SHA1 here is fine. The `id` + * generated is used as a hash lookup key, not as a verifier + * of the OCSP data itself. This all according to RFC 5019. */ id = OCSP_cert_to_id(EVP_sha1(), cert, issuer); break; } @@ -2608,7 +2328,14 @@ static CURLcode verifystatus(struct Curl_cfilter *cf, goto end; } - /* Validate the corresponding single OCSP response */ + /* Validate the OCSP response issuing and update times. + * - `thisupd` is the time the OCSP response was issued + * - `nextupd` is the time the OCSP response should be updated + * (valid life time assigned by the OCSP responder) + * - 3rd param: how many seconds of clock skew we allow between + * our clock and the instance that issued the OCSP response + * - 4th param: how many seconds in the past `thisupd` may be, with + * -1 meaning there is no limit. */ if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) { failf(data, "OCSP response has expired"); result = CURLE_SSL_INVALIDCERTSTATUS; @@ -2645,31 +2372,6 @@ end: static const char *ssl_msg_type(int ssl_ver, int msg) { -#ifdef SSL2_VERSION_MAJOR /* OpenSSL 1.0.2, LibreSSL <=3.9.2 */ - if(ssl_ver == SSL2_VERSION_MAJOR) { - switch(msg) { - case SSL2_MT_ERROR: - return "Error"; - case SSL2_MT_CLIENT_HELLO: - return "Client hello"; - case SSL2_MT_CLIENT_MASTER_KEY: - return "Client key"; - case SSL2_MT_CLIENT_FINISHED: - return "Client finished"; - case SSL2_MT_SERVER_HELLO: - return "Server hello"; - case SSL2_MT_SERVER_VERIFY: - return "Server verify"; - case SSL2_MT_SERVER_FINISHED: - return "Server finished"; - case SSL2_MT_REQUEST_CERTIFICATE: - return "Request CERT"; - case SSL2_MT_CLIENT_CERTIFICATE: - return "Client CERT"; - } - } - else -#endif if(ssl_ver == SSL3_VERSION_MAJOR) { switch(msg) { case SSL3_MT_HELLO_REQUEST: @@ -2756,7 +2458,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, const void *buf, size_t len, SSL *ssl, void *userp) { - const char *verstr = "???"; + const char *verstr; struct Curl_cfilter *cf = userp; struct Curl_easy *data = NULL; char unknown[32]; @@ -2768,11 +2470,6 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, return; switch(ssl_ver) { -#ifdef SSL2_VERSION /* removed in recent versions */ - case SSL2_VERSION: - verstr = "SSLv2"; - break; -#endif #ifdef SSL3_VERSION case SSL3_VERSION: verstr = "SSLv3"; @@ -2791,15 +2488,11 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, verstr = "TLSv1.2"; break; #endif -#ifdef TLS1_3_VERSION /* OpenSSL 1.1.1+, all forks */ case TLS1_3_VERSION: verstr = "TLSv1.3"; break; -#endif - case 0: - break; default: - msnprintf(unknown, sizeof(unknown), "(%x)", ssl_ver); + curl_msnprintf(unknown, sizeof(unknown), "(%x)", ssl_ver); verstr = unknown; break; } @@ -2816,16 +2509,18 @@ 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 */ ssl_ver >>= 8; /* check the upper 8 bits only below */ /* SSLv2 does not seem to have TLS record-type headers, so OpenSSL - * always pass-up content-type as 0. But the interesting message-type + * always pass-up content-type as 0, but the interesting message-type * is at 'buf[0]'. */ if(ssl_ver == SSL3_VERSION_MAJOR && content_type) @@ -2834,22 +2529,28 @@ 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); } - txt_len = msnprintf(ssl_buf, sizeof(ssl_buf), - "%s (%s), %s, %s (%d):\n", - verstr, direction ? "OUT" : "IN", - tls_rt_name, msg_name, msg_type); + txt_len = curl_msnprintf(ssl_buf, sizeof(ssl_buf), + "%s (%s), %s, %s (%d):\n", + verstr, direction ? "OUT" : "IN", + tls_rt_name, msg_name, msg_type); Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len); } @@ -2858,18 +2559,13 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, (void)ssl; } -/* Check for ALPN support. */ -#ifndef OPENSSL_NO_TLSEXT -# define HAS_ALPN_OPENSSL -#endif - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */ -static CURLcode -ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) +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... */ - long curl_ssl_version_min = conn_config->version; + long curl_ssl_version_min = (long)ssl_version_min; long curl_ssl_version_max; /* convert curl min SSL version option to OpenSSL constant */ @@ -2880,6 +2576,8 @@ ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) long ossl_ssl_version_min = 0; long ossl_ssl_version_max = 0; #endif + /* it cannot be default here */ + DEBUGASSERT(curl_ssl_version_min != CURL_SSLVERSION_DEFAULT); switch(curl_ssl_version_min) { case CURL_SSLVERSION_TLSv1: /* TLS 1.x */ case CURL_SSLVERSION_TLSv1_0: @@ -2892,24 +2590,8 @@ ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) ossl_ssl_version_min = TLS1_2_VERSION; break; case CURL_SSLVERSION_TLSv1_3: -#ifdef TLS1_3_VERSION ossl_ssl_version_min = TLS1_3_VERSION; break; -#else - return CURLE_NOT_BUILT_IN; -#endif - } - - /* CURL_SSLVERSION_DEFAULT means that no option was selected. - We do not want to pass 0 to SSL_CTX_set_min_proto_version as - it would enable all versions down to the lowest supported by - the library. - So we skip this, and stay with the library default - */ - if(curl_ssl_version_min != CURL_SSLVERSION_DEFAULT) { - if(!SSL_CTX_set_min_proto_version(ctx, ossl_ssl_version_min)) { - return CURLE_SSL_CONNECT_ERROR; - } } /* ... then, TLS max version */ @@ -2926,102 +2608,34 @@ ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) case CURL_SSLVERSION_MAX_TLSv1_2: ossl_ssl_version_max = TLS1_2_VERSION; break; -#ifdef TLS1_3_VERSION case CURL_SSLVERSION_MAX_TLSv1_3: ossl_ssl_version_max = TLS1_3_VERSION; break; -#endif case CURL_SSLVERSION_MAX_NONE: /* none selected */ case CURL_SSLVERSION_MAX_DEFAULT: /* max selected */ default: - /* SSL_CTX_set_max_proto_version states that: - setting the maximum to 0 will enable - protocol versions up to the highest version - supported by the library */ + /* SSL_CTX_set_max_proto_version states that: setting the maximum to 0 + enables protocol versions up to the highest version supported by + the library */ ossl_ssl_version_max = 0; break; } - if(!SSL_CTX_set_max_proto_version(ctx, ossl_ssl_version_max)) { + if(!SSL_CTX_set_min_proto_version(ctx, ossl_ssl_version_min) || + !SSL_CTX_set_max_proto_version(ctx, ossl_ssl_version_max)) return CURLE_SSL_CONNECT_ERROR; - } return CURLE_OK; } -#endif #ifdef HAVE_BORINGSSL_LIKE typedef uint32_t ctx_option_t; #elif defined(HAVE_OPENSSL3) typedef uint64_t ctx_option_t; -#elif OPENSSL_VERSION_NUMBER >= 0x10100000L && \ - !defined(LIBRESSL_VERSION_NUMBER) -typedef unsigned long ctx_option_t; -#else +#elif defined(LIBRESSL_VERSION_NUMBER) typedef long ctx_option_t; -#endif - -#if OPENSSL_VERSION_NUMBER < 0x10100000L /* 1.1.0 */ -static CURLcode -ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, - struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - long ssl_version = conn_config->version; - long ssl_version_max = conn_config->version_max; - - (void)data; /* In case it is unused. */ - - switch(ssl_version) { - case CURL_SSLVERSION_TLSv1_3: -#ifdef TLS1_3_VERSION - { - struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; - DEBUGASSERT(octx); - SSL_CTX_set_max_proto_version(octx->ssl_ctx, TLS1_3_VERSION); - *ctx_options |= SSL_OP_NO_TLSv1_2; - } #else - (void)ctx_options; - failf(data, OSSL_PACKAGE " was built without TLS 1.3 support"); - return CURLE_NOT_BUILT_IN; -#endif - FALLTHROUGH(); - case CURL_SSLVERSION_TLSv1_2: - *ctx_options |= SSL_OP_NO_TLSv1_1; - FALLTHROUGH(); - case CURL_SSLVERSION_TLSv1_1: - *ctx_options |= SSL_OP_NO_TLSv1; - FALLTHROUGH(); - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1: - break; - } - - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_TLSv1_0: - *ctx_options |= SSL_OP_NO_TLSv1_1; - FALLTHROUGH(); - case CURL_SSLVERSION_MAX_TLSv1_1: - *ctx_options |= SSL_OP_NO_TLSv1_2; - FALLTHROUGH(); - case CURL_SSLVERSION_MAX_TLSv1_2: -#ifdef TLS1_3_VERSION - *ctx_options |= SSL_OP_NO_TLSv1_3; -#endif - break; - case CURL_SSLVERSION_MAX_TLSv1_3: -#ifdef TLS1_3_VERSION - break; -#else - failf(data, OSSL_PACKAGE " was built without TLS 1.3 support"); - return CURLE_NOT_BUILT_IN; -#endif - } - return CURLE_OK; -} +typedef unsigned long ctx_option_t; #endif CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, @@ -3033,7 +2647,6 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, unsigned char *quic_tp, size_t quic_tp_len) { - const struct ssl_config_data *config; unsigned char *der_session_buf = NULL; unsigned char *qtp_clone = NULL; CURLcode result = CURLE_OK; @@ -3041,8 +2654,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, if(!cf || !data) goto out; - config = Curl_ssl_cf_get_config(cf, data); - if(config->primary.cache_session) { + if(Curl_ssl_scache_use(cf, data)) { struct Curl_ssl_session *sc_session = NULL; size_t der_session_size; unsigned char *der_session_ptr; @@ -3054,7 +2666,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, goto out; } - der_session_buf = der_session_ptr = malloc(der_session_size); + der_session_buf = der_session_ptr = curlx_malloc(der_session_size); if(!der_session_buf) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -3070,7 +2682,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; @@ -3080,7 +2692,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 */ @@ -3091,7 +2703,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, } out: - free(der_session_buf); + curlx_free(der_session_buf); return result; } @@ -3100,13 +2712,12 @@ out: */ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) { - struct Curl_cfilter *cf = (struct Curl_cfilter*) SSL_get_app_data(ssl); + struct Curl_cfilter *cf = (struct Curl_cfilter *)SSL_get_app_data(ssl); if(cf) { struct Curl_easy *data = CF_DATA_CURRENT(cf); struct ssl_connect_data *connssl = cf->ctx; Curl_ossl_add_session(cf, data, connssl->peer.scache_key, ssl_sessionid, - SSL_version(ssl), connssl->negotiated.alpn, - NULL, 0); + SSL_version(ssl), connssl->negotiated.alpn, NULL, 0); } return 0; } @@ -3118,7 +2729,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; @@ -3160,7 +2771,14 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, } } +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +#endif sk_X509_INFO_pop_free(inf, X509_INFO_free); +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic pop +#endif BIO_free(cbio); /* if we did not end up importing anything, treat that as an error */ @@ -3168,24 +2786,27 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, } #ifdef USE_WIN32_CRYPTO -static CURLcode import_windows_cert_store(struct Curl_easy *data, - const char *name, - X509_STORE *store, - bool *imported) +static CURLcode ossl_win_load_store(struct Curl_easy *data, + struct Curl_cfilter *cf, + const char *win_store, + X509_STORE *store, + bool *padded) { CURLcode result = CURLE_OK; HCERTSTORE hStore; - *imported = FALSE; + *padded = FALSE; - hStore = CertOpenSystemStoreA(0, name); + hStore = CertOpenSystemStoreA(0, win_store); if(hStore) { PCCERT_CONTEXT pContext = NULL; - /* The array of enhanced key usage OIDs will vary per certificate and + /* The array of enhanced key usage OIDs varies per certificate and is declared outside of the loop so that rather than malloc/free each iteration we can grow it with realloc, when necessary. */ CERT_ENHKEY_USAGE *enhkey_usage = NULL; DWORD enhkey_usage_size = 0; + VERBOSE(size_t total = 0); + VERBOSE(size_t imported = 0); /* This loop makes a best effort to import all valid certificates from the MS root store. If a certificate cannot be imported it is @@ -3202,8 +2823,10 @@ static CURLcode import_windows_cert_store(struct Curl_easy *data, if(!pContext) break; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - else { + VERBOSE(++total); + +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) + { char cert_name[256]; if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, cert_name, sizeof(cert_name))) @@ -3239,9 +2862,9 @@ static CURLcode import_windows_cert_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) { - void *tmp = realloc(enhkey_usage, req_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) { failf(data, "SSL: Out of memory allocating for OID list"); @@ -3287,264 +2910,326 @@ static CURLcode import_windows_cert_store(struct Curl_easy *data, if(!x509) continue; - /* Try to import the certificate. This may fail for legitimate - reasons such as duplicate certificate, which is allowed by MS but - not OpenSSL. */ + /* Try to import the certificate. This may fail for legitimate reasons + 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) + VERBOSE(++imported); +#ifdef DEBUGBUILD infof(data, "SSL: Imported cert"); #endif - *imported = TRUE; + *padded = TRUE; } X509_free(x509); } - free(enhkey_usage); + curlx_free(enhkey_usage); CertFreeCertificateContext(pContext); CertCloseStore(hStore, 0); + CURL_TRC_CF(data, cf, + "ossl_win_load_store() found: %zu imported: %zu certs in %s.", + total, imported, win_store); + if(result) return result; } return result; } + +static CURLcode ossl_windows_load_anchors(struct Curl_cfilter *cf, + struct Curl_easy *data, + X509_STORE *store, + bool *padded) +{ + /* Import certificates from the Windows root certificate store if + requested. + https://stackoverflow.com/questions/9507184/ + https://github.com/d3x0r/SACK/blob/ff15424d3c581b86d40f818532e5a400c516d39d/src/netlib/ssl_layer.c#L1410 + https://datatracker.ietf.org/doc/html/rfc5280 */ + const char *win_stores[] = { + "ROOT", /* Trusted Root Certification Authorities */ + "CA" /* Intermediate Certification Authorities */ + }; + size_t i; + CURLcode result = CURLE_OK; + + *padded = FALSE; + for(i = 0; i < CURL_ARRAYSIZE(win_stores); ++i) { + bool store_added = FALSE; + result = ossl_win_load_store(data, cf, win_stores[i], store, &store_added); + if(result) + return result; + if(store_added) { + CURL_TRC_CF(data, cf, "added trust anchors from Windows %s store", + win_stores[i]); + *padded = TRUE; + } + else + infof(data, "error importing Windows %s store, continuing anyway", + win_stores[i]); + } + return result; +} + +#endif /* USE_WIN32_CRYPTO */ + +static CURLcode ossl_load_trust_anchors(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ossl_ctx *octx, + X509_STORE *store) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode result = CURLE_OK; + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (conn_config->ca_info_blob ? NULL : conn_config->CAfile); + const char * const ssl_capath = conn_config->CApath; + bool have_native_check = FALSE; + + octx->store_is_empty = TRUE; + if(ssl_config->native_ca_store) { +#ifdef USE_WIN32_CRYPTO + bool added = FALSE; + result = ossl_windows_load_anchors(cf, data, store, &added); + if(result) + return result; + if(added) { + infof(data, " Native: Windows System Stores ROOT+CA"); + octx->store_is_empty = FALSE; + } +#elif defined(USE_APPLE_SECTRUST) + infof(data, " Native: Apple SecTrust"); + have_native_check = TRUE; #endif + } + + if(conn_config->ca_info_blob) { + result = load_cacert_from_memory(store, conn_config->ca_info_blob); + if(result) { + failf(data, "error adding trust anchors from certificate blob: %d", + result); + return result; + } + infof(data, " CA Blob from configuration"); + octx->store_is_empty = FALSE; + } + + if(ssl_cafile || ssl_capath) { +#ifdef HAVE_OPENSSL3 + /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ + if(ssl_cafile) { + if(!X509_STORE_load_file(store, ssl_cafile)) { + if(octx->store_is_empty && !have_native_check) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error adding trust anchors from file: %s", ssl_cafile); + return CURLE_SSL_CACERT_BADFILE; + } + else + infof(data, "error setting certificate file, continuing anyway"); + } + infof(data, " CAfile: %s", ssl_cafile); + octx->store_is_empty = FALSE; + } + if(ssl_capath) { + if(!X509_STORE_load_path(store, ssl_capath)) { + if(octx->store_is_empty && !have_native_check) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error adding trust anchors from path: %s", ssl_capath); + return CURLE_SSL_CACERT_BADFILE; + } + else + infof(data, "error setting certificate path, continuing anyway"); + } + infof(data, " CApath: %s", ssl_capath); + octx->store_is_empty = FALSE; + } +#else + /* tell OpenSSL where to find CA certificates that are used to verify the + server's certificate. */ + if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) { + if(octx->store_is_empty && !have_native_check) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error adding trust anchors from locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return CURLE_SSL_CACERT_BADFILE; + } + else { + infof(data, "error setting certificate verify locations," + " continuing anyway"); + } + } + if(ssl_cafile) + infof(data, " CAfile: %s", ssl_cafile); + if(ssl_capath) + infof(data, " CApath: %s", ssl_capath); + octx->store_is_empty = FALSE; +#endif + } + +#ifdef CURL_CA_FALLBACK + if(octx->store_is_empty) { + /* verifying the peer without any CA certificates does not + work so use OpenSSL's built-in default as fallback */ + X509_STORE_set_default_paths(store); + infof(data, " OpenSSL default paths (fallback)"); + octx->store_is_empty = FALSE; + } +#endif + if(octx->store_is_empty && !have_native_check) + infof(data, " no trust anchors configured"); + + return result; +} static CURLcode ossl_populate_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, + struct ossl_ctx *octx, X509_STORE *store) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); CURLcode result = CURLE_OK; X509_LOOKUP *lookup = NULL; - const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; - const char * const ssl_cafile = - /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ca_info_blob ? NULL : conn_config->CAfile); - const char * const ssl_capath = conn_config->CApath; const char * const ssl_crlfile = ssl_config->primary.CRLfile; - const bool verifypeer = conn_config->verifypeer; - bool imported_native_ca = FALSE; - bool imported_ca_info_blob = FALSE; + unsigned long x509flags = 0; - CURL_TRC_CF(data, cf, "ossl_populate_x509_store, path=%s, blob=%d", - ssl_cafile ? ssl_cafile : "none", !!ca_info_blob); + CURL_TRC_CF(data, cf, "configuring OpenSSL's x509 trust store"); if(!store) return CURLE_OUT_OF_MEMORY; - if(verifypeer) { -#ifdef USE_WIN32_CRYPTO - /* Import certificates from the Windows root certificate store if - requested. - https://stackoverflow.com/questions/9507184/ - https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 - https://datatracker.ietf.org/doc/html/rfc5280 */ - if(ssl_config->native_ca_store) { - const char *storeNames[] = { - "ROOT", /* Trusted Root Certification Authorities */ - "CA" /* Intermediate Certification Authorities */ - }; - size_t i; - for(i = 0; i < CURL_ARRAYSIZE(storeNames); ++i) { - bool imported = FALSE; - result = import_windows_cert_store(data, storeNames[i], store, - &imported); - if(result) - return result; - if(imported) { - infof(data, "successfully imported Windows %s store", storeNames[i]); - imported_native_ca = TRUE; - } - else - infof(data, "error importing Windows %s store, continuing anyway", - storeNames[i]); - } - } -#endif - if(ca_info_blob) { - result = load_cacert_from_memory(store, ca_info_blob); - if(result) { - failf(data, "error importing CA certificate blob"); - return result; - } - else { - imported_ca_info_blob = TRUE; - infof(data, "successfully imported CA certificate blob"); - } - } - - if(ssl_cafile || ssl_capath) { -#ifdef HAVE_OPENSSL3 - /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ - if(ssl_cafile && !X509_STORE_load_file(store, ssl_cafile)) { - if(!imported_native_ca && !imported_ca_info_blob) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate file: %s", ssl_cafile); - return CURLE_SSL_CACERT_BADFILE; - } - else - infof(data, "error setting certificate file, continuing anyway"); - } - if(ssl_capath && !X509_STORE_load_path(store, ssl_capath)) { - if(!imported_native_ca && !imported_ca_info_blob) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate path: %s", ssl_capath); - return CURLE_SSL_CACERT_BADFILE; - } - else - infof(data, "error setting certificate path, continuing anyway"); - } -#else - /* tell OpenSSL where to find CA certificates that are used to verify the - server's certificate. */ - if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) { - if(!imported_native_ca && !imported_ca_info_blob) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return CURLE_SSL_CACERT_BADFILE; - } - else { - infof(data, "error setting certificate verify locations," - " continuing anyway"); - } - } -#endif - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } - -#ifdef CURL_CA_FALLBACK - if(!ssl_cafile && !ssl_capath && - !imported_native_ca && !imported_ca_info_blob) { - /* verifying the peer without any CA certificates will not - work so use OpenSSL's built-in default as fallback */ - X509_STORE_set_default_paths(store); - } -#endif + if(!conn_config->verifypeer) { + infof(data, "SSL Trust: peer verification disabled"); + return CURLE_OK; } + infof(data, "SSL Trust Anchors:"); + result = ossl_load_trust_anchors(cf, data, octx, store); + if(result) + return result; + + /* Does not make sense to load a CRL file without peer verification */ if(ssl_crlfile) { /* tell OpenSSL where to find CRL file that is used to check certificate * 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; } - /* Everything is fine. */ - infof(data, "successfully loaded CRL file:"); - X509_STORE_set_flags(store, - X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); - - infof(data, " CRLfile: %s", ssl_crlfile); + x509flags = X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL; + infof(data, " CRLfile: %s", ssl_crlfile); } - if(verifypeer) { - /* Try building a chain using issuers in the trusted store first to avoid - problems with server-sent legacy intermediates. Newer versions of - OpenSSL do alternate chain checking by default but we do not know how to - determine that in a reliable manner. - https://web.archive.org/web/20190422050538/ - rt.openssl.org/Ticket/Display.html?id=3621 + /* Try building a chain using issuers in the trusted store first to avoid + problems with server-sent legacy intermediates. Newer versions of + OpenSSL do alternate chain checking by default but we do not know how to + determine that in a reliable manner. + https://web.archive.org/web/20190422050538/rt.openssl.org/Ticket/Display.html?id=3621 + */ + x509flags |= X509_V_FLAG_TRUSTED_FIRST; + + if(!ssl_config->no_partialchain && !ssl_crlfile) { + /* Have intermediate certificates in the trust store be treated as + trust-anchors, in the same way as self-signed root CA certificates are. + This allows users to verify servers using the intermediate cert only, + instead of needing the whole chain. + + Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we + cannot do partial chains with a CRL check. */ - X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST); - if(!ssl_config->no_partialchain && !ssl_crlfile) { - /* Have intermediate certificates in the trust store be treated as - trust-anchors, in the same way as self-signed root CA certificates - are. This allows users to verify servers using the intermediate cert - only, instead of needing the whole chain. - - Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we - cannot do partial chains with a CRL check. - */ - X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN); - } + x509flags |= X509_V_FLAG_PARTIAL_CHAIN; } + (void)X509_STORE_set_flags(store, x509flags); return result; } -#ifdef HAVE_SSL_X509_STORE_SHARE - /* key to use at `multi->proto_hash` */ -#define MPROTO_OSSL_X509_KEY "tls:ossl:x509:share" +#define MPROTO_OSSL_X509_KEY "tls:ossl:x509:share" struct ossl_x509_share { char *CAfile; /* CAfile path used to generate X509 store */ X509_STORE *store; /* cached X509 store or NULL if none */ struct curltime time; /* when the cached store was created */ + BIT(store_is_empty); /* no certs/paths/blobs are in the store */ + BIT(no_partialchain); /* keep partial chain state */ }; static void oss_x509_share_free(void *key, size_t key_len, void *p) { struct ossl_x509_share *share = p; - DEBUGASSERT(key_len == (sizeof(MPROTO_OSSL_X509_KEY)-1)); + DEBUGASSERT(key_len == (sizeof(MPROTO_OSSL_X509_KEY) - 1)); DEBUGASSERT(!memcmp(MPROTO_OSSL_X509_KEY, key, key_len)); (void)key; (void)key_len; if(share->store) { X509_STORE_free(share->store); } - free(share->CAfile); - free(share); + curlx_free(share->CAfile); + curlx_free(share); } -static bool -ossl_cached_x509_store_expired(const struct Curl_easy *data, - const struct ossl_x509_share *mb) +static bool ossl_cached_x509_store_expired(struct Curl_easy *data, + const struct ossl_x509_share *mb) { const struct ssl_general_config *cfg = &data->set.general_ssl; if(cfg->ca_cache_timeout < 0) return FALSE; else { - struct curltime now = curlx_now(); - timediff_t elapsed_ms = curlx_timediff(now, mb->time); + timediff_t elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &mb->time); timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; return elapsed_ms >= timeout_ms; } } -static bool -ossl_cached_x509_store_different(struct Curl_cfilter *cf, - const struct ossl_x509_share *mb) +static bool ossl_cached_x509_store_different(struct Curl_cfilter *cf, + const struct Curl_easy *data, + const struct ossl_x509_share *mb) { 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, CURL_UNCONST(data)); + if(mb->no_partialchain != ssl_config->no_partialchain) + return TRUE; if(!mb->CAfile || !conn_config->CAfile) return mb->CAfile != conn_config->CAfile; - return strcmp(mb->CAfile, conn_config->CAfile); } static X509_STORE *ossl_get_cached_x509_store(struct Curl_cfilter *cf, - const struct Curl_easy *data) + struct Curl_easy *data, + bool *pempty) { struct Curl_multi *multi = data->multi; struct ossl_x509_share *share; X509_STORE *store = NULL; DEBUGASSERT(multi); + *pempty = TRUE; share = multi ? Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_OSSL_X509_KEY), - sizeof(MPROTO_OSSL_X509_KEY)-1) : NULL; + sizeof(MPROTO_OSSL_X509_KEY) - 1) : NULL; if(share && share->store && !ossl_cached_x509_store_expired(data, share) && - !ossl_cached_x509_store_different(cf, share)) { + !ossl_cached_x509_store_different(cf, data, share)) { store = share->store; + *pempty = (bool)share->store_is_empty; } return store; } static void ossl_set_cached_x509_store(struct Curl_cfilter *cf, - const struct Curl_easy *data, - X509_STORE *store) + struct Curl_easy *data, + X509_STORE *store, + bool is_empty) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct Curl_multi *multi = data->multi; @@ -3555,26 +3240,28 @@ static void ossl_set_cached_x509_store(struct Curl_cfilter *cf, return; share = Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_OSSL_X509_KEY), - sizeof(MPROTO_OSSL_X509_KEY)-1); + sizeof(MPROTO_OSSL_X509_KEY) - 1); if(!share) { - share = calloc(1, sizeof(*share)); + share = curlx_calloc(1, sizeof(*share)); if(!share) return; if(!Curl_hash_add2(&multi->proto_hash, CURL_UNCONST(MPROTO_OSSL_X509_KEY), - sizeof(MPROTO_OSSL_X509_KEY)-1, + sizeof(MPROTO_OSSL_X509_KEY) - 1, share, oss_x509_share_free)) { - free(share); + curlx_free(share); return; } } if(X509_STORE_up_ref(store)) { char *CAfile = NULL; + struct ssl_config_data *ssl_config = + Curl_ssl_cf_get_config(cf, CURL_UNCONST(data)); if(conn_config->CAfile) { - CAfile = strdup(conn_config->CAfile); + CAfile = curlx_strdup(conn_config->CAfile); if(!CAfile) { X509_STORE_free(store); return; @@ -3583,24 +3270,26 @@ static void ossl_set_cached_x509_store(struct Curl_cfilter *cf, if(share->store) { X509_STORE_free(share->store); - free(share->CAfile); + curlx_free(share->CAfile); } - share->time = curlx_now(); + share->time = *Curl_pgrs_now(data); share->store = store; + share->store_is_empty = is_empty; share->CAfile = CAfile; + share->no_partialchain = ssl_config->no_partialchain; } } CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, - SSL_CTX *ssl_ctx) + struct ossl_ctx *octx) { 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); CURLcode result = CURLE_OK; X509_STORE *cached_store; - bool cache_criteria_met; + bool cache_criteria_met, is_empty; /* Consider the X509 store cacheable if it comes exclusively from a CAfile, or no source is provided and we are falling back to OpenSSL's built-in @@ -3614,16 +3303,17 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, ERR_set_mark(); - cached_store = ossl_get_cached_x509_store(cf, data); + cached_store = ossl_get_cached_x509_store(cf, data, &is_empty); if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) { - SSL_CTX_set_cert_store(ssl_ctx, cached_store); + SSL_CTX_set_cert_store(octx->ssl_ctx, cached_store); + octx->store_is_empty = is_empty; } else { - X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); + X509_STORE *store = SSL_CTX_get_cert_store(octx->ssl_ctx); - result = ossl_populate_x509_store(cf, data, store); + result = ossl_populate_x509_store(cf, data, octx, store); if(result == CURLE_OK && cache_criteria_met) { - ossl_set_cached_x509_store(cf, data, store); + ossl_set_cached_x509_store(cf, data, store, (bool)octx->store_is_empty); } } @@ -3631,25 +3321,6 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, return result; } -#else /* HAVE_SSL_X509_STORE_SHARE */ -CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, - struct Curl_easy *data, - SSL_CTX *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); - - ERR_pop_to_mark(); - - return result; -} -#endif /* HAVE_SSL_X509_STORE_SHARE */ - static CURLcode ossl_init_session_and_alpns(struct ossl_ctx *octx, @@ -3660,14 +3331,14 @@ ossl_init_session_and_alpns(struct ossl_ctx *octx, Curl_ossl_init_session_reuse_cb *sess_reuse_cb) { 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); octx->reused_session = FALSE; - if(ssl_config->primary.cache_session) { + if(Curl_ssl_scache_use(cf, data) && !conn_cfg->verifystatus) { struct Curl_ssl_session *scs = NULL; result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs); @@ -3677,40 +3348,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; -#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) - 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(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->bits.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); + } + } #else - (void)sess_reuse_cb; + (void)ssl_config; + (void)sess_reuse_cb; #endif + } } SSL_SESSION_free(ssl_session); } @@ -3721,7 +3406,6 @@ ossl_init_session_and_alpns(struct ossl_ctx *octx, Curl_ssl_scache_return(cf, data, peer->scache_key, scs); } -#ifdef HAS_ALPN_OPENSSL if(alpns.count) { struct alpn_proto_buf proto; memset(&proto, 0, sizeof(proto)); @@ -3730,17 +3414,26 @@ 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; } } -#endif return CURLE_OK; } -#ifdef USE_ECH_OPENSSL +#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST +bool Curl_ossl_need_httpsrr(struct Curl_easy *data) +{ + if(!CURLECH_ENABLED(data)) + return FALSE; + if((data->set.tls_ech & CURLECH_GREASE) || + (data->set.tls_ech & CURLECH_CLA_CFG)) + return FALSE; + return TRUE; +} + static CURLcode ossl_init_ech(struct ossl_ctx *octx, struct Curl_cfilter *cf, struct Curl_easy *data, @@ -3750,21 +3443,21 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, size_t ech_config_len = 0; char *outername = data->set.str[STRING_ECH_PUBLIC]; int trying_ech_now = 0; - CURLcode result; + CURLcode result = CURLE_OK; - if(!ECH_ENABLED(data)) + if(!CURLECH_ENABLED(data)) return CURLE_OK; 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]; @@ -3779,100 +3472,91 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, if(data->set.tls_ech & CURLECH_HARD) return result; } - if(SSL_set1_ech_config_list(octx->ssl, ech_config, - ech_config_len) != 1) { + if(SSL_set1_ech_config_list(octx->ssl, ech_config, ech_config_len) != 1) { infof(data, "ECH: SSL_ECH_set1_ech_config_list failed"); if(data->set.tls_ech & CURLECH_HARD) { - free(ech_config); + curlx_free(ech_config); return CURLE_SSL_CONNECT_ERROR; } } - free(ech_config); + curlx_free(ech_config); trying_ech_now = 1; -# else - ech_config = (unsigned char *) data->set.str[STRING_ECH_CONFIG]; +#else + ech_config = (unsigned char *)data->set.str[STRING_ECH_CONFIG]; if(!ech_config) { infof(data, "ECH: ECHConfig from command line empty"); return CURLE_SSL_CONNECT_ERROR; } ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]); - if(SSL_set1_ech_config_list(octx->ssl, ech_config, - ech_config_len) != 1) { + if(SSL_set1_ech_config_list(octx->ssl, ech_config, ech_config_len) != 1) { infof(data, "ECH: SSL_ECH_set1_ech_config_list failed"); if(data->set.tls_ech & CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } else trying_ech_now = 1; -# endif +#endif /* HAVE_BORINGSSL_LIKE */ infof(data, "ECH: ECHConfig from command line"); } else { - struct Curl_dns_entry *dns = NULL; + const struct Curl_https_rrinfo *rinfo = + Curl_conn_dns_get_https(data, cf->sockindex); - if(peer->hostname) - dns = Curl_dnscache_get(data, peer->hostname, peer->port, - cf->conn->ip_version); - if(!dns) { - infof(data, "ECH: requested but no DNS info available"); - if(data->set.tls_ech & CURLECH_HARD) - return CURLE_SSL_CONNECT_ERROR; - } - else { - struct Curl_https_rrinfo *rinfo = NULL; + if(rinfo && rinfo->echconfiglist) { + const unsigned char *ecl = rinfo->echconfiglist; + size_t elen = rinfo->echconfiglist_len; - rinfo = dns->hinfo; - if(rinfo && rinfo->echconfiglist) { - unsigned char *ecl = rinfo->echconfiglist; - size_t elen = rinfo->echconfiglist_len; - - infof(data, "ECH: ECHConfig from DoH HTTPS RR"); - if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { - infof(data, "ECH: SSL_set1_ech_config_list failed"); - if(data->set.tls_ech & CURLECH_HARD) - return CURLE_SSL_CONNECT_ERROR; - } - else { - trying_ech_now = 1; - infof(data, "ECH: imported ECHConfigList of length %zu", elen); - } - } - else { - infof(data, "ECH: requested but no ECHConfig available"); + infof(data, "ECH: ECHConfig from HTTPS RR"); + if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { + infof(data, "ECH: SSL_set1_ech_config_list failed"); if(data->set.tls_ech & CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } - Curl_resolv_unlink(data, &dns); + else { + trying_ech_now = 1; + infof(data, "ECH: imported ECHConfigList of length %zu", elen); + } + } + else { + infof(data, "ECH: requested but no ECHConfig available"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; } } -# ifdef HAVE_BORINGSSL_LIKE +#ifdef HAVE_BORINGSSL_LIKE + (void)peer; 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); result = SSL_ech_set1_server_names(octx->ssl, - peer->hostname, outername, - 0 /* do send outer */); + peer->hostname, outername, + 0 /* do send outer */); if(result != 1) { infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result); return CURLE_SSL_CONNECT_ERROR; } } -# endif /* HAVE_BORINGSSL_LIKE */ - if(trying_ech_now - && SSL_set_min_proto_version(octx->ssl, TLS1_3_VERSION) != 1) { +#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]"); return CURLE_SSL_CONNECT_ERROR; } return CURLE_OK; } -#endif /* USE_ECH_OPENSSL */ - +#else /* HAVE_SSL_SET1_ECH_CONFIG_LIST */ +bool Curl_ossl_need_httpsrr(struct Curl_easy *data) +{ + (void)data; + return FALSE; +} +#endif /* else HAVE_SSL_SET1_ECH_CONFIG_LIST */ static CURLcode ossl_init_ssl(struct ossl_ctx *octx, struct Curl_cfilter *cf, @@ -3893,14 +3577,13 @@ static CURLcode ossl_init_ssl(struct ossl_ctx *octx, SSL_set_app_data(octx->ssl, ssl_user_data); -#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP) +#ifndef OPENSSL_NO_OCSP if(Curl_ssl_cf_get_primary_config(cf)->verifystatus) SSL_set_tlsext_status_type(octx->ssl, TLSEXT_STATUSTYPE_ocsp); #endif SSL_set_connect_state(octx->ssl); - octx->server_cert = NULL; if(peer->sni) { if(!SSL_set_tlsext_host_name(octx->ssl, peer->sni)) { failf(data, "Failed set SNI"); @@ -3908,19 +3591,18 @@ static CURLcode ossl_init_ssl(struct ossl_ctx *octx, } } -#ifdef USE_ECH_OPENSSL +#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST { CURLcode result = ossl_init_ech(octx, cf, data, peer); if(result) return result; } -#endif /* USE_ECH_OPENSSL */ +#endif /* HAVE_SSL_SET1_ECH_CONFIG_LIST */ return ossl_init_session_and_alpns(octx, cf, data, peer, alpns_requested, sess_reuse_cb); } - static CURLcode ossl_init_method(struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, @@ -3931,22 +3613,18 @@ 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: case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_3: - /* it will be handled later with the context options */ -#if OPENSSL_VERSION_NUMBER >= 0x10100000L + /* it is handled later with the context options */ *pmethod = TLS_client_method(); -#else - *pmethod = SSLv23_client_method(); -#endif break; case CURL_SSLVERSION_SSLv2: failf(data, "No SSLv2 support"); @@ -3962,18 +3640,13 @@ static CURLcode ossl_init_method(struct Curl_cfilter *cf, case TRNSPRT_QUIC: *pssl_version_min = CURL_SSLVERSION_TLSv1_3; if(conn_config->version_max && + (conn_config->version_max != CURL_SSLVERSION_MAX_DEFAULT) && (conn_config->version_max != CURL_SSLVERSION_MAX_TLSv1_3)) { failf(data, "QUIC needs at least TLS version 1.3"); return CURLE_SSL_CONNECT_ERROR; } -#ifdef USE_OPENSSL_QUIC - *pmethod = OSSL_QUIC_client_method(); -#elif (OPENSSL_VERSION_NUMBER >= 0x10100000L) *pmethod = TLS_method(); -#else - *pmethod = SSLv23_client_method(); -#endif break; default: failf(data, "unsupported transport %d in SSL init", peer->transport); @@ -3983,7 +3656,6 @@ static CURLcode ossl_init_method(struct Curl_cfilter *cf, return *pmethod ? CURLE_OK : CURLE_SSL_CONNECT_ERROR; } - CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, struct Curl_cfilter *cf, struct Curl_easy *data, @@ -4004,7 +3676,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, char * const ssl_cert = ssl_config->primary.clientcert; const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; const char * const ssl_cert_type = ssl_config->cert_type; - const bool verifypeer = conn_config->verifypeer; unsigned int ssl_version_min; char error_buffer[256]; @@ -4048,7 +3719,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, /* OpenSSL contains code to work around lots of bugs and flaws in various SSL-implementations. SSL_CTX_set_options() is used to enabled those - work-arounds. The manpage for this option states that SSL_OP_ALL enables + work-arounds. The man page for this option states that SSL_OP_ALL enables all the work-arounds and that "It is usually safe to use SSL_OP_ALL to enable the bug workaround options if compatibility with somewhat broken implementations is desired." @@ -4059,8 +3730,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. @@ -4092,14 +3763,14 @@ 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: /* TLS >= version 1.0 */ case CURL_SSLVERSION_TLSv1_0: /* TLS >= version 1.0 */ case CURL_SSLVERSION_TLSv1_1: /* TLS >= version 1.1 */ case CURL_SSLVERSION_TLSv1_2: /* TLS >= version 1.2 */ @@ -4109,11 +3780,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, ctx_options |= SSL_OP_NO_SSLv2; ctx_options |= SSL_OP_NO_SSLv3; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */ - result = ossl_set_ssl_version_min_max(cf, octx->ssl_ctx); -#else - result = ossl_set_ssl_version_min_max_legacy(&ctx_options, cf, data); -#endif + result = ossl_set_ssl_version_min_max(cf, octx->ssl_ctx, ssl_version_min); if(result) return result; break; @@ -4130,7 +3797,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, OpenSSL supports processing "jumbo TLS record" (8 TLS records) in one go for some algorithms, so match that here. Experimentation shows that a slightly larger buffer is needed - to avoid short reads. + to avoid short reads. However using a large buffer (8 packets) actually decreases performance. 4 packets is better. @@ -4144,7 +3811,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, ciphers = conn_config->cipher_list; if(!ciphers && (peer->transport != TRNSPRT_QUIC)) - ciphers = DEFAULT_CIPHER_SELECTION; + ciphers = NULL; if(ciphers && (ssl_version_min < CURL_SSLVERSION_TLSv1_3)) { if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, ciphers)) { failf(data, "failed setting cipher list: %s", ciphers); @@ -4158,6 +3825,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, const char *ciphers13 = conn_config->cipher_list13; if(ciphers13 && (!conn_config->version_max || + (conn_config->version_max == CURL_SSLVERSION_MAX_DEFAULT) || (conn_config->version_max >= CURL_SSLVERSION_MAX_TLSv1_3))) { if(!SSL_CTX_set_ciphersuites(octx->ssl_ctx, ciphers13)) { failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13); @@ -4238,15 +3906,14 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, } #endif /* HAVE_OPENSSL_SRP && USE_TLS_SRP */ - /* OpenSSL always tries to verify the peer, this only says whether it should - * fail to connect if the verification fails, or if it should continue - * anyway. In the latter case the result of the verification is checked with - * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(octx->ssl_ctx, - verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); + /* OpenSSL always tries to verify the peer. By setting the failure mode + * to NONE, we allow the connect to complete, regardless of the outcome. + * We then explicitly check the result and may try alternatives like + * Apple's SecTrust for verification. */ + 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); } @@ -4269,7 +3936,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, * we need to do the full initialization before calling it. * See: #11800 */ if(!octx->x509_store_setup) { - result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); + result = Curl_ssl_setup_x509_store(cf, data, octx); if(result) return result; octx->x509_store_setup = TRUE; @@ -4295,33 +3962,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) +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; @@ -4346,8 +3996,7 @@ void Curl_ossl_report_handshake(struct Curl_easy *data, #else (void)data; (void)octx; -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ - +#endif /* CURLVERBOSE */ } static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, @@ -4379,7 +4028,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #ifdef HAVE_SSL_SET0_WBIO /* with OpenSSL v1.1.1 we get an alternative to SSL_set_bio() that works * without backward compat quirks. Every call takes one reference, so we - * up it and pass. SSL* then owns it and will free. + * up it and pass. SSL* then owns and frees it. * We check on the function in configure, since LibreSSL and friends * each have their own versions to add support for this. */ BIO_up_ref(bio); @@ -4389,56 +4038,55 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, SSL_set_bio(octx->ssl, bio, bio); #endif -#ifdef HAS_ALPN_OPENSSL if(connssl->alpn && (connssl->state != ssl_connection_deferred)) { struct alpn_proto_buf proto; memset(&proto, 0, sizeof(proto)); Curl_alpn_to_proto_str(&proto, connssl->alpn); infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } -#endif + connssl->connecting_state = ssl_connect_2; return CURLE_OK; } -#ifdef USE_ECH_OPENSSL +#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST /* If we have retry configs, then trace those out */ -static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl, +static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL *ssl, int reason) { CURLcode result = CURLE_OK; size_t rcl = 0; int rv = 1; -# ifndef HAVE_BORINGSSL_LIKE +#ifndef HAVE_BORINGSSL_LIKE char *inner = NULL; - unsigned char *rcs = 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)) + if(!CURLECH_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; size_t blen = 0; - result = curlx_base64_encode((const char *)rcs, rcl, &b64str, &blen); + result = curlx_base64_encode(rcs, rcl, &b64str, &blen); if(!result && b64str) { infof(data, "ECH: retry_configs %s", b64str); - free(b64str); + curlx_free(b64str); #ifndef HAVE_BORINGSSL_LIKE rv = SSL_ech_get1_status(ssl, &inner, &outer); infof(data, "ECH: retry_configs for %s from %s, %d %d", @@ -4455,9 +4103,11 @@ 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 - OPENSSL_free((void *)rcs); -# endif +#ifndef HAVE_BORINGSSL_LIKE + OPENSSL_free(inner); + OPENSSL_free(rcs); + OPENSSL_free(outer); +#endif return; } @@ -4481,13 +4131,13 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, if(!octx->x509_store_setup) { /* After having send off the ClientHello, we prepare the x509 * store to verify the coming certificate from the server */ - CURLcode result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); + CURLcode result = Curl_ssl_setup_x509_store(cf, data, octx); if(result) return result; 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). */ @@ -4529,7 +4179,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, else { /* untreated error */ sslerr_t errdetail; - char error_buffer[256]=""; + char error_buffer[256] = ""; CURLcode result; long lerr; int lib; @@ -4547,8 +4197,12 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, reason = ERR_GET_REASON(errdetail); if((lib == ERR_LIB_SSL) && - ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) || - (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) { + ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) +/* Missing from OpenSSL 4+ OPENSSL_NO_DEPRECATED_3_0 builds */ +#ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED + || (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED) +#endif + )) { result = CURLE_PEER_FAILED_VERIFICATION; lerr = SSL_get_verify_result(octx->ssl); @@ -4572,13 +4226,13 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, ossl_strerror(errdetail, error_buffer, sizeof(error_buffer))); } #endif -#ifdef USE_ECH_OPENSSL +#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); @@ -4600,12 +4254,12 @@ 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) { - char extramsg[80]=""; + if(result == CURLE_SSL_CONNECT_ERROR && errdetail == 0) { + char extramsg[80] = ""; int sockerr = SOCKERRNO; if(sockerr && detail == SSL_ERROR_SYSCALL) - Curl_strerror(sockerr, extramsg, sizeof(extramsg)); + curlx_strerror(sockerr, extramsg, sizeof(extramsg)); failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ", extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), connssl->peer.hostname, connssl->peer.port); @@ -4619,8 +4273,8 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, connssl->connecting_state = ssl_connect_3; Curl_ossl_report_handshake(data, octx); -#if defined(USE_ECH_OPENSSL) && !defined(HAVE_BORINGSSL_LIKE) - if(ECH_ENABLED(data)) { +#if defined(HAVE_SSL_SET1_ECH_CONFIG_LIST) && !defined(HAVE_BORINGSSL_LIKE) + if(CURLECH_ENABLED(data)) { char *inner = NULL, *outer = NULL; const char *status = NULL; int rv; @@ -4651,12 +4305,21 @@ 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); + infof(data, "ECH: unexpected status %d", rv); } infof(data, "ECH: result: status is %s, inner is %s, outer is %s", (status ? status : "NULL"), @@ -4668,8 +4331,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; } @@ -4677,9 +4339,8 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, else { infof(data, "ECH: result: status is not attempted"); } -#endif /* USE_ECH_OPENSSL && !HAVE_BORINGSSL_LIKE */ +#endif /* HAVE_SSL_SET1_ECH_CONFIG_LIST && !HAVE_BORINGSSL_LIKE */ -#ifdef HAS_ALPN_OPENSSL /* Sets data and len to negotiated protocol, len is 0 if no protocol was * negotiated */ @@ -4690,7 +4351,6 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, return Curl_alpn_set_negotiated(cf, data, connssl, neg_protocol, len); } -#endif return CURLE_OK; } @@ -4700,7 +4360,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, * Heavily modified from: * https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL */ -static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, +static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509 *cert, const char *pinnedpubkey) { /* Scratch */ @@ -4718,16 +4378,13 @@ static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, return result; do { - /* Begin Gyrations to get the subjectPublicKeyInfo */ - /* Thanks to Viktor Dukhovni on the OpenSSL mailing list */ - - /* https://groups.google.com/group/mailing.openssl.users/browse_thread - /thread/d61858dae102c6c7 */ + /* Get the subjectPublicKeyInfo */ + /* https://groups.google.com/group/mailing.openssl.users/browse_thread/thread/d61858dae102c6c7 */ len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL); if(len1 < 1) break; /* failed */ - buff1 = temp = malloc(len1); + buff1 = temp = curlx_malloc(len1); if(!buff1) break; /* failed */ @@ -4737,7 +4394,7 @@ static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, /* * These checks are verifying we got back the same values as when we * sized the buffer. it is pretty weak since they should always be the - * same. But it gives us something to test. + * same, but it gives us something to test. */ if((len1 != len2) || !temp || ((temp - buff1) != len1)) break; /* failed */ @@ -4749,15 +4406,14 @@ static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, } while(0); if(buff1) - free(buff1); + curlx_free(buff1); return result; } -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ - !(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; @@ -4765,11 +4421,16 @@ static void infof_certstack(struct Curl_easy *data, const SSL *ssl) int num_cert_levels; int cert_level; + if(!Curl_trc_is_verbose(data)) + return; + verify_result = SSL_get_verify_result(ssl); if(verify_result != X509_V_OK) certstack = SSL_get_peer_cert_chain(ssl); else certstack = SSL_get0_verified_chain(ssl); + if(!certstack) + return; num_cert_levels = sk_X509_num(certstack); for(cert_level = 0; cert_level < num_cert_levels; cert_level++) { @@ -4785,12 +4446,17 @@ static void infof_certstack(struct Curl_easy *data, const SSL *ssl) const char *type_name; current_cert = sk_X509_value(certstack, cert_level); + if(!current_cert) + continue; + + current_pkey = X509_get0_pubkey(current_cert); + if(!current_pkey) + continue; X509_get0_signature(NULL, &palg_cert, current_cert); X509_ALGOR_get0(&paobj_cert, NULL, NULL, palg_cert); OBJ_obj2txt(cert_algorithm, sizeof(cert_algorithm), paobj_cert, 0); - current_pkey = X509_get0_pubkey(current_cert); key_bits = EVP_PKEY_bits(current_pkey); #ifndef HAVE_OPENSSL3 #define EVP_PKEY_get_security_bits EVP_PKEY_security_bits @@ -4801,16 +4467,16 @@ static void infof_certstack(struct Curl_easy *data, const SSL *ssl) char group_name[80] = ""; get_group_name = EVP_PKEY_get_group_name(current_pkey, group_name, sizeof(group_name), NULL); - msnprintf(group_name_final, sizeof(group_name_final), "/%s", group_name); + curl_msnprintf(group_name_final, sizeof(group_name_final), "/%s", + group_name); } - type_name = current_pkey ? EVP_PKEY_get0_type_name(current_pkey) : NULL; + type_name = EVP_PKEY_get0_type_name(current_pkey); #else get_group_name = 0; type_name = NULL; #endif - infof(data, - " Certificate level %d: " + infof(data, " Certificate level %d: " "Public key type %s%s (%d/%d Bits/secBits), signed using %s", cert_level, type_name ? type_name : "?", get_group_name == 0 ? "" : group_name_final, @@ -4820,8 +4486,236 @@ 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, + X509 *server_cert) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + X509 *issuer = NULL; + BIO *fp = NULL; + char err_buf[256] = ""; + bool verify_enabled = (conn_config->verifypeer || conn_config->verifyhost); + CURLcode result = CURLE_OK; + + /* e.g. match issuer name with provided issuer certificate */ + if(conn_config->issuercert_blob) { + fp = BIO_new_mem_buf(conn_config->issuercert_blob->data, + (int)conn_config->issuercert_blob->len); + if(!fp) { + failf(data, "BIO_new_mem_buf NULL, " OSSL_PACKAGE " error %s", + ossl_strerror(ERR_get_error(), err_buf, sizeof(err_buf))); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + else if(conn_config->issuercert) { + fp = BIO_new(BIO_s_file()); + if(!fp) { + failf(data, "BIO_new return NULL, " OSSL_PACKAGE " error %s", + ossl_strerror(ERR_get_error(), err_buf, sizeof(err_buf))); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + if(BIO_read_filename(fp, conn_config->issuercert) <= 0) { + if(verify_enabled) + failf(data, "SSL: Unable to open issuer cert (%s)", + conn_config->issuercert); + result = CURLE_SSL_ISSUER_ERROR; + goto out; + } + } + + if(fp) { + issuer = PEM_read_bio_X509(fp, NULL, ZERO_NULL, NULL); + if(!issuer) { + if(verify_enabled) + failf(data, "SSL: Unable to read issuer cert (%s)", + conn_config->issuercert); + result = CURLE_SSL_ISSUER_ERROR; + goto out; + } + + if(X509_check_issued(issuer, server_cert) != X509_V_OK) { + if(verify_enabled) + failf(data, "SSL: Certificate issuer check failed (%s)", + conn_config->issuercert); + result = CURLE_SSL_ISSUER_ERROR; + goto out; + } + + infof(data, " SSL certificate issuer check ok (%s)", + conn_config->issuercert); + } + +out: + if(fp) + BIO_free(fp); + if(issuer) + X509_free(issuer); + return result; +} + +static CURLcode ossl_check_pinned_key(struct Curl_cfilter *cf, + struct Curl_easy *data, + X509 *server_cert) +{ + const char *ptr; + CURLcode result = CURLE_OK; + + (void)cf; +#ifndef CURL_DISABLE_PROXY + ptr = Curl_ssl_cf_is_proxy(cf) ? + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : + data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#else + ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; +#endif + if(ptr) { + result = ossl_pkp_pin_peer_pubkey(data, server_cert, ptr); + if(result) + failf(data, "SSL: public key does not match pinned public key"); + } + return result; +} + +#ifdef CURLVERBOSE #define MAX_CERT_NAME_LENGTH 2048 +static CURLcode ossl_infof_cert(struct Curl_cfilter *cf, + struct Curl_easy *data, + X509 *server_cert) +{ + BIO *mem = NULL; + struct dynbuf dname; + char err_buf[256] = ""; + char *buf; + long len; + CURLcode result = CURLE_OK; + + if(!Curl_trc_is_verbose(data)) + return CURLE_OK; + + curlx_dyn_init(&dname, MAX_CERT_NAME_LENGTH); + mem = BIO_new(BIO_s_mem()); + if(!mem) { + failf(data, "BIO_new return NULL, " OSSL_PACKAGE " error %s", + ossl_strerror(ERR_get_error(), err_buf, sizeof(err_buf))); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + infof(data, "%s certificate:", Curl_ssl_cf_is_proxy(cf) ? + "Proxy" : "Server"); + + result = x509_name_oneline(X509_get_subject_name(server_cert), &dname); + infof(data, " subject: %s", result ? "[NONE]" : curlx_dyn_ptr(&dname)); + + ASN1_TIME_print(mem, X509_get0_notBefore(server_cert)); + len = BIO_get_mem_data(mem, (char **)&buf); + infof(data, " start date: %.*s", (int)len, buf); + (void)BIO_reset(mem); + + ASN1_TIME_print(mem, X509_get0_notAfter(server_cert)); + len = BIO_get_mem_data(mem, (char **)&buf); + infof(data, " expire date: %.*s", (int)len, buf); + (void)BIO_reset(mem); + + result = x509_name_oneline(X509_get_issuer_name(server_cert), &dname); + if(result) /* should be only fatal stuff like OOM */ + goto out; + infof(data, " issuer: %s", curlx_dyn_ptr(&dname)); + +out: + BIO_free(mem); + curlx_dyn_free(&dname); + return result; +} +#endif /* CURLVERBOSE */ + +#ifdef USE_APPLE_SECTRUST +struct ossl_certs_ctx { + STACK_OF(X509) *sk; + size_t num_certs; +}; + +static CURLcode ossl_chain_get_der(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data, + size_t i, + unsigned char **pder, + size_t *pder_len) +{ + struct ossl_certs_ctx *chain = user_data; + X509 *cert; + int der_len; + + (void)cf; + (void)data; + *pder_len = 0; + *pder = NULL; + + if(i >= chain->num_certs) + return CURLE_TOO_LARGE; + cert = sk_X509_value(chain->sk, (int)i); + if(!cert) + return CURLE_FAILED_INIT; + der_len = i2d_X509(cert, pder); + if(der_len < 0) + return CURLE_FAILED_INIT; + *pder_len = (size_t)der_len; + return CURLE_OK; +} + +static CURLcode ossl_apple_verify(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ossl_ctx *octx, + struct ssl_peer *peer, + bool *pverified) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ossl_certs_ctx chain; + CURLcode result; + + memset(&chain, 0, sizeof(chain)); + chain.sk = SSL_get_peer_cert_chain(octx->ssl); + chain.num_certs = chain.sk ? sk_X509_num(chain.sk) : 0; + + if(!chain.num_certs && + (conn_config->verifypeer || conn_config->verifyhost)) { + 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 + const uint8_t *ocsp_data = NULL; +#else + unsigned char *ocsp_data = NULL; +#endif + long ocsp_len = 0; + if(conn_config->verifystatus && !octx->reused_session) + ocsp_len = (long)SSL_get_tlsext_status_ocsp_resp(octx->ssl, &ocsp_data); + + /* SSL_get_tlsext_status_ocsp_resp() returns the length of the OCSP + response data or -1 if there is no OCSP response data. */ + if(ocsp_len < 0) + ocsp_len = 0; /* no data available */ + result = Curl_vtls_apple_verify(cf, data, peer, chain.num_certs, + ossl_chain_get_der, &chain, + ocsp_data, ocsp_len); + } + *pverified = !result; + return result; +} +#endif /* USE_APPLE_SECTRUST */ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -4832,210 +4726,106 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); CURLcode result = CURLE_OK; - long lerr; - X509 *issuer; - BIO *fp = NULL; - char error_buffer[256]=""; - const char *ptr; - BIO *mem = BIO_new(BIO_s_mem()); - bool strict = (conn_config->verifypeer || conn_config->verifyhost); - struct dynbuf dname; + long ossl_verify; + X509 *server_cert; + bool verified = FALSE; +#if !defined(OPENSSL_NO_OCSP) && defined(USE_APPLE_SECTRUST) + bool sectrust_verified = FALSE; +#endif - DEBUGASSERT(octx); - - curlx_dyn_init(&dname, MAX_CERT_NAME_LENGTH); - - if(!mem) { - failf(data, - "BIO_new return NULL, " OSSL_PACKAGE " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - return CURLE_OUT_OF_MEMORY; + if(data->set.ssl.certinfo && !octx->reused_session) { + /* asked to gather certificate info. Reused sessions do not have cert + chains */ + result = ossl_certchain(data, octx->ssl); + if(result) + return result; } - if(data->set.ssl.certinfo) - /* asked to gather certificate info */ - (void)ossl_certchain(data, octx->ssl); - - octx->server_cert = SSL_get1_peer_certificate(octx->ssl); - if(!octx->server_cert) { - BIO_free(mem); - if(!strict) - return CURLE_OK; + server_cert = SSL_get1_peer_certificate(octx->ssl); + if(!server_cert) { + /* no verification at all, this maybe acceptable */ + if(!(conn_config->verifypeer || conn_config->verifyhost)) + goto out; failf(data, "SSL: could not get peer certificate"); - return CURLE_PEER_FAILED_VERIFICATION; + result = CURLE_PEER_FAILED_VERIFICATION; + goto out; } - infof(data, "%s certificate:", - Curl_ssl_cf_is_proxy(cf) ? "Proxy" : "Server"); - - result = x509_name_oneline(X509_get_subject_name(octx->server_cert), - &dname); - infof(data, " subject: %s", result ? "[NONE]" : curlx_dyn_ptr(&dname)); - -#ifndef CURL_DISABLE_VERBOSE_STRINGS - { - char *buf; - long len; - ASN1_TIME_print(mem, X509_get0_notBefore(octx->server_cert)); - len = BIO_get_mem_data(mem, (char **) &buf); - infof(data, " start date: %.*s", (int)len, buf); - (void)BIO_reset(mem); - - ASN1_TIME_print(mem, X509_get0_notAfter(octx->server_cert)); - len = BIO_get_mem_data(mem, (char **) &buf); - infof(data, " expire date: %.*s", (int)len, buf); - (void)BIO_reset(mem); - } +#ifdef CURLVERBOSE + result = ossl_infof_cert(cf, data, server_cert); + if(result) + goto out; + infof_certstack(data, octx->ssl); #endif - BIO_free(mem); - if(conn_config->verifyhost) { - result = ossl_verifyhost(data, conn, peer, octx->server_cert); - if(result) { - X509_free(octx->server_cert); - octx->server_cert = NULL; - curlx_dyn_free(&dname); - return result; + result = ossl_verifyhost(data, conn, peer, server_cert); + if(result) + goto out; + } + /* `verifyhost` is either OK or not requested from here on */ + + 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) + infof(data, "SSL certificate verified via OpenSSL."); + +#ifdef USE_APPLE_SECTRUST + if(!verified && conn_config->verifypeer && ssl_config->native_ca_store) { + /* we verify using Apple SecTrust *unless* OpenSSL already verified. + * This may happen if the application intercepted the OpenSSL callback + * and installed its own. */ + result = ossl_apple_verify(cf, data, octx, peer, &verified); + if(result && (result != CURLE_PEER_FAILED_VERIFICATION)) + goto out; /* unexpected error */ + if(verified) { + infof(data, "SSL certificate verified via Apple SecTrust."); + ssl_config->certverifyresult = X509_V_OK; +#ifndef OPENSSL_NO_OCSP + sectrust_verified = TRUE; +#endif } } +#endif - result = x509_name_oneline(X509_get_issuer_name(octx->server_cert), - &dname); - if(result) { - if(strict) - failf(data, "SSL: could not get X509-issuer name"); - result = CURLE_PEER_FAILED_VERIFICATION; - } - else { - infof(data, " issuer: %s", curlx_dyn_ptr(&dname)); - curlx_dyn_free(&dname); - - /* We could do all sorts of certificate verification stuff here before - deallocating the certificate. */ - - /* e.g. match issuer name with provided issuer certificate */ - if(conn_config->issuercert || conn_config->issuercert_blob) { - if(conn_config->issuercert_blob) { - fp = BIO_new_mem_buf(conn_config->issuercert_blob->data, - (int)conn_config->issuercert_blob->len); - if(!fp) { - failf(data, - "BIO_new_mem_buf NULL, " OSSL_PACKAGE " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - X509_free(octx->server_cert); - octx->server_cert = NULL; - return CURLE_OUT_OF_MEMORY; - } - } - else { - fp = BIO_new(BIO_s_file()); - if(!fp) { - failf(data, - "BIO_new return NULL, " OSSL_PACKAGE " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - X509_free(octx->server_cert); - octx->server_cert = NULL; - return CURLE_OUT_OF_MEMORY; - } - - if(BIO_read_filename(fp, conn_config->issuercert) <= 0) { - if(strict) - failf(data, "SSL: Unable to open issuer cert (%s)", - conn_config->issuercert); - BIO_free(fp); - X509_free(octx->server_cert); - octx->server_cert = NULL; - return CURLE_SSL_ISSUER_ERROR; - } - } - - issuer = PEM_read_bio_X509(fp, NULL, ZERO_NULL, NULL); - if(!issuer) { - if(strict) - failf(data, "SSL: Unable to read issuer cert (%s)", - conn_config->issuercert); - BIO_free(fp); - X509_free(issuer); - X509_free(octx->server_cert); - octx->server_cert = NULL; - return CURLE_SSL_ISSUER_ERROR; - } - - if(X509_check_issued(issuer, octx->server_cert) != X509_V_OK) { - if(strict) - failf(data, "SSL: Certificate issuer check failed (%s)", - conn_config->issuercert); - BIO_free(fp); - X509_free(issuer); - X509_free(octx->server_cert); - octx->server_cert = NULL; - return CURLE_SSL_ISSUER_ERROR; - } - - infof(data, " SSL certificate issuer check ok (%s)", - conn_config->issuercert); - BIO_free(fp); - X509_free(issuer); + if(!verified) { + /* no trust established, report the OpenSSL status */ + if(conn_config->verifypeer) { + failf(data, "SSL certificate OpenSSL verify result: %s (%ld)", + X509_verify_cert_error_string(ossl_verify), ossl_verify); + result = CURLE_PEER_FAILED_VERIFICATION; + goto out; } - - lerr = SSL_get_verify_result(octx->ssl); - ssl_config->certverifyresult = lerr; - if(lerr != X509_V_OK) { - if(conn_config->verifypeer) { - /* We probably never reach this, because SSL_connect() will fail - and we return earlier if verifypeer is set? */ - if(strict) - failf(data, "SSL certificate verify result: %s (%ld)", - X509_verify_cert_error_string(lerr), lerr); - result = CURLE_PEER_FAILED_VERIFICATION; - } - else - infof(data, " SSL certificate verify result: %s (%ld)," - " continuing anyway.", - X509_verify_cert_error_string(lerr), lerr); - } - else - infof(data, " SSL certificate verify ok."); + infof(data, " SSL certificate verification failed, continuing anyway!"); } - infof_certstack(data, octx->ssl); -#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP) - if(conn_config->verifystatus && !octx->reused_session) { +#ifndef OPENSSL_NO_OCSP + if(conn_config->verifystatus && +#ifdef USE_APPLE_SECTRUST + !sectrust_verified && /* already verified via apple sectrust, cannot + * verifystate via OpenSSL in that case as it + * does not have the trust anchors */ +#endif + !octx->reused_session) { /* do not do this after Session ID reuse */ result = verifystatus(cf, data, octx); - if(result) { - X509_free(octx->server_cert); - octx->server_cert = NULL; - return result; - } - } -#endif - - if(!strict) - /* when not strict, we do not bother about the verify cert problems */ - result = CURLE_OK; - -#ifndef CURL_DISABLE_PROXY - ptr = Curl_ssl_cf_is_proxy(cf) ? - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; -#else - ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; -#endif - if(!result && ptr) { - result = ossl_pkp_pin_peer_pubkey(data, octx->server_cert, ptr); if(result) - failf(data, "SSL: public key does not match pinned public key"); + goto out; } +#endif - X509_free(octx->server_cert); - octx->server_cert = NULL; + result = ossl_check_issuer(cf, data, server_cert); + if(result) + goto out; + result = ossl_check_pinned_key(cf, data, server_cert); + +out: + X509_free(server_cert); return result; } @@ -5106,10 +4896,10 @@ static CURLcode ossl_send_earlydata(struct Curl_cfilter *cf, if(sslerror) ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)); else if(sockerr) - Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); + curlx_strerror(sockerr, error_buffer, sizeof(error_buffer)); else - msnprintf(error_buffer, sizeof(error_buffer), "%s", - SSL_ERROR_to_str(err)); + curl_msnprintf(error_buffer, sizeof(error_buffer), "%s", + SSL_ERROR_to_str(err)); failf(data, OSSL_PACKAGE " SSL_write:early_data: %s, errno %d", error_buffer, sockerr); @@ -5159,6 +4949,11 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_NONE; if(ssl_connect_1 == connssl->connecting_state) { + if(Curl_ossl_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); + return CURLE_OK; + } CURL_TRC_CF(data, cf, "ossl_connect, step1"); result = ossl_connect_step1(cf, data); if(result) @@ -5224,7 +5019,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, @@ -5244,7 +5039,6 @@ static CURLcode ossl_send(struct Curl_cfilter *cf, CURLcode result = CURLE_OK; int nwritten; - (void)data; DEBUGASSERT(octx); *pnwritten = 0; ERR_clear_error(); @@ -5253,7 +5047,7 @@ static CURLcode ossl_send(struct Curl_cfilter *cf, memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; if(octx->blocked_ssl_write_len && (octx->blocked_ssl_write_len != memlen)) { /* The previous SSL_write() call was blocked, using that length. - * We need to use that again or OpenSSL will freak out. A shorter + * We need to use that again or OpenSSL freaks out. A shorter * length should not happen and is a bug in libcurl. */ if(octx->blocked_ssl_write_len > memlen) { DEBUGASSERT(0); @@ -5279,8 +5073,7 @@ static CURLcode ossl_send(struct Curl_cfilter *cf, result = CURLE_AGAIN; octx->blocked_ssl_write_len = memlen; goto out; - case SSL_ERROR_SYSCALL: - { + case SSL_ERROR_SYSCALL: { int sockerr = SOCKERRNO; if(octx->io_result == CURLE_AGAIN) { @@ -5292,10 +5085,10 @@ static CURLcode ossl_send(struct Curl_cfilter *cf, if(sslerror) ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)); else if(sockerr) - Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); + curlx_strerror(sockerr, error_buffer, sizeof(error_buffer)); else - msnprintf(error_buffer, sizeof(error_buffer), "%s", - SSL_ERROR_to_str(err)); + curl_msnprintf(error_buffer, sizeof(error_buffer), "%s", + SSL_ERROR_to_str(err)); failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", error_buffer, sockerr); @@ -5333,13 +5126,11 @@ static CURLcode ossl_recv(struct Curl_cfilter *cf, char error_buffer[256]; unsigned long sslerror; int buffsize; - struct connectdata *conn = cf->conn; struct ssl_connect_data *connssl = cf->ctx; struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; CURLcode result = CURLE_OK; int nread; - (void)data; DEBUGASSERT(octx); *pnread = 0; @@ -5353,7 +5144,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 */ @@ -5363,9 +5154,10 @@ static CURLcode ossl_recv(struct Curl_cfilter *cf, if(cf->sockindex == FIRSTSOCKET) /* mark the connection for close if it is indeed the control connection */ - connclose(conn, "TLS close_notify"); + CURL_TRC_CF(data, cf, "TLS close_notify"); break; case SSL_ERROR_WANT_READ: + connssl->io_need = CURL_SSL_IO_NEED_RECV; result = CURLE_AGAIN; goto out; case SSL_ERROR_WANT_WRITE: @@ -5388,10 +5180,10 @@ static CURLcode ossl_recv(struct Curl_cfilter *cf, if(sslerror) ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)); else if(sockerr && err == SSL_ERROR_SYSCALL) - Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); + curlx_strerror(sockerr, error_buffer, sizeof(error_buffer)); else - msnprintf(error_buffer, sizeof(error_buffer), "%s", - SSL_ERROR_to_str(err)); + curl_msnprintf(error_buffer, sizeof(error_buffer), "%s", + SSL_ERROR_to_str(err)); failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d", error_buffer, sockerr); result = CURLE_RECV_ERROR; @@ -5407,14 +5199,14 @@ static CURLcode ossl_recv(struct Curl_cfilter *cf, result = CURLE_RECV_ERROR; } else { - /* We should no longer get here nowadays. But handle + /* We should no longer get here nowadays, but handle * the error in case of some weirdness in the OSSL stack */ int sockerr = SOCKERRNO; if(sockerr) - Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); + curlx_strerror(sockerr, error_buffer, sizeof(error_buffer)); else { - msnprintf(error_buffer, sizeof(error_buffer), - "Connection closed abruptly"); + curl_msnprintf(error_buffer, sizeof(error_buffer), + "Connection closed abruptly"); } failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d", error_buffer, sockerr); @@ -5440,15 +5232,15 @@ out: return result; } -static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex, +static CURLcode ossl_get_channel_binding(struct Curl_easy *data, + int sockindex, struct dynbuf *binding) { - /* required for X509_get_signature_nid support */ -#if OPENSSL_VERSION_NUMBER > 0x10100000L X509 *cert; - int algo_nid; - const EVP_MD *algo_type; - const char *algo_name; + int mdnid; + bool no_digest_acceptable = FALSE; + const EVP_MD *algo_type = NULL; + const char *algo_name = NULL; unsigned int length; unsigned char buf[EVP_MAX_MD_SIZE]; @@ -5456,6 +5248,7 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex, struct connectdata *conn = data->conn; struct Curl_cfilter *cf = conn->cfilter[sockindex]; struct ossl_ctx *octx = NULL; + CURLcode result = CURLE_OK; do { const struct Curl_cftype *cft = cf->cft; @@ -5466,10 +5259,8 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex, break; } - if(cf->next) - cf = cf->next; - - } while(cf->next); + cf = cf->next; + } while(cf); if(!octx) { failf(data, "Failed to find the SSL filter"); @@ -5477,51 +5268,114 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex, } cert = SSL_get1_peer_certificate(octx->ssl); - if(!cert) { - /* No server certificate, don't do channel binding */ + if(!cert) + /* No server certificate, do not do channel binding */ return CURLE_OK; - } - if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &algo_nid, NULL)) { +#ifdef HAVE_OPENSSL3 + { + int pknid, secbits; + uint32_t flags; + EVP_PKEY *pkey = X509_get0_pubkey(cert); + + if(!X509_get_signature_info(cert, &mdnid, &pknid, &secbits, &flags)) { + failf(data, "certificate signature algorithm not recognized"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; + } + + if(mdnid != NID_undef) { + if(mdnid == NID_md5 || mdnid == NID_sha1) { + algo_type = EVP_sha256(); + } + else + algo_type = EVP_get_digestbynid(mdnid); + } + else if(pkey && !EVP_PKEY_is_a(pkey, OBJ_nid2sn(pknid))) { + /* The cert's pkey is different from the algorithm used to sign + * the certificate. Since the reported `mdnid` is undefined, there + * is no digest algorithm available here. This happens in PQC + * and is accepted, resulting in no addition to the binding. */ + no_digest_acceptable = TRUE; + } + else if(pkey) { + /* cert's pkey type is the same as the cert signer (or same family). + * Ask for the mandatory/advisory digest algorithm for the pkey. + */ + char mdname[128] = ""; + int rc = EVP_PKEY_get_default_digest_name(pkey, mdname, sizeof(mdname)); + bool md_is_undef = !strcmp(mdname, "UNDEF"); + + if(rc == 2 && md_is_undef) { + /* OpenSSL declares "undef" the *mandatory* digest for this key. + * This is some PQC shit, accept it, no addition to binding. */ + no_digest_acceptable = TRUE; + } + else if(rc > 0 && mdname[0] != '\0' && !md_is_undef) { + infof(data, "Digest algorithm : %s%s (derived from public key)" + ", but unavailable", + mdname, rc == 2 ? " [mandatory]" : " [advisory]"); + } + } + } +#else /* HAVE_OPENSSL3 */ + + if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &mdnid, NULL)) { failf(data, "Unable to find digest NID for certificate signature algorithm"); - return CURLE_SSL_INVALIDCERTSTATUS; + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; } /* https://datatracker.ietf.org/doc/html/rfc5929#section-4.1 */ - if(algo_nid == NID_md5 || algo_nid == NID_sha1) { + if(mdnid == NID_md5 || mdnid == NID_sha1) { algo_type = EVP_sha256(); } else { - algo_type = EVP_get_digestbynid(algo_nid); + algo_type = EVP_get_digestbynid(mdnid); if(!algo_type) { - algo_name = OBJ_nid2sn(algo_nid); + algo_name = OBJ_nid2sn(mdnid); failf(data, "Could not find digest algorithm %s (NID %d)", - algo_name ? algo_name : "(null)", algo_nid); - return CURLE_SSL_INVALIDCERTSTATUS; + algo_name ? algo_name : "(null)", mdnid); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; } } +#endif /* HAVE_OPENSSL3, else */ + + if(!algo_type) { + if(no_digest_acceptable) { + infof(data, "certificate exposes no signing digest algorithm, " + "nothing to add to channel binding"); + result = CURLE_OK; + goto out; + } + /* unacceptable, something is wrong, fail */ + algo_name = OBJ_nid2sn(mdnid); + failf(data, "Unable to find digest algorithm %s (NID %d) " + "for channel binding", algo_name ? algo_name : "(null)", mdnid); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; + } + if(!X509_digest(cert, algo_type, buf, &length)) { - failf(data, "X509_digest() failed"); - return CURLE_SSL_INVALIDCERTSTATUS; + failf(data, "X509_digest() failed for channel binding"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; } /* Append "tls-server-end-point:" */ - if(curlx_dyn_addn(binding, prefix, sizeof(prefix) - 1) != CURLE_OK) - return CURLE_OUT_OF_MEMORY; - /* Append digest */ - if(curlx_dyn_addn(binding, buf, length)) - return CURLE_OUT_OF_MEMORY; + result = curlx_dyn_addn(binding, prefix, sizeof(prefix) - 1); + if(result) + goto out; - return CURLE_OK; -#else - /* No X509_get_signature_nid support */ - (void)data; - (void)sockindex; - (void)binding; - return CURLE_OK; -#endif + /* Append digest */ + result = curlx_dyn_addn(binding, buf, length); + +out: + X509_free(cert); + return result; } size_t Curl_ossl_version(char *buffer, size_t size) @@ -5534,7 +5388,7 @@ size_t Curl_ossl_version(char *buffer, size_t size) if(curl_strnequal(ver, expected, sizeof(expected) - 1)) { ver += sizeof(expected) - 1; } - count = msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver); + count = curl_msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver); for(p = buffer; *p; ++p) { if(ISBLANK(*p)) *p = '_'; @@ -5542,49 +5396,17 @@ size_t Curl_ossl_version(char *buffer, size_t size) return count; #elif defined(OPENSSL_IS_BORINGSSL) #ifdef CURL_BORINGSSL_VERSION - return msnprintf(buffer, size, "%s/%s", - OSSL_PACKAGE, CURL_BORINGSSL_VERSION); + return curl_msnprintf(buffer, size, "%s/%s", + OSSL_PACKAGE, CURL_BORINGSSL_VERSION); #else - return msnprintf(buffer, size, OSSL_PACKAGE); + return curl_msnprintf(buffer, size, OSSL_PACKAGE); #endif #elif defined(OPENSSL_IS_AWSLC) - return msnprintf(buffer, size, "%s/%s", - OSSL_PACKAGE, AWSLC_VERSION_NUMBER_STRING); -#elif defined(OPENSSL_VERSION_STRING) /* OpenSSL 3+ */ - return msnprintf(buffer, size, "%s/%s", - OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING)); -#else - /* not LibreSSL, BoringSSL and not using OpenSSL_version */ - - char sub[3]; - unsigned long ssleay_value; - sub[2]='\0'; - sub[1]='\0'; - ssleay_value = OpenSSL_version_num(); - if(ssleay_value&0xff0) { - int minor_ver = (ssleay_value >> 4) & 0xff; - if(minor_ver > 26) { - /* handle extended version introduced for 0.9.8za */ - sub[1] = (char) ((minor_ver - 1) % 26 + 'a' + 1); - sub[0] = 'z'; - } - else { - sub[0] = (char) (minor_ver + 'a' - 1); - } - } - else - sub[0]='\0'; - - return msnprintf(buffer, size, "%s/%lx.%lx.%lx%s" -#ifdef OPENSSL_FIPS - "-fips" -#endif - , - OSSL_PACKAGE, - (ssleay_value >> 28) & 0xf, - (ssleay_value >> 20) & 0xff, - (ssleay_value >> 12) & 0xff, - sub); + return curl_msnprintf(buffer, size, "%s/%s", + OSSL_PACKAGE, AWSLC_VERSION_NUMBER_STRING); +#else /* OpenSSL 3+ */ + return curl_msnprintf(buffer, size, "%s/%s", + OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING)); #endif } @@ -5601,12 +5423,11 @@ 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; } -#ifndef OPENSSL_NO_SHA256 static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */ size_t tmplen, unsigned char *sha256sum /* output */, @@ -5628,11 +5449,10 @@ static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */ EVP_MD_CTX_destroy(mdctx); return CURLE_OK; } -#endif static bool ossl_cert_status_request(void) { -#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP) +#ifndef OPENSSL_NO_OCSP return TRUE; #else return FALSE; @@ -5663,12 +5483,16 @@ const struct Curl_ssl Curl_ssl_openssl = { #ifdef HAVE_SSL_CTX_SET1_SIGALGS SSLSUPP_SIGNATURE_ALGORITHMS | #endif -#ifdef USE_ECH_OPENSSL +#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST SSLSUPP_ECH | #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), @@ -5687,11 +5511,7 @@ const struct Curl_ssl Curl_ssl_openssl = { ossl_set_engine, /* set_engine or provider */ ossl_set_engine_default, /* set_engine_default */ ossl_engines_list, /* engines_list */ -#ifndef OPENSSL_NO_SHA256 ossl_sha256sum, /* sha256sum */ -#else - NULL, /* sha256sum */ -#endif ossl_recv, /* recv decrypted data */ ossl_send, /* send data to encrypt */ ossl_get_channel_binding /* get_channel_binding */ diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index e263ee2eb2..61d4a1757e 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -23,10 +23,43 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_OPENSSL + +#ifdef USE_WIN32_CRYPTO +#include +/* If is included directly, or indirectly via , + * , , or something else, does this: + * #define X509_NAME ((LPCSTR)7) + * + * 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 +#undef PKCS7_SIGNER_INFO +#undef OCSP_REQUEST +#undef OCSP_RESPONSE +#endif /* USE_WIN32_CRYPTO */ + /* * This header should only be needed to get included by vtls.c, openssl.c * and ngtcp2.c @@ -35,7 +68,7 @@ #include #include -#include "../urldata.h" +#include "urldata.h" #if OPENSSL_VERSION_NUMBER >= 0x30000000L #define HAVE_OPENSSL3 /* non-fork OpenSSL 3.x or later */ @@ -45,14 +78,19 @@ #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 * BoringSSL: supported since d28f59c27bac (committed 2015-11-19) * LibreSSL: not supported. 3.5.0+ has a stub function that does nothing. */ -#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \ - !defined(LIBRESSL_VERSION_NUMBER)) || defined(HAVE_BORINGSSL_LIKE) +#ifndef LIBRESSL_VERSION_NUMBER #define HAVE_KEYLOG_CALLBACK #endif @@ -71,17 +109,17 @@ struct ossl_ctx { /* these ones requires specific SSL-types */ SSL_CTX* ssl_ctx; SSL* ssl; - X509* server_cert; BIO_METHOD *bio_method; 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; #endif BIT(x509_store_setup); /* x509 store has been set up */ + BIT(store_is_empty); /* no certs/paths/blobs in x509 store */ BIT(reused_session); /* session-ID was reused for this */ }; @@ -102,13 +140,16 @@ 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, void *ssl_user_data, Curl_ossl_init_session_reuse_cb *sess_reuse_cb); +/* Is a resolved HTTPS-RR needed for initializing OpenSSL? */ +bool Curl_ossl_need_httpsrr(struct Curl_easy *data); + #ifndef HAVE_OPENSSL3 #define SSL_get1_peer_certificate SSL_get_peer_certificate #endif @@ -117,12 +158,12 @@ extern const struct Curl_ssl Curl_ssl_openssl; /** * Setup the OpenSSL X509_STORE in `ssl_ctx` for the cfilter `cf` and - * easy handle `data`. Will allow reuse of a shared cache if suitable + * easy handle `data`. Allows reuse of a shared cache if suitable * and configured. */ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, - SSL_CTX *ssl_ctx); + struct ossl_ctx *octx); CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -134,7 +175,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, @@ -151,8 +192,7 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, struct ssl_peer *peer); /* Report properties of a successful handshake */ -void Curl_ossl_report_handshake(struct Curl_easy *data, - struct ossl_ctx *octx); +void Curl_ossl_report_handshake(struct Curl_easy *data, struct ossl_ctx *octx); #endif /* USE_OPENSSL */ #endif /* HEADER_CURL_SSLUSE_H */ diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 221a7a6215..fcd4f7289e 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -5,8 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Jacob Hoffman-Andrews, - * + * Copyright (C) Jacob Hoffman-Andrews, * Copyright (C) kpcyrd, * Copyright (C) Daniel McCarney, * @@ -24,31 +23,29 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_RUSTLS -#include "../curl_printf.h" - #include -#include "../curlx/inet_pton.h" -#include "../urldata.h" -#include "../sendf.h" -#include "vtls.h" -#include "vtls_int.h" -#include "rustls.h" -#include "keylog.h" -#include "../strerror.h" -#include "cipher_suite.h" -#include "x509asn1.h" +#include "curlx/fopen.h" +#include "curlx/strerr.h" +#include "urldata.h" +#include "cf-dns.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" +#ifdef USE_ECH +#include "curlx/base64.h" +#endif -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" - -struct rustls_ssl_backend_data -{ +struct rustls_ssl_backend_data { const struct rustls_client_config *config; struct rustls_connection *conn; size_t plain_out_buffered; @@ -63,17 +60,17 @@ static CURLcode map_error(const rustls_result r) return CURLE_PEER_FAILED_VERIFICATION; } switch(r) { - case RUSTLS_RESULT_OK: - return CURLE_OK; - case RUSTLS_RESULT_NULL_PARAMETER: - return CURLE_BAD_FUNCTION_ARGUMENT; - default: - return CURLE_RECV_ERROR; + case RUSTLS_RESULT_OK: + return CURLE_OK; + case RUSTLS_RESULT_NULL_PARAMETER: + return CURLE_BAD_FUNCTION_ARGUMENT; + default: + return CURLE_RECV_ERROR; } } -static void -rustls_failf(struct Curl_easy *data, const rustls_result rr, const char *msg) +static void rustls_failf(struct Curl_easy *data, const rustls_result rr, + const char *msg) { char errorbuf[STRERROR_LEN]; size_t errorlen; @@ -81,8 +78,8 @@ rustls_failf(struct Curl_easy *data, const rustls_result rr, const char *msg) failf(data, "%s: %.*s", msg, (int)errorlen, errorbuf); } -static bool -cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) +static bool cr_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) { const struct ssl_connect_data *ctx = cf->ctx; struct rustls_ssl_backend_data *backend; @@ -90,7 +87,7 @@ cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) (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 { @@ -98,11 +95,11 @@ struct io_ctx { struct Curl_easy *data; }; -static int -read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) +static int read_cb(void *userdata, uint8_t *buf, uintptr_t len, + uintptr_t *out_n) { const struct io_ctx *io_ctx = userdata; - struct ssl_connect_data *const connssl = io_ctx->cf->ctx; + struct ssl_connect_data * const connssl = io_ctx->cf->ctx; CURLcode result; int ret = 0; size_t nread; @@ -112,7 +109,7 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) if(result) { nread = 0; /* !checksrc! disable ERRNOVAR 4 */ - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) ret = EAGAIN; else ret = EINVAL; @@ -121,12 +118,12 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) connssl->peer_closed = TRUE; *out_n = (uintptr_t)nread; CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %d, %zu", - len, result, nread); + (size_t)len, result, nread); return ret; } -static int -write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) +static int write_cb(void *userdata, const uint8_t *buf, uintptr_t len, + uintptr_t *out_n) { const struct io_ctx *io_ctx = userdata; CURLcode result; @@ -134,10 +131,10 @@ write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) size_t nwritten; result = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data, - (const char *)buf, len, FALSE, &nwritten); + buf, len, FALSE, &nwritten); if(result) { nwritten = 0; - if(CURLE_AGAIN == result) + if(result == CURLE_AGAIN) ret = EAGAIN; else ret = EINVAL; @@ -151,8 +148,8 @@ write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) static ssize_t tls_recv_more(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode *err) { - const struct ssl_connect_data *const connssl = cf->ctx; - struct rustls_ssl_backend_data *const backend = + const struct ssl_connect_data * const connssl = cf->ctx; + struct rustls_ssl_backend_data * const backend = (struct rustls_ssl_backend_data *)connssl->backend; struct io_ctx io_ctx; size_t tls_bytes_read = 0; @@ -170,7 +167,7 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf, else if(io_error) { char buffer[STRERROR_LEN]; failf(data, "reading from socket: %s", - Curl_strerror(io_error, buffer, sizeof(buffer))); + curlx_strerror(io_error, buffer, sizeof(buffer))); *err = CURLE_RECV_ERROR; return -1; } @@ -188,23 +185,14 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf, } /* - * On each run: - * - Read a chunk of bytes from the socket into Rustls' TLS input buffer. - * - Tell Rustls to process any new packets. - * - Read out as many plaintext bytes from Rustls as possible, until hitting - * error, EOF, or EAGAIN/EWOULDBLOCK, or plainbuf/plainlen is filled up. - * - * it is okay to call this function with plainbuf == NULL and plainlen == 0. In - * that case, it will copy bytes from the socket into Rustls' TLS input - * buffer, and process packets, but will not consume bytes from Rustls' - * plaintext output buffer. + * Filter receive method implementation. `plainbuf` and `plainlen` + * are always not NULL/0. */ -static CURLcode -cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *plainbuf, size_t plainlen, size_t *pnread) +static CURLcode cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *plainbuf, size_t plainlen, size_t *pnread) { - const struct ssl_connect_data *const connssl = cf->ctx; - struct rustls_ssl_backend_data *const backend = + const struct ssl_connect_data * const connssl = cf->ctx; + struct rustls_ssl_backend_data * const backend = (struct rustls_ssl_backend_data *)connssl->backend; struct rustls_connection *rconn = NULL; CURLcode result = CURLE_OK; @@ -290,7 +278,7 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data, else if(io_error) { char buffer[STRERROR_LEN]; failf(data, "writing to socket: %s", - Curl_strerror(io_error, buffer, sizeof(buffer))); + curlx_strerror(io_error, buffer, sizeof(buffer))); return CURLE_SEND_ERROR; } if(tlswritten == 0) { @@ -310,15 +298,15 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data, * we get either an error or EAGAIN/EWOULDBLOCK. * * it is okay to call this function with plainbuf == NULL and plainlen == 0. - * In that case, it will not read anything into Rustls' plaintext input buffer. - * It will only drain Rustls' plaintext output buffer into the socket. + * In that case, it does not read anything into Rustls' plaintext input buffer. + * It only drains Rustls' plaintext output buffer into the socket. */ -static CURLcode -cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *plainbuf, size_t plainlen, size_t *pnwritten) +static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *plainbuf, size_t plainlen, + size_t *pnwritten) { - const struct ssl_connect_data *const connssl = cf->ctx; - struct rustls_ssl_backend_data *const backend = + const struct ssl_connect_data * const connssl = cf->ctx; + struct rustls_ssl_backend_data * const backend = (struct rustls_ssl_backend_data *)connssl->backend; struct rustls_connection *rconn = NULL; size_t plainwritten = 0; @@ -371,7 +359,7 @@ 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; @@ -392,35 +380,34 @@ out: /* A server certificate verify callback for Rustls that always returns RUSTLS_RESULT_OK, or in other words disable certificate verification. */ -static uint32_t -cr_verify_none(void *userdata, - const rustls_verify_server_cert_params *params) +static uint32_t cr_verify_none(void *userdata, + const rustls_verify_server_cert_params *params) { (void)userdata; (void)params; return RUSTLS_RESULT_OK; } -static int -read_file_into(const char *filename, - struct dynbuf *out) +static int read_file_into(const char *filename, struct dynbuf *out) { - FILE *f = fopen(filename, FOPEN_READTEXT); + FILE *f = curlx_fopen(filename, FOPEN_READTEXT); if(!f) { return 0; } - while(!feof(f)) { + for(;;) { uint8_t buf[256]; const size_t rr = fread(buf, 1, sizeof(buf), f); - if(rr == 0 || - CURLE_OK != curlx_dyn_addn(out, buf, rr)) { - fclose(f); + if((!rr && !feof(f)) || + curlx_dyn_addn(out, buf, rr)) { + curlx_fclose(f); return 0; } + if(rr < sizeof(buf)) + break; } - return fclose(f) == 0; + return curlx_fclose(f) == 0; } static void @@ -432,7 +419,7 @@ cr_get_selected_ciphers(struct Curl_easy *data, { const size_t supported_len = *selected_size; const size_t default_len = rustls_default_crypto_provider_ciphersuites_len(); - const struct rustls_supported_ciphersuite *entry; + const struct rustls_supported_ciphersuite *entry = NULL; const char *ciphers = ciphers12; size_t count = 0, default13_count = 0, i, j; const char *ptr, *end; @@ -475,16 +462,17 @@ add_ciphers: if(!id) { if(ptr[0] != '\0') infof(data, "rustls: unknown cipher in list: \"%.*s\"", - (int) (end - ptr), ptr); + (int)(end - ptr), ptr); continue; } /* No duplicates allowed (so selected cannot overflow) */ - for(i = 0; i < count && selected[i] != entry; i++); + for(i = 0; i < count && selected[i] != entry; i++) + ; if(i < count) { if(i >= default13_count) infof(data, "rustls: duplicate cipher in list: \"%.*s\"", - (int) (end - ptr), ptr); + (int)(end - ptr), ptr); continue; } @@ -501,11 +489,12 @@ add_ciphers: for(j = 0; j < default_len; j++) { entry = rustls_default_crypto_provider_ciphersuites_get(j); if(rustls_supported_ciphersuite_protocol_version(entry) == - RUSTLS_TLS_VERSION_TLSV1_3) + RUSTLS_TLS_VERSION_TLSV1_3) continue; /* No duplicates allowed (so selected cannot overflow) */ - for(i = 0; i < count && selected[i] != entry; i++); + for(i = 0; i < count && selected[i] != entry; i++) + ; if(i < count) continue; @@ -516,16 +505,16 @@ add_ciphers: *selected_size = count; } -static void -cr_keylog_log_cb(struct rustls_str label, - const uint8_t *client_random, size_t client_random_len, - const uint8_t *secret, size_t secret_len) +static void cr_keylog_log_cb(struct rustls_str label, + const uint8_t *client_random, + size_t client_random_len, const uint8_t *secret, + size_t secret_len) { char clabel[KEYLOG_LABEL_MAXLEN]; (void)client_random_len; DEBUGASSERT(client_random_len == CLIENT_RANDOM_SIZE); /* Turning a "rustls_str" into a null delimited "c" string */ - msnprintf(clabel, label.len + 1, "%.*s", (int)label.len, label.data); + curl_msnprintf(clabel, sizeof(clabel), "%.*s", (int)label.len, label.data); Curl_tls_keylog_write(clabel, client_random, secret, secret_len); } @@ -539,18 +528,17 @@ init_config_builder(struct Curl_easy *data, const struct rustls_crypto_provider *custom_provider = NULL; uint16_t tls_versions[2] = { - RUSTLS_TLS_VERSION_TLSV1_2, - RUSTLS_TLS_VERSION_TLSV1_3, + RUSTLS_TLS_VERSION_TLSV1_2, + RUSTLS_TLS_VERSION_TLSV1_3, }; size_t tls_versions_len = 2; - size_t cipher_suites_len = - rustls_default_crypto_provider_ciphersuites_len(); + size_t cipher_suites_len = rustls_default_crypto_provider_ciphersuites_len(); 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: @@ -586,14 +574,14 @@ init_config_builder(struct Curl_easy *data, } #ifdef USE_ECH - if(ECH_ENABLED(data)) { + if(CURLECH_ENABLED(data)) { tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3; tls_versions_len = 1; infof(data, "rustls: ECH enabled, forcing TLSv1.3"); } #endif /* USE_ECH */ - cipher_suites = 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; @@ -613,7 +601,7 @@ init_config_builder(struct Curl_easy *data, &custom_provider_builder); if(rr != RUSTLS_RESULT_OK) { rustls_failf(data, rr, - "failed to create crypto provider builder from default"); + "failed to create crypto provider builder from default"); result = CURLE_SSL_CIPHER; goto cleanup; } @@ -625,13 +613,13 @@ init_config_builder(struct Curl_easy *data, cipher_suites_len); if(rr != RUSTLS_RESULT_OK) { rustls_failf(data, rr, - "failed to set ciphersuites for crypto provider builder"); + "failed to set ciphersuites for crypto provider builder"); result = CURLE_SSL_CIPHER; goto cleanup; } - rr = rustls_crypto_provider_builder_build( - custom_provider_builder, &custom_provider); + rr = rustls_crypto_provider_builder_build(custom_provider_builder, + &custom_provider); if(rr != RUSTLS_RESULT_OK) { rustls_failf(data, rr, "failed to build custom crypto provider"); result = CURLE_SSL_CIPHER; @@ -639,9 +627,9 @@ init_config_builder(struct Curl_easy *data, } rr = rustls_client_config_builder_new_custom(custom_provider, - tls_versions, - tls_versions_len, - config_builder); + tls_versions, + tls_versions_len, + config_builder); if(rr != RUSTLS_RESULT_OK) { rustls_failf(data, rr, "failed to create client config builder"); result = CURLE_SSL_CIPHER; @@ -650,7 +638,7 @@ init_config_builder(struct Curl_easy *data, cleanup: if(cipher_suites) { - free(cipher_suites); + curlx_free(cipher_suites); } if(custom_provider_builder) { rustls_crypto_provider_builder_free(custom_provider_builder); @@ -664,7 +652,8 @@ cleanup: static void init_config_builder_alpn(struct Curl_easy *data, const struct ssl_connect_data *connssl, - struct rustls_client_config_builder *config_builder) { + struct rustls_client_config_builder *config_builder) +{ struct alpn_proto_buf proto; rustls_slice_bytes alpn[ALPN_ENTRIES_MAX]; size_t i; @@ -679,8 +668,7 @@ init_config_builder_alpn(struct Curl_easy *data, infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } -static CURLcode -init_config_builder_verifier_crl( +static CURLcode init_config_builder_verifier_crl( struct Curl_easy *data, const struct ssl_primary_config *conn_config, struct rustls_web_pki_server_cert_verifier_builder *builder) @@ -716,7 +704,8 @@ init_config_builder_verifier(struct Curl_easy *data, struct rustls_client_config_builder *builder, const struct ssl_primary_config *conn_config, const struct curl_blob *ca_info_blob, - const char * const ssl_cafile) { + const char * const ssl_cafile) +{ const struct rustls_root_cert_store *roots = NULL; struct rustls_root_cert_store_builder *roots_builder = NULL; struct rustls_web_pki_server_cert_verifier_builder *verifier_builder = NULL; @@ -732,7 +721,6 @@ init_config_builder_verifier(struct Curl_easy *data, 1); if(rr != RUSTLS_RESULT_OK) { rustls_failf(data, rr, "failed to parse trusted certificates from blob"); - result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } @@ -743,7 +731,6 @@ init_config_builder_verifier(struct Curl_easy *data, 1); if(rr != RUSTLS_RESULT_OK) { rustls_failf(data, rr, "failed to load trusted certificates"); - result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } @@ -753,15 +740,20 @@ init_config_builder_verifier(struct Curl_easy *data, if(rr != RUSTLS_RESULT_OK) { rustls_failf(data, rr, "failed to build trusted root certificate store"); result = CURLE_SSL_CACERT_BADFILE; + goto cleanup; } verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots); + if(!verifier_builder) { + result = CURLE_OUT_OF_MEMORY; + goto cleanup; + } if(conn_config->CRLfile) { result = init_config_builder_verifier_crl(data, - conn_config, - verifier_builder); - if(result != CURLE_OK) { + conn_config, + verifier_builder); + if(result) { goto cleanup; } } @@ -793,8 +785,7 @@ cleanup: return result; } -static CURLcode -init_config_builder_platform_verifier( +static CURLcode init_config_builder_platform_verifier( struct Curl_easy *data, struct rustls_client_config_builder *builder) { @@ -894,10 +885,8 @@ init_config_builder_client_auth(struct Curl_easy *data, rr = rustls_certified_key_keys_match(certified_key); if(rr != RUSTLS_RESULT_OK) { - rustls_failf(data, - rr, + rustls_failf(data, rr, "rustls: client certificate and keypair files do not match:"); - result = CURLE_SSL_CERTPROBLEM; goto cleanup; } @@ -921,16 +910,26 @@ cleanup: } #ifdef USE_ECH + +static bool cr_ech_need_httpsrr(struct Curl_easy *data) +{ + if(!CURLECH_ENABLED(data)) + return FALSE; + if((data->set.tls_ech & CURLECH_GREASE) || + (data->set.tls_ech & CURLECH_CLA_CFG)) + return FALSE; + return TRUE; +} + static CURLcode init_config_builder_ech(struct Curl_easy *data, - const struct ssl_connect_data *connssl, + struct Curl_cfilter *cf, struct rustls_client_config_builder *builder) { const rustls_hpke *hpke = rustls_supported_hpke(); unsigned char *ech_config = NULL; size_t ech_config_len = 0; struct Curl_dns_entry *dns = NULL; - struct Curl_https_rrinfo *rinfo = NULL; CURLcode result = CURLE_OK; rustls_result rr; @@ -975,16 +974,9 @@ init_config_builder_ech(struct Curl_easy *data, } } else { - if(connssl->peer.hostname) { - dns = Curl_dnscache_get(data, connssl->peer.hostname, - connssl->peer.port, data->conn->ip_version); - } - if(!dns) { - failf(data, "rustls: ECH requested but no DNS info available"); - result = CURLE_SSL_CONNECT_ERROR; - goto cleanup; - } - rinfo = dns->hinfo; + const struct Curl_https_rrinfo *rinfo = + Curl_conn_dns_get_https(data, cf->sockindex); + if(!rinfo || !rinfo->echconfiglist) { failf(data, "rustls: ECH requested but no ECHConfig available"); result = CURLE_SSL_CONNECT_ERROR; @@ -1006,18 +998,18 @@ init_config_builder_ech(struct Curl_easy *data, cleanup: /* if we base64 decoded, we can free now */ if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) { - free(ech_config); + curlx_free(ech_config); } if(dns) { - Curl_resolv_unlink(data, &dns); + Curl_dns_entry_unlink(data, &dns); } return result; } #endif /* USE_ECH */ -static CURLcode -cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, - struct rustls_ssl_backend_data *const backend) +static CURLcode cr_init_backend(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct rustls_ssl_backend_data * const backend) { const struct ssl_connect_data *connssl = cf->ctx; const struct ssl_primary_config *conn_config = @@ -1080,8 +1072,8 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, } #ifdef USE_ECH - if(ECH_ENABLED(data)) { - result = init_config_builder_ech(data, connssl, config_builder); + if(CURLECH_ENABLED(data)) { + result = init_config_builder_ech(data, cf, config_builder); if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) { rustls_client_config_builder_free(config_builder); return result; @@ -1095,13 +1087,9 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, return result; } - rr = rustls_client_config_builder_build( - config_builder, - &backend->config); + rr = rustls_client_config_builder_build(config_builder, &backend->config); if(rr != RUSTLS_RESULT_OK) { rustls_failf(data, rr, "failed to build client config"); - rustls_client_config_builder_free(config_builder); - rustls_client_config_free(backend->config); return CURLE_SSL_CONNECT_ERROR; } @@ -1110,7 +1098,9 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, connssl->peer.hostname, &rconn); if(rr != RUSTLS_RESULT_OK) { - rustls_failf(data, result, "rustls_client_connection_new"); + rustls_failf(data, rr, "rustls_client_connection_new"); + rustls_client_config_free(backend->config); + backend->config = NULL; return CURLE_COULDNT_CONNECT; } DEBUGASSERT(rconn); @@ -1120,11 +1110,11 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, return result; } -static void -cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data, - const struct rustls_connection *rconn) +static void cr_set_negotiated_alpn(struct Curl_cfilter *cf, + struct Curl_easy *data, + const struct rustls_connection *rconn) { - struct ssl_connect_data *const connssl = cf->ctx; + struct ssl_connect_data * const connssl = cf->ctx; const uint8_t *protocol = NULL; size_t len = 0; @@ -1134,34 +1124,44 @@ cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data, /* Given an established network connection, do a TLS handshake. * - * This function will set `*done` to true once the handshake is complete. + * This function sets `*done` to true once the handshake is complete. * This function never reads the value of `*done*`. */ -static CURLcode -cr_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, bool *done) +static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data, + bool *done) { - struct ssl_connect_data *const connssl = cf->ctx; - const struct rustls_ssl_backend_data *const backend = + struct ssl_connect_data * const connssl = cf->ctx; + const struct rustls_ssl_backend_data * const backend = (struct rustls_ssl_backend_data *)connssl->backend; const struct rustls_connection *rconn = NULL; CURLcode tmperr = CURLE_OK; - int result; + CURLcode result; bool wants_read; bool wants_write; + ssize_t nread; DEBUGASSERT(backend); CURL_TRC_CF(data, cf, "cr_connect, state=%d", connssl->state); *done = FALSE; - if(!backend->conn) { - result = cr_init_backend(cf, data, - (struct rustls_ssl_backend_data *)connssl->backend); - CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result); - if(result != CURLE_OK) { - return result; +#ifdef USE_ECH + /* if we do ECH and need the HTTPS-RR information for it, + * we delay the connect until it arrives or DNS resolve fails. */ + if(cr_ech_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); + return CURLE_OK; } +#endif /* USE_ECH */ + + if(!backend->conn) { + result = + cr_init_backend(cf, data, + (struct rustls_ssl_backend_data *)connssl->backend); + CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result); + if(result) + return result; connssl->state = ssl_connection_negotiating; } rconn = backend->conn; @@ -1169,9 +1169,9 @@ cr_connect(struct Curl_cfilter *cf, /* Read/write data until the handshake is done or the socket would block. */ for(;;) { /* - * Connection has been established according to Rustls. Set send/recv - * handlers, and update the state machine. - */ + * Connection has been established according to Rustls. Set send/recv + * handlers, and update the state machine. + */ connssl->io_need = CURL_SSL_IO_NEED_NONE; if(!rustls_connection_is_handshaking(rconn)) { /* Rustls claims it is no longer handshaking *before* it has @@ -1190,8 +1190,8 @@ cr_connect(struct Curl_cfilter *cf, } /* REALLY Done with the handshake. */ { - const uint16_t proto = - rustls_connection_get_protocol_version(rconn); +#ifdef CURLVERBOSE + const uint16_t proto = rustls_connection_get_protocol_version(rconn); const rustls_str ciphersuite_name = rustls_connection_get_negotiated_ciphersuite_name(rconn); const rustls_str kex_group_name = @@ -1201,6 +1201,7 @@ cr_connect(struct Curl_cfilter *cf, 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", @@ -1212,13 +1213,19 @@ cr_connect(struct Curl_cfilter *cf, } if(data->set.ssl.certinfo) { size_t num_certs = 0; - while(rustls_connection_get_peer_certificate(rconn, (int)num_certs)) { + size_t i; + while(rustls_connection_get_peer_certificate(rconn, num_certs)) { num_certs++; + if(num_certs > MAX_ALLOWED_CERT_AMOUNT) { + failf(data, "%zu certificates is more than allowed (%u)", + num_certs, MAX_ALLOWED_CERT_AMOUNT); + return CURLE_SSL_CONNECT_ERROR; + } } result = Curl_ssl_init_certinfo(data, (int)num_certs); if(result) return result; - for(size_t i = 0; i < num_certs; i++) { + for(i = 0; i < num_certs; i++) { const rustls_certificate *cert; const unsigned char *der_data; size_t der_len; @@ -1231,8 +1238,8 @@ cr_connect(struct Curl_cfilter *cf, size_t errorlen; rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); failf(data, - "Failed getting DER of server certificate #%ld: %.*s", i, - (int)errorlen, errorbuf); + "Failed getting DER of server certificate #%zu: %.*s", i, + (int)errorlen, errorbuf); return map_error(rresult); } { @@ -1274,7 +1281,13 @@ cr_connect(struct Curl_cfilter *cf, if(wants_read) { CURL_TRC_CF(data, cf, "rustls_connection wants us to read_tls."); - if(tls_recv_more(cf, data, &tmperr) < 0) { + nread = tls_recv_more(cf, data, &tmperr); + if(nread == 0) { + connssl->peer_closed = TRUE; + failf(data, "TLS connect error: Connection closed abruptly"); + return CURLE_SSL_CONNECT_ERROR; + } + if(nread < 0) { if(tmperr == CURLE_AGAIN) { CURL_TRC_CF(data, cf, "reading would block"); connssl->io_need = CURL_SSL_IO_NEED_RECV; @@ -1295,9 +1308,7 @@ cr_connect(struct Curl_cfilter *cf, DEBUGASSERT(FALSE); } -static void * -cr_get_internals(struct ssl_connect_data *connssl, - CURLINFO info) +static void *cr_get_internals(struct ssl_connect_data *connssl, CURLINFO info) { struct rustls_ssl_backend_data *backend = (struct rustls_ssl_backend_data *)connssl->backend; @@ -1306,16 +1317,14 @@ cr_get_internals(struct ssl_connect_data *connssl, return backend->conn; } -static CURLcode -cr_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data, - const bool send_shutdown, bool *done) +static CURLcode cr_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, + const bool send_shutdown, bool *done) { struct ssl_connect_data *connssl = cf->ctx; struct rustls_ssl_backend_data *backend = (struct rustls_ssl_backend_data *)connssl->backend; CURLcode result = CURLE_OK; - size_t i, nread, nwritten; + size_t i, nread = 0, nwritten; DEBUGASSERT(backend); if(!backend->conn || cf->shutdown) { @@ -1371,8 +1380,7 @@ out: return result; } -static void -cr_close(struct Curl_cfilter *cf, struct Curl_easy *data) +static void cr_close(struct Curl_cfilter *cf, struct Curl_easy *data) { const struct ssl_connect_data *connssl = cf->ctx; struct rustls_ssl_backend_data *backend = @@ -1393,16 +1401,15 @@ cr_close(struct Curl_cfilter *cf, struct Curl_easy *data) static size_t cr_version(char *buffer, size_t size) { const struct rustls_str ver = rustls_version(); - return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data); + return curl_msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data); } -static CURLcode -cr_random(struct Curl_easy *data, unsigned char *entropy, size_t length) +static CURLcode cr_random(struct Curl_easy *data, unsigned char *entropy, + size_t length) { rustls_result rresult = 0; (void)data; - rresult = - rustls_default_crypto_provider_random(entropy, length); + rresult = rustls_default_crypto_provider_random(entropy, length); return map_error(rresult); } @@ -1418,7 +1425,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/lib/vtls/rustls.h b/lib/vtls/rustls.h index 74d39d4d11..b6ddbd1b7e 100644 --- a/lib/vtls/rustls.h +++ b/lib/vtls/rustls.h @@ -1,3 +1,5 @@ +#ifndef HEADER_CURL_RUSTLS_H +#define HEADER_CURL_RUSTLS_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -5,8 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Jacob Hoffman-Andrews, - * + * Copyright (C) Jacob Hoffman-Andrews, * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,10 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#ifndef HEADER_CURL_RUSTLS_H -#define HEADER_CURL_RUSTLS_H - -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_RUSTLS diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index 1afc6790cc..05bd6a5194 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -23,45 +23,37 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * 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 "../sendf.h" -#include "../connect.h" /* for the connect timeout */ -#include "../strdup.h" -#include "../strerror.h" -#include "../select.h" /* for the socket readiness */ -#include "../curlx/inet_pton.h" /* for IP addr SNI check */ -#include "../curlx/multibyte.h" -#include "../curlx/warnless.h" -#include "x509asn1.h" -#include "../curl_printf.h" -#include "../multiif.h" -#include "../system_win32.h" -#include "../curlx/version_win32.h" -#include "../rand.h" -#include "../curlx/strparse.h" -#include "../progress.h" - -/* The last #include file should be: */ -#include "../curl_memory.h" -#include "../memdebug.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 @@ -70,11 +62,11 @@ */ #ifdef CURL_SCHANNEL_DEV_DEBUG #define SCH_DEV(x) x -#define SCH_DEV_SHOWBOOL(x) \ +#define SCH_DEV_SHOWBOOL(x) \ infof(data, "schannel: " #x " %s", (x) ? "TRUE" : "FALSE"); #else -#define SCH_DEV(x) do { } while(0) -#define SCH_DEV_SHOWBOOL(x) do { } while(0) +#define SCH_DEV(x) do {} while(0) +#define SCH_DEV_SHOWBOOL(x) do {} while(0) #endif /* Offered by mingw-w64 v8+. MS SDK 7.0A+. */ @@ -109,21 +101,10 @@ #define CERT_THUMBPRINT_DATA_LEN 20 /* Uncomment to force verbose output - * #define infof(x, y, ...) printf(y, __VA_ARGS__) - * #define failf(x, y, ...) printf(y, __VA_ARGS__) + * #define infof(x, y, ...) curl_mprintf(y, __VA_ARGS__) + * #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 - -/* Work around typo in CeGCC (as of 0.59.1) w32api headers */ -#if defined(__MINGW32CE__) && \ - !defined(ALG_CLASS_DHASH) && defined(ALG_CLASS_HASH) -#define ALG_CLASS_DHASH ALG_CLASS_HASH -#endif - /* Offered by mingw-w64 v4+. MS SDK 6.0A+. */ #ifndef PKCS12_NO_PERSIST_KEY #define PKCS12_NO_PERSIST_KEY 0x00008000 @@ -139,7 +120,7 @@ /* ALPN requires version 8.1 of the Windows SDK, which was shipped with Visual Studio 2013, aka _MSC_VER 1800: - https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx + https://learn.microsoft.com/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/hh831771 Or mingw-w64 9.0 or upper. */ #if (defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 9) || \ @@ -148,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) { @@ -168,10 +145,9 @@ static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr, desc->cBuffers = NumArrElem; } -static CURLcode -schannel_set_ssl_version_min_max(DWORD *enabled_protocols, - struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode schannel_set_ssl_version_min_max(DWORD *enabled_protocols, + struct Curl_cfilter *cf, + struct Curl_easy *data) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); long ssl_version = conn_config->version; @@ -199,20 +175,20 @@ schannel_set_ssl_version_min_max(DWORD *enabled_protocols, for(; i <= (ssl_version_max >> 16); ++i) { switch(i) { case CURL_SSLVERSION_TLSv1_0: - (*enabled_protocols) |= SP_PROT_TLS1_0_CLIENT; + *enabled_protocols |= SP_PROT_TLS1_0_CLIENT; break; case CURL_SSLVERSION_TLSv1_1: - (*enabled_protocols) |= SP_PROT_TLS1_1_CLIENT; + *enabled_protocols |= SP_PROT_TLS1_1_CLIENT; break; case CURL_SSLVERSION_TLSv1_2: - (*enabled_protocols) |= SP_PROT_TLS1_2_CLIENT; + *enabled_protocols |= SP_PROT_TLS1_2_CLIENT; break; case CURL_SSLVERSION_TLSv1_3: /* Windows Server 2022 and newer */ if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { - (*enabled_protocols) |= SP_PROT_TLS1_3_CLIENT; + *enabled_protocols |= SP_PROT_TLS1_3_CLIENT; break; } else { /* Windows 10 and older */ @@ -224,14 +200,14 @@ 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; int id; }; -static const struct algo algs[]= { +static const struct algo algs[] = { CIPHEROPTION(CALG_MD2), CIPHEROPTION(CALG_MD4), CIPHEROPTION(CALG_MD5), @@ -240,107 +216,55 @@ 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) +static int get_alg_id_by_name(const char *name) { const char *nameEnd = strchr(name, ':'); size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name); @@ -353,11 +277,10 @@ get_alg_id_by_name(const char *name) return 0; /* not found */ } -#define NUM_CIPHERS 47 /* There are 47 options listed above */ +#define NUM_CIPHERS 47 /* There are a maximum of 47 options listed above */ -static CURLcode -set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, - ALG_ID *algIds) +static CURLcode set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, + ALG_ID *algIds) { const char *startCur = ciphers; int algCount = 0; @@ -384,11 +307,9 @@ set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, return CURLE_OK; } -#ifndef UNDER_CE /* Function allocates memory for store_path only if CURLE_OK is returned */ -static CURLcode -get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, - TCHAR **thumbprint) +static CURLcode get_cert_location(TCHAR *path, DWORD *store_name, + TCHAR **store_path, TCHAR **thumbprint) { TCHAR *sep; TCHAR *store_path_start; @@ -410,14 +331,11 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, *store_name = CERT_SYSTEM_STORE_SERVICES; else if(_tcsncmp(path, TEXT("Users"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_USERS; - else if(_tcsncmp(path, TEXT("CurrentUserGroupPolicy"), - store_name_len) == 0) + else if(_tcsncmp(path, TEXT("CurrentUserGroupPolicy"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY; - else if(_tcsncmp(path, TEXT("LocalMachineGroupPolicy"), - store_name_len) == 0) + else if(_tcsncmp(path, TEXT("LocalMachineGroupPolicy"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY; - else if(_tcsncmp(path, TEXT("LocalMachineEnterprise"), - store_name_len) == 0) + else if(_tcsncmp(path, TEXT("LocalMachineEnterprise"), store_name_len) == 0) *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE; else return CURLE_SSL_CERTPROBLEM; @@ -433,114 +351,27 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, return CURLE_SSL_CERTPROBLEM; *sep = TEXT('\0'); - *store_path = _tcsdup(store_path_start); + *store_path = curlx_tcsdup(store_path_start); *sep = TEXT('\\'); if(!*store_path) return CURLE_OUT_OF_MEMORY; return CURLE_OK; } -#endif -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; + CURLcode result = CURLE_OK; - /* 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; - } - -#ifndef UNDER_CE - /* 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; @@ -548,54 +379,63 @@ 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); - if(result && (data->set.ssl.primary.clientcert[0]!='\0')) - fInCert = fopen(data->set.ssl.primary.clientcert, "rb"); + /* '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"); if(result && !fInCert) { failf(data, "schannel: Failed to get certificate location" " or file for %s", data->set.ssl.primary.clientcert); - curlx_unicodefree(cert_path); return result; } } - if((fInCert || blob) && (data->set.ssl.cert_type) && - (!curl_strequal(data->set.ssl.cert_type, "P12"))) { + 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_unicodefree(cert_path); + curlx_free(cert_store_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 - https://social.msdn.microsoft.com/Forums/windowsdesktop/ - en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5 + /* 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; + 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_unicodefree(cert_path); + curlx_free(cert_store_path); if(fInCert) { long cert_tell = 0; bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0; @@ -607,28 +447,29 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, certsize = (size_t)cert_tell; if(continue_reading) continue_reading = fseek(fInCert, 0, SEEK_SET) == 0; - if(continue_reading) - certdata = malloc(certsize + 1); + if(continue_reading && (certsize < CURL_MAX_INPUT_LENGTH)) + certdata = curlx_malloc(certsize + 1); if((!certdata) || ((int) fread(certdata, certsize, 1, fInCert) != 1)) continue_reading = FALSE; - fclose(fInCert); + curlx_fclose(fInCert); if(!continue_reading) { failf(data, "schannel: Failed to read cert file %s", data->set.ssl.primary.clientcert); - free(certdata); + curlx_free(certdata); return CURLE_SSL_CERTPROBLEM; } } /* Convert key-pair data to the in-memory certificate store */ - datablob.pbData = (BYTE*)certdata; + datablob.pbData = (BYTE *)certdata; datablob.cbData = (DWORD)certsize; if(data->set.ssl.key_passwd) pwd_len = strlen(data->set.ssl.key_passwd); - pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1)); + 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, @@ -641,16 +482,12 @@ 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); - - free(pszPassword); + cert_store = PFXImportCertStore(&datablob, pszPassword, + PKCS12_NO_PERSIST_KEY); + curlx_free(pszPassword); } if(!blob) - free(certdata); + curlx_free(certdata); if(!cert_store) { DWORD errorcode = GetLastError(); if(errorcode == ERROR_INVALID_PASSWORD) @@ -659,7 +496,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, cert_showfilename_error); else failf(data, "schannel: Failed to import cert file %s, " - "last error is 0x%lx", + "last error is 0x%08lx", cert_showfilename_error, errorcode); return CURLE_SSL_CERTPROBLEM; } @@ -672,13 +509,14 @@ 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%lx", + ", last error is 0x%08lx", cert_showfilename_error, GetLastError()); CertCloseStore(cert_store, 0); return CURLE_SSL_CERTPROBLEM; @@ -700,16 +538,14 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, char *path_utf8 = curlx_convert_tchar_to_UTF8(cert_store_path); failf(data, "schannel: Failed to open cert store %lx %s, " - "last error is 0x%lx", - cert_store_name, - (path_utf8 ? path_utf8 : "(unknown)"), + "last error is 0x%08lx", + cert_store_name, (path_utf8 ? path_utf8 : "(unknown)"), GetLastError()); - free(cert_store_path); - curlx_unicodefree(path_utf8); - curlx_unicodefree(cert_path); + curlx_free(cert_store_path); + curlx_free(path_utf8); return CURLE_SSL_CERTPROBLEM; } - free(cert_store_path); + curlx_free(cert_store_path); cert_thumbprint.pbData = cert_thumbprint_data; cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; @@ -720,18 +556,15 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, cert_thumbprint_data, &cert_thumbprint.cbData, NULL, NULL)) { - curlx_unicodefree(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_unicodefree(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"); @@ -740,27 +573,23 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, } client_cert_store = cert_store; } -#endif - /* allocate memory for the reusable credential handle */ - backend->cred = (struct Curl_schannel_cred *) - calloc(1, sizeof(struct Curl_schannel_cred)); - if(!backend->cred) { - failf(data, "schannel: unable to allocate memory"); + *out_cert_store = client_cert_store; + *out_cert_context = client_cert; - if(client_certs[0]) - CertFreeCertificateContext(client_certs[0]); - if(client_cert_store) - CertCloseStore(client_cert_store, 0); + return CURLE_OK; +} - 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; +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 @@ -771,7 +600,15 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, SCH_CREDENTIALS credentials = { 0 }; TLS_PARAMETERS tls_parameters = { 0 }; - CRYPTO_SETTINGS crypto_settings[1] = { { 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)); tls_parameters.pDisabledCrypto = crypto_settings; @@ -783,8 +620,7 @@ 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; @@ -796,15 +632,21 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, (TCHAR *)CURL_UNCONST(UNISP_NAME), SECPKG_CRED_OUTBOUND, NULL, &credentials, NULL, NULL, - &backend->cred->cred_handle, - &backend->cred->time_stamp); + &backend->cred->cred_handle, NULL); } else { /* Pre-Windows 10 1809 or the user set a legacy algorithm list. - Schannel will not negotiate TLS 1.3 when SCHANNEL_CRED is used. */ + Schannel does not negotiate TLS 1.3 when SCHANNEL_CRED is used. */ 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; @@ -835,18 +677,13 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, (TCHAR *)CURL_UNCONST(UNISP_NAME), SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL, - &backend->cred->cred_handle, - &backend->cred->time_stamp); + &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", Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - Curl_safefree(backend->cred); switch(sspi_status) { case SEC_E_INSUFFICIENT_MEMORY: return CURLE_OUT_OF_MEMORY; @@ -863,16 +700,136 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, return CURLE_OK; } -static CURLcode -schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) +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) { size_t written = 0; struct ssl_connect_data *connssl = cf->ctx; struct schannel_ssl_backend_data *backend = (struct schannel_ssl_backend_data *)connssl->backend; -#ifndef UNDER_CE struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); -#endif struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); SecBuffer outbuf; SecBufferDesc outbuf_desc; @@ -885,29 +842,15 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) CURLcode result; DEBUGASSERT(backend); - DEBUGF(infof(data, - "schannel: SSL/TLS connection with %s port %d (step 1/3)", + 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 backend->use_alpn = FALSE; #endif -#ifdef UNDER_CE - /* certificate validation on Windows CE does not seem to work right; we will - * do it following a more manual process. */ - backend->use_manual_cred_validation = TRUE; -#else if(conn_config->CAfile || conn_config->ca_info_blob) { if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { @@ -915,18 +858,17 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } else { failf(data, "schannel: this version of Windows is too old to support " - "certificate verification via CA bundle file."); + "certificate verification via CA bundle file."); return CURLE_SSL_CACERT_BADFILE; } } else backend->use_manual_cred_validation = FALSE; -#endif backend->cred = NULL; /* check for an existing reusable credential handle */ - if(ssl_config->primary.cache_session) { + if(Curl_ssl_scache_use(cf, data)) { struct Curl_schannel_cred *old_cred; Curl_ssl_scache_lock(data); old_cred = Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key); @@ -969,23 +911,23 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) int cur = 0; int list_start_index = 0; unsigned int *extension_len = NULL; - unsigned short* list_len = NULL; + unsigned short *list_len = NULL; struct alpn_proto_buf proto; - /* The first four bytes will be an unsigned int indicating number + /* The first four bytes is an unsigned int indicating number of bytes of data in the rest of the buffer. */ extension_len = (unsigned int *)(void *)(&alpn_buffer[cur]); cur += (int)sizeof(unsigned int); - /* The next four bytes are an indicator that this buffer will contain + /* The next four bytes are an indicator that this buffer contains ALPN data, as opposed to NPN, for example. */ *(unsigned int *)(void *)&alpn_buffer[cur] = SecApplicationProtocolNegotiationExt_ALPN; cur += (int)sizeof(unsigned int); - /* The next two bytes will be an unsigned short indicating the number + /* The next two bytes is an unsigned short indicating the number of bytes used to list the preferred protocols. */ - list_len = (unsigned short*)(void *)(&alpn_buffer[cur]); + list_len = (unsigned short *)(void *)(&alpn_buffer[cur]); cur += (int)sizeof(unsigned short); list_start_index = cur; @@ -1022,24 +964,22 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) InitSecBufferDesc(&outbuf_desc, &outbuf, 1); /* security request flags */ - backend->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | + 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; - } + 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 *) - calloc(1, sizeof(struct Curl_schannel_ctxt)); + curlx_calloc(1, sizeof(struct Curl_schannel_ctxt)); if(!backend->ctxt) { failf(data, "schannel: unable to allocate memory"); return CURLE_OUT_OF_MEMORY; } /* Schannel InitializeSecurityContext: - https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx + https://learn.microsoft.com/windows/win32/api/rrascfg/nn-rrascfg-ieapproviderconfig At the moment we do not pass inbuf unless we are using ALPN since we only use it for that, and WINE (for which we currently disable ALPN) is giving @@ -1050,11 +990,11 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) backend->req_flags, 0, 0, (backend->use_alpn ? &inbuf_desc : NULL), 0, &backend->ctxt->ctxt_handle, - &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp); + &outbuf_desc, &backend->ret_flags, NULL); if(sspi_status != SEC_I_CONTINUE_NEEDED) { char buffer[STRERROR_LEN]; - Curl_safefree(backend->ctxt); + curlx_safefree(backend->ctxt); switch(sspi_status) { case SEC_E_INSUFFICIENT_MEMORY: failf(data, "schannel: initial InitializeSecurityContext failed: %s", @@ -1087,7 +1027,8 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* send initial handshake data which is now stored in output buffer */ result = Curl_conn_cf_send(cf->next, data, - outbuf.pvBuffer, outbuf.cbBuffer, FALSE, + (const uint8_t *)outbuf.pvBuffer, + outbuf.cbBuffer, FALSE, &written); Curl_pSecFn->FreeContextBuffer(outbuf.pvBuffer); if(result || (outbuf.cbBuffer != written)) { @@ -1112,16 +1053,176 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_OK; } -static CURLcode -schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) +static CURLcode schannel_error(struct Curl_easy *data, + SECURITY_STATUS sspi_status) +{ + char buffer[STRERROR_LEN]; + switch(sspi_status) { + case SEC_E_INSUFFICIENT_MEMORY: + failf(data, "schannel: next InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_OUT_OF_MEMORY; + case SEC_E_WRONG_PRINCIPAL: + failf(data, "schannel: SNI or certificate check failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_PEER_FAILED_VERIFICATION; + case SEC_E_UNTRUSTED_ROOT: + failf(data, "schannel: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_PEER_FAILED_VERIFICATION; +#if 0 + case SEC_E_INVALID_HANDLE: + case SEC_E_INVALID_TOKEN: + case SEC_E_LOGON_DENIED: + case SEC_E_TARGET_UNKNOWN: + case SEC_E_NO_AUTHENTICATING_AUTHORITY: + case SEC_E_INTERNAL_ERROR: + case SEC_E_NO_CREDENTIALS: + case SEC_E_UNSUPPORTED_FUNCTION: + case SEC_E_APPLICATION_PROTOCOL_MISMATCH: +#endif + default: + failf(data, "schannel: next InitializeSecurityContext failed: %s", + Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); + return CURLE_SSL_CONNECT_ERROR; + } +} + +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) { struct ssl_connect_data *connssl = cf->ctx; struct schannel_ssl_backend_data *backend = (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]; @@ -1136,66 +1237,50 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) doread = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ? FALSE : TRUE; connssl->io_need = CURL_SSL_IO_NEED_NONE; - DEBUGF(infof(data, - "schannel: SSL/TLS connection with %s port %d (step 2/3)", + DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 2/3)", connssl->peer.hostname, connssl->peer.port)); if(!backend->cred || !backend->ctxt) 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 = 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 = 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 = 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")); @@ -1213,7 +1298,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } 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)); } @@ -1221,11 +1306,12 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) 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, malloc(backend->encdata_offset), - curlx_uztoul(backend->encdata_offset)); + InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, + curlx_malloc(backend->encdata.offset), + curlx_uztoul(backend->encdata.offset)); InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); InitSecBufferDesc(&inbuf_desc, inbuf, 2); @@ -1241,17 +1327,17 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } /* 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 + /* The socket must be writable (or a poll error occurred) before we call InitializeSecurityContext to continue processing the received TLS - records. This is because that function is not idempotent and we don't + records. This is because that function is not idempotent and we do not support partial save/resume sending replies of handshake tokens. */ if(!SOCKET_WRITABLE(Curl_conn_cf_get_socket(cf, data), 0)) { - SCH_DEV(infof(data, "schannel: handshake waiting for writeable socket")); + SCH_DEV(infof(data, "schannel: handshake waiting for writable socket")); connssl->io_need = CURL_SSL_IO_NEED_SEND; - free(inbuf[0].pvBuffer); + curlx_free(inbuf[0].pvBuffer); return CURLE_OK; } @@ -1259,88 +1345,68 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) &backend->cred->cred_handle, &backend->ctxt->ctxt_handle, backend->cred->sni_hostname, backend->req_flags, 0, 0, &inbuf_desc, 0, NULL, - &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp); + &outbuf_desc, &backend->ret_flags, NULL); /* free buffer for received handshake data */ - Curl_safefree(inbuf[0].pvBuffer); + curlx_safefree(inbuf[0].pvBuffer); /* check if the handshake was incomplete */ - if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { + switch(sspi_status) { + case SEC_E_INCOMPLETE_MESSAGE: backend->encdata_is_incomplete = TRUE; connssl->io_need = CURL_SSL_IO_NEED_RECV; DEBUGF(infof(data, "schannel: received incomplete message, need more data")); return CURLE_OK; - } - /* If the server has requested a client certificate, attempt to continue - the handshake without one. This will allow connections to servers which - request a client certificate but do not require it. */ - if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS && - !(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { - backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; - connssl->io_need = CURL_SSL_IO_NEED_SEND; - DEBUGF(infof(data, - "schannel: a client certificate has been requested")); - return CURLE_OK; - } - - /* check if the handshake needs to be continued */ - if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) { + case SEC_I_CONTINUE_NEEDED: + case SEC_E_OK: + /* check if the handshake needs to be continued */ + result = CURLE_OK; 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)); /* send handshake token to server */ result = Curl_conn_cf_send(cf->next, data, - outbuf[i].pvBuffer, outbuf[i].cbBuffer, + (const uint8_t *)outbuf[i].pvBuffer, + outbuf[i].cbBuffer, FALSE, &written); if(result || (outbuf[i].cbBuffer != written)) { failf(data, "schannel: failed to send next handshake data: " "sent %zu of %lu bytes", written, outbuf[i].cbBuffer); - return CURLE_SSL_CONNECT_ERROR; + result = CURLE_SSL_CONNECT_ERROR; } } - + } + for(i = 0; i < 3; i++) { /* free obsolete buffer */ - if(outbuf[i].pvBuffer) { + if(outbuf[i].pvBuffer) Curl_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer); - } } - } - else { - char buffer[STRERROR_LEN]; - switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - failf(data, "schannel: next InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_OUT_OF_MEMORY; - case SEC_E_WRONG_PRINCIPAL: - failf(data, "schannel: SNI or certificate check failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_PEER_FAILED_VERIFICATION; - case SEC_E_UNTRUSTED_ROOT: - failf(data, "schannel: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_PEER_FAILED_VERIFICATION; -#if 0 - case SEC_E_INVALID_HANDLE: - case SEC_E_INVALID_TOKEN: - case SEC_E_LOGON_DENIED: - case SEC_E_TARGET_UNKNOWN: - case SEC_E_NO_AUTHENTICATING_AUTHORITY: - case SEC_E_INTERNAL_ERROR: - case SEC_E_NO_CREDENTIALS: - case SEC_E_UNSUPPORTED_FUNCTION: - case SEC_E_APPLICATION_PROTOCOL_MISMATCH: -#endif - default: - failf(data, "schannel: next InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_SSL_CONNECT_ERROR; + if(result) + return result; + break; + + case SEC_I_INCOMPLETE_CREDENTIALS: + if(!(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { + /* If the server has requested a client certificate, attempt to + continue the handshake without one. This allows connections to + servers which request a client certificate but do not require + it. */ + backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; + connssl->io_need = CURL_SSL_IO_NEED_SEND; + DEBUGF(infof(data, + "schannel: a client certificate has been requested")); + return CURLE_OK; } + FALLTHROUGH(); + + default: + return schannel_error(data, sspi_status); } /* check if there was additional remaining encrypted data */ @@ -1349,21 +1415,21 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) 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) - + 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; + backend->encdata.offset = inbuf[1].cbBuffer; if(sspi_status == SEC_I_CONTINUE_NEEDED) { doread = FALSE; continue; @@ -1371,7 +1437,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } } else { - backend->encdata_offset = 0; + backend->encdata.offset = 0; } break; } @@ -1409,15 +1475,14 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) } /* Verify the hostname manually when certificate verification is disabled, - because in that case Schannel will not verify it. */ + because in that case Schannel does not verify it. */ if(!conn_config->verifypeer && conn_config->verifyhost) return Curl_verify_host(cf, data); return CURLE_OK; } -static bool -valid_cert_encoding(const CERT_CONTEXT *cert_context) +static bool valid_cert_encoding(const CERT_CONTEXT *cert_context) { return (cert_context != NULL) && ((cert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) && @@ -1425,12 +1490,11 @@ valid_cert_encoding(const CERT_CONTEXT *cert_context) (cert_context->cbCertEncoded > 0); } -typedef bool(*Read_crt_func)(const CERT_CONTEXT *ccert_context, - bool reverse_order, void *arg); +typedef bool (*Read_crt_func)(const CERT_CONTEXT *ccert_context, + bool reverse_order, void *arg); -static void -traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func, - void *arg) +static void traverse_cert_store(const CERT_CONTEXT *context, + Read_crt_func func, void *arg) { const CERT_CONTEXT *current_context = NULL; bool should_continue = TRUE; @@ -1455,32 +1519,31 @@ traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func, CertFreeCertificateContext(current_context); } -static bool -cert_counter_callback(const CERT_CONTEXT *ccert_context, bool reverse_order, - void *certs_count) +static bool cert_counter_callback(const CERT_CONTEXT *ccert_context, + bool reverse_order, void *certs_count) { (void)reverse_order; if(valid_cert_encoding(ccert_context)) (*(int *)certs_count)++; + if(*(int *)certs_count > MAX_ALLOWED_CERT_AMOUNT) + return FALSE; return TRUE; } -struct Adder_args -{ +struct Adder_args { struct Curl_easy *data; CURLcode result; int idx; int certs_count; }; -static bool -add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, bool reverse_order, - void *raw_arg) +static bool add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, + bool reverse_order, void *raw_arg) { - struct Adder_args *args = (struct Adder_args*)raw_arg; + struct Adder_args *args = (struct Adder_args *)raw_arg; args->result = CURLE_OK; if(valid_cert_encoding(ccert_context)) { - const char *beg = (const char *) ccert_context->pbCertEncoded; + const char *beg = (const char *)ccert_context->pbCertEncoded; const char *end = beg + ccert_context->cbCertEncoded; int insert_index = reverse_order ? (args->certs_count - 1) - args->idx : args->idx; @@ -1500,23 +1563,22 @@ static void schannel_session_free(void *sessionid) cred->refcount--; if(cred->refcount == 0) { Curl_pSecFn->FreeCredentialsHandle(&cred->cred_handle); - curlx_unicodefree(cred->sni_hostname); + curlx_free(cred->sni_hostname); if(cred->client_cert_store) { CertCloseStore(cred->client_cert_store, 0); cred->client_cert_store = NULL; } - Curl_safefree(cred); + curlx_safefree(cred); } } } -static CURLcode -schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) +static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; struct schannel_ssl_backend_data *backend = (struct schannel_ssl_backend_data *)connssl->backend; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); CURLcode result = CURLE_OK; SECURITY_STATUS sspi_status = SEC_E_OK; CERT_CONTEXT *ccert_context = NULL; @@ -1527,8 +1589,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); DEBUGASSERT(backend); - DEBUGF(infof(data, - "schannel: SSL/TLS connection with %s port %d (step 3/3)", + DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 3/3)", connssl->peer.hostname, connssl->peer.port)); if(!backend->cred) @@ -1553,8 +1614,8 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) 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"); @@ -1584,7 +1645,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) #endif /* save the current session data for possible reuse */ - if(ssl_config->primary.cache_session) { + if(Curl_ssl_scache_use(cf, data)) { Curl_ssl_scache_lock(data); /* Up ref count since call takes ownership */ backend->cred->refcount++; @@ -1599,8 +1660,8 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) 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"); @@ -1608,6 +1669,12 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) } traverse_cert_store(ccert_context, cert_counter_callback, &certs_count); + if(certs_count > MAX_ALLOWED_CERT_AMOUNT) { + failf(data, "%d certificates is more than allowed (%u)", + certs_count, MAX_ALLOWED_CERT_AMOUNT); + CertFreeCertificateContext(ccert_context); + return CURLE_SSL_CONNECT_ERROR; + } result = Curl_ssl_init_certinfo(data, certs_count); if(!result) { @@ -1690,18 +1757,25 @@ enum schannel_renegotiate_caller_t { SCH_RENEG_CALLER_IS_SEND }; +/* The maximum time we allow for Schannel renegotiation which may in some + rare cases block either due to libcurl (waiting on the socket) or Windows + (waiting on an interactive security prompt). Note Schannel "renegotiation" + is not necessarily literal TLS renegotiation, but means DecryptMessage + returned SEC_I_RENEGOTIATE which means at least the security context needs + to be re-established. */ +#define MAX_RENEG_BLOCK_TIME (60 * 1000) /* 60 seconds in milliseconds */ + /* This function renegotiates the connection due to a server request received by schannel_recv. This function returns CURLE_AGAIN if the renegotiation is 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; - const timediff_t max_renegotiate_ms = 5 * 60 * 1000; /* 5 minutes */ struct ssl_connect_data *connssl = cf->ctx; struct schannel_ssl_backend_data *backend = (struct schannel_ssl_backend_data *)connssl->backend; @@ -1711,15 +1785,11 @@ schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data, failf(data, "schannel: unexpected call to schannel_recv_renegotiate"); return CURLE_SSL_CONNECT_ERROR; } - + DEBUGASSERT(caller <= SCH_RENEG_CALLER_IS_SEND); if(caller == SCH_RENEG_CALLER_IS_RECV) SCH_DEV(infof(data, "schannel: renegotiation caller is schannel_recv")); - else if(caller == SCH_RENEG_CALLER_IS_SEND) + else SCH_DEV(infof(data, "schannel: renegotiation caller is schannel_send")); - else { - failf(data, "schannel: unknown caller for schannel_recv_renegotiate"); - return CURLE_SSL_CONNECT_ERROR; - } sockfd = Curl_conn_cf_get_socket(cf, data); @@ -1736,7 +1806,7 @@ schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data, connssl->connecting_state = ssl_connect_2; memset(rs, 0, sizeof(*rs)); rs->io_need = CURL_SSL_IO_NEED_SEND; - rs->start_time = curlx_now(); + rs->start_time = *Curl_pgrs_now(data); rs->started = TRUE; } @@ -1745,8 +1815,8 @@ schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t readfd, writefd; timediff_t elapsed; - elapsed = curlx_timediff(curlx_now(), rs->start_time); - if(elapsed >= max_renegotiate_ms) { + elapsed = curlx_ptimediff_ms(Curl_pgrs_now(data), &rs->start_time); + if(elapsed >= MAX_RENEG_BLOCK_TIME) { failf(data, "schannel: renegotiation timeout"); result = CURLE_SSL_CONNECT_ERROR; break; @@ -1781,13 +1851,13 @@ schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data, * occur if the user is waiting on the socket only in one direction. * * For example, if the user has called recv then they may not be waiting - * for a writeable socket and vice versa, so we block to avoid that. + * for a writable socket and vice versa, so we block to avoid that. * * In practice a wait is unlikely to occur. For caller recv if handshake - * data needs to be sent then we block for a writeable socket that should - * be writeable immediately except for OS resource constraints. For caller + * data needs to be sent then we block for a writable socket that should + * be writable immediately except for OS resource constraints. For caller * send if handshake data needs to be received then we block for a readable - * socket, which could take some time, but it's more likely the user has + * socket, which could take some time, but it is more likely the user has * called recv since they had called it prior (only recv can start * renegotiation and probably the user is going to call it again to get * more of their data before calling send). @@ -1805,47 +1875,46 @@ schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data, for(;;) { int what; - timediff_t timeout, remaining; + timediff_t timeout_ms, remaining; - if(Curl_pgrsUpdate(data)) { - result = CURLE_ABORTED_BY_CALLBACK; + result = Curl_pgrsUpdate(data); + if(result) break; - } - elapsed = curlx_timediff(curlx_now(), rs->start_time); - if(elapsed >= max_renegotiate_ms) { + elapsed = curlx_ptimediff_ms(Curl_pgrs_now(data), &rs->start_time); + if(elapsed >= MAX_RENEG_BLOCK_TIME) { failf(data, "schannel: renegotiation timeout"); result = CURLE_SSL_CONNECT_ERROR; break; } - remaining = max_renegotiate_ms - elapsed; + remaining = MAX_RENEG_BLOCK_TIME - elapsed; if(blocking) { - timeout = Curl_timeleft(data, NULL, FALSE); + timeout_ms = Curl_timeleft_ms(data); - if(timeout < 0) { + if(timeout_ms < 0) { result = CURLE_OPERATION_TIMEDOUT; break; } /* the blocking is in intervals so that the progress function can be called every second */ - if(!timeout || timeout > 1000) - timeout = 1000; + if(!timeout_ms || timeout_ms > 1000) + timeout_ms = 1000; - if(timeout > remaining) - timeout = remaining; + if(timeout_ms > remaining) + timeout_ms = remaining; } else - timeout = 0; + timeout_ms = 0; SCH_DEV(infof(data, "schannel: renegotiation wait until socket is" "%s%s for up to %" FMT_TIMEDIFF_T " ms", ((readfd != CURL_SOCKET_BAD) ? " readable" : ""), - ((writefd != CURL_SOCKET_BAD) ? " writeable" : ""), - timeout)); + ((writefd != CURL_SOCKET_BAD) ? " writable" : ""), + timeout_ms)); - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, timeout); + what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, timeout_ms); if(what > 0 && (what & (CURL_CSELECT_IN | CURL_CSELECT_OUT))) { SCH_DEV(infof(data, "schannel: renegotiation socket %s%s", @@ -1884,9 +1953,8 @@ schannel_recv_renegotiate(struct Curl_cfilter *cf, struct Curl_easy *data, return result; } -static CURLcode -schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, size_t *pnwritten) +static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, size_t *pnwritten) { size_t data_len = 0; unsigned char *ptr = NULL; @@ -1926,7 +1994,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* calculate the complete message length and allocate a buffer for it */ data_len = backend->stream_sizes.cbHeader + len + backend->stream_sizes.cbTrailer; - ptr = (unsigned char *)malloc(data_len); + ptr = (unsigned char *)curlx_malloc(data_len); if(!ptr) { return CURLE_OUT_OF_MEMORY; } @@ -1945,7 +2013,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* copy data into output buffer */ memcpy(outbuf[1].pvBuffer, buf, len); - /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */ + /* https://learn.microsoft.com/windows/win32/api/sspi/nf-sspi-encryptmessage */ sspi_status = Curl_pSecFn->EncryptMessage(&backend->ctxt->ctxt_handle, 0, &outbuf_desc, 0); @@ -1966,7 +2034,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, sent. The unwritten encrypted bytes would be the first bytes to send on the next invocation. Here's the catch with this - if we tell the client that all the - bytes have been sent, will the client call this method again to + bytes have been sent, does the client call this method again to send the buffered data? Looking at who calls this function, it seems the answer is NO. */ @@ -1975,11 +2043,11 @@ 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(data, NULL, 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; } @@ -1993,15 +2061,16 @@ 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; } /* socket is writable */ result = Curl_conn_cf_send(cf->next, data, - ptr + *pnwritten, len - *pnwritten, + (const uint8_t *)ptr + *pnwritten, + len - *pnwritten, FALSE, &this_write); if(result == CURLE_AGAIN) continue; @@ -2015,11 +2084,11 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) { result = CURLE_OUT_OF_MEMORY; } - else{ + else { result = CURLE_SEND_ERROR; } - Curl_safefree(ptr); + curlx_safefree(ptr); if(len == *pnwritten) /* Encrypted message including header, data and trailer entirely sent. @@ -2029,21 +2098,15 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, return result; } -static CURLcode -schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, size_t *pnread) +static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, size_t *pnread) { 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; @@ -2075,7 +2138,7 @@ 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; @@ -2095,68 +2158,56 @@ 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 = 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); @@ -2164,10 +2215,10 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0); InitSecBufferDesc(&inbuf_desc, inbuf, 4); - /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx + /* https://learn.microsoft.com/windows/win32/api/sspi/nf-sspi-decryptmessage */ sspi_status = Curl_pSecFn->DecryptMessage(&backend->ctxt->ctxt_handle, - &inbuf_desc, 0, NULL); + &inbuf_desc, 0, NULL); /* check if everything went fine (server may want to renegotiate or shutdown the connection context) */ @@ -2179,40 +2230,24 @@ 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 = 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 */ @@ -2223,22 +2258,22 @@ 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) - + memmove(backend->encdata.buffer, + (backend->encdata.buffer + backend->encdata.offset) - inbuf[3].cbBuffer, inbuf[3].cbBuffer); - backend->encdata_offset = 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 */ @@ -2265,7 +2300,7 @@ 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) @@ -2283,21 +2318,19 @@ 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 */ @@ -2309,7 +2342,7 @@ cleanup: 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)"); @@ -2319,16 +2352,16 @@ cleanup: 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; } @@ -2357,8 +2390,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; @@ -2373,7 +2406,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool send_shutdown, bool *done) { - /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx + /* See https://learn.microsoft.com/windows/win32/secauthn/shutting-down-an-schannel-connection * Shutting Down an Schannel Connection */ struct ssl_connect_data *connssl = cf->ctx; @@ -2415,7 +2448,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]; @@ -2440,22 +2473,22 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, 0, &backend->ctxt->ctxt_handle, &outbuf_desc, - &backend->ret_flags, - &backend->ctxt->time_stamp); + &backend->ret_flags, NULL); if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) { /* send close message which is in output buffer */ size_t written; result = Curl_conn_cf_send(cf->next, data, - outbuf.pvBuffer, outbuf.cbBuffer, + (const uint8_t *)outbuf.pvBuffer, + outbuf.cbBuffer, FALSE, &written); Curl_pSecFn->FreeContextBuffer(outbuf.pvBuffer); if(!result) { if(written < outbuf.cbBuffer) { + result = CURLE_SEND_ERROR; failf(data, "schannel: failed to send close msg: %s" " (bytes written: %zu)", curl_easy_strerror(result), written); - result = CURLE_SEND_ERROR; goto out; } backend->sent_shutdown = TRUE; @@ -2468,8 +2501,8 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, } else { if(!backend->recv_connection_closed) { - failf(data, "schannel: error sending close msg: %d", result); result = CURLE_SEND_ERROR; + failf(data, "schannel: error sending close msg: %d", result); goto out; } /* Looks like server already closed the connection. @@ -2523,7 +2556,7 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data) if(backend->ctxt) { DEBUGF(infof(data, "schannel: clear security context handle")); Curl_pSecFn->DeleteSecurityContext(&backend->ctxt->ctxt_handle); - Curl_safefree(backend->ctxt); + curlx_safefree(backend->ctxt); } /* free SSPI Schannel API credential handle */ @@ -2535,40 +2568,49 @@ 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) { + curlx_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) { + curlx_safefree(backend->decdata.buffer); + backend->decdata.length = 0; + backend->decdata.offset = 0; } } static int schannel_init(void) { -#if defined(HAS_ALPN_SCHANNEL) && !defined(UNDER_CE) +#ifdef HAS_ALPN_SCHANNEL typedef const char *(APIENTRY *WINE_GET_VERSION_FN)(void); +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +#endif WINE_GET_VERSION_FN p_wine_get_version = CURLX_FUNCTION_CAST(WINE_GET_VERSION_FN, - (GetProcAddress(GetModuleHandleA("ntdll"), - "wine_get_version"))); + GetProcAddress(GetModuleHandle(TEXT("ntdll")), "wine_get_version")); +#if defined(__clang__) && __clang_major__ >= 16 +#pragma clang diagnostic pop +#endif if(p_wine_get_version) { /* WINE detected */ + curl_off_t ver = 0; const char *wine_version = p_wine_get_version(); /* e.g. "6.0.2" */ /* Assume ALPN support with WINE 6.0 or upper */ - s_win_has_alpn = wine_version && atoi(wine_version) >= 6; + if(wine_version) + curlx_str_number(&wine_version, &ver, 20); + s_win_has_alpn = (ver >= 6); } else { /* ALPN is supported on Windows 8.1 / Server 2012 R2 and above. */ s_win_has_alpn = curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL); } -#endif /* HAS_ALPN_SCHANNEL && !UNDER_CE */ +#endif /* HAS_ALPN_SCHANNEL */ return Curl_sspi_global_init() == CURLE_OK ? 1 : 0; } @@ -2580,7 +2622,7 @@ static void schannel_cleanup(void) static size_t schannel_version(char *buffer, size_t size) { - return msnprintf(buffer, size, "Schannel"); + return curl_msnprintf(buffer, size, "Schannel"); } static CURLcode schannel_random(struct Curl_easy *data, @@ -2591,75 +2633,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, @@ -2669,9 +2642,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 @@ -2683,15 +2653,14 @@ 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 */ -#ifdef __MINGW32CE__ - /* workaround for CeGCC, should be (const BYTE*) */ - if(!CryptHashData(hHash, (BYTE*)CURL_UNCONST(input), (DWORD)inputlen, 0)) -#else if(!CryptHashData(hHash, input, (DWORD)inputlen, 0)) -#endif break; /* failed */ /* get hash size */ @@ -2735,7 +2704,7 @@ static void *schannel_get_internals(struct ssl_connect_data *connssl, } HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, - const struct Curl_easy *data) + struct Curl_easy *data) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct Curl_multi *multi = data->multi; @@ -2743,8 +2712,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; - struct curltime now; unsigned char info_blob_digest[CURL_SHA256_DIGEST_LENGTH]; DEBUGASSERT(multi); @@ -2755,7 +2722,7 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, share = Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY), - sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1); + sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1); if(!share || !share->cert_store) { return NULL; } @@ -2770,8 +2737,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) { - now = curlx_now(); - elapsed_ms = curlx_timediff(now, share->time); + timediff_t elapsed_ms = + curlx_ptimediff_ms(Curl_pgrs_now(data), &share->time); if(elapsed_ms >= timeout_ms) { return NULL; } @@ -2803,19 +2770,19 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, static void schannel_cert_share_free(void *key, size_t key_len, void *p) { struct schannel_cert_share *share = p; - DEBUGASSERT(key_len == (sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1)); + DEBUGASSERT(key_len == (sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1)); DEBUGASSERT(!memcmp(MPROTO_SCHANNEL_CERT_SHARE_KEY, key, key_len)); (void)key; (void)key_len; if(share->cert_store) { CertCloseStore(share->cert_store, 0); } - free(share->CAfile); - free(share); + curlx_free(share->CAfile); + curlx_free(share); } bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, - const struct Curl_easy *data, + struct Curl_easy *data, HCERTSTORE cert_store) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); @@ -2833,17 +2800,17 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, share = Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY), - sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1); + sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1); if(!share) { - share = calloc(1, sizeof(*share)); + share = curlx_calloc(1, sizeof(*share)); if(!share) { return FALSE; } if(!Curl_hash_add2(&multi->proto_hash, CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY), - sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1, + sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1, share, schannel_cert_share_free)) { - free(share); + curlx_free(share); return FALSE; } } @@ -2857,7 +2824,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, } else { if(conn_config->CAfile) { - CAfile = strdup(conn_config->CAfile); + CAfile = curlx_strdup(conn_config->CAfile); if(!CAfile) { return FALSE; } @@ -2868,7 +2835,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, if(share->cert_store) { CertCloseStore(share->cert_store, 0); } - free(share->CAfile); + curlx_free(share->CAfile); share->time = curlx_now(); share->cert_store = cert_store; diff --git a/lib/vtls/schannel.h b/lib/vtls/schannel.h index 9d0bea221a..fe49e9a9d0 100644 --- a/lib/vtls/schannel.h +++ b/lib/vtls/schannel.h @@ -24,60 +24,19 @@ * 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; -CURLcode Curl_verify_host(struct Curl_cfilter *cf, - struct Curl_easy *data); +CURLcode Curl_verify_host(struct Curl_cfilter *cf, struct Curl_easy *data); CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, struct Curl_easy *data); diff --git a/lib/vtls/schannel_int.h b/lib/vtls/schannel_int.h index 5483d12b5b..496635f2c2 100644 --- a/lib/vtls/schannel_int.h +++ b/lib/vtls/schannel_int.h @@ -24,13 +24,14 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_SCHANNEL -#include "vtls.h" +#include "vtls/vtls.h" +#include "curl_sha256.h" -#if defined(_MSC_VER) && (_MSC_VER <= 1600) +#if defined(_MSC_VER) && (_MSC_VER < 1700) /* Workaround for warning: 'type cast' : conversion from 'int' to 'LPCSTR' of greater size */ #undef CERT_STORE_PROV_MEMORY @@ -90,16 +91,10 @@ 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 { CredHandle cred_handle; - TimeStamp time_stamp; TCHAR *sni_hostname; HCERTSTORE client_cert_store; int refcount; @@ -107,20 +102,21 @@ struct Curl_schannel_cred { struct Curl_schannel_ctxt { CtxtHandle ctxt_handle; - TimeStamp time_stamp; +}; + +/* 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 { @@ -134,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); }; @@ -159,10 +159,10 @@ struct num_ip_data { }; HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, - const struct Curl_easy *data); + struct Curl_easy *data); bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, - const struct Curl_easy *data, + struct Curl_easy *data, HCERTSTORE cert_store); #endif /* USE_SCHANNEL */ diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 17e4270763..3e0130c8e9 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -23,58 +23,37 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * 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/inet_pton.h" -#include "vtls.h" -#include "vtls_int.h" -#include "../sendf.h" -#include "../strerror.h" -#include "../curlx/winapi.h" -#include "../curlx/multibyte.h" -#include "../curl_printf.h" -#include "hostcheck.h" -#include "../curlx/version_win32.h" - -/* The last #include file should be: */ -#include "../curl_memory.h" -#include "../memdebug.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) -#ifdef __MINGW32CE__ -#define CERT_QUERY_OBJECT_BLOB 0x00000002 -#define CERT_QUERY_CONTENT_CERT 1 -#define CERT_QUERY_CONTENT_FLAG_CERT (1 << CERT_QUERY_CONTENT_CERT) -#define CERT_QUERY_FORMAT_BINARY 1 -#define CERT_QUERY_FORMAT_BASE64_ENCODED 2 -#define CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED 3 -#define CERT_QUERY_FORMAT_FLAG_ALL \ - (1 << CERT_QUERY_FORMAT_BINARY) | \ - (1 << CERT_QUERY_FORMAT_BASE64_ENCODED) | \ - (1 << CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED) -#define CERT_CHAIN_REVOCATION_CHECK_CHAIN 0x20000000 -#define CERT_NAME_DISABLE_IE4_UTF8_FLAG 0x00010000 -#define CERT_TRUST_IS_OFFLINE_REVOCATION 0x01000000 -#endif /* __MINGW32CE__ */ - -#define MAX_CAFILE_SIZE 1048576 /* 1 MiB */ -#define BEGIN_CERT "-----BEGIN CERTIFICATE-----" -#define END_CERT "\n-----END CERTIFICATE-----" +#define MAX_CAFILE_SIZE (1024 * 1024) /* 1 MiB */ +#define BEGIN_CERT "-----BEGIN CERTIFICATE-----" +#define END_CERT "\n-----END CERTIFICATE-----" struct cert_chain_engine_config_win8 { DWORD cbSize; @@ -113,7 +92,6 @@ struct cert_chain_engine_config_win7 { HCERTSTORE hExclusiveTrustedPeople; }; -#ifndef UNDER_CE static int is_cr_or_lf(char c) { return c == '\r' || c == '\n'; @@ -179,11 +157,12 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, const CERT_CONTEXT *cert_context = NULL; BOOL add_cert_result = FALSE; DWORD actual_content_type = 0; - DWORD cert_size = (DWORD) - ((end_cert_ptr + end_cert_len) - begin_cert_ptr); + DWORD cert_size = + (DWORD)((end_cert_ptr + end_cert_len) - begin_cert_ptr); cert_blob.pbData = (BYTE *)CURL_UNCONST(begin_cert_ptr); cert_blob.cbData = cert_size; + /* Caution: CryptQueryObject() is deprecated */ if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &cert_blob, CERT_QUERY_CONTENT_FLAG_CERT, @@ -222,7 +201,6 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, cert_context, CERT_STORE_ADD_ALWAYS, NULL); - CertFreeCertificateContext(cert_context); if(!add_cert_result) { char buffer[WINAPI_ERROR_LEN]; failf(data, @@ -238,6 +216,21 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, num_certs++; } } + + switch(actual_content_type) { + case CERT_QUERY_CONTENT_CERT: + case CERT_QUERY_CONTENT_SERIALIZED_CERT: + CertFreeCertificateContext(cert_context); + break; + case CERT_QUERY_CONTENT_CRL: + case CERT_QUERY_CONTENT_SERIALIZED_CRL: + CertFreeCRLContext((PCCRL_CONTEXT)cert_context); + break; + case CERT_QUERY_CONTENT_CTL: + case CERT_QUERY_CONTENT_SERIALIZED_CTL: + CertFreeCTLContext((PCCTL_CONTEXT)cert_context); + break; + } } } } @@ -245,13 +238,11 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, if(result == CURLE_OK) { if(!num_certs) { - infof(data, - "schannel: did not add any certificates from CA file '%s'", + infof(data, "schannel: did not add any certificates from CA file '%s'", ca_file_text); } else { - infof(data, - "schannel: added %d certificate(s) from CA file '%s'", + infof(data, "schannel: added %d certificate(s) from CA file '%s'", num_certs, ca_file_text); } } @@ -263,41 +254,27 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, struct Curl_easy *data) { CURLcode result; - HANDLE ca_file_handle = INVALID_HANDLE_VALUE; + HANDLE ca_file_handle; LARGE_INTEGER file_size; char *ca_file_buffer = NULL; - TCHAR *ca_file_tstr = NULL; size_t ca_file_bufsize = 0; DWORD total_bytes_read = 0; - ca_file_tstr = curlx_convert_UTF8_to_tchar(ca_file); - if(!ca_file_tstr) { - char buffer[WINAPI_ERROR_LEN]; - failf(data, - "schannel: invalid path name for CA file '%s': %s", - ca_file, - curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - goto cleanup; - } - /* * Read the CA file completely into memory before parsing it. This - * optimizes for the common case where the CA file will be relatively + * optimizes for the common case where the CA file is relatively * small ( < 1 MiB ). */ - ca_file_handle = CreateFile(ca_file_tstr, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); + ca_file_handle = curlx_CreateFile(ca_file, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); if(ca_file_handle == INVALID_HANDLE_VALUE) { char buffer[WINAPI_ERROR_LEN]; - failf(data, - "schannel: failed to open CA file '%s': %s", - ca_file, + failf(data, "schannel: failed to open CA file '%s': %s", ca_file, curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; @@ -305,8 +282,7 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, if(!GetFileSizeEx(ca_file_handle, &file_size)) { char buffer[WINAPI_ERROR_LEN]; - failf(data, - "schannel: failed to determine size of CA file '%s': %s", + failf(data, "schannel: failed to determine size of CA file '%s': %s", ca_file, curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; @@ -314,15 +290,14 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, } if(file_size.QuadPart > MAX_CAFILE_SIZE) { - failf(data, - "schannel: CA file exceeds max size of %u bytes", + failf(data, "schannel: CA file exceeds max size of %u bytes", MAX_CAFILE_SIZE); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } ca_file_bufsize = (size_t)file_size.QuadPart; - ca_file_buffer = (char *)malloc(ca_file_bufsize + 1); + ca_file_buffer = (char *)curlx_malloc(ca_file_bufsize + 1); if(!ca_file_buffer) { result = CURLE_OUT_OF_MEMORY; goto cleanup; @@ -335,9 +310,7 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, if(!ReadFile(ca_file_handle, ca_file_buffer + total_bytes_read, bytes_to_read, &bytes_read, NULL)) { char buffer[WINAPI_ERROR_LEN]; - failf(data, - "schannel: failed to read from CA file '%s': %s", - ca_file, + failf(data, "schannel: failed to read from CA file '%s': %s", ca_file, curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; @@ -363,8 +336,7 @@ cleanup: if(ca_file_handle != INVALID_HANDLE_VALUE) { CloseHandle(ca_file_handle); } - Curl_safefree(ca_file_buffer); - curlx_unicodefree(ca_file_tstr); + curlx_safefree(ca_file_buffer); return result; } @@ -388,11 +360,13 @@ static DWORD cert_get_name_string(struct Curl_easy *data, LPTSTR current_pos = NULL; DWORD i; -/* Offered by mingw-w64 v4+. MS SDK ~10+/~VS2017+. */ -#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */ if(Win8_compat) { - /* CertGetNameString will provide the 8-bit character string without +/* Offered by mingw-w64 v4+. MS SDK ~10+/~VS2017+. */ +#ifndef CERT_NAME_SEARCH_ALL_NAMES_FLAG +#define CERT_NAME_SEARCH_ALL_NAMES_FLAG 0x2 +#endif + /* CertGetNameString provides the 8-bit character string without * any decoding */ DWORD name_flags = CERT_NAME_DISABLE_IE4_UTF8_FLAG | CERT_NAME_SEARCH_ALL_NAMES_FLAG; @@ -404,10 +378,6 @@ static DWORD cert_get_name_string(struct Curl_easy *data, length); return actual_length; } -#else - (void)cert_context; - (void)Win8_compat; -#endif if(!alt_name_info) return 0; @@ -462,32 +432,29 @@ static DWORD cert_get_name_string(struct Curl_easy *data, } /* -* Returns TRUE if the hostname is a numeric IPv4/IPv6 Address, -* and populates the buffer with IPv4/IPv6 info. -*/ + * Returns TRUE if the hostname is a numeric IPv4/IPv6 Address, + * and populates the buffer with IPv4/IPv6 info. + */ -static bool get_num_host_info(struct num_ip_data *ip_blob, - LPCSTR hostname) +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; - int res = curlx_inet_pton(AF_INET, hostname, &ia); if(res) { ip_blob->size = sizeof(struct in_addr); memcpy(&ip_blob->bData.ia, &ia, sizeof(struct in_addr)); - result = TRUE; + return TRUE; } else { + struct in6_addr ia6; res = curlx_inet_pton(AF_INET6, hostname, &ia6); if(res) { ip_blob->size = sizeof(struct in6_addr); memcpy(&ip_blob->bData.ia6, &ia6, sizeof(struct in6_addr)); - result = TRUE; + return TRUE; } } - return result; + return FALSE; } static bool get_alt_name_info(struct Curl_easy *data, @@ -495,20 +462,19 @@ static bool get_alt_name_info(struct Curl_easy *data, PCERT_ALT_NAME_INFO *alt_name_info, LPDWORD alt_name_info_size) { - bool result = FALSE; PCERT_INFO cert_info = NULL; PCERT_EXTENSION extension = NULL; CRYPT_DECODE_PARA decode_para = { sizeof(CRYPT_DECODE_PARA), NULL, NULL }; if(!ctx) { failf(data, "schannel: Null certificate context."); - return result; + return FALSE; } cert_info = ctx->pCertInfo; if(!cert_info) { failf(data, "schannel: Null certificate info."); - return result; + return FALSE; } extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2, @@ -516,7 +482,7 @@ static bool get_alt_name_info(struct Curl_easy *data, cert_info->rgExtension); if(!extension) { failf(data, "schannel: CertFindExtension() returned no extension."); - return result; + return FALSE; } if(!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, @@ -527,75 +493,19 @@ static bool get_alt_name_info(struct Curl_easy *data, &decode_para, alt_name_info, alt_name_info_size)) { - failf(data, - "schannel: CryptDecodeObjectEx() returned no alternate name " + failf(data, "schannel: CryptDecodeObjectEx() returned no alternate name " "information."); - return result; + return FALSE; } - result = TRUE; - return result; + return TRUE; } -#endif /* !UNDER_CE */ /* Verify the server's hostname */ -CURLcode Curl_verify_host(struct Curl_cfilter *cf, - struct Curl_easy *data) +CURLcode Curl_verify_host(struct Curl_cfilter *cf, struct Curl_easy *data) { CURLcode result = CURLE_PEER_FAILED_VERIFICATION; struct ssl_connect_data *connssl = cf->ctx; CERT_CONTEXT *pCertContextServer = NULL; -#ifdef UNDER_CE - TCHAR cert_hostname_buff[256]; - DWORD len; - - /* This code does not support certificates with multiple alternative names. - * Right now we are only asking for the first preferred alternative name. - * Instead we would need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG - * (If Windows CE supports that?) and run this section in a loop for each. - * https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx - * curl: (51) schannel: CertGetNameString() certificate hostname - * (.google.com) did not match connection (google.com) - */ - len = CertGetNameString(pCertContextServer, - CERT_NAME_DNS_TYPE, - CERT_NAME_DISABLE_IE4_UTF8_FLAG, - NULL, - cert_hostname_buff, - 256); - if(len > 0) { - /* Comparing the cert name and the connection hostname encoded as UTF-8 - * is acceptable since both values are assumed to use ASCII - * (or some equivalent) encoding - */ - char *cert_hostname = curlx_convert_tchar_to_UTF8(cert_hostname_buff); - if(!cert_hostname) { - result = CURLE_OUT_OF_MEMORY; - } - else{ - const char *conn_hostname = connssl->peer.hostname; - if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname), - conn_hostname, strlen(conn_hostname))) { - infof(data, - "schannel: connection hostname (%s) validated " - "against certificate name (%s)\n", - conn_hostname, cert_hostname); - result = CURLE_OK; - } - else{ - failf(data, - "schannel: connection hostname (%s) " - "does not match certificate name (%s)", - conn_hostname, cert_hostname); - } - Curl_safefree(cert_hostname); - } - } - else { - failf(data, - "schannel: CertGetNameString did not provide any " - "certificate name information"); - } -#else SECURITY_STATUS sspi_status; TCHAR *cert_hostname_buff = NULL; size_t cert_hostname_buff_index = 0; @@ -612,8 +522,8 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, sspi_status = Curl_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, - &pCertContextServer); + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + &pCertContextServer); if((sspi_status != SEC_E_OK) || !pCertContextServer) { char buffer[WINAPI_ERROR_LEN]; @@ -659,22 +569,23 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, goto cleanup; } - /* CertGetNameString guarantees that the returned name will not contain + /* CertGetNameString guarantees that the returned name does not contain * embedded null bytes. This appears to be undocumented behavior. */ - cert_hostname_buff = (LPTSTR)malloc(len * sizeof(TCHAR)); + cert_hostname_buff = (LPTSTR)curlx_malloc(len * sizeof(TCHAR)); if(!cert_hostname_buff) { result = CURLE_OUT_OF_MEMORY; goto cleanup; } actual_len = cert_get_name_string(data, pCertContextServer, - (LPTSTR)cert_hostname_buff, len, alt_name_info, Win8_compat); + (LPTSTR)cert_hostname_buff, len, + alt_name_info, Win8_compat); /* Sanity check */ if(actual_len != len) { failf(data, - "schannel: CertGetNameString() returned certificate " - "name information of unexpected size"); + "schannel: CertGetNameString() returned certificate " + "name information of unexpected size"); goto cleanup; } @@ -685,7 +596,6 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, while(cert_hostname_buff_index < len && cert_hostname_buff[cert_hostname_buff_index] != TEXT('\0') && result == CURLE_PEER_FAILED_VERIFICATION) { - char *cert_hostname; /* Comparing the cert name and the connection hostname encoded as UTF-8 @@ -693,15 +603,14 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, * (or some equivalent) encoding */ cert_hostname = curlx_convert_tchar_to_UTF8( - &cert_hostname_buff[cert_hostname_buff_index]); + &cert_hostname_buff[cert_hostname_buff_index]); if(!cert_hostname) { result = CURLE_OUT_OF_MEMORY; } else { if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname), conn_hostname, hostlen)) { - infof(data, - "schannel: connection hostname (%s) validated " + infof(data, "schannel: connection hostname (%s) validated " "against certificate name (%s)", conn_hostname, cert_hostname); result = CURLE_OK; @@ -722,7 +631,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, result = CURLE_PEER_FAILED_VERIFICATION; } - curlx_unicodefree(cert_hostname); + curlx_free(cert_hostname); } } @@ -737,11 +646,11 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, } cleanup: - Curl_safefree(cert_hostname_buff); + LocalFree(alt_name_info); + curlx_safefree(cert_hostname_buff); if(pCertContextServer) CertFreeCertificateContext(pCertContextServer); -#endif /* !UNDER_CE */ return result; } @@ -758,10 +667,8 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, CERT_CONTEXT *pCertContextServer = NULL; const CERT_CHAIN_CONTEXT *pChainContext = NULL; HCERTCHAINENGINE cert_chain_engine = NULL; -#ifndef UNDER_CE HCERTSTORE trust_store = NULL; HCERTSTORE own_trust_store = NULL; -#endif /* !UNDER_CE */ DEBUGASSERT(BACKEND); @@ -777,7 +684,6 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, result = CURLE_PEER_FAILED_VERIFICATION; } -#ifndef UNDER_CE if(result == CURLE_OK && (conn_config->CAfile || conn_config->ca_info_blob) && BACKEND->use_manual_cred_validation) { @@ -854,7 +760,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, else engine_config.cbSize = sizeof(struct cert_chain_engine_config_win7); - /* CertCreateCertificateChainEngine will check the expected size of the + /* CertCreateCertificateChainEngine checks the expected size of the * CERT_CHAIN_ENGINE_CONFIG structure and fail if the specified size * does not match the expected size. When this occurs, it indicates that * CAINFO is not supported on the version of Windows in use. @@ -871,7 +777,6 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, } } } -#endif /* !UNDER_CE */ if(result == CURLE_OK) { CERT_CHAIN_PARA ChainPara; @@ -889,7 +794,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, NULL, &pChainContext)) { char buffer[WINAPI_ERROR_LEN]; - failf(data, "schannel: CertGetCertificateChain failed: %s", + failf(data, "schannel: failed to get the certificate chain: %s", curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); pChainContext = NULL; result = CURLE_PEER_FAILED_VERIFICATION; @@ -905,28 +810,25 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, * list URL, or when the list could not be downloaded because the * server is currently unreachable. */ dwTrustErrorMask &= ~(DWORD)(CERT_TRUST_REVOCATION_STATUS_UNKNOWN | - CERT_TRUST_IS_OFFLINE_REVOCATION); + CERT_TRUST_IS_OFFLINE_REVOCATION); } if(dwTrustErrorMask) { if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_REVOKED"); + failf(data, "schannel: trust for this certificate or one of " + "the certificates in the certificate chain has been revoked"); else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_PARTIAL_CHAIN"); + failf(data, "schannel: the certificate chain is incomplete"); else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_UNTRUSTED_ROOT"); + failf(data, "schannel: the certificate or certificate chain is " + "based on an untrusted root"); else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_NOT_TIME_VALID"); + failf(data, "schannel: this certificate or one of the certificates " + "in the certificate chain is not time valid"); else if(dwTrustErrorMask & CERT_TRUST_REVOCATION_STATUS_UNKNOWN) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_REVOCATION_STATUS_UNKNOWN"); + failf(data, "schannel: the revocation status is unknown"); else - failf(data, "schannel: CertGetCertificateChain error mask: 0x%08lx", - dwTrustErrorMask); + failf(data, "schannel: error 0x%08lx", dwTrustErrorMask); result = CURLE_PEER_FAILED_VERIFICATION; } } @@ -938,7 +840,6 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, } } -#ifndef UNDER_CE if(cert_chain_engine) { CertFreeCertificateChainEngine(cert_chain_engine); } @@ -946,7 +847,6 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, if(own_trust_store) { CertCloseStore(own_trust_store, 0); } -#endif /* !UNDER_CE */ if(pChainContext) CertFreeCertificateChain(pChainContext); diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index f17f9142be..bed3a13ec8 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -38,57 +38,50 @@ 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 -#ifdef HAVE_FCNTL_H -#include + +#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 #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 "../sendf.h" -#include "../strcase.h" -#include "../url.h" -#include "../progress.h" -#include "../share.h" -#include "../multiif.h" -#include "../curlx/timeval.h" -#include "../curl_md5.h" -#include "../curl_sha256.h" -#include "../curlx/warnless.h" -#include "../curlx/base64.h" -#include "../curl_printf.h" -#include "../curlx/inet_pton.h" -#include "../connect.h" -#include "../select.h" -#include "../strdup.h" -#include "../rand.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" - #define CLONE_STRING(var) \ do { \ if(source->var) { \ - dest->var = strdup(source->var); \ + dest->var = curlx_strdup(source->var); \ if(!dest->var) \ return FALSE; \ } \ @@ -96,21 +89,20 @@ dest->var = NULL; \ } while(0) -#define CLONE_BLOB(var) \ - do { \ - if(blobdup(&dest->var, source->var)) \ - return FALSE; \ +#define CLONE_BLOB(var) \ + do { \ + if(blobdup(&dest->var, source->var)) \ + return FALSE; \ } while(0) -static CURLcode blobdup(struct curl_blob **dest, - struct curl_blob *src) +static CURLcode blobdup(struct curl_blob **dest, struct curl_blob *src) { DEBUGASSERT(dest); DEBUGASSERT(!*dest); if(src) { /* only if there is data to dupe! */ struct curl_blob *d; - d = malloc(sizeof(struct curl_blob) + src->len); + d = curlx_malloc(sizeof(struct curl_blob) + src->len); if(!d) return CURLE_OUT_OF_MEMORY; d->len = src->len; @@ -141,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 @@ -149,31 +143,39 @@ static const struct alpn_spec ALPN_SPEC_H2 = { static const struct alpn_spec ALPN_SPEC_H2_H11 = { { ALPN_H2, ALPN_HTTP_1_1 }, 2 }; -#endif +static const struct alpn_spec ALPN_SPEC_H11_H2 = { + { ALPN_HTTP_1_1, ALPN_H2 }, 2 +}; +#endif /* USE_HTTP2 */ -#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_PROXY) -static const struct alpn_spec * -alpn_get_spec(http_majors allowed, bool use_alpn) +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(allowed & CURL_HTTP_V2x) { - if(allowed & CURL_HTTP_V1x) - return &ALPN_SPEC_H2_H11; + if(wanted & CURL_HTTP_V2x) { + if(wanted & CURL_HTTP_V1x) + return (preferred == CURL_HTTP_V1x) ? + &ALPN_SPEC_H11_H2 : &ALPN_SPEC_H2_H11; return &ALPN_SPEC_H2; } #else - (void)allowed; + (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 */ #endif /* USE_SSL */ - void Curl_ssl_easy_config_init(struct Curl_easy *data) { /* @@ -188,10 +190,9 @@ void Curl_ssl_easy_config_init(struct Curl_easy *data) #endif } -static bool -match_ssl_primary_config(struct Curl_easy *data, - struct ssl_primary_config *c1, - struct ssl_primary_config *c2) +static bool match_ssl_primary_config(struct Curl_easy *data, + struct ssl_primary_config *c1, + struct ssl_primary_config *c2) { (void)data; if((c1->version == c2->version) && @@ -271,83 +272,120 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, static void free_primary_ssl_config(struct ssl_primary_config *sslc) { - Curl_safefree(sslc->CApath); - Curl_safefree(sslc->CAfile); - Curl_safefree(sslc->issuercert); - Curl_safefree(sslc->clientcert); - Curl_safefree(sslc->cipher_list); - Curl_safefree(sslc->cipher_list13); - Curl_safefree(sslc->pinned_key); - Curl_safefree(sslc->cert_blob); - Curl_safefree(sslc->ca_info_blob); - Curl_safefree(sslc->issuercert_blob); - Curl_safefree(sslc->curves); - Curl_safefree(sslc->signature_algorithms); - Curl_safefree(sslc->CRLfile); + curlx_safefree(sslc->CApath); + curlx_safefree(sslc->CAfile); + curlx_safefree(sslc->issuercert); + curlx_safefree(sslc->clientcert); + curlx_safefree(sslc->cipher_list); + curlx_safefree(sslc->cipher_list13); + curlx_safefree(sslc->pinned_key); + curlx_safefree(sslc->cert_blob); + curlx_safefree(sslc->ca_info_blob); + curlx_safefree(sslc->issuercert_blob); + curlx_safefree(sslc->curves); + curlx_safefree(sslc->signature_algorithms); + curlx_safefree(sslc->CRLfile); #ifdef USE_TLS_SRP - Curl_safefree(sslc->username); - Curl_safefree(sslc->password); + curlx_safefree(sslc->username); + curlx_safefree(sslc->password); #endif } CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) { - data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH]; - data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE]; - data->set.ssl.primary.CRLfile = data->set.str[STRING_SSL_CRLFILE]; - data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; - data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; - data->set.ssl.primary.cipher_list = - data->set.str[STRING_SSL_CIPHER_LIST]; - data->set.ssl.primary.cipher_list13 = - data->set.str[STRING_SSL_CIPHER13_LIST]; - data->set.ssl.primary.signature_algorithms = - data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS]; - data->set.ssl.primary.pinned_key = - data->set.str[STRING_SSL_PINNEDPUBLICKEY]; - data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT]; - data->set.ssl.primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; - data->set.ssl.primary.curves = data->set.str[STRING_SSL_EC_CURVES]; -#ifdef USE_TLS_SRP - data->set.ssl.primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; - data->set.ssl.primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; + struct ssl_config_data *sslc = &data->set.ssl; +#if defined(CURL_CA_PATH) || defined(CURL_CA_BUNDLE) + struct UserDefined *set = &data->set; + CURLcode result; #endif - data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE]; - data->set.ssl.key = data->set.str[STRING_KEY]; - data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE]; - data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD]; - data->set.ssl.primary.clientcert = data->set.str[STRING_CERT]; - data->set.ssl.key_blob = data->set.blobs[BLOB_KEY]; + + if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { +#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 +#ifdef CURL_CA_PATH + if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH]) { + result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH); + if(result) + return result; + } +#endif +#ifdef CURL_CA_BUNDLE + if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE]) { + result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE); + if(result) + return result; + } +#endif + } + sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE]; + sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE]; + sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH]; + sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; + sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; + sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST]; + sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST]; + sslc->primary.signature_algorithms = + data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS]; + sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + sslc->primary.cert_blob = data->set.blobs[BLOB_CERT]; + sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO]; + sslc->primary.curves = data->set.str[STRING_SSL_EC_CURVES]; +#ifdef USE_TLS_SRP + sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; + sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; +#endif + sslc->cert_type = data->set.str[STRING_CERT_TYPE]; + sslc->key = data->set.str[STRING_KEY]; + sslc->key_type = data->set.str[STRING_KEY_TYPE]; + sslc->key_passwd = data->set.str[STRING_KEY_PASSWD]; + sslc->primary.clientcert = data->set.str[STRING_CERT]; + sslc->key_blob = data->set.blobs[BLOB_KEY]; #ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; - data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; - data->set.proxy_ssl.primary.cipher_list = - data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; - data->set.proxy_ssl.primary.cipher_list13 = - data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; - data->set.proxy_ssl.primary.pinned_key = - data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; - data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; - data->set.proxy_ssl.primary.ca_info_blob = - data->set.blobs[BLOB_CAINFO_PROXY]; - data->set.proxy_ssl.primary.issuercert = - data->set.str[STRING_SSL_ISSUERCERT_PROXY]; - data->set.proxy_ssl.primary.issuercert_blob = - data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; - data->set.proxy_ssl.primary.CRLfile = - data->set.str[STRING_SSL_CRLFILE_PROXY]; - data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; - data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY]; - data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; - data->set.proxy_ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; - data->set.proxy_ssl.primary.clientcert = data->set.str[STRING_CERT_PROXY]; - data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY]; + sslc = &data->set.proxy_ssl; + if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { +#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 +#ifdef CURL_CA_PATH + if(!sslc->custom_capath && !set->str[STRING_SSL_CAPATH_PROXY]) { + result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], + CURL_CA_PATH); + if(result) + return result; + } +#endif +#ifdef CURL_CA_BUNDLE + if(!sslc->custom_cafile && !set->str[STRING_SSL_CAFILE_PROXY]) { + result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY], + CURL_CA_BUNDLE); + if(result) + return result; + } +#endif + } + sslc->primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; + sslc->primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; + sslc->primary.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; + sslc->primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; + sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; + sslc->primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY]; + sslc->primary.ca_info_blob = data->set.blobs[BLOB_CAINFO_PROXY]; + sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY]; + sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY]; + sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY]; + sslc->cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; + sslc->key = data->set.str[STRING_KEY_PROXY]; + sslc->key_type = data->set.str[STRING_KEY_TYPE_PROXY]; + sslc->key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; + sslc->primary.clientcert = data->set.str[STRING_CERT_PROXY]; + sslc->key_blob = data->set.blobs[BLOB_KEY_PROXY]; #ifdef USE_TLS_SRP - data->set.proxy_ssl.primary.username = - data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; - data->set.proxy_ssl.primary.password = - data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; + sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; + sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; #endif #endif /* CURL_DISABLE_PROXY */ @@ -464,16 +502,16 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, struct ssl_connect_data *ctx; (void)data; - ctx = calloc(1, sizeof(*ctx)); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) return NULL; ctx->ssl_impl = Curl_ssl; ctx->alpn = alpn; Curl_bufq_init2(&ctx->earlydata, CURL_SSL_EARLY_MAX, 1, BUFQ_OPT_NO_SPARES); - ctx->backend = calloc(1, ctx->ssl_impl->sizeof_ssl_backend_data); + ctx->backend = curlx_calloc(1, ctx->ssl_impl->sizeof_ssl_backend_data); if(!ctx->backend) { - free(ctx); + curlx_free(ctx); return NULL; } return ctx; @@ -482,10 +520,10 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, static void cf_ctx_free(struct ssl_connect_data *ctx) { if(ctx) { - Curl_safefree(ctx->negotiated.alpn); + curlx_safefree(ctx->negotiated.alpn); Curl_bufq_free(&ctx->earlydata); - free(ctx->backend); - free(ctx); + curlx_free(ctx->backend); + curlx_free(ctx); } } @@ -503,9 +541,9 @@ void Curl_ssl_close_all(struct Curl_easy *data) Curl_ssl->close_all(data); } -CURLcode Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +CURLcode Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) { struct ssl_connect_data *connssl = cf->ctx; @@ -578,7 +616,7 @@ void Curl_ssl_free_certinfo(struct Curl_easy *data) ci->certinfo[i] = NULL; } - free(ci->certinfo); /* free the actual array too */ + curlx_free(ci->certinfo); /* free the actual array too */ ci->certinfo = NULL; ci->num_of_certs = 0; } @@ -593,7 +631,7 @@ CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num) Curl_ssl_free_certinfo(data); /* Allocate the required certificate information structures */ - table = calloc((size_t) num, sizeof(struct curl_slist *)); + table = curlx_calloc((size_t)num, sizeof(struct curl_slist *)); if(!table) return CURLE_OUT_OF_MEMORY; @@ -626,8 +664,7 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, curlx_dyn_addn(&build, value, valuelen)) return CURLE_OUT_OF_MEMORY; - nl = Curl_slist_append_nodup(ci->certinfo[certnum], - curlx_dyn_ptr(&build)); + nl = Curl_slist_append_nodup(ci->certinfo[certnum], curlx_dyn_ptr(&build)); if(!nl) { curlx_dyn_free(&build); curl_slist_free_all(ci->certinfo[certnum]); @@ -640,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; } @@ -657,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; @@ -720,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) @@ -733,8 +766,9 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, /* only do this if pinnedpubkey starts with "sha256//", length 8 */ if(!strncmp(pinnedpubkey, "sha256//", 8)) { CURLcode encode; - size_t encodedlen = 0; - char *encoded = NULL, *pinkeycopy, *begin_pos, *end_pos; + char *cert_hash = NULL; + const char *pinned_hash, *end_pos; + size_t cert_hash_len = 0, pinned_hash_len; unsigned char *sha256sumdigest; if(!Curl_ssl->sha256sum) { @@ -743,58 +777,45 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, } /* compute sha256sum of public key */ - sha256sumdigest = malloc(CURL_SHA256_DIGEST_LENGTH); + sha256sumdigest = curlx_malloc(CURL_SHA256_DIGEST_LENGTH); if(!sha256sumdigest) return CURLE_OUT_OF_MEMORY; encode = Curl_ssl->sha256sum(pubkey, pubkeylen, sha256sumdigest, CURL_SHA256_DIGEST_LENGTH); if(!encode) - encode = curlx_base64_encode((char *)sha256sumdigest, - CURL_SHA256_DIGEST_LENGTH, &encoded, - &encodedlen); - Curl_safefree(sha256sumdigest); + encode = curlx_base64_encode(sha256sumdigest, + CURL_SHA256_DIGEST_LENGTH, + &cert_hash, &cert_hash_len); + curlx_safefree(sha256sumdigest); if(encode) return encode; - infof(data, " public key hash: sha256//%s", encoded); + infof(data, " public key hash: sha256//%s", cert_hash); - /* it starts with sha256//, copy so we can modify it */ - pinkeycopy = strdup(pinnedpubkey); - if(!pinkeycopy) { - Curl_safefree(encoded); - return CURLE_OUT_OF_MEMORY; - } - /* point begin_pos to the copy, and start extracting keys */ - begin_pos = pinkeycopy; - do { - end_pos = strstr(begin_pos, ";sha256//"); - /* - * if there is an end_pos, null-terminate, otherwise it will go to the - * end of the original string - */ - if(end_pos) - end_pos[0] = '\0'; + pinned_hash = pinnedpubkey; + while(pinned_hash && + !strncmp(pinned_hash, "sha256//", (sizeof("sha256//") - 1))) { + pinned_hash = pinned_hash + (sizeof("sha256//") - 1); + end_pos = strchr(pinned_hash, ';'); + pinned_hash_len = end_pos ? + (size_t)(end_pos - pinned_hash) : strlen(pinned_hash); - /* compare base64 sha256 digests, 8 is the length of "sha256//" */ - if(encodedlen == strlen(begin_pos + 8) && - !memcmp(encoded, begin_pos + 8, encodedlen)) { + /* compare base64 sha256 digests" */ + if(cert_hash_len == pinned_hash_len && + !memcmp(cert_hash, pinned_hash, cert_hash_len)) { + DEBUGF(infof(data, "public key hash matches pinned value")); result = CURLE_OK; break; } - /* - * change back the null-terminator we changed earlier, - * and look for next begin - */ - if(end_pos) { - end_pos[0] = ';'; - begin_pos = strstr(end_pos, "sha256//"); - } - } while(end_pos && begin_pos); - Curl_safefree(encoded); - Curl_safefree(pinkeycopy); + DEBUGF(infof(data, "public key hash does not match 'sha256//%.*s'", + (int)pinned_hash_len, pinned_hash)); + /* next one or we are at the end */ + pinned_hash = end_pos ? (end_pos + 1) : NULL; + } + curlx_safefree(cert_hash); } else { long filesize; @@ -803,7 +824,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, struct dynbuf buf; char unsigned *pem_ptr = NULL; size_t left; - FILE *fp = fopen(pinnedpubkey, "rb"); + FILE *fp = curlx_fopen(pinnedpubkey, "rb"); if(!fp) return result; @@ -822,7 +843,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, * if the size of our certificate is bigger than the file * size then it cannot match */ - size = curlx_sotouz((curl_off_t) filesize); + size = curlx_sotouz((curl_off_t)filesize); if(pubkeylen > size) goto end; @@ -848,7 +869,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, } /* - * Otherwise we will assume it is PEM and try to decode it after placing + * Otherwise we assume it is PEM and try to decode it after placing * null-terminator */ pem_read = pubkey_pem_to_der(curlx_dyn_ptr(&buf), &pem_ptr, &pem_len); @@ -864,8 +885,8 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, result = CURLE_OK; end: curlx_dyn_free(&buf); - Curl_safefree(pem_ptr); - fclose(fp); + curlx_safefree(pem_ptr); + curlx_fclose(fp); } return result; @@ -1041,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; @@ -1053,8 +1074,8 @@ static size_t multissl_version(char *buffer, size_t size) bool paren = (selected != available_backends[i]); if(available_backends[i]->version(vb, sizeof(vb))) { - p += msnprintf(p, end - p, "%s%s%s%s", (p != backends ? " " : ""), - (paren ? "(" : ""), vb, (paren ? ")" : "")); + p += curl_msnprintf(p, end - p, "%s%s%s%s", (p != backends ? " " : ""), + (paren ? "(" : ""), vb, (paren ? ")" : "")); } } @@ -1062,10 +1083,7 @@ static size_t multissl_version(char *buffer, size_t size) } if(size) { - if(backends_len < size) - strcpy(buffer, backends); - else - *buffer = 0; /* did not fit */ + curlx_strcopy(buffer, size, backends, backends_len); } return 0; } @@ -1091,7 +1109,7 @@ static int multissl_setup(const struct Curl_ssl *backend) for(i = 0; available_backends[i]; i++) { if(curl_strequal(env, available_backends[i]->info.name)) { Curl_ssl = available_backends[i]; - free(env); + curlx_free(env); return 0; } } @@ -1102,7 +1120,7 @@ static int multissl_setup(const struct Curl_ssl *backend) if(curl_strequal(CURL_DEFAULT_SSL_BACKEND, available_backends[i]->info.name)) { Curl_ssl = available_backends[i]; - free(env); + curlx_free(env); return 0; } } @@ -1110,7 +1128,7 @@ static int multissl_setup(const struct Curl_ssl *backend) /* Fall back to first available backend */ Curl_ssl = available_backends[0]; - free(env); + curlx_free(env); return 0; } @@ -1161,12 +1179,12 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, void Curl_ssl_peer_cleanup(struct ssl_peer *peer) { - Curl_safefree(peer->sni); + curlx_safefree(peer->sni); if(peer->dispname != peer->hostname) - free(peer->dispname); + curlx_free(peer->dispname); peer->dispname = NULL; - Curl_safefree(peer->hostname); - Curl_safefree(peer->scache_key); + curlx_safefree(peer->hostname); + curlx_safefree(peer->scache_key); peer->type = CURL_SSL_PEER_DNS; } @@ -1203,7 +1221,7 @@ static ssl_peer_type get_peer_type(const char *hostname) CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf, const char *tls_id, - int transport) + uint8_t transport) { const char *ehostname, *edispname; CURLcode result = CURLE_OUT_OF_MEMORY; @@ -1230,7 +1248,7 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, { ehostname = cf->conn->host.name; edispname = cf->conn->host.dispname; - peer->port = cf->conn->remote_port; + peer->port = (uint16_t)cf->conn->remote_port; } /* hostname MUST exist and not be empty */ @@ -1239,13 +1257,13 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, goto out; } - peer->hostname = strdup(ehostname); + peer->hostname = curlx_strdup(ehostname); if(!peer->hostname) goto out; if(!edispname || !strcmp(ehostname, edispname)) peer->dispname = peer->hostname; else { - peer->dispname = strdup(edispname); + peer->dispname = curlx_strdup(edispname); if(!peer->dispname) goto out; } @@ -1254,10 +1272,10 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, /* not an IP address, normalize according to RCC 6066 ch. 3, * max len of SNI is 2^16-1, no trailing dot */ size_t len = strlen(peer->hostname); - if(len && (peer->hostname[len-1] == '.')) + if(len && (peer->hostname[len - 1] == '.')) len--; if(len < USHRT_MAX) { - peer->sni = calloc(1, len + 1); + peer->sni = curlx_calloc(1, len + 1); if(!peer->sni) goto out; Curl_strntolower(peer->sni, peer->hostname, len); @@ -1325,6 +1343,15 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, DEBUGASSERT(connssl); *done = FALSE; + + if(!connssl->prefs_checked) { + if(!ssl_prefs_check(data)) { + result = CURLE_SSL_CONNECT_ERROR; + goto out; + } + connssl->prefs_checked = TRUE; + } + if(!connssl->peer.hostname) { char tls_id[80]; connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1); @@ -1333,18 +1360,13 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, goto out; } - if(!connssl->prefs_checked) { - if(!ssl_prefs_check(data)) - return CURLE_SSL_CONNECT_ERROR; - connssl->prefs_checked = TRUE; - } - result = connssl->ssl_impl->do_connect(cf, data, done); if(!result && *done) { cf->connected = TRUE; - if(connssl->state == ssl_connection_complete) - connssl->handshake_done = curlx_now(); + if(connssl->state == ssl_connection_complete) { + connssl->handshake_done = *Curl_pgrs_now(data); + } /* Connection can be deferred when sending early data */ DEBUGASSERT(connssl->state == ssl_connection_complete || connssl->state == ssl_connection_deferred); @@ -1393,7 +1415,7 @@ static CURLcode ssl_cf_connect_deferred(struct Curl_cfilter *cf, result = ssl_cf_set_earlydata(cf, data, buf, blen); if(result) return result; - /* we buffered any early data we'd like to send. Actually + /* we buffered any early data we would like to send. Actually * do the connect now which sends it and performs the handshake. */ connssl->earlydata_state = ssl_earlydata_sending; connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata); @@ -1433,21 +1455,21 @@ static bool ssl_cf_data_pending(struct Curl_cfilter *cf, { struct ssl_connect_data *connssl = cf->ctx; struct cf_call_data save; - bool result; + bool pending; CF_DATA_SAVE(save, cf, data); if(connssl->ssl_impl->data_pending && connssl->ssl_impl->data_pending(cf, data)) - result = TRUE; + pending = TRUE; else - result = cf->next->cft->has_data_pending(cf->next, data); + pending = cf->next->cft->has_data_pending(cf->next, data); CF_DATA_RESTORE(cf, save); - return result; + return pending; } static CURLcode ssl_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t blen, + const uint8_t *buf, size_t blen, bool eos, size_t *pnwritten) { struct ssl_connect_data *connssl = cf->ctx; @@ -1479,7 +1501,7 @@ static CURLcode ssl_cf_send(struct Curl_cfilter *cf, } else { *pnwritten = connssl->earlydata_skip; - buf = ((const char *)buf) + connssl->earlydata_skip; + buf = buf + connssl->earlydata_skip; blen -= connssl->earlydata_skip; connssl->earlydata_skip = 0; } @@ -1537,7 +1559,7 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf, *done = TRUE; /* If we have done the SSL handshake, shut down the connection cleanly */ if(cf->connected && (connssl->state == ssl_connection_complete) && - !cf->shutdown && Curl_ssl->shut_down) { + !cf->shutdown && Curl_ssl->shut_down) { struct cf_call_data save; CF_DATA_SAVE(save, cf, data); @@ -1662,7 +1684,7 @@ struct Curl_cftype Curl_cft_ssl = { struct Curl_cftype Curl_cft_ssl_proxy = { "SSL-PROXY", - CF_TYPE_SSL|CF_TYPE_PROXY, + CF_TYPE_SSL | CF_TYPE_PROXY, CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, @@ -1697,7 +1719,9 @@ static CURLcode cf_ssl_create(struct Curl_cfilter **pcf, ctx = cf_ctx_new(data, NULL); #else ctx = cf_ctx_new(data, alpn_get_spec(data->state.http_neg.wanted, - conn->bits.tls_enable_alpn)); + data->state.http_neg.preferred, + (bool)data->state.http_neg.only_10, + (bool)conn->bits.tls_enable_alpn)); #endif if(!ctx) { result = CURLE_OUT_OF_MEMORY; @@ -1748,18 +1772,18 @@ 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; - http_majors allowed = CURL_HTTP_V1x; + bool use_alpn = (bool)data->set.ssl_enable_alpn; + http_majors wanted = CURL_HTTP_V1x; (void)conn; #ifdef USE_HTTP2 if(conn->http_proxy.proxytype == CURLPROXY_HTTPS2) { use_alpn = TRUE; - allowed = (CURL_HTTP_V1x|CURL_HTTP_V2x); + wanted = (CURL_HTTP_V1x | CURL_HTTP_V2x); } #endif - ctx = cf_ctx_new(data, alpn_get_spec(allowed, use_alpn)); + ctx = cf_ctx_new(data, alpn_get_spec(wanted, 0, false, use_alpn)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1811,16 +1835,17 @@ static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf, *done = FALSE; while(!result && !*done && loop--) { - timeout_ms = Curl_shutdown_timeleft(cf->conn, cf->sockindex, NULL); + timeout_ms = Curl_shutdown_timeleft(data, cf->conn, cf->sockindex); if(timeout_ms < 0) { /* no need to continue if time is already up */ failf(data, "SSL shutdown timeout"); - return CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; + goto out; } result = connssl->ssl_impl->shut_down(cf, data, send_shutdown, done); - if(result ||*done) + if(result || *done) goto out; if(connssl->io_need) { @@ -1857,12 +1882,12 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, if(cf->cft == &Curl_cft_ssl) { bool done; CURL_TRC_CF(data, cf, "shutdown and remove SSL, start"); - Curl_shutdown_start(data, sockindex, 0, NULL); + Curl_shutdown_start(data, sockindex, 0); result = vtls_shutdown_blocking(cf, data, send_shutdown, &done); Curl_shutdown_clear(data, sockindex); if(!result && !done) /* blocking failed? */ result = CURLE_SSL_SHUTDOWN_FAILED; - Curl_conn_cf_discard_sub(head, cf, data, FALSE); + Curl_conn_cf_discard(&cf, data); CURL_TRC_CF(data, cf, "shutdown and remove SSL, done -> %d", result); break; } @@ -1909,7 +1934,7 @@ CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf, len = strlen(spec->entries[i]); if(len >= ALPN_NAME_MAX) return CURLE_FAILED_INIT; - blen = (unsigned char)len; + blen = (unsigned char)len; if(off + blen + 1 >= (int)sizeof(buf->data)) return CURLE_FAILED_INIT; buf->data[off++] = blen; @@ -1993,6 +2018,11 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, result = CURLE_SSL_CONNECT_ERROR; goto out; } + else if(!proto) { + DEBUGASSERT(0); /* with length, we need a pointer */ + result = CURLE_SSL_CONNECT_ERROR; + goto out; + } else if((strlen(connssl->negotiated.alpn) != proto_len) || memcmp(connssl->negotiated.alpn, proto, proto_len)) { failf(data, "ALPN: asked for '%s' from previous session, but server " @@ -2010,15 +2040,13 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, if(proto && proto_len) { if(memchr(proto, '\0', proto_len)) { failf(data, "ALPN: server selected protocol contains NUL. " - "Refusing to continue."); + "Refusing to continue."); result = CURLE_SSL_CONNECT_ERROR; goto out; } - connssl->negotiated.alpn = malloc(proto_len + 1); + connssl->negotiated.alpn = curlx_memdup0((const char *)proto, proto_len); if(!connssl->negotiated.alpn) return CURLE_OUT_OF_MEMORY; - memcpy(connssl->negotiated.alpn, proto, proto_len); - connssl->negotiated.alpn[proto_len] = 0; } if(proto && proto_len) { @@ -2038,4 +2066,34 @@ out: 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/lib/vtls/vtls.h b/lib/vtls/vtls.h index c62b8ae258..6db67cf748 100644 --- a/lib/vtls/vtls.h +++ b/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; @@ -32,24 +32,32 @@ struct Curl_cfilter; struct Curl_easy; struct dynbuf; -#define SSLSUPP_CA_PATH (1<<0) /* supports CAPATH */ -#define SSLSUPP_CERTINFO (1<<1) /* supports CURLOPT_CERTINFO */ -#define SSLSUPP_PINNEDPUBKEY (1<<2) /* supports CURLOPT_PINNEDPUBLICKEY */ -#define SSLSUPP_SSL_CTX (1<<3) /* supports CURLOPT_SSL_CTX */ -#define SSLSUPP_HTTPS_PROXY (1<<4) /* supports access via HTTPS proxies */ -#define SSLSUPP_TLS13_CIPHERSUITES (1<<5) /* supports TLS 1.3 ciphersuites */ -#define SSLSUPP_CAINFO_BLOB (1<<6) -#define SSLSUPP_ECH (1<<7) -#define SSLSUPP_CA_CACHE (1<<8) -#define SSLSUPP_CIPHER_LIST (1<<9) /* supports TLS 1.0-1.2 ciphersuites */ -#define SSLSUPP_SIGNATURE_ALGORITHMS (1<<10) /* supports TLS sigalgs */ +#define SSLSUPP_CA_PATH (1 << 0) /* supports CAPATH */ +#define SSLSUPP_CERTINFO (1 << 1) /* supports CURLOPT_CERTINFO */ +#define SSLSUPP_PINNEDPUBKEY (1 << 2) /* supports CURLOPT_PINNEDPUBLICKEY */ +#define SSLSUPP_SSL_CTX (1 << 3) /* supports CURLOPT_SSL_CTX */ +#define SSLSUPP_HTTPS_PROXY (1 << 4) /* supports access via HTTPS proxies */ +#define SSLSUPP_TLS13_CIPHERSUITES (1 << 5) /* supports TLS 1.3 ciphersuites */ +#define SSLSUPP_CAINFO_BLOB (1 << 6) +#define SSLSUPP_ECH (1 << 7) +#define SSLSUPP_CA_CACHE (1 << 8) +#define SSLSUPP_CIPHER_LIST (1 << 9) /* supports TLS 1.0-1.2 ciphersuites */ +#define 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)\ - ) +/* 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_ENABLED(data) \ + ((data)->set.tls_ech && !((data)->set.tls_ech & CURLECH_DISABLE)) #endif /* USE_ECH */ #define ALPN_ACCEPTED "ALPN: server accepted " @@ -88,20 +96,14 @@ struct ssl_peer { char *sni; /* SNI version of hostname or NULL if not usable */ char *scache_key; /* for lookups in session cache */ ssl_peer_type type; /* type of the peer information */ - int port; /* port we are talking to */ - int transport; /* one of TRNSPRT_* defines */ + uint16_t port; /* port we are talking to */ + uint8_t transport; /* one of TRNSPRT_* defines */ }; 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 /* 1MB */ -#endif - -#ifndef CURL_SHA256_DIGEST_LENGTH -#define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */ -#endif +#define MAX_PINNED_PUBKEY_SIZE (1024 * 1024) /* 1 MiB */ curl_sslbackend Curl_ssl_backend(void); @@ -138,7 +140,7 @@ bool Curl_ssl_conn_config_match(struct Curl_easy *data, bool proxy); /* Update certain connection SSL config flags after they have - * been changed on the easy handle. Will work for `verifypeer`, + * been changed on the easy handle. Works for `verifypeer`, * `verifyhost` and `verifystatus`. */ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); @@ -148,7 +150,7 @@ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf, const char *tls_id, - int transport); + uint8_t transport); /** * Free all allocated data and reset peer information. */ @@ -169,6 +171,7 @@ void Curl_ssl_version(char *buffer, size_t size); /* Certificate information list handling. */ #define CURL_X509_STR_MAX 100000 +#define MAX_ALLOWED_CERT_AMOUNT 100 void Curl_ssl_free_certinfo(struct Curl_easy *data); CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num); @@ -181,8 +184,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, @@ -252,20 +255,20 @@ 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 -#define Curl_ssl_set_engine(x,y) CURLE_NOT_BUILT_IN +#define Curl_ssl_set_engine(x, y) CURLE_NOT_BUILT_IN #define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN #define Curl_ssl_engines_list(x) NULL #define Curl_ssl_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 -#define Curl_ssl_cfilter_remove(a,b,c) CURLE_OK -#define Curl_ssl_cf_get_config(a,b) NULL +#define Curl_ssl_supports(a, b) FALSE +#define Curl_ssl_cfilter_add(a, b, c) CURLE_NOT_BUILT_IN +#define Curl_ssl_cfilter_remove(a, b, c) CURLE_OK +#define Curl_ssl_cf_get_config(a, b) NULL #define Curl_ssl_cf_get_primary_config(a) NULL #endif diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index de0b735e22..6700ee74cb 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -23,17 +23,22 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "../curl_setup.h" -#include "../cfilters.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 @@ -71,8 +76,7 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, const unsigned char *proto, size_t proto_len); -bool Curl_alpn_contains_proto(const struct alpn_spec *spec, - const char *proto); +bool Curl_alpn_contains_proto(const struct alpn_spec *spec, const char *proto); /* enum for the nonblocking SSL connection state machine */ typedef enum { @@ -98,12 +102,12 @@ typedef enum { ssl_earlydata_rejected } ssl_earlydata_state; -#define CURL_SSL_IO_NEED_NONE (0) -#define CURL_SSL_IO_NEED_RECV (1<<0) -#define CURL_SSL_IO_NEED_SEND (1<<1) +#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 { @@ -124,17 +128,13 @@ struct ssl_connect_data { ssl_connect_state connecting_state; ssl_earlydata_state earlydata_state; int io_need; /* TLS signals special SEND/RECV needs */ - BIT(use_alpn); /* if ALPN shall be used in handshake */ BIT(peer_closed); /* peer has closed connection */ BIT(prefs_checked); /* SSL preferences have been checked */ BIT(input_pending); /* data for SSL_read() may be available */ }; - #undef CF_CTX_CALL_DATA -#define CF_CTX_CALL_DATA(cf) \ - ((struct ssl_connect_data *)(cf)->ctx)->call_data - +#define CF_CTX_CALL_DATA(cf) ((struct ssl_connect_data *)(cf)->ctx)->call_data /* Definitions for SSL Implementations */ @@ -153,8 +153,11 @@ struct Curl_ssl { size_t (*version)(char *buffer, size_t size); CURLcode (*shut_down)(struct Curl_cfilter *cf, struct Curl_easy *data, bool send_shutdown, bool *done); - bool (*data_pending)(struct Curl_cfilter *cf, - const struct Curl_easy *data); + + /* data_pending() shall return TRUE when it wants to get called again to + drain internal buffers and deliver data instead of waiting for the socket + to get readable */ + bool (*data_pending)(struct Curl_cfilter *cf, const struct Curl_easy *data); /* return 0 if a find random is filled in */ CURLcode (*random)(struct Curl_easy *data, unsigned char *entropy, @@ -177,7 +180,7 @@ struct Curl_ssl { struct curl_slist *(*engines_list)(struct Curl_easy *data); CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen, - unsigned char *sha256sum, size_t sha256sumlen); + unsigned char *sha256sum, size_t sha256sumlen); CURLcode (*recv_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, size_t *pnread); CURLcode (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -185,7 +188,6 @@ struct Curl_ssl { CURLcode (*get_channel_binding)(struct Curl_easy *data, int sockindex, struct dynbuf *binding); - }; extern const struct Curl_ssl *Curl_ssl; @@ -199,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/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 662539cd89..3977046168 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -21,43 +21,30 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include "../curl_setup.h" +#include "curl_setup.h" #ifdef USE_SSL #ifdef HAVE_SYS_TYPES_H #include #endif -#ifdef HAVE_FCNTL_H -#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 "../share.h" -#include "../curl_trc.h" -#include "../curl_sha256.h" -#include "../rand.h" -#include "../curlx/warnless.h" -#include "../curl_printf.h" -#include "../strdup.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" -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.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 { @@ -71,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 */ }; @@ -80,312 +67,16 @@ struct Curl_ssl_scache_peer { #define GOOD_SCACHE(x) ((x) && (x)->magic == CURL_SCACHE_MAGIC) -struct Curl_ssl_scache { - unsigned int magic; - struct Curl_ssl_scache_peer *peers; - size_t peer_count; - int default_lifetime_secs; - long age; -}; - -static struct Curl_ssl_scache *cf_ssl_scache_get(struct Curl_easy *data) -{ - struct Curl_ssl_scache *scache = NULL; - /* If a share is present, its ssl_scache has preference over the multi */ - if(data->share && data->share->ssl_scache) - scache = data->share->ssl_scache; - else if(data->multi && data->multi->ssl_scache) - scache = data->multi->ssl_scache; - if(scache && !GOOD_SCACHE(scache)) { - failf(data, "transfer would use an invalid scache at %p, denied", - (void *)scache); - DEBUGASSERT(0); - return NULL; - } - return scache; -} - -static void cf_ssl_scache_session_ldestroy(void *udata, void *obj) -{ - struct Curl_ssl_session *s = obj; - (void)udata; - free(CURL_UNCONST(s->sdata)); - free(CURL_UNCONST(s->quic_tp)); - free((void *)s->alpn); - free(s); -} - -CURLcode -Curl_ssl_session_create(void *sdata, size_t sdata_len, - int ietf_tls_id, const char *alpn, - curl_off_t valid_until, size_t earlydata_max, - struct Curl_ssl_session **psession) -{ - return Curl_ssl_session_create2(sdata, sdata_len, ietf_tls_id, alpn, - valid_until, earlydata_max, - NULL, 0, psession); -} - -CURLcode -Curl_ssl_session_create2(void *sdata, size_t sdata_len, - int ietf_tls_id, const char *alpn, - curl_off_t valid_until, size_t earlydata_max, - unsigned char *quic_tp, size_t quic_tp_len, - struct Curl_ssl_session **psession) -{ - struct Curl_ssl_session *s; - - if(!sdata || !sdata_len) { - free(sdata); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - *psession = NULL; - s = calloc(1, sizeof(*s)); - if(!s) { - free(sdata); - free(quic_tp); - return CURLE_OUT_OF_MEMORY; - } - - s->ietf_tls_id = ietf_tls_id; - s->valid_until = valid_until; - s->earlydata_max = earlydata_max; - s->sdata = sdata; - s->sdata_len = sdata_len; - s->quic_tp = quic_tp; - s->quic_tp_len = quic_tp_len; - if(alpn) { - s->alpn = strdup(alpn); - if(!s->alpn) { - cf_ssl_scache_session_ldestroy(NULL, s); - return CURLE_OUT_OF_MEMORY; - } - } - *psession = s; - return CURLE_OK; -} - -void Curl_ssl_session_destroy(struct Curl_ssl_session *s) -{ - if(s) { - /* if in the list, the list destructor takes care of it */ - if(Curl_node_llist(&s->list)) - Curl_node_remove(&s->list); - else { - cf_ssl_scache_session_ldestroy(NULL, s); - } - } -} - -static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer) -{ - Curl_llist_destroy(&peer->sessions, NULL); - if(peer->sobj) { - DEBUGASSERT(peer->sobj_free); - if(peer->sobj_free) - peer->sobj_free(peer->sobj); - peer->sobj = NULL; - } - peer->sobj_free = NULL; - Curl_safefree(peer->clientcert); -#ifdef USE_TLS_SRP - Curl_safefree(peer->srp_username); - Curl_safefree(peer->srp_password); -#endif - Curl_safefree(peer->ssl_peer_key); - peer->age = 0; - peer->hmac_set = FALSE; -} - -static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer, - void *sobj, - Curl_ssl_scache_obj_dtor *sobj_free) -{ - DEBUGASSERT(peer); - if(peer->sobj_free) { - peer->sobj_free(peer->sobj); - } - peer->sobj = sobj; - peer->sobj_free = sobj_free; -} - -static void cf_ssl_cache_peer_update(struct Curl_ssl_scache_peer *peer) -{ - /* The sessions of this peer are exportable if - * - it has no confidential information - * - its peer key is not yet known, because sessions were - * imported using only the salt+hmac - * - the peer key is global, e.g. carrying no relative paths */ - peer->exportable = (!peer->clientcert && !peer->srp_username && - !peer->srp_password && - (!peer->ssl_peer_key || - cf_ssl_peer_key_is_global(peer->ssl_peer_key))); -} - -static CURLcode -cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, - const char *ssl_peer_key, - const char *clientcert, - const char *srp_username, - const char *srp_password, - const unsigned char *salt, - const unsigned char *hmac) -{ - CURLcode result = CURLE_OUT_OF_MEMORY; - - DEBUGASSERT(!peer->ssl_peer_key); - if(ssl_peer_key) { - peer->ssl_peer_key = strdup(ssl_peer_key); - if(!peer->ssl_peer_key) - goto out; - peer->hmac_set = FALSE; - } - else if(salt && hmac) { - memcpy(peer->key_salt, salt, sizeof(peer->key_salt)); - memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac)); - peer->hmac_set = TRUE; - } - else { - result = CURLE_BAD_FUNCTION_ARGUMENT; - goto out; - } - if(clientcert) { - peer->clientcert = strdup(clientcert); - if(!peer->clientcert) - goto out; - } - if(srp_username) { - peer->srp_username = strdup(srp_username); - if(!peer->srp_username) - goto out; - } - if(srp_password) { - peer->srp_password = strdup(srp_password); - if(!peer->srp_password) - goto out; - } - - cf_ssl_cache_peer_update(peer); - result = CURLE_OK; -out: - if(result) - cf_ssl_scache_clear_peer(peer); - return result; -} - -static void cf_scache_session_remove(struct Curl_ssl_scache_peer *peer, - struct Curl_ssl_session *s) -{ - (void)peer; - DEBUGASSERT(Curl_node_llist(&s->list) == &peer->sessions); - Curl_ssl_session_destroy(s); -} - -static bool cf_scache_session_expired(struct Curl_ssl_session *s, - curl_off_t now) -{ - return (s->valid_until > 0) && (s->valid_until < now); -} - -static void cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer *peer, - curl_off_t now) -{ - struct Curl_llist_node *n = Curl_llist_head(&peer->sessions); - while(n) { - struct Curl_ssl_session *s = Curl_node_elem(n); - n = Curl_node_next(n); - if(cf_scache_session_expired(s, now)) - cf_scache_session_remove(peer, s); - } -} - -static void cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer *peer) -{ - struct Curl_llist_node *n = Curl_llist_head(&peer->sessions); - while(n) { - struct Curl_ssl_session *s = Curl_node_elem(n); - n = Curl_node_next(n); - if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3) - cf_scache_session_remove(peer, s); - } -} - -CURLcode Curl_ssl_scache_create(size_t max_peers, - size_t max_sessions_per_peer, - struct Curl_ssl_scache **pscache) -{ - struct Curl_ssl_scache *scache; - struct Curl_ssl_scache_peer *peers; - size_t i; - - *pscache = NULL; - peers = calloc(max_peers, sizeof(*peers)); - if(!peers) - return CURLE_OUT_OF_MEMORY; - - scache = calloc(1, sizeof(*scache)); - if(!scache) { - free(peers); - return CURLE_OUT_OF_MEMORY; - } - - scache->magic = CURL_SCACHE_MAGIC; - scache->default_lifetime_secs = (24*60*60); /* 1 day */ - scache->peer_count = max_peers; - scache->peers = peers; - scache->age = 1; - for(i = 0; i < scache->peer_count; ++i) { - scache->peers[i].max_sessions = max_sessions_per_peer; - Curl_llist_init(&scache->peers[i].sessions, - cf_ssl_scache_session_ldestroy); - } - - *pscache = scache; - return CURLE_OK; -} - -void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache) -{ - if(scache && GOOD_SCACHE(scache)) { - size_t i; - scache->magic = 0; - for(i = 0; i < scache->peer_count; ++i) { - cf_ssl_scache_clear_peer(&scache->peers[i]); - } - free(scache->peers); - free(scache); - } -} - -/* Lock shared SSL session data */ -void Curl_ssl_scache_lock(struct Curl_easy *data) -{ - if(CURL_SHARE_ssl_scache(data)) - Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE); -} - -/* Unlock shared SSL session data */ -void Curl_ssl_scache_unlock(struct Curl_easy *data) -{ - if(CURL_SHARE_ssl_scache(data)) - Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION); -} - static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, const char *name, - char *path, + 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 UNDER_CE - (void)is_local; - return curlx_dyn_addf(buf, ":%s-%s", name, path); -#elif defined(_WIN32) + /* 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); @@ -395,7 +86,8 @@ static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, char *abspath = realpath(path, NULL); if(abspath) { CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath); - (free)(abspath); /* allocated by libc, free without memdebug */ + /* !checksrc! disable BANNEDFUNC 1 */ + free(abspath); /* allocated by libc, free without memdebug */ return r; } *is_local = TRUE; @@ -509,7 +201,7 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, if(ssl->version || ssl->version_max) { r = curlx_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version, - (ssl->version_max >> 16)); + (ssl->version_max >> 16)); if(r) goto out; } @@ -546,11 +238,6 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, 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) @@ -562,6 +249,11 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, goto out; } } + if(ssl->cert_blob) { + r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_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) @@ -595,7 +287,7 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, goto out; *ppeer_key = curlx_dyn_take(&buf, &key_len); - /* we just added printable char, and dynbuf always null-terminates, no need + /* we added printable char, and dynbuf always null-terminates, no need * to track length */ out: @@ -603,6 +295,305 @@ out: return r; } +struct Curl_ssl_scache { + unsigned int magic; + struct Curl_ssl_scache_peer *peers; + size_t peer_count; + int default_lifetime_secs; + long age; +}; + +static struct Curl_ssl_scache *cf_ssl_scache_get(struct Curl_easy *data) +{ + struct Curl_ssl_scache *scache = NULL; + /* If a share is present, its ssl_scache has preference over the multi */ + if(data->share && data->share->ssl_scache) + scache = data->share->ssl_scache; + else if(data->multi && data->multi->ssl_scache) + scache = data->multi->ssl_scache; + if(scache && !GOOD_SCACHE(scache)) { + failf(data, "transfer would use an invalid scache at %p, denied", + (void *)scache); + DEBUGASSERT(0); + return NULL; + } + return scache; +} + +static void cf_ssl_scache_session_ldestroy(void *udata, void *obj) +{ + struct Curl_ssl_session *s = obj; + (void)udata; + curlx_free(CURL_UNCONST(s->sdata)); + curlx_free(CURL_UNCONST(s->quic_tp)); + curlx_free((void *)s->alpn); + curlx_free(s); +} + +CURLcode Curl_ssl_session_create(void *sdata, size_t sdata_len, + int ietf_tls_id, const char *alpn, + curl_off_t valid_until, size_t earlydata_max, + struct Curl_ssl_session **psession) +{ + return Curl_ssl_session_create2(sdata, sdata_len, ietf_tls_id, alpn, + valid_until, earlydata_max, + NULL, 0, psession); +} + +CURLcode Curl_ssl_session_create2(void *sdata, size_t sdata_len, + int ietf_tls_id, const char *alpn, + curl_off_t valid_until, size_t earlydata_max, + unsigned char *quic_tp, size_t quic_tp_len, + struct Curl_ssl_session **psession) +{ + struct Curl_ssl_session *s; + + if(!sdata || !sdata_len) { + curlx_free(sdata); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + *psession = NULL; + s = curlx_calloc(1, sizeof(*s)); + if(!s) { + curlx_free(sdata); + curlx_free(quic_tp); + return CURLE_OUT_OF_MEMORY; + } + + s->ietf_tls_id = ietf_tls_id; + s->valid_until = valid_until; + s->earlydata_max = earlydata_max; + s->sdata = sdata; + s->sdata_len = sdata_len; + s->quic_tp = quic_tp; + s->quic_tp_len = quic_tp_len; + if(alpn) { + s->alpn = curlx_strdup(alpn); + if(!s->alpn) { + cf_ssl_scache_session_ldestroy(NULL, s); + return CURLE_OUT_OF_MEMORY; + } + } + *psession = s; + return CURLE_OK; +} + +void Curl_ssl_session_destroy(struct Curl_ssl_session *s) +{ + if(s) { + /* if in the list, the list destructor takes care of it */ + if(Curl_node_llist(&s->list)) + Curl_node_remove(&s->list); + else { + cf_ssl_scache_session_ldestroy(NULL, s); + } + } +} + +static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer) +{ + Curl_llist_destroy(&peer->sessions, NULL); + if(peer->sobj) { + DEBUGASSERT(peer->sobj_free); + if(peer->sobj_free) + peer->sobj_free(peer->sobj); + peer->sobj = NULL; + } + peer->sobj_free = NULL; + curlx_safefree(peer->clientcert); +#ifdef USE_TLS_SRP + curlx_safefree(peer->srp_username); + curlx_safefree(peer->srp_password); +#endif + curlx_safefree(peer->ssl_peer_key); + peer->age = 0; + peer->hmac_set = FALSE; +} + +static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer, + void *sobj, + Curl_ssl_scache_obj_dtor *sobj_free) +{ + DEBUGASSERT(peer); + if(peer->sobj_free) { + peer->sobj_free(peer->sobj); + } + peer->sobj = sobj; + peer->sobj_free = sobj_free; +} + +static void cf_ssl_cache_peer_update(struct Curl_ssl_scache_peer *peer) +{ + /* The sessions of this peer are exportable if + * - it has no confidential information + * - its peer key is not yet known, because sessions were + * imported using only the salt+hmac + * - the peer key is global, e.g. carrying no relative paths */ + peer->exportable = (!peer->clientcert && !peer->srp_username && + !peer->srp_password && + (!peer->ssl_peer_key || + cf_ssl_peer_key_is_global(peer->ssl_peer_key))); +} + +static CURLcode +cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, + const char *ssl_peer_key, + const char *clientcert, + const char *srp_username, + const char *srp_password, + const unsigned char *salt, + const unsigned char *hmac) +{ + CURLcode result = CURLE_OUT_OF_MEMORY; + + DEBUGASSERT(!peer->ssl_peer_key); + if(ssl_peer_key) { + peer->ssl_peer_key = curlx_strdup(ssl_peer_key); + if(!peer->ssl_peer_key) + goto out; + peer->hmac_set = FALSE; + } + else if(salt && hmac) { + memcpy(peer->key_salt, salt, sizeof(peer->key_salt)); + memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac)); + peer->hmac_set = TRUE; + } + else { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + if(clientcert) { + peer->clientcert = curlx_strdup(clientcert); + if(!peer->clientcert) + goto out; + } + if(srp_username) { + peer->srp_username = curlx_strdup(srp_username); + if(!peer->srp_username) + goto out; + } + if(srp_password) { + peer->srp_password = curlx_strdup(srp_password); + if(!peer->srp_password) + goto out; + } + + cf_ssl_cache_peer_update(peer); + result = CURLE_OK; +out: + if(result) + cf_ssl_scache_clear_peer(peer); + return result; +} + +static void cf_scache_session_remove(struct Curl_ssl_scache_peer *peer, + struct Curl_ssl_session *s) +{ + (void)peer; + DEBUGASSERT(Curl_node_llist(&s->list) == &peer->sessions); + Curl_ssl_session_destroy(s); +} + +static bool cf_scache_session_expired(struct Curl_ssl_session *s, + curl_off_t now) +{ + return (s->valid_until > 0) && (s->valid_until < now); +} + +static void cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer *peer, + curl_off_t now) +{ + struct Curl_llist_node *n = Curl_llist_head(&peer->sessions); + while(n) { + struct Curl_ssl_session *s = Curl_node_elem(n); + n = Curl_node_next(n); + if(cf_scache_session_expired(s, now)) + cf_scache_session_remove(peer, s); + } +} + +static void cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer *peer) +{ + struct Curl_llist_node *n = Curl_llist_head(&peer->sessions); + while(n) { + struct Curl_ssl_session *s = Curl_node_elem(n); + n = Curl_node_next(n); + if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3) + cf_scache_session_remove(peer, s); + } +} + +CURLcode Curl_ssl_scache_create(size_t max_peers, + size_t max_sessions_per_peer, + struct Curl_ssl_scache **pscache) +{ + struct Curl_ssl_scache *scache; + struct Curl_ssl_scache_peer *peers; + size_t i; + + *pscache = NULL; + peers = curlx_calloc(max_peers, sizeof(*peers)); + if(!peers) + return CURLE_OUT_OF_MEMORY; + + scache = curlx_calloc(1, sizeof(*scache)); + if(!scache) { + curlx_free(peers); + return CURLE_OUT_OF_MEMORY; + } + + scache->magic = CURL_SCACHE_MAGIC; + scache->default_lifetime_secs = (24 * 60 * 60); /* 1 day */ + scache->peer_count = max_peers; + scache->peers = peers; + scache->age = 1; + for(i = 0; i < scache->peer_count; ++i) { + scache->peers[i].max_sessions = max_sessions_per_peer; + Curl_llist_init(&scache->peers[i].sessions, + cf_ssl_scache_session_ldestroy); + } + + *pscache = scache; + return CURLE_OK; +} + +void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache) +{ + if(GOOD_SCACHE(scache)) { + size_t i; + scache->magic = 0; + for(i = 0; i < scache->peer_count; ++i) { + cf_ssl_scache_clear_peer(&scache->peers[i]); + } + curlx_free(scache->peers); + curlx_free(scache); + } +} + +bool Curl_ssl_scache_use(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + if(cf_ssl_scache_get(data)) { + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + return ssl_config ? ssl_config->primary.cache_session : FALSE; + } + return FALSE; +} + +/* Lock shared SSL session data */ +void Curl_ssl_scache_lock(struct Curl_easy *data) +{ + if(CURL_SHARE_ssl_scache(data)) + Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE); +} + +/* Unlock shared SSL session data */ +void Curl_ssl_scache_unlock(struct Curl_easy *data) +{ + if(CURL_SHARE_ssl_scache(data)) + Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION); +} + static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, struct ssl_primary_config *conn_config) { @@ -618,19 +609,18 @@ static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, else if(!Curl_safecmp(peer->clientcert, conn_config->clientcert)) return FALSE; #ifdef USE_TLS_SRP - if(Curl_timestrcmp(peer->srp_username, conn_config->username) || - Curl_timestrcmp(peer->srp_password, conn_config->password)) - return FALSE; + if(Curl_timestrcmp(peer->srp_username, conn_config->username) || + Curl_timestrcmp(peer->srp_password, conn_config->password)) + return FALSE; #endif return TRUE; } -static CURLcode -cf_ssl_find_peer_by_key(struct Curl_easy *data, - struct Curl_ssl_scache *scache, - const char *ssl_peer_key, - struct ssl_primary_config *conn_config, - struct Curl_ssl_scache_peer **ppeer) +static CURLcode cf_ssl_find_peer_by_key(struct Curl_easy *data, + struct Curl_ssl_scache *scache, + const char *ssl_peer_key, + struct ssl_primary_config *conn_config, + struct Curl_ssl_scache_peer **ppeer) { size_t i, peer_key_len = 0; CURLcode result = CURLE_OK; @@ -674,7 +664,7 @@ cf_ssl_find_peer_by_key(struct Curl_easy *data, /* remember peer_key for future lookups */ CURL_TRC_SSLS(data, "peer entry %zu key recovered: %s", i, ssl_peer_key); - scache->peers[i].ssl_peer_key = strdup(ssl_peer_key); + scache->peers[i].ssl_peer_key = curlx_strdup(ssl_peer_key); if(!scache->peers[i].ssl_peer_key) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -720,12 +710,11 @@ cf_ssl_get_free_peer(struct Curl_ssl_scache *scache) return peer; } -static CURLcode -cf_ssl_add_peer(struct Curl_easy *data, - struct Curl_ssl_scache *scache, - const char *ssl_peer_key, - struct ssl_primary_config *conn_config, - struct Curl_ssl_scache_peer **ppeer) +static CURLcode cf_ssl_add_peer(struct Curl_easy *data, + struct Curl_ssl_scache *scache, + const char *ssl_peer_key, + struct ssl_primary_config *conn_config, + struct Curl_ssl_scache_peer **ppeer) { struct Curl_ssl_scache_peer *peer = NULL; CURLcode result = CURLE_OK; @@ -745,12 +734,19 @@ cf_ssl_add_peer(struct Curl_easy *data, peer = cf_ssl_get_free_peer(scache); if(peer) { + char buffer[64]; const char *ccert = conn_config ? conn_config->clientcert : NULL; const char *username = NULL, *password = NULL; #ifdef USE_TLS_SRP username = conn_config ? conn_config->username : NULL; password = conn_config ? conn_config->password : NULL; #endif + if(!ccert && conn_config && conn_config->cert_blob) { + /* when using a client cert blob, create a name for it */ + curl_msnprintf(buffer, sizeof(buffer), + "cert-%p", conn_config->cert_blob->data); + ccert = buffer; /* data is strduped by cf_ssl_scache_peer_init */ + } result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert, username, password, NULL, NULL); if(result) @@ -905,7 +901,6 @@ CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf, peer->age = scache->age; /* set this as used in this age */ } } - Curl_ssl_scache_unlock(data); if(s) { *ps = s; CURL_TRC_SSLS(data, "took session for %s [proto=0x%x, " @@ -917,6 +912,7 @@ CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf, else { CURL_TRC_SSLS(data, "no cached session for %s", ssl_peer_key); } + Curl_ssl_scache_unlock(data); return result; } @@ -924,7 +920,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); @@ -932,7 +928,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; @@ -945,12 +941,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; } @@ -1002,7 +998,7 @@ void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf, #ifdef USE_SSLS_EXPORT -#define CURL_SSL_TICKET_MAX (16*1024) +#define CURL_SSL_TICKET_MAX (16 * 1024) static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer) { @@ -1158,9 +1154,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; @@ -1183,7 +1182,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) { @@ -1211,10 +1210,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/lib/vtls/vtls_scache.h b/lib/vtls/vtls_scache.h index deedccfe8c..916e4b505b 100644 --- a/lib/vtls/vtls_scache.h +++ b/lib/vtls/vtls_scache.h @@ -23,12 +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; @@ -37,8 +38,8 @@ struct ssl_peer; /* RFC 8446 (TLSv1.3) restrict lifetime to one week max, for * other, less secure versions, we restrict it to a day */ -#define CURL_SCACHE_MAX_13_LIFETIME_SEC (60*60*24*7) -#define CURL_SCACHE_MAX_12_LIFETIME_SEC (60*60*24) +#define CURL_SCACHE_MAX_13_LIFETIME_SEC (60 * 60 * 24 * 7) +#define CURL_SCACHE_MAX_12_LIFETIME_SEC (60 * 60 * 24) /* Create a session cache for up to max_peers endpoints with a total * of up to max_sessions SSL sessions per peer */ @@ -51,7 +52,7 @@ void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache); /* Create a key from peer and TLS configuration information that is * unique for how the connection filter wants to establish a TLS * connection to the peer. - * If the filter is a TLS proxy filter, it will use the proxy relevant + * If the filter is a TLS proxy filter, it uses the proxy relevant * information. * @param cf the connection filter wanting to use it * @param peer the peer the filter wants to talk to @@ -65,6 +66,12 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, const char *tls_id, char **ppeer_key); +/* Return if there is a session cache shall be used. + * An ssl session might not be configured or not available for + * "connect-only" transfers. + */ +bool Curl_ssl_scache_use(struct Curl_cfilter *cf, struct Curl_easy *data); + /* Lock session cache mutex. * Call this before calling other Curl_ssl_*session* functions * Caller should unlock this mutex as soon as possible, as it may block @@ -136,22 +143,18 @@ struct Curl_ssl_session { * in case this is not known. * @param psession on return the scached session instance created */ -CURLcode -Curl_ssl_session_create(void *sdata, size_t sdata_len, - int ietf_tls_id, const char *alpn, - curl_off_t valid_until, - size_t earlydata_max, - struct Curl_ssl_session **psession); +CURLcode Curl_ssl_session_create(void *sdata, size_t sdata_len, + int ietf_tls_id, const char *alpn, + curl_off_t valid_until, size_t earlydata_max, + struct Curl_ssl_session **psession); /* Variation of session creation with quic transport parameter bytes, * Takes ownership of `quic_tp` regardless of return code. */ -CURLcode -Curl_ssl_session_create2(void *sdata, size_t sdata_len, - int ietf_tls_id, const char *alpn, - curl_off_t valid_until, - size_t earlydata_max, - unsigned char *quic_tp, size_t quic_tp_len, - struct Curl_ssl_session **psession); +CURLcode Curl_ssl_session_create2(void *sdata, size_t sdata_len, + int ietf_tls_id, const char *alpn, + curl_off_t valid_until, size_t earlydata_max, + unsigned char *quic_tp, size_t quic_tp_len, + struct Curl_ssl_session **psession); /* Destroy a `session` instance. Can be called with NULL. * Does NOT need locking. */ diff --git a/lib/vtls/vtls_spack.c b/lib/vtls/vtls_spack.c index 10d16f213d..d96f4e41bd 100644 --- a/lib/vtls/vtls_spack.c +++ b/lib/vtls/vtls_spack.c @@ -21,31 +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" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" - -#ifdef _MSC_VER -#if _MSC_VER >= 1600 -#include -#else -typedef unsigned char uint8_t; -typedef unsigned __int16 uint16_t; -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; -#endif -#endif /* _MSC_VER */ +#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 @@ -67,8 +51,8 @@ static CURLcode spack_enc8(struct dynbuf *buf, uint8_t b) return curlx_dyn_addn(buf, &b, 1); } -static CURLcode -spack_dec8(uint8_t *val, const uint8_t **src, const uint8_t *end) +static CURLcode spack_dec8(uint8_t *val, const uint8_t **src, + const uint8_t *end) { if(end - *src < 1) return CURLE_READ_ERROR; @@ -85,8 +69,8 @@ static CURLcode spack_enc16(struct dynbuf *buf, uint16_t val) return curlx_dyn_addn(buf, nval, sizeof(nval)); } -static CURLcode -spack_dec16(uint16_t *val, const uint8_t **src, const uint8_t *end) +static CURLcode spack_dec16(uint16_t *val, const uint8_t **src, + const uint8_t *end) { if(end - *src < 2) return CURLE_READ_ERROR; @@ -105,8 +89,8 @@ static CURLcode spack_enc32(struct dynbuf *buf, uint32_t val) return curlx_dyn_addn(buf, nval, sizeof(nval)); } -static CURLcode -spack_dec32(uint32_t *val, const uint8_t **src, const uint8_t *end) +static CURLcode spack_dec32(uint32_t *val, const uint8_t **src, + const uint8_t *end) { if(end - *src < 4) return CURLE_READ_ERROR; @@ -122,7 +106,7 @@ static CURLcode spack_enc64(struct dynbuf *buf, uint64_t val) nval[0] = (uint8_t)(val >> 56); nval[1] = (uint8_t)(val >> 48); nval[2] = (uint8_t)(val >> 40); - nval[3] = (uint8_t)(val >> 32); \ + nval[3] = (uint8_t)(val >> 32); nval[4] = (uint8_t)(val >> 24); nval[5] = (uint8_t)(val >> 16); nval[6] = (uint8_t)(val >> 8); @@ -130,8 +114,8 @@ static CURLcode spack_enc64(struct dynbuf *buf, uint64_t val) return curlx_dyn_addn(buf, nval, sizeof(nval)); } -static CURLcode -spack_dec64(uint64_t *val, const uint8_t **src, const uint8_t *end) +static CURLcode spack_dec64(uint64_t *val, const uint8_t **src, + const uint8_t *end) { if(end - *src < 8) return CURLE_READ_ERROR; @@ -156,8 +140,8 @@ static CURLcode spack_encstr16(struct dynbuf *buf, const char *s) return r; } -static CURLcode -spack_decstr16(char **val, const uint8_t **src, const uint8_t *end) +static CURLcode spack_decstr16(char **val, const uint8_t **src, + const uint8_t *end) { uint16_t slen; CURLcode r; @@ -168,13 +152,13 @@ spack_decstr16(char **val, const uint8_t **src, const uint8_t *end) 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; } -static CURLcode spack_encdata16(struct dynbuf *buf, - const uint8_t *data, size_t data_len) +static CURLcode spack_encdata16(struct dynbuf *buf, const uint8_t *data, + size_t data_len) { CURLcode r; if(data_len > UINT16_MAX) @@ -186,9 +170,8 @@ static CURLcode spack_encdata16(struct dynbuf *buf, return r; } -static CURLcode -spack_decdata16(uint8_t **val, size_t *val_len, - const uint8_t **src, const uint8_t *end) +static CURLcode spack_decdata16(uint8_t **val, size_t *val_len, + const uint8_t **src, const uint8_t *end) { uint16_t data_len; CURLcode r; @@ -199,7 +182,7 @@ 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; @@ -278,7 +261,7 @@ CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, goto out; } - s = calloc(1, sizeof(*s)); + s = curlx_calloc(1, sizeof(*s)); if(!s) { r = CURLE_OUT_OF_MEMORY; goto out; diff --git a/lib/vtls/vtls_spack.h b/lib/vtls/vtls_spack.h index 86796ee62e..4479384de9 100644 --- a/lib/vtls/vtls_spack.h +++ b/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/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index afbb9b8218..15c81c2874 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -21,24 +21,23 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * Source file for all wolfSSL 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_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" +#if LIBWOLFSSL_VERSION_HEX < 0x05000000 /* wolfSSL 5.0.0 (2021-11-01) */ +#error "wolfSSL version should be at least 5.0.0" +#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: @@ -54,35 +53,27 @@ #endif #endif -#include - -#include "../urldata.h" -#include "../sendf.h" -#include "../curlx/inet_pton.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "keylog.h" -#include "../parsedate.h" -#include "../connect.h" /* for the connect timeout */ -#include "../progress.h" -#include "../select.h" -#include "../strdup.h" -#include "x509asn1.h" -#include "../curl_printf.h" -#include "../multiif.h" +#include "urldata.h" +#include "curl_trc.h" +#include "httpsrr.h" +#include "cf-dns.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" +#ifdef USE_ECH +#include "curlx/base64.h" +#endif #include #include -#include "wolfssl.h" -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" - -#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG -#define USE_ECH_WOLFSSL -#endif +#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 @@ -117,24 +108,20 @@ #undef USE_BIO_CHAIN #endif -static CURLcode wssl_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done); - #ifdef OPENSSL_EXTRA /* * Availability note: * The TLS 1.3 secret callback (wolfSSL_set_tls13_secret_cb) was added in * wolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that - * option is not set, then TLS 1.3 will not be logged. + * option is not set, then TLS 1.3 is not logged. * For TLS 1.2 and before, we use wolfSSL_get_keys(). * wolfSSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA * (--enable-opensslextra or --enable-all). */ #if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) -static int -wssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret, - int secretSz, void *ctx) +static int wssl_tls13_secret_callback(SSL *ssl, int id, + const unsigned char *secret, + int secretSz, void *ctx) { const char *label; unsigned char client_random[SSL3_RANDOM_SIZE]; @@ -185,7 +172,6 @@ static void wssl_log_tls12_secret(WOLFSSL *ssl) unsigned char *ms, *sr, *cr; unsigned int msLen, srLen, crLen, i, x = 0; -#if LIBWOLFSSL_VERSION_HEX >= 0x0300d000 /* >= 3.13.0 */ /* wolfSSL_GetVersion is available since 3.13, we use it instead of * SSL_version since the latter relies on OPENSSL_ALL (--enable-opensslall or * --enable-all). Failing to perform this check could result in an unusable @@ -201,7 +187,6 @@ static void wssl_log_tls12_secret(WOLFSSL *ssl) * is not directly usable. */ return; } -#endif if(wolfSSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != WOLFSSL_SUCCESS) { @@ -314,8 +299,7 @@ static long wssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr) return ret; } -static int wssl_bio_cf_out_write(WOLFSSL_BIO *bio, - const char *buf, int blen) +static int wssl_bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) { struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; @@ -335,14 +319,15 @@ static int wssl_bio_cf_out_write(WOLFSSL_BIO *bio, skiplen = (ssize_t)(blen - wssl->io_send_blocked_len); blen = wssl->io_send_blocked_len; } - result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &nwritten); + result = Curl_conn_cf_send(cf->next, data, + (const uint8_t *)buf, blen, FALSE, &nwritten); wssl->io_result = result; CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %d, %zu", blen, result, nwritten); #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; @@ -358,12 +343,15 @@ static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) struct ssl_connect_data *connssl = cf->ctx; struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); - size_t nread; + size_t nread = 0; CURLcode result = CURLE_OK; DEBUGASSERT(data); - /* OpenSSL catches this case, so should we. */ - if(!buf) + if(!data || (blen < 0)) { + wssl->io_result = CURLE_FAILED_INIT; + return -1; + } + if(!buf || !blen) return 0; if((connssl->connecting_state == ssl_connect_2) && @@ -384,7 +372,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; @@ -393,29 +381,34 @@ static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) static WOLFSSL_BIO_METHOD *wssl_bio_cf_method = NULL; -static void wssl_bio_cf_init_methods(void) +static int wssl_bio_cf_init_methods(void) { wssl_bio_cf_method = wolfSSL_BIO_meth_new(WOLFSSL_BIO_MEMORY, - "wolfSSL CF BIO"); + "wolfSSL CF BIO"); + if(!wssl_bio_cf_method) + return FALSE; /* error */ wolfSSL_BIO_meth_set_write(wssl_bio_cf_method, &wssl_bio_cf_out_write); wolfSSL_BIO_meth_set_read(wssl_bio_cf_method, &wssl_bio_cf_in_read); wolfSSL_BIO_meth_set_ctrl(wssl_bio_cf_method, &wssl_bio_cf_ctrl); wolfSSL_BIO_meth_set_create(wssl_bio_cf_method, &wssl_bio_cf_create); wolfSSL_BIO_meth_set_destroy(wssl_bio_cf_method, &wssl_bio_cf_destroy); + return TRUE; /* fine */ } static void wssl_bio_cf_free_methods(void) { wolfSSL_BIO_meth_free(wssl_bio_cf_method); + wssl_bio_cf_method = NULL; } #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 */ +#ifdef HAVE_EX_DATA CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, struct Curl_easy *data, const char *ssl_peer_key, @@ -427,7 +420,7 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, { CURLcode result = CURLE_OK; struct Curl_ssl_session *sc_session = NULL; - unsigned char *sdata = NULL, *qtp_clone = NULL; + unsigned char *sdata = NULL, *sdata_ptr, *qtp_clone = NULL; unsigned int sdata_len; unsigned int earlydata_max = 0; @@ -440,22 +433,24 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, result = CURLE_FAILED_INIT; goto out; } - sdata = calloc(1, sdata_len); + sdata = sdata_ptr = curlx_calloc(1, sdata_len); if(!sdata) { failf(data, "unable to allocate session buffer of %u bytes", sdata_len); result = CURLE_OUT_OF_MEMORY; goto out; } - sdata_len = wolfSSL_i2d_SSL_SESSION(session, &sdata); + /* wolfSSL right now does not change the last parameter here, but it + * might one day decide to do so for OpenSSL compatibility. */ + sdata_len = wolfSSL_i2d_SSL_SESSION(session, &sdata_ptr); if(sdata_len <= 0) { CURL_TRC_CF(data, cf, "fail to serialize session: %u", sdata_len); result = CURLE_FAILED_INIT; 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) { - free(sdata); + curlx_free(sdata); return CURLE_OUT_OF_MEMORY; } } @@ -476,7 +471,7 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, } out: - free(sdata); + curlx_free(sdata); return result; } @@ -484,7 +479,7 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) { struct Curl_cfilter *cf; - cf = (struct Curl_cfilter*)wolfSSL_get_app_data(ssl); + cf = (struct Curl_cfilter *)wolfSSL_get_app_data(ssl); DEBUGASSERT(cf != NULL); if(cf && session) { struct ssl_connect_data *connssl = cf->ctx; @@ -499,6 +494,7 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) } return 0; } +#endif static CURLcode wssl_on_session_reuse(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -507,36 +503,18 @@ static CURLcode wssl_on_session_reuse(struct Curl_cfilter *cf, bool *do_early_data) { struct ssl_connect_data *connssl = cf->ctx; - struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend; - CURLcode result = CURLE_OK; - - *do_early_data = FALSE; #ifdef WOLFSSL_EARLY_DATA + struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend; + 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 @@ -570,13 +548,15 @@ wssl_setup_session(struct Curl_cfilter *cf, infof(data, "SSL reusing session with ALPN '%s'", scs->alpn ? scs->alpn : "-"); if(ssl_config->earlydata && - !cf->conn->connect_only && + !cf->conn->bits.connect_only && !strcmp("TLSv1.3", wolfSSL_get_version(wss->ssl))) { bool do_early_data = FALSE; if(sess_reuse_cb) { result = sess_reuse_cb(cf, data, alpns, scs, &do_early_data); - if(result) - goto out; + if(result) { + wolfSSL_SESSION_free(session); + goto out; + } } #ifdef WOLFSSL_EARLY_DATA if(do_early_data) { @@ -635,7 +615,7 @@ static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf, } #else infof(data, "ignoring native CA option because wolfSSL was built without " - "native CA support"); + "native CA support"); #endif } #endif /* !NO_FILESYSTEM */ @@ -680,10 +660,10 @@ 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:"); + " continuing anyway:"); } } else { @@ -702,32 +682,30 @@ static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf, #define MPROTO_WSSL_X509_KEY "tls:wssl:x509:share" struct wssl_x509_share { - char *CAfile; /* CAfile path used to generate X509 store */ + char *CAfile; /* CAfile path used to generate X509 store */ WOLFSSL_X509_STORE *store; /* cached X509 store or NULL if none */ - struct curltime time; /* when the cached store was created */ + struct curltime time; /* when the cached store was created */ }; static void wssl_x509_share_free(void *key, size_t key_len, void *p) { struct wssl_x509_share *share = p; - DEBUGASSERT(key_len == (sizeof(MPROTO_WSSL_X509_KEY)-1)); + DEBUGASSERT(key_len == (sizeof(MPROTO_WSSL_X509_KEY) - 1)); DEBUGASSERT(!memcmp(MPROTO_WSSL_X509_KEY, key, key_len)); (void)key; (void)key_len; if(share->store) { wolfSSL_X509_STORE_free(share->store); } - free(share->CAfile); - free(share); + curlx_free(share->CAfile); + curlx_free(share); } -static bool -wssl_cached_x509_store_expired(const struct Curl_easy *data, - const struct wssl_x509_share *mb) +static bool wssl_cached_x509_store_expired(struct Curl_easy *data, + const struct wssl_x509_share *mb) { const struct ssl_general_config *cfg = &data->set.general_ssl; - struct curltime now = curlx_now(); - timediff_t elapsed_ms = curlx_timediff(now, mb->time); + timediff_t elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &mb->time); timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; if(timeout_ms < 0) @@ -736,9 +714,8 @@ wssl_cached_x509_store_expired(const struct Curl_easy *data, return elapsed_ms >= timeout_ms; } -static bool -wssl_cached_x509_store_different(struct Curl_cfilter *cf, - const struct wssl_x509_share *mb) +static bool wssl_cached_x509_store_different(struct Curl_cfilter *cf, + const struct wssl_x509_share *mb) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); if(!mb->CAfile || !conn_config->CAfile) @@ -748,7 +725,7 @@ wssl_cached_x509_store_different(struct Curl_cfilter *cf, } static WOLFSSL_X509_STORE *wssl_get_cached_x509_store(struct Curl_cfilter *cf, - const struct Curl_easy *data) + struct Curl_easy *data) { struct Curl_multi *multi = data->multi; struct wssl_x509_share *share; @@ -757,7 +734,7 @@ static WOLFSSL_X509_STORE *wssl_get_cached_x509_store(struct Curl_cfilter *cf, DEBUGASSERT(multi); share = multi ? Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_WSSL_X509_KEY), - sizeof(MPROTO_WSSL_X509_KEY)-1) : NULL; + sizeof(MPROTO_WSSL_X509_KEY) - 1) : NULL; if(share && share->store && !wssl_cached_x509_store_expired(data, share) && !wssl_cached_x509_store_different(cf, share)) { @@ -768,7 +745,7 @@ static WOLFSSL_X509_STORE *wssl_get_cached_x509_store(struct Curl_cfilter *cf, } static void wssl_set_cached_x509_store(struct Curl_cfilter *cf, - const struct Curl_easy *data, + struct Curl_easy *data, WOLFSSL_X509_STORE *store) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); @@ -780,17 +757,17 @@ static void wssl_set_cached_x509_store(struct Curl_cfilter *cf, return; share = Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_WSSL_X509_KEY), - sizeof(MPROTO_WSSL_X509_KEY)-1); + sizeof(MPROTO_WSSL_X509_KEY) - 1); if(!share) { - share = calloc(1, sizeof(*share)); + share = curlx_calloc(1, sizeof(*share)); if(!share) return; if(!Curl_hash_add2(&multi->proto_hash, CURL_UNCONST(MPROTO_WSSL_X509_KEY), - sizeof(MPROTO_WSSL_X509_KEY)-1, + sizeof(MPROTO_WSSL_X509_KEY) - 1, share, wssl_x509_share_free)) { - free(share); + curlx_free(share); return; } } @@ -799,7 +776,7 @@ static void wssl_set_cached_x509_store(struct Curl_cfilter *cf, char *CAfile = NULL; if(conn_config->CAfile) { - CAfile = strdup(conn_config->CAfile); + CAfile = curlx_strdup(conn_config->CAfile); if(!CAfile) { wolfSSL_X509_STORE_free(store); return; @@ -808,10 +785,10 @@ static void wssl_set_cached_x509_store(struct Curl_cfilter *cf, if(share->store) { wolfSSL_X509_STORE_free(share->store); - free(share->CAfile); + curlx_free(share->CAfile); } - share->time = curlx_now(); + share->time = *Curl_pgrs_now(data); share->store = store; share->CAfile = CAfile; } @@ -862,17 +839,16 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, } } else { - /* We never share the CTX's store, use it. */ - WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ssl_ctx); - result = wssl_populate_x509_store(cf, data, store, wssl); + /* We never share the CTX's store, use it. */ + WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ssl_ctx); + result = wssl_populate_x509_store(cf, data, store, wssl); } return result; } #ifdef WOLFSSL_TLS13 -static CURLcode -wssl_add_default_ciphers(bool tls13, struct dynbuf *buf) +static CURLcode wssl_add_default_ciphers(bool tls13, struct dynbuf *buf) { int i; char *str; @@ -898,10 +874,8 @@ wssl_add_default_ciphers(bool tls13, struct dynbuf *buf) } #endif -/* 4.2.0 (2019) */ -#if LIBWOLFSSL_VERSION_HEX < 0x04002000 || !defined(OPENSSL_EXTRA) -static int -wssl_legacy_CTX_set_min_proto_version(WOLFSSL_CTX* ctx, int version) +#ifndef OPENSSL_EXTRA +static int wssl_legacy_CTX_set_min_proto_version(WOLFSSL_CTX *ctx, int version) { int res; switch(version) { @@ -928,19 +902,19 @@ wssl_legacy_CTX_set_min_proto_version(WOLFSSL_CTX* ctx, int version) } return res; } -static int -wssl_legacy_CTX_set_max_proto_version(WOLFSSL_CTX* ctx, int version) + +static int wssl_legacy_CTX_set_max_proto_version(WOLFSSL_CTX *ctx, int version) { (void)ctx, (void)version; return WOLFSSL_NOT_IMPLEMENTED; } #define wolfSSL_CTX_set_min_proto_version wssl_legacy_CTX_set_min_proto_version #define wolfSSL_CTX_set_max_proto_version wssl_legacy_CTX_set_max_proto_version -#endif +#endif /* OPENSSL_EXTRA */ -static CURLcode client_certificate(struct Curl_easy *data, - struct ssl_config_data *ssl_config, - struct wssl_ctx *wctx) +static CURLcode wssl_client_cert(struct Curl_easy *data, + struct ssl_config_data *ssl_config, + struct wssl_ctx *wctx) { /* Load the client certificate, and private key */ #ifndef NO_FILESYSTEM @@ -1035,70 +1009,323 @@ static CURLcode client_certificate(struct Curl_easy *data, static CURLcode ssl_version(struct Curl_easy *data, struct ssl_primary_config *conn_config, - struct wssl_ctx *wctx) + struct wssl_ctx *wctx, + int *min_version, int *max_version) { 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: - res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_VERSION); + *min_version = TLS1_VERSION; break; case CURL_SSLVERSION_TLSv1_1: - res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_1_VERSION); + *min_version = TLS1_1_VERSION; break; case CURL_SSLVERSION_TLSv1_2: - res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_2_VERSION); + *min_version = TLS1_2_VERSION; break; #ifdef WOLFSSL_TLS13 case CURL_SSLVERSION_TLSv1_3: - res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_3_VERSION); + *min_version = TLS1_3_VERSION; break; #endif default: failf(data, "wolfSSL: unsupported minimum TLS version value"); return CURLE_SSL_CONNECT_ERROR; } - if(res != WOLFSSL_SUCCESS) { - failf(data, "wolfSSL: failed set the minimum TLS version"); - return CURLE_SSL_CONNECT_ERROR; - } switch(conn_config->version_max) { #ifdef WOLFSSL_TLS13 case CURL_SSLVERSION_MAX_TLSv1_3: - res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_3_VERSION); + *max_version = TLS1_3_VERSION; break; #endif case CURL_SSLVERSION_MAX_TLSv1_2: - res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_2_VERSION); + *max_version = TLS1_2_VERSION; break; case CURL_SSLVERSION_MAX_TLSv1_1: - res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_1_VERSION); + *max_version = TLS1_1_VERSION; break; case CURL_SSLVERSION_MAX_TLSv1_0: - res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_VERSION); + *max_version = TLS1_VERSION; break; case CURL_SSLVERSION_MAX_DEFAULT: case CURL_SSLVERSION_MAX_NONE: - res = WOLFSSL_SUCCESS; break; default: failf(data, "wolfSSL: unsupported maximum TLS version value"); return CURLE_SSL_CONNECT_ERROR; } + + res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, *min_version); if(res != WOLFSSL_SUCCESS) { - failf(data, "wolfSSL: failed set the maximum TLS version"); + failf(data, "wolfSSL: failed set the minimum TLS version"); return CURLE_SSL_CONNECT_ERROR; } + + if(*max_version) { + res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, *max_version); + if(res != WOLFSSL_SUCCESS) { + failf(data, "wolfSSL: failed set the maximum TLS version"); + return CURLE_SSL_CONNECT_ERROR; + } + } return CURLE_OK; } - -#define QUIC_CIPHERS \ - "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ - "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" #define QUIC_GROUPS "P-256:P-384:P-521" +#ifdef WOLFSSL_TLS13 +#define MAX_CIPHER_LEN 4096 +#endif + +static CURLcode wssl_init_ciphers(struct Curl_easy *data, + struct wssl_ctx *wctx, + struct ssl_primary_config *conn_config, + int tls_min, int tls_max) +{ +#ifndef WOLFSSL_TLS13 + const char *ciphers = conn_config->cipher_list; + (void)tls_min; + (void)tls_max; + if(ciphers) { + if(!SSL_CTX_set_cipher_list(wctx->ssl_ctx, ciphers)) { + failf(data, "failed setting cipher list: %s", ciphers); + return CURLE_SSL_CIPHER; + } + infof(data, "Cipher selection: %s", ciphers); + } + return CURLE_OK; +#else + CURLcode result = CURLE_OK; + if(conn_config->cipher_list || conn_config->cipher_list13) { + const char *ciphers12 = conn_config->cipher_list; + const char *ciphers13 = conn_config->cipher_list13; + struct dynbuf c; + curlx_dyn_init(&c, MAX_CIPHER_LEN); + + if(!tls_max || (tls_max >= TLS1_3_VERSION)) { + if(ciphers13) + result = curlx_dyn_add(&c, ciphers13); + else + result = wssl_add_default_ciphers(TRUE, &c); + } + + if(!result && (tls_min < TLS1_3_VERSION)) { + if(ciphers12) { + if(curlx_dyn_len(&c)) + result = curlx_dyn_addn(&c, ":", 1); + if(!result) + result = curlx_dyn_add(&c, ciphers12); + } + else + result = wssl_add_default_ciphers(FALSE, &c); + } + if(!result) { + if(!wolfSSL_CTX_set_cipher_list(wctx->ssl_ctx, curlx_dyn_ptr(&c))) { + failf(data, "failed setting cipher list: %s", curlx_dyn_ptr(&c)); + result = CURLE_SSL_CIPHER; + } + else + infof(data, "Cipher selection: %s", curlx_dyn_ptr(&c)); + } + curlx_dyn_free(&c); + } + return result; +#endif +} + +static CURLcode wssl_init_curves(struct Curl_easy *data, + struct wssl_ctx *wctx, + struct ssl_primary_config *conn_config, + unsigned char transport +#ifdef WOLFSSL_HAVE_KYBER + , word16 *out_pqkem +#endif + ) +{ + char *curves = conn_config->curves; + if(!curves && (transport == TRNSPRT_QUIC)) + curves = (char *)CURL_UNCONST(QUIC_GROUPS); + + if(curves) { +#ifdef WOLFSSL_HAVE_KYBER + size_t idx; + for(idx = 0; gnm[idx].name != NULL; idx++) { + if(!strncmp(curves, gnm[idx].name, strlen(gnm[idx].name))) { + *out_pqkem = gnm[idx].group; + break; + } + } + + if(*out_pqkem == 0) +#endif + { + if(!wolfSSL_CTX_set1_curves_list(wctx->ssl_ctx, curves)) { + failf(data, "failed setting curves list: '%s'", curves); + return CURLE_SSL_CIPHER; + } + } + } + return CURLE_OK; +} + +static CURLcode wssl_init_ssl_handle(struct wssl_ctx *wctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + struct alpn_spec *alpns, + void *ssl_user_data, + unsigned char transport, +#ifdef WOLFSSL_HAVE_KYBER + word16 pqkem, +#endif + Curl_wssl_init_session_reuse_cb + *sess_reuse_cb) +{ + /* Let's make an SSL structure */ + wctx->ssl = wolfSSL_new(wctx->ssl_ctx); + if(!wctx->ssl) { + failf(data, "SSL: could not create a handle"); + return CURLE_OUT_OF_MEMORY; + } + +#ifdef HAVE_EX_DATA + wolfSSL_set_app_data(wctx->ssl, ssl_user_data); +#else + (void)ssl_user_data; +#endif +#ifdef WOLFSSL_QUIC + if(transport == TRNSPRT_QUIC) + wolfSSL_set_quic_use_legacy_codepoint(wctx->ssl, 0); +#else + (void)transport; +#endif + +#ifdef WOLFSSL_HAVE_KYBER + if(pqkem) { + if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) != + WOLFSSL_SUCCESS) { + failf(data, "unable to use PQ KEM"); + } + } +#endif + + /* Check if there is a cached ID we can/should use here! */ + if(Curl_ssl_scache_use(cf, data)) { + /* Set session from cache if there is one */ + (void)wssl_setup_session(cf, data, wctx, alpns, peer->scache_key, + sess_reuse_cb); + } + +#ifdef HAVE_ALPN + if(alpns->count) { + struct alpn_proto_buf proto; + memset(&proto, 0, sizeof(proto)); + Curl_alpn_to_proto_str(&proto, alpns); + + if(wolfSSL_UseALPN(wctx->ssl, (char *)proto.data, + (unsigned int)proto.len, + WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) + != WOLFSSL_SUCCESS) { + failf(data, "SSL: failed setting ALPN protocols"); + return CURLE_SSL_CONNECT_ERROR; + } + CURL_TRC_CF(data, cf, "set ALPN: %s", proto.data); + } +#endif /* HAVE_ALPN */ + +#ifdef OPENSSL_EXTRA + if(Curl_tls_keylog_enabled()) { + /* Ensure the Client Random is preserved. */ + wolfSSL_KeepArrays(wctx->ssl); +#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) + wolfSSL_set_tls13_secret_cb(wctx->ssl, wssl_tls13_secret_callback, NULL); +#endif + } +#endif /* OPENSSL_EXTRA */ + +#ifdef HAVE_SECURE_RENEGOTIATION + if(wolfSSL_UseSecureRenegotiation(wctx->ssl) != SSL_SUCCESS) { + failf(data, "SSL: failed setting secure renegotiation"); + return CURLE_SSL_CONNECT_ERROR; + } +#endif /* HAVE_SECURE_RENEGOTIATION */ + + return CURLE_OK; +} + +#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG +static CURLcode wssl_init_ech(struct wssl_ctx *wctx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + int trying_ech_now = 0; + + if(data->set.str[STRING_ECH_PUBLIC]) { + infof(data, "ECH: outername not (yet) supported" + " with wolfSSL"); + return CURLE_SSL_CONNECT_ERROR; + } + if(data->set.tls_ech == CURLECH_GREASE) { + infof(data, "ECH: GREASE is done by default by" + " wolfSSL: no need to ask"); + } + if(data->set.tls_ech & CURLECH_CLA_CFG && + data->set.str[STRING_ECH_CONFIG]) { + char *b64val = data->set.str[STRING_ECH_CONFIG]; + word32 b64len = 0; + + b64len = (word32)strlen(b64val); + if(b64len && wolfSSL_SetEchConfigsBase64(wctx->ssl, b64val, + b64len) != WOLFSSL_SUCCESS) { + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + else { + trying_ech_now = 1; + infof(data, "ECH: ECHConfig from command line"); + } + } + else { + const struct Curl_https_rrinfo *rinfo = + Curl_conn_dns_get_https(data, cf->sockindex); + + if(rinfo && rinfo->echconfiglist) { + const unsigned char *ecl = rinfo->echconfiglist; + size_t elen = rinfo->echconfiglist_len; + + infof(data, "ECH: ECHConfig from HTTPS RR"); + if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) != + WOLFSSL_SUCCESS) { + infof(data, "ECH: wolfSSL_SetEchConfigs failed"); + if(data->set.tls_ech & CURLECH_HARD) { + return CURLE_SSL_CONNECT_ERROR; + } + } + else { + trying_ech_now = 1; + infof(data, "ECH: imported ECHConfigList of length %zu", elen); + } + } + else { + infof(data, "ECH: requested but no ECHConfig available"); + if(data->set.tls_ech & CURLECH_HARD) { + return CURLE_SSL_CONNECT_ERROR; + } + } + } + + if(trying_ech_now && + wolfSSL_set_min_proto_version(wctx->ssl, TLS1_3_VERSION) != 1) { + infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); + return CURLE_SSL_CONNECT_ERROR; + } + return CURLE_OK; +} +#endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, struct Curl_cfilter *cf, @@ -1112,15 +1339,14 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, { struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct ssl_primary_config *conn_config; - WOLFSSL_METHOD* req_method = NULL; + WOLFSSL_METHOD *req_method = NULL; struct alpn_spec alpns; - char *curves; #ifdef WOLFSSL_HAVE_KYBER word16 pqkem = 0; - size_t idx = 0; #endif CURLcode result = CURLE_FAILED_INIT; unsigned char transport; + int tls_min, tls_max; DEBUGASSERT(!wctx->ssl_ctx); DEBUGASSERT(!wctx->ssl); @@ -1133,11 +1359,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, DEBUGASSERT(cf->next); transport = Curl_conn_cf_get_transport(cf->next, data); -#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */ - req_method = wolfSSLv23_client_method(); -#else req_method = wolfTLS_client_method(); -#endif if(!req_method) { failf(data, "wolfSSL: could not create a client method"); result = CURLE_OUT_OF_MEMORY; @@ -1154,84 +1376,23 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, goto out; } - result = ssl_version(data, conn_config, wctx); + result = ssl_version(data, conn_config, wctx, &tls_min, &tls_max); if(result) goto out; -#ifndef WOLFSSL_TLS13 - { - 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); - result = CURLE_SSL_CIPHER; - goto out; - } - infof(data, "Cipher selection: %s", ciphers); - } - } -#else -#define MAX_CIPHER_LEN 4096 - if(conn_config->cipher_list || conn_config->cipher_list13) { - const char *ciphers12 = conn_config->cipher_list; - const char *ciphers13 = conn_config->cipher_list13; - struct dynbuf c; - curlx_dyn_init(&c, MAX_CIPHER_LEN); + result = wssl_init_ciphers(data, wctx, conn_config, tls_min, tls_max); + if(result) + goto out; - if(ciphers13) - result = curlx_dyn_add(&c, ciphers13); - else - result = wssl_add_default_ciphers(TRUE, &c); - - if(!result) { - if(ciphers12) { - if(curlx_dyn_len(&c)) - result = curlx_dyn_addn(&c, ":", 1); - if(!result) - result = curlx_dyn_add(&c, ciphers12); - } - else - result = wssl_add_default_ciphers(FALSE, &c); - } - if(result) - goto out; - - if(!wolfSSL_CTX_set_cipher_list(wctx->ssl_ctx, curlx_dyn_ptr(&c))) { - failf(data, "failed setting cipher list: %s", curlx_dyn_ptr(&c)); - curlx_dyn_free(&c); - result = CURLE_SSL_CIPHER; - goto out; - } - infof(data, "Cipher selection: %s", curlx_dyn_ptr(&c)); - curlx_dyn_free(&c); - } -#endif - - curves = conn_config->curves; - if(!curves && (transport == TRNSPRT_QUIC)) - curves = (char *)CURL_UNCONST(QUIC_GROUPS); - - if(curves) { + result = wssl_init_curves(data, wctx, conn_config, transport #ifdef WOLFSSL_HAVE_KYBER - for(idx = 0; gnm[idx].name != NULL; idx++) { - if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) { - pqkem = gnm[idx].group; - break; - } - } - - if(pqkem == 0) + , &pqkem #endif - { - if(!wolfSSL_CTX_set1_curves_list(wctx->ssl_ctx, curves)) { - failf(data, "failed setting curves list: '%s'", curves); - result = CURLE_SSL_CIPHER; - goto out; - } - } - } + ); + if(result) + goto out; - result = client_certificate(data, ssl_config, wctx); + result = wssl_client_cert(data, ssl_config, wctx); if(result) goto out; @@ -1239,9 +1400,8 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ - wolfSSL_CTX_set_verify(wctx->ssl_ctx, - conn_config->verifypeer ? WOLFSSL_VERIFY_PEER : - WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_CTX_set_verify(wctx->ssl_ctx, conn_config->verifypeer ? + WOLFSSL_VERIFY_PEER : WOLFSSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI if(peer->sni) { @@ -1258,10 +1418,12 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, } #endif - if(ssl_config->primary.cache_session && (transport != TRNSPRT_QUIC)) { +#ifdef HAVE_EX_DATA + if(Curl_ssl_scache_use(cf, data) && (transport != TRNSPRT_QUIC)) { /* Register to get notified when a new session is received */ wolfSSL_CTX_sess_set_new_cb(wctx->ssl_ctx, wssl_vtls_new_session_cb); } +#endif if(cb_setup) { result = cb_setup(cf, data, cb_user_data); @@ -1286,7 +1448,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, #ifdef NO_FILESYSTEM else if(conn_config->verifypeer) { failf(data, "SSL: Certificates cannot be loaded because wolfSSL was built" - " with \"no file system\". Either disable peer verification" + " with no file system. Either disable peer verification" " (insecure) or if you are building an application with libcurl you" " can load certificates via CURLOPT_SSL_CTX_FUNCTION."); result = CURLE_SSL_CONNECT_ERROR; @@ -1294,157 +1456,22 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, } #endif - /* Let's make an SSL structure */ - wctx->ssl = wolfSSL_new(wctx->ssl_ctx); - if(!wctx->ssl) { - failf(data, "SSL: could not create a handle"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - wolfSSL_set_app_data(wctx->ssl, ssl_user_data); -#ifdef WOLFSSL_QUIC - if(transport == TRNSPRT_QUIC) - wolfSSL_set_quic_use_legacy_codepoint(wctx->ssl, 0); -#endif - + result = wssl_init_ssl_handle(wctx, cf, data, peer, &alpns, ssl_user_data, + transport, #ifdef WOLFSSL_HAVE_KYBER - if(pqkem) { - if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) != WOLFSSL_SUCCESS) { - failf(data, "unable to use PQ KEM"); - } - } + pqkem, #endif - - /* Check if there is a cached ID we can/should use here! */ - if(ssl_config->primary.cache_session) { - /* Set session from cache if there is one */ - (void)wssl_setup_session(cf, data, wctx, &alpns, - peer->scache_key, sess_reuse_cb); - } - -#ifdef HAVE_ALPN - if(alpns.count) { - struct alpn_proto_buf proto; - memset(&proto, 0, sizeof(proto)); - Curl_alpn_to_proto_str(&proto, &alpns); - - if(wolfSSL_UseALPN(wctx->ssl, (char *)proto.data, - (unsigned int)proto.len, - WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != WOLFSSL_SUCCESS) { - failf(data, "SSL: failed setting ALPN protocols"); - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - CURL_TRC_CF(data, cf, "set ALPN: %s", proto.data); - } -#endif /* HAVE_ALPN */ - -#ifdef OPENSSL_EXTRA - if(Curl_tls_keylog_enabled()) { - /* Ensure the Client Random is preserved. */ - wolfSSL_KeepArrays(wctx->ssl); -#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) - wolfSSL_set_tls13_secret_cb(wctx->ssl, - wssl_tls13_secret_callback, NULL); -#endif - } -#endif /* OPENSSL_EXTRA */ - -#ifdef HAVE_SECURE_RENEGOTIATION - if(wolfSSL_UseSecureRenegotiation(wctx->ssl) != SSL_SUCCESS) { - failf(data, "SSL: failed setting secure renegotiation"); - result = CURLE_SSL_CONNECT_ERROR; + sess_reuse_cb); + if(result) goto out; - } -#endif /* HAVE_SECURE_RENEGOTIATION */ -#ifdef USE_ECH_WOLFSSL - if(ECH_ENABLED(data)) { - int trying_ech_now = 0; - - if(data->set.str[STRING_ECH_PUBLIC]) { - infof(data, "ECH: outername not (yet) supported with wolfSSL"); - result = CURLE_SSL_CONNECT_ERROR; +#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG + if(CURLECH_ENABLED(data)) { + result = wssl_init_ech(wctx, cf, data); + if(result) goto out; - } - if(data->set.tls_ech == CURLECH_GREASE) { - infof(data, "ECH: GREASE is done by default by wolfSSL: no need to ask"); - } - if(data->set.tls_ech & CURLECH_CLA_CFG - && data->set.str[STRING_ECH_CONFIG]) { - char *b64val = data->set.str[STRING_ECH_CONFIG]; - word32 b64len = 0; - - b64len = (word32) strlen(b64val); - if(b64len - && wolfSSL_SetEchConfigsBase64(wctx->ssl, b64val, b64len) - != WOLFSSL_SUCCESS) { - if(data->set.tls_ech & CURLECH_HARD) { - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } - else { - trying_ech_now = 1; - infof(data, "ECH: ECHConfig from command line"); - } - } - else { - struct ssl_connect_data *connssl = cf->ctx; - struct Curl_dns_entry *dns = NULL; - - dns = Curl_dnscache_get(data, connssl->peer.hostname, connssl->peer.port, - cf->conn->ip_version); - if(!dns) { - infof(data, "ECH: requested but no DNS info available"); - if(data->set.tls_ech & CURLECH_HARD) { - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } - else { - struct Curl_https_rrinfo *rinfo = NULL; - - rinfo = dns->hinfo; - if(rinfo && rinfo->echconfiglist) { - unsigned char *ecl = rinfo->echconfiglist; - size_t elen = rinfo->echconfiglist_len; - - infof(data, "ECH: ECHConfig from DoH HTTPS RR"); - if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32) elen) != - WOLFSSL_SUCCESS) { - infof(data, "ECH: wolfSSL_SetEchConfigs failed"); - if(data->set.tls_ech & CURLECH_HARD) { - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } - else { - trying_ech_now = 1; - infof(data, "ECH: imported ECHConfigList of length %ld", elen); - } - } - else { - infof(data, "ECH: requested but no ECHConfig available"); - if(data->set.tls_ech & CURLECH_HARD) { - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } - Curl_resolv_unlink(data, &dns); - } - } - - if(trying_ech_now && wolfSSL_set_min_proto_version(wctx->ssl, - TLS1_3_VERSION) != 1) { - infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } -#endif /* USE_ECH_WOLFSSL */ +#endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */ result = CURLE_OK; @@ -1460,12 +1487,27 @@ out: return result; } +bool Curl_wssl_need_httpsrr(struct Curl_easy *data) +{ +#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG + if(!CURLECH_ENABLED(data)) + return FALSE; + if((data->set.tls_ech & CURLECH_GREASE) || + (data->set.tls_ech & CURLECH_CLA_CFG)) + return FALSE; + return TRUE; +#else + (void)data; + return FALSE; +#endif +} + /* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. */ -static CURLcode -wssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) +static CURLcode wssl_connect_step1(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend; @@ -1477,9 +1519,8 @@ wssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(connssl->state == ssl_connection_complete) return CURLE_OK; - result = Curl_wssl_ctx_init(wssl, cf, data, &connssl->peer, - connssl->alpn, NULL, NULL, cf, - wssl_on_session_reuse); + result = Curl_wssl_ctx_init(wssl, cf, data, &connssl->peer, connssl->alpn, + NULL, NULL, cf, wssl_on_session_reuse); if(result) return result; @@ -1492,11 +1533,10 @@ wssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } #endif - /* Enable RFC2818 checks */ - if(conn_config->verifyhost) { - char *snihost = connssl->peer.sni ? - connssl->peer.sni : connssl->peer.hostname; - if(wolfSSL_check_domain_name(wssl->ssl, snihost) != + /* Enable RFC2818 checks on domain names. This cannot check + * IP addresses which we need to do extra after the handshake. */ + if(conn_config->verifyhost && connssl->peer.sni) { + if(wolfSSL_check_domain_name(wssl->ssl, connssl->peer.sni) != WOLFSSL_SUCCESS) { return CURLE_SSL_CONNECT_ERROR; } @@ -1506,6 +1546,8 @@ wssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { WOLFSSL_BIO *bio; + if(!wssl_bio_cf_method) + return CURLE_FAILED_INIT; bio = wolfSSL_BIO_new(wssl_bio_cf_method); if(!bio) return CURLE_OUT_OF_MEMORY; @@ -1513,21 +1555,23 @@ wssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) wolfSSL_BIO_set_data(bio, cf); wolfSSL_set_bio(wssl->ssl, bio, bio); } -#else /* USE_BIO_CHAIN */ +#else /* !USE_BIO_CHAIN */ + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + if(sockfd > INT_MAX) { + failf(data, "SSL: socket value too large"); + return CURLE_SSL_CONNECT_ERROR; + } /* pass the raw socket into the SSL layer */ - if(!wolfSSL_set_fd(wssl->ssl, - (int)Curl_conn_cf_get_socket(cf, data))) { + if(!wolfSSL_set_fd(wssl->ssl, (int)sockfd)) { failf(data, "SSL: wolfSSL_set_fd failed"); return CURLE_SSL_CONNECT_ERROR; } -#endif /* !USE_BIO_CHAIN */ +#endif /* USE_BIO_CHAIN */ return CURLE_OK; } - -static char *wssl_strerror(unsigned long error, char *buf, - unsigned long size) +static char *wssl_strerror(unsigned long error, char *buf, unsigned long size) { DEBUGASSERT(size > 40); *buf = '\0'; @@ -1536,8 +1580,7 @@ static char *wssl_strerror(unsigned long error, char *buf, if(!*buf) { const char *msg = error ? "Unknown error" : "No error"; - /* the string fits because the assert above assures this */ - strcpy(buf, msg); + curlx_strcopy(buf, size, msg, strlen(msg)); } return buf; @@ -1547,6 +1590,8 @@ CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf, struct Curl_easy *data, struct wssl_ctx *wssl) { + WOLFSSL_X509 *x509 = NULL; + CURLcode result = CURLE_OK; #ifndef CURL_DISABLE_PROXY const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf) ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : @@ -1558,50 +1603,48 @@ CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf, if(pinnedpubkey) { #ifdef KEEP_PEER_CERT - WOLFSSL_X509 *x509; const char *x509_der; int x509_der_len; struct Curl_X509certificate x509_parsed; struct Curl_asn1Element *pubkey; - CURLcode result; + + result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; x509 = wolfSSL_get_peer_certificate(wssl->ssl); if(!x509) { failf(data, "SSL: failed retrieving server certificate"); - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; + goto end; } x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len); if(!x509_der) { failf(data, "SSL: failed retrieving ASN.1 server certificate"); - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; + goto end; } memset(&x509_parsed, 0, sizeof(x509_parsed)); if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; + goto end; pubkey = &x509_parsed.subjectPublicKeyInfo; if(!pubkey->header || pubkey->end <= pubkey->header) { failf(data, "SSL: failed retrieving public key from server certificate"); - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; + goto end; } - result = Curl_pin_peer_pubkey(data, - pinnedpubkey, + result = Curl_pin_peer_pubkey(data, pinnedpubkey, (const unsigned char *)pubkey->header, (size_t)(pubkey->end - pubkey->header)); - wolfSSL_FreeX509(x509); - if(result) { + if(result) failf(data, "SSL: public key does not match pinned public key"); - return result; - } #else failf(data, "Library lacks pinning support built-in"); return CURLE_NOT_BUILT_IN; #endif } - return CURLE_OK; +end: + wolfSSL_FreeX509(x509); + return result; } #ifdef WOLFSSL_EARLY_DATA @@ -1610,7 +1653,6 @@ static CURLcode wssl_send_earlydata(struct Curl_cfilter *cf, { struct ssl_connect_data *connssl = cf->ctx; struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend; - CURLcode result = CURLE_OK; const unsigned char *buf; size_t blen; @@ -1625,24 +1667,17 @@ static CURLcode wssl_send_earlydata(struct Curl_cfilter *cf, blen, rc, nwritten); if(rc < 0) { 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: - result = CURLE_AGAIN; - break; - default: { - char error_buffer[256]; - int detail = wolfSSL_get_error(wssl->ssl, err); - CURL_TRC_CF(data, cf, "SSL send early data, error: '%s'(%d)", - wssl_strerror((unsigned long)err, error_buffer, - sizeof(error_buffer)), - detail); - result = CURLE_SEND_ERROR; - break; + return CURLE_AGAIN; } - } - goto out; + CURL_TRC_CF(data, cf, "SSL send early data, error: '%s'(%d)", + wssl_strerror((unsigned long)err, error_buffer, + sizeof(error_buffer)), err); + return CURLE_SEND_ERROR; } Curl_bufq_skip(&connssl->earlydata, (size_t)nwritten); @@ -1652,13 +1687,11 @@ static CURLcode wssl_send_earlydata(struct Curl_cfilter *cf, if(!Curl_ssl_cf_is_proxy(cf)) Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip); infof(data, "SSL sending %zu bytes of early data", connssl->earlydata_skip); -out: - return result; + return CURLE_OK; } #endif /* WOLFSSL_EARLY_DATA */ -static CURLcode wssl_handshake(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend; @@ -1716,11 +1749,30 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, wolfSSL_FreeArrays(wssl->ssl); } } -#endif /* OPENSSL_EXTRA */ +#endif /* OPENSSL_EXTRA */ detail = wolfSSL_get_error(wssl->ssl, ret); CURL_TRC_CF(data, cf, "wolfSSL_connect() -> %d, detail=%d", ret, detail); + /* On a successful handshake with an IP address, do an extra check + * on the peer certificate */ + if(ret == WOLFSSL_SUCCESS && + conn_config->verifyhost && + !connssl->peer.sni) { + /* we have an IP address as hostname. */ + WOLFSSL_X509 *cert = wolfSSL_get_peer_certificate(wssl->ssl); + if(!cert) { + failf(data, "unable to get peer certificate"); + return CURLE_PEER_FAILED_VERIFICATION; + } + ret = wolfSSL_X509_check_ip_asc(cert, connssl->peer.hostname, 0); + CURL_TRC_CF(data, cf, "check peer certificate for IP match on %s -> %d", + connssl->peer.hostname, ret); + if(ret != WOLFSSL_SUCCESS) + detail = DOMAIN_NAME_MISMATCH; + wolfSSL_X509_free(cert); + } + if(ret == WOLFSSL_SUCCESS) { return CURLE_OK; } @@ -1735,7 +1787,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, } else if(DOMAIN_NAME_MISMATCH == detail) { /* There is no easy way to override only the CN matching. - * This will enable the override of both mismatching SubjectAltNames + * This enables the override of both mismatching SubjectAltNames * as also mismatching CN fields */ failf(data, " subject alt name(s) or common name do not match \"%s\"", connssl->peer.dispname); @@ -1746,7 +1798,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, 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"); @@ -1762,14 +1814,14 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, } else if(wssl->io_result) { switch(wssl->io_result) { - case CURLE_SEND_ERROR: - case CURLE_RECV_ERROR: - return CURLE_SSL_CONNECT_ERROR; - default: - return wssl->io_result; + case CURLE_SEND_ERROR: + case CURLE_RECV_ERROR: + return CURLE_SSL_CONNECT_ERROR; + default: + return wssl->io_result; } } -#ifdef USE_ECH_WOLFSSL +#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG else if(detail == -1) { /* try access a retry_config ECHConfigList for tracing */ byte echConfigs[1000]; @@ -1777,8 +1829,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, int rv = 0; /* this currently does not produce the retry_configs */ - rv = wolfSSL_GetEchConfigs(wssl->ssl, echConfigs, - &echConfigsLen); + rv = wolfSSL_GetEchConfigs(wssl->ssl, echConfigs, &echConfigsLen); if(rv != WOLFSSL_SUCCESS) { infof(data, "Failed to get ECHConfigs"); } @@ -1786,11 +1837,11 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, char *b64str = NULL; size_t blen = 0; - result = curlx_base64_encode((const char *)echConfigs, echConfigsLen, + result = curlx_base64_encode(echConfigs, echConfigsLen, &b64str, &blen); if(!result && b64str) infof(data, "ECH: (not yet) retry_configs %s", b64str); - free(b64str); + curlx_free(b64str); } return CURLE_SSL_CONNECT_ERROR; } @@ -1876,10 +1927,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; - int detail; + VERBOSE(char error_buffer[256]); DEBUGASSERT(wctx); if(!wctx->ssl || cf->shutdown) { @@ -1921,7 +1971,8 @@ static CURLcode wssl_shutdown(struct Curl_cfilter *cf, * was not complete, we are lacking the close notify from the server. */ if(send_shutdown) { wolfSSL_ERR_clear_error(); - if(wolfSSL_shutdown(wctx->ssl) == 1) { + nread = wolfSSL_shutdown(wctx->ssl); + if(nread == 1) { CURL_TRC_CF(data, cf, "SSL shutdown finished"); *done = TRUE; goto out; @@ -1947,7 +1998,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. */ @@ -1959,11 +2010,10 @@ static CURLcode wssl_shutdown(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_SEND; break; default: - detail = wolfSSL_get_error(wctx->ssl, err); CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s'(%d)", wssl_strerror((unsigned long)err, error_buffer, sizeof(error_buffer)), - detail); + err); result = CURLE_RECV_ERROR; break; } @@ -2021,13 +2071,13 @@ static CURLcode wssl_recv(struct Curl_cfilter *cf, case WOLFSSL_ERROR_NONE: case WOLFSSL_ERROR_WANT_READ: case WOLFSSL_ERROR_WANT_WRITE: - if(!wssl->io_result && connssl->peer_closed) { - CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen); - return CURLE_OK; + if(!wssl->io_result && !connssl->peer_closed) { + /* there is data pending, re-invoke wolfSSL_read() */ + CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen); + return CURLE_AGAIN; } - /* there is data pending, re-invoke wolfSSL_read() */ - CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen); - return CURLE_AGAIN; + /* fall through to default error handling below */ + FALLTHROUGH(); default: if(wssl->io_result == CURLE_AGAIN) { CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen); @@ -2054,14 +2104,9 @@ static CURLcode wssl_recv(struct Curl_cfilter *cf, size_t Curl_wssl_version(char *buffer, size_t size) { -#if LIBWOLFSSL_VERSION_HEX >= 0x03006000 - return msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version()); -#elif defined(WOLFSSL_VERSION) - return msnprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION); -#endif + return curl_msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version()); } - static int wssl_init(void) { int ret; @@ -2070,11 +2115,11 @@ static int wssl_init(void) Curl_tls_keylog_open(); #endif ret = (wolfSSL_Init() == WOLFSSL_SUCCESS); - wssl_bio_cf_init_methods(); + if(ret) + ret = wssl_bio_cf_init_methods(); return ret; } - static void wssl_cleanup(void) { wssl_bio_cf_free_methods(); @@ -2084,7 +2129,6 @@ static void wssl_cleanup(void) #endif } - static bool wssl_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { @@ -2101,16 +2145,12 @@ static bool wssl_data_pending(struct Curl_cfilter *cf, return FALSE; } -void Curl_wssl_report_handshake(struct Curl_easy *data, - struct wssl_ctx *wssl) +void Curl_wssl_report_handshake(struct Curl_easy *data, struct wssl_ctx *wssl) { -#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010) - infof(data, "SSL connection using %s / %s", - wolfSSL_get_version(wssl->ssl), - wolfSSL_get_cipher_name(wssl->ssl)); -#else - infof(data, "SSL connected"); -#endif + (void)wssl; + infof(data, "SSL connection using %s / %s", + wolfSSL_get_version(wssl->ssl), + wolfSSL_get_cipher_name(wssl->ssl)); } static CURLcode wssl_connect(struct Curl_cfilter *cf, @@ -2131,6 +2171,15 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_NONE; if(ssl_connect_1 == connssl->connecting_state) { +#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG + /* if we do ECH and need the HTTPS-RR information for it, + * we delay the connect until it arrives or DNS resolve fails. */ + if(Curl_wssl_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); + return CURLE_OK; + } +#endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */ result = wssl_connect_step1(cf, data); if(result) return result; @@ -2151,8 +2200,8 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, } if(ssl_connect_3 == connssl->connecting_state) { - /* Once the handshake has errored, it stays in that state and will - * error again on every call. */ + /* Once the handshake has errored, it stays in that state and + * errors again on every call. */ if(wssl->hs_result) { result = wssl->hs_result; goto out; @@ -2270,7 +2319,7 @@ const struct Curl_ssl Curl_ssl_wolfssl = { #endif SSLSUPP_CA_PATH | SSLSUPP_CAINFO_BLOB | -#ifdef USE_ECH_WOLFSSL +#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG SSLSUPP_ECH | #endif SSLSUPP_SSL_CTX | @@ -2278,7 +2327,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/lib/vtls/wolfssl.h b/lib/vtls/wolfssl.h index 7ff4cfb881..778002840d 100644 --- a/lib/vtls/wolfssl.h +++ b/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; @@ -41,9 +41,9 @@ extern const struct Curl_ssl Curl_ssl_wolfssl; struct wssl_ctx { struct WOLFSSL_CTX *ssl_ctx; - struct WOLFSSL *ssl; - CURLcode io_result; /* result of last BIO cfilter operation */ - CURLcode hs_result; /* result of handshake */ + struct WOLFSSL *ssl; + CURLcode io_result; /* result of last BIO cfilter operation */ + CURLcode hs_result; /* result of handshake */ int io_send_blocked_len; /* length of last BIO write that EAGAIN-ed */ BIT(x509_store_setup); /* x509 store has been set up */ BIT(shutting_down); /* TLS is being shut down */ @@ -65,16 +65,20 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, - const struct alpn_spec *alpns, + const struct alpn_spec *alpns_requested, Curl_wssl_ctx_setup_cb *cb_setup, void *cb_user_data, void *ssl_user_data, Curl_wssl_init_session_reuse_cb *sess_reuse_cb); +/* Is a resolved HTTPS-RR needed for initializing wolfSSL? */ +bool Curl_wssl_need_httpsrr(struct Curl_easy *data); + CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, struct wssl_ctx *wssl); +#ifdef HAVE_EX_DATA CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, struct Curl_easy *data, const char *ssl_peer_key, @@ -83,13 +87,13 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, const char *alpn, unsigned char *quic_tp, size_t quic_tp_len); +#endif CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf, struct Curl_easy *data, struct wssl_ctx *wssl); -void Curl_wssl_report_handshake(struct Curl_easy *data, - struct wssl_ctx *wssl); +void Curl_wssl_report_handshake(struct Curl_easy *data, struct wssl_ctx *wssl); #endif /* USE_WOLFSSL */ #endif /* HEADER_CURL_WOLFSSL_H */ diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c index fe47e81a87..7b83dd542a 100644 --- a/lib/vtls/x509asn1.c +++ b/lib/vtls/x509asn1.c @@ -21,8 +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) @@ -37,68 +36,22 @@ #define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */ #endif -#include -#include "../urldata.h" -#include "../curl_ctype.h" -#include "hostcheck.h" -#include "vtls.h" -#include "vtls_int.h" -#include "../sendf.h" -#include "../curlx/inet_pton.h" -#include "../curlx/base64.h" -#include "x509asn1.h" -#include "../curlx/dynbuf.h" - -/* The last 3 #include files should be in this order */ -#include "../curl_printf.h" -#include "../curl_memory.h" -#include "../memdebug.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. */ /* Largest supported ASN.1 structure. */ -#define CURL_ASN1_MAX ((size_t) 0x40000) /* 256K */ - -/* ASN.1 classes. */ -/* #define CURL_ASN1_UNIVERSAL 0 */ -/* #define CURL_ASN1_APPLICATION 1 */ -/* #define CURL_ASN1_CONTEXT_SPECIFIC 2 */ -/* #define CURL_ASN1_PRIVATE 3 */ - -/* ASN.1 types. */ -#define CURL_ASN1_BOOLEAN 1 -#define CURL_ASN1_INTEGER 2 -#define CURL_ASN1_BIT_STRING 3 -#define CURL_ASN1_OCTET_STRING 4 -#define CURL_ASN1_NULL 5 -#define CURL_ASN1_OBJECT_IDENTIFIER 6 -/* #define CURL_ASN1_OBJECT_DESCRIPTOR 7 */ -/* #define CURL_ASN1_INSTANCE_OF 8 */ -/* #define CURL_ASN1_REAL 9 */ -#define CURL_ASN1_ENUMERATED 10 -/* #define CURL_ASN1_EMBEDDED 11 */ -#define CURL_ASN1_UTF8_STRING 12 -/* #define CURL_ASN1_RELATIVE_OID 13 */ -/* #define CURL_ASN1_SEQUENCE 16 */ -/* #define CURL_ASN1_SET 17 */ -#define CURL_ASN1_NUMERIC_STRING 18 -#define CURL_ASN1_PRINTABLE_STRING 19 -#define CURL_ASN1_TELETEX_STRING 20 -/* #define CURL_ASN1_VIDEOTEX_STRING 21 */ -#define CURL_ASN1_IA5_STRING 22 -#define CURL_ASN1_UTC_TIME 23 -#define CURL_ASN1_GENERALIZED_TIME 24 -/* #define CURL_ASN1_GRAPHIC_STRING 25 */ -#define CURL_ASN1_VISIBLE_STRING 26 -/* #define CURL_ASN1_GENERAL_STRING 27 */ -#define CURL_ASN1_UNIVERSAL_STRING 28 -/* #define CURL_ASN1_CHARACTER_STRING 29 */ -#define CURL_ASN1_BMP_STRING 30 - +#define CURL_ASN1_MAX ((size_t)0x40000) /* 256K */ #ifdef WANT_EXTRACT_CERTINFO + /* ASN.1 OID table entry. */ struct Curl_OID { const char *numoid; /* Dotted-numeric OID. */ @@ -158,7 +111,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 */ @@ -172,10 +125,6 @@ static const struct Curl_OID OIDtable[] = { * Please note there is no pretension here to rewrite a full SSL library. */ -static const char *getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) - WARN_UNUSED_RESULT; - #define CURL_ASN1_MAX_RECURSIONS 16 static const char *getASN1Element_(struct Curl_asn1Element *elem, @@ -192,14 +141,14 @@ static const char *getASN1Element_(struct Curl_asn1Element *elem, if an error occurs. */ if(!beg || !end || beg >= end || !*beg || ((size_t)(end - beg) > CURL_ASN1_MAX) || - lvl >= CURL_ASN1_MAX_RECURSIONS) + lvl >= CURL_ASN1_MAX_RECURSIONS) return NULL; /* Process header byte. */ elem->header = beg; - b = (unsigned char) *beg++; + b = (unsigned char)*beg++; elem->constructed = (b & 0x20) != 0; - elem->class = (b >> 6) & 3; + elem->eclass = (b >> 6) & 3; b &= 0x1F; if(b == 0x1F) return NULL; /* Long tag values not supported here. */ @@ -208,35 +157,38 @@ static const char *getASN1Element_(struct Curl_asn1Element *elem, /* Process length. */ if(beg >= end) return NULL; - b = (unsigned char) *beg++; + b = (unsigned char)*beg++; if(!(b & 0x80)) len = b; - else if(!(b &= 0x7F)) { - /* Unspecified length. Since we have all the data, we can determine the - effective length by skipping element until an end element is found. */ - if(!elem->constructed) - return NULL; - elem->beg = beg; - while(beg < end && *beg) { - beg = getASN1Element_(&lelem, beg, end, lvl + 1); - if(!beg) - return NULL; - } - if(beg >= end) - return NULL; - elem->end = beg; - return beg + 1; - } - else if((unsigned)b > (size_t)(end - beg)) - return NULL; /* Does not fit in source. */ else { - /* Get long length. */ - len = 0; - do { - if(len & 0xFF000000L) - return NULL; /* Lengths > 32 bits are not supported. */ - len = (len << 8) | (unsigned char) *beg++; - } while(--b); + b &= 0x7F; + if(!b) { + /* Unspecified length. Since we have all the data, we can determine the + effective length by skipping element until an end element is found. */ + if(!elem->constructed) + return NULL; + elem->beg = beg; + while(beg < end && *beg) { + beg = getASN1Element_(&lelem, beg, end, lvl + 1); + if(!beg) + return NULL; + } + if(beg >= end) + return NULL; + elem->end = beg; + return beg + 1; + } + else if((unsigned)b > (size_t)(end - beg)) + return NULL; /* Does not fit in source. */ + else { + /* Get long length. */ + len = 0; + do { + if(len & 0xFF000000L) + return NULL; /* Lengths > 32 bits are not supported. */ + len = (len << 8) | (unsigned char) *beg++; + } while(--b); + } } if(len > (size_t)(end - beg)) return NULL; /* Element data does not fit in source. */ @@ -245,8 +197,13 @@ static const char *getASN1Element_(struct Curl_asn1Element *elem, return elem->end; } -static const char *getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) +/* + * unit test @1657 + */ +UNITTEST const char *getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end); +UNITTEST const char *getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end) { return getASN1Element_(elem, beg, end, 0); } @@ -267,17 +224,6 @@ static const struct Curl_OID *searchOID(const char *oid) return NULL; } -#ifdef UNITTESTS -/* used by unit1657.c */ -CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) -{ - if(getASN1Element(elem, beg, end)) - return CURLE_OK; - return CURLE_BAD_FUNCTION_ARGUMENT; -} -#endif - /* * Convert an ASN.1 Boolean value into its string representation. * @@ -287,9 +233,13 @@ CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem, static CURLcode bool2str(struct dynbuf *store, const char *beg, const char *end) { + uint8_t val; if(end - beg != 1) return CURLE_BAD_FUNCTION_ARGUMENT; - return curlx_dyn_add(store, *beg ? "TRUE": "FALSE"); + val = (uint8_t)*beg; + if((val != 0xff) && (val != 0x00)) + return CURLE_BAD_FUNCTION_ARGUMENT; + return curlx_dyn_add(store, val ? "TRUE" : "FALSE"); } /* @@ -303,17 +253,21 @@ static CURLcode octet2str(struct dynbuf *store, CURLcode result = CURLE_OK; while(!result && beg < end) - result = curlx_dyn_addf(store, "%02x:", (unsigned char) *beg++); + result = curlx_dyn_addf(store, "%02x:", (unsigned char)*beg++); return result; } -static CURLcode bit2str(struct dynbuf *store, - const char *beg, const char *end) +static CURLcode bit2str(struct dynbuf *store, const char *beg, const char *end) { /* Convert an ASN.1 bit string to a printable string. */ - if(++beg > end) + if(beg < end) { + uint8_t unused_bits = (uint8_t)*beg++; + if(unused_bits > 7) + return CURLE_BAD_FUNCTION_ARGUMENT; + } + else return CURLE_BAD_FUNCTION_ARGUMENT; return octet2str(store, beg, end); } @@ -323,11 +277,13 @@ static CURLcode bit2str(struct dynbuf *store, * * Returns error. */ -static CURLcode int2str(struct dynbuf *store, - const char *beg, const char *end) +static CURLcode int2str(struct dynbuf *store, const char *beg, const char *end) { - unsigned int val = 0; + uint32_t uval = 0; + int32_t sval = 0; size_t n = end - beg; + size_t i; + const unsigned char *p = (const unsigned char *)beg; if(!n) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -335,14 +291,20 @@ static CURLcode int2str(struct dynbuf *store, if(n > 4) return octet2str(store, beg, end); - /* Represent integers <= 32-bit as a single value. */ - if(*beg & 0x80) - val = ~val; + if(p[0] & 0x80) + uval = ~0U; - do - val = (val << 8) | *(const unsigned char *) beg++; - while(beg < end); - return curlx_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val); + for(i = 0; i < n; i++) + uval = (uval << 8) | p[i]; + + sval = (int32_t)uval; + + /* small values are easier to read as decimals */ + if(sval > -10000 && sval < 10000) + return curlx_dyn_addf(store, "%d", sval); + + /* represent larger values as hex */ + return curlx_dyn_addf(store, "0x%x", uval); } /* @@ -353,8 +315,8 @@ static CURLcode int2str(struct dynbuf *store, * * Returns error. */ -static CURLcode -utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end) +static CURLcode utf8asn1str(struct dynbuf *to, int type, const char *from, + const char *end) { size_t inlength = end - from; int size = 1; @@ -383,7 +345,7 @@ utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end) return CURLE_BAD_FUNCTION_ARGUMENT; if(type == CURL_ASN1_UTF8_STRING) { - /* Just copy. */ + /* copy. */ if(inlength) result = curlx_dyn_addn(to, from, inlength); } @@ -395,14 +357,14 @@ utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end) switch(size) { case 4: - wc = (wc << 8) | *(const unsigned char *) from++; - wc = (wc << 8) | *(const unsigned char *) from++; + wc = (wc << 8) | *(const unsigned char *)from++; + wc = (wc << 8) | *(const unsigned char *)from++; FALLTHROUGH(); case 2: - wc = (wc << 8) | *(const unsigned char *) from++; + wc = (wc << 8) | *(const unsigned char *)from++; FALLTHROUGH(); default: /* case 1: */ - wc = (wc << 8) | *(const unsigned char *) from++; + wc = (wc << 8) | *(const unsigned char *)from++; } if(wc >= 0x00000080) { if(wc >= 0x00000800) { @@ -411,19 +373,19 @@ utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end) /* Invalid char. size for target encoding. */ return CURLE_WEIRD_SERVER_REPLY; } - buf[3] = (char) (0x80 | (wc & 0x3F)); + buf[3] = (char)(0x80 | (wc & 0x3F)); wc = (wc >> 6) | 0x00010000; charsize++; } - buf[2] = (char) (0x80 | (wc & 0x3F)); + buf[2] = (char)(0x80 | (wc & 0x3F)); wc = (wc >> 6) | 0x00000800; charsize++; } - buf[1] = (char) (0x80 | (wc & 0x3F)); + buf[1] = (char)(0x80 | (wc & 0x3F)); wc = (wc >> 6) | 0x000000C0; charsize++; } - buf[0] = (char) wc; + buf[0] = (char)wc; result = curlx_dyn_addn(to, buf, charsize); } } @@ -431,33 +393,68 @@ utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end) } /* - * Convert an ASN.1 OID into its dotted string representation. + * Convert an ASN.1 OID into its dotted string representation. Each field is + * limited to unsigned 32-bit values. + * + * A minor limitation is in the second number, which can be no larger than 32 + * bits - 80 == 4294967215. * * Return error code. + * + * @unittest 1666 */ -static CURLcode encodeOID(struct dynbuf *store, - const char *beg, const char *end) +UNITTEST CURLcode encodeOID(struct dynbuf *store, + const char *beg, const char *end); +UNITTEST CURLcode encodeOID(struct dynbuf *store, + const char *beg, const char *end) { - unsigned int x; - unsigned int y; + uint32_t x; + uint32_t y; CURLcode result = CURLE_OK; - /* Process the first two numbers. */ - y = *(const unsigned char *) beg++; - x = y / 40; - y -= x * 40; + if(end <= beg) + return CURLE_BAD_FUNCTION_ARGUMENT; - result = curlx_dyn_addf(store, "%u.%u", x, y); + /* Process the first two numbers. The initial digit cannot be larger than + 2 */ + y = *(const unsigned char *)beg++; + if(y <= 80) + x = y / 40; + else + x = 2; + result = curlx_dyn_addf(store, "%u", x); + if(result) + return result; + + if(y & 0x80) { + uint32_t t = (y & 0x7f); /* the second number */ + if(!t) + /* reject leading 0x80 padding */ + return CURLE_BAD_FUNCTION_ARGUMENT; + do { + if((t & 0xFE000000) || (beg >= end)) + return CURLE_BAD_FUNCTION_ARGUMENT; + y = *(const unsigned char *)beg++; + t = (t << 7) | (y & 0x7F); + } while(y & 0x80); + y = t; + } + + result = curlx_dyn_addf(store, ".%u", y - (x * 40)); if(result) return result; /* Process the trailing numbers. */ - while(beg < end) { + while((beg < end) && !result) { x = 0; + y = 0; do { - if(x & 0xFF000000) - return 0; - y = *(const unsigned char *) beg++; + /* 0x80 as the first value is invalid since it only adds padding */ + if((y & 0x80) && !x) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if((x & 0xFE000000) || (beg == end)) + return CURLE_BAD_FUNCTION_ARGUMENT; + y = *(const unsigned char *)beg++; x = (x << 7) | (y & 0x7F); } while(y & 0x80); result = curlx_dyn_addf(store, ".%u", x); @@ -472,32 +469,33 @@ static CURLcode encodeOID(struct dynbuf *store, */ static CURLcode OID2str(struct dynbuf *store, - const char *beg, const char *end, bool symbolic) + const char *beg, const char *end) { CURLcode result = CURLE_OK; if(beg < end) { - if(symbolic) { - struct dynbuf buf; - curlx_dyn_init(&buf, CURL_X509_STR_MAX); - result = encodeOID(&buf, beg, end); + struct dynbuf buf; + curlx_dyn_init(&buf, CURL_X509_STR_MAX); + result = encodeOID(&buf, beg, end); - if(!result) { - const struct Curl_OID *op = searchOID(curlx_dyn_ptr(&buf)); - if(op) - result = curlx_dyn_add(store, op->textoid); - else - result = curlx_dyn_add(store, curlx_dyn_ptr(&buf)); - curlx_dyn_free(&buf); - } + if(!result) { + const struct Curl_OID *op = searchOID(curlx_dyn_ptr(&buf)); + if(op) + result = curlx_dyn_add(store, op->textoid); + else + result = curlx_dyn_add(store, curlx_dyn_ptr(&buf)); } - else - result = encodeOID(store, beg, end); + curlx_dyn_free(&buf); } return result; } -static CURLcode GTime2str(struct dynbuf *store, - const char *beg, const char *end) +/* + * Unit test @1656 + */ +UNITTEST CURLcode GTime2str(struct dynbuf *store, + const char *beg, const char *end); +UNITTEST CURLcode GTime2str(struct dynbuf *store, + const char *beg, const char *end) { const char *tzp; const char *fracp; @@ -570,19 +568,10 @@ 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); } -#ifdef UNITTESTS -/* used by unit1656.c */ -CURLcode Curl_x509_GTime2str(struct dynbuf *store, - const char *beg, const char *end) -{ - return GTime2str(store, beg, end); -} -#endif - /* * Convert an ASN.1 UTC time to a printable string. * @@ -616,8 +605,6 @@ static CURLcode UTime2str(struct dynbuf *store, tzp = "GMT"; end = tzp + 3; } - else - tzp++; tzl = end - tzp; return curlx_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s", @@ -631,16 +618,16 @@ static CURLcode UTime2str(struct dynbuf *store, * * Return error */ -static CURLcode ASN1tostr(struct dynbuf *store, - struct Curl_asn1Element *elem, int type) +UNITTEST CURLcode ASN1tostr(struct dynbuf *store, + struct Curl_asn1Element *elem); +UNITTEST CURLcode ASN1tostr(struct dynbuf *store, + struct Curl_asn1Element *elem) { CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; + int type = elem->tag; if(elem->constructed) return result; /* No conversion of structured elements. */ - if(!type) - type = elem->tag; /* Type not forced: use element tag as type. */ - switch(type) { case CURL_ASN1_BOOLEAN: result = bool2str(store, elem->beg, elem->end); @@ -659,7 +646,7 @@ static CURLcode ASN1tostr(struct dynbuf *store, result = curlx_dyn_addn(store, "", 1); break; case CURL_ASN1_OBJECT_IDENTIFIER: - result = OID2str(store, elem->beg, elem->end, TRUE); + result = OID2str(store, elem->beg, elem->end); break; case CURL_ASN1_UTC_TIME: result = UTime2str(store, elem->beg, elem->end); @@ -724,7 +711,7 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn) goto error; } curlx_dyn_reset(&temp); - result = ASN1tostr(&temp, &oid, 0); + result = ASN1tostr(&temp, &oid); if(result) goto error; @@ -759,7 +746,7 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn) goto error; /* Generate value. */ - result = ASN1tostr(store, &value, 0); + result = ASN1tostr(store, &value); if(result) goto error; curlx_dyn_reset(&temp); @@ -917,7 +904,7 @@ static CURLcode dumpAlgo(struct dynbuf *store, if(!p) return CURLE_BAD_FUNCTION_ARGUMENT; } - return OID2str(store, oid.beg, oid.end, TRUE); + return OID2str(store, oid.beg, oid.end); } /* @@ -946,7 +933,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); @@ -968,7 +955,7 @@ static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum, /* Generate a certificate information record for the public key. */ - result = ASN1tostr(&out, elem, 0); + result = ASN1tostr(&out, elem); if(!result) { if(data->set.ssl.certinfo) result = ssl_push_certinfo_dyn(data, certnum, label, &out); @@ -978,8 +965,8 @@ static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum, } /* return 0 on success, 1 on error */ -static int do_pubkey(struct Curl_easy *data, int certnum, - const char *algo, struct Curl_asn1Element *param, +static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo, + struct Curl_asn1Element *param, struct Curl_asn1Element *pubkey) { struct Curl_asn1Element elem; @@ -993,12 +980,17 @@ static int do_pubkey(struct Curl_easy *data, int certnum, * 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]; - (void)msnprintf(q, sizeof(q), "%zu", len); + 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; } @@ -1024,7 +1016,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, len = ((elem.end - q) * 8); if(len) { unsigned int i; - for(i = *(const unsigned char *) q; !(i & 0x80); i <<= 1) + for(i = *(const unsigned char *)q; !(i & 0x80); i <<= 1) len--; } if(len > 32) @@ -1032,8 +1024,8 @@ static int do_pubkey(struct Curl_easy *data, int certnum, if(!certnum) infof(data, " RSA Public Key (%zu bits)", len); if(data->set.ssl.certinfo) { - char r[sizeof(len) * 8 / 3 + 1]; - msnprintf(r, sizeof(r), "%zu", len); + 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; } @@ -1083,8 +1075,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, * Convert an ASN.1 distinguished name into a printable string. * Return error. */ -static CURLcode DNtostr(struct dynbuf *store, - struct Curl_asn1Element *dn) +static CURLcode DNtostr(struct dynbuf *store, struct Curl_asn1Element *dn) { return encodeDN(store, dn); } @@ -1140,7 +1131,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, /* Version (always fits in less than 32 bits). */ version = 0; for(ptr = cert.version.beg; ptr < cert.version.end; ptr++) - version = (version << 8) | *(const unsigned char *) ptr; + version = (version << 8) | *(const unsigned char *)ptr; if(data->set.ssl.certinfo) { result = curlx_dyn_addf(&out, "%x", version); if(result) @@ -1152,7 +1143,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, } /* Serial number. */ - result = ASN1tostr(&out, &cert.serialNumber, 0); + result = ASN1tostr(&out, &cert.serialNumber); if(result) goto done; if(data->set.ssl.certinfo) { @@ -1168,15 +1159,14 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, if(result) goto done; if(data->set.ssl.certinfo) { - result = ssl_push_certinfo_dyn(data, certnum, "Signature Algorithm", - &out); + result = ssl_push_certinfo_dyn(data, certnum, "Signature Algorithm", &out); if(result) goto done; } curlx_dyn_reset(&out); /* Start Date. */ - result = ASN1tostr(&out, &cert.notBefore, 0); + result = ASN1tostr(&out, &cert.notBefore); if(result) goto done; if(data->set.ssl.certinfo) { @@ -1187,7 +1177,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, curlx_dyn_reset(&out); /* Expire Date. */ - result = ASN1tostr(&out, &cert.notAfter, 0); + result = ASN1tostr(&out, &cert.notAfter); if(result) goto done; if(data->set.ssl.certinfo) { @@ -1218,7 +1208,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, curlx_dyn_reset(&out); /* Signature. */ - result = ASN1tostr(&out, &cert.signature, 0); + result = ASN1tostr(&out, &cert.signature); if(result) goto done; if(data->set.ssl.certinfo) { @@ -1229,7 +1219,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, curlx_dyn_reset(&out); /* Generate PEM certificate. */ - result = curlx_base64_encode(cert.certificate.beg, + result = curlx_base64_encode((const uint8_t *)cert.certificate.beg, cert.certificate.end - cert.certificate.beg, &certptr, &clen); if(result) @@ -1261,7 +1251,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, if(!result) result = curlx_dyn_add(&out, "-----END CERTIFICATE-----\n"); } - free(certptr); + curlx_free(certptr); if(!result) if(data->set.ssl.certinfo) result = ssl_push_certinfo_dyn(data, certnum, "Cert", &out); @@ -1275,5 +1265,5 @@ done: #endif /* WANT_EXTRACT_CERTINFO */ -#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_MBEDTLS or +#endif /* USE_GNUTLS || USE_WOLFSSL || USE_SCHANNEL || USE_MBEDTLS || USE_RUSTLS */ diff --git a/lib/vtls/x509asn1.h b/lib/vtls/x509asn1.h index f9bd455b70..dda55ac77f 100644 --- a/lib/vtls/x509asn1.h +++ b/lib/vtls/x509asn1.h @@ -24,14 +24,52 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "curl_setup.h" -#include "../curl_setup.h" +/* forward-declare to make it visible in all builds */ +struct Curl_asn1Element; #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" + +/* ASN.1 classes. */ +/* #define CURL_ASN1_UNIVERSAL 0 */ +/* #define CURL_ASN1_APPLICATION 1 */ +/* #define CURL_ASN1_CONTEXT_SPECIFIC 2 */ +/* #define CURL_ASN1_PRIVATE 3 */ + +/* ASN.1 types. */ +#define CURL_ASN1_BOOLEAN 1 +#define CURL_ASN1_INTEGER 2 +#define CURL_ASN1_BIT_STRING 3 +#define CURL_ASN1_OCTET_STRING 4 +#define CURL_ASN1_NULL 5 +#define CURL_ASN1_OBJECT_IDENTIFIER 6 +/* #define CURL_ASN1_OBJECT_DESCRIPTOR 7 */ +/* #define CURL_ASN1_INSTANCE_OF 8 */ +/* #define CURL_ASN1_REAL 9 */ +#define CURL_ASN1_ENUMERATED 10 +/* #define CURL_ASN1_EMBEDDED 11 */ +#define CURL_ASN1_UTF8_STRING 12 +/* #define CURL_ASN1_RELATIVE_OID 13 */ +/* #define CURL_ASN1_SEQUENCE 16 */ +/* #define CURL_ASN1_SET 17 */ +#define CURL_ASN1_NUMERIC_STRING 18 +#define CURL_ASN1_PRINTABLE_STRING 19 +#define CURL_ASN1_TELETEX_STRING 20 +/* #define CURL_ASN1_VIDEOTEX_STRING 21 */ +#define CURL_ASN1_IA5_STRING 22 +#define CURL_ASN1_UTC_TIME 23 +#define CURL_ASN1_GENERALIZED_TIME 24 +/* #define CURL_ASN1_GRAPHIC_STRING 25 */ +#define CURL_ASN1_VISIBLE_STRING 26 +/* #define CURL_ASN1_GENERAL_STRING 27 */ +#define CURL_ASN1_UNIVERSAL_STRING 28 +/* #define CURL_ASN1_CHARACTER_STRING 29 */ +#define CURL_ASN1_BMP_STRING 30 /* * Types. @@ -42,7 +80,7 @@ struct Curl_asn1Element { const char *header; /* Pointer to header byte. */ const char *beg; /* Pointer to element data. */ const char *end; /* Pointer to 1st byte after element. */ - unsigned char class; /* ASN.1 element class. */ + unsigned char eclass; /* ASN.1 element class. */ unsigned char tag; /* ASN.1 element tag. */ BIT(constructed); /* Element is constructed. */ }; @@ -76,20 +114,6 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum, const char *beg, const char *end); CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data, const char *beg, const char *end); - -#ifdef UNITTESTS -#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS) || \ - defined(USE_RUSTLS) - -/* used by unit1656.c */ -CURLcode Curl_x509_GTime2str(struct dynbuf *store, - const char *beg, const char *end); -/* used by unit1657.c */ -CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end); -#endif -#endif - -#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_MBEDTLS or +#endif /* USE_GNUTLS || USE_WOLFSSL || USE_SCHANNEL || USE_MBEDTLS || USE_RUSTLS */ #endif /* HEADER_CURL_X509ASN1_H */ diff --git a/lib/ws.c b/lib/ws.c index 3b65428160..e3b8f6ce9b 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -22,11 +22,11 @@ * ***************************************************************************/ #include "curl_setup.h" -#include - -#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) - #include "urldata.h" +#include "ws.h" + +#ifndef CURL_DISABLE_WEBSOCKETS + #include "url.h" #include "bufq.h" #include "curlx/dynbuf.h" @@ -34,19 +34,13 @@ #include "curlx/base64.h" #include "connect.h" #include "sendf.h" +#include "curl_trc.h" #include "multiif.h" -#include "ws.h" #include "easyif.h" #include "transfer.h" #include "select.h" -#include "curlx/nonblock.h" #include "curlx/strparse.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - +#include "curlx/strcopy.h" /*** RFC 6455 Section 5.2 @@ -58,26 +52,27 @@ |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_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_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 +#ifdef CURLVERBOSE +#define WSBIT_OPCODE_MASK 0xf +#endif #define WSBIT_MASK 0x80 /* buffer dimensioning */ -#define WS_CHUNK_SIZE 65535 +#define WS_CHUNK_SIZE 65535 #define WS_CHUNK_COUNT 2 - /* a client-side WS frame decoder, parsing frame headers and * payload, keeping track of current position and stats */ enum ws_dec_state { @@ -91,7 +86,7 @@ struct ws_decoder { int frame_flags; /* See the CURLWS_* defines */ curl_off_t payload_offset; /* the offset parsing is at */ curl_off_t payload_len; - unsigned char head[10]; + uint8_t head[10]; int head_len, head_total; enum ws_dec_state state; int cont_flags; @@ -103,8 +98,8 @@ struct ws_encoder { curl_off_t payload_len; /* payload length of current frame */ curl_off_t payload_remain; /* remaining payload of current */ unsigned int xori; /* xor index */ - unsigned char mask[4]; /* 32-bit mask for this connection */ - unsigned char firstbyte; /* first byte of frame we encode */ + uint8_t mask[4]; /* 32-bit mask for this connection */ + uint8_t firstbyte; /* first byte of frame we encode */ BIT(contfragment); /* set TRUE if the previous fragment sent was not final */ }; @@ -114,7 +109,7 @@ struct ws_encoder { struct ws_cntrl_frame { unsigned int type; size_t payload_len; - unsigned char payload[WS_MAX_CNTRL_LEN]; + uint8_t payload[WS_MAX_CNTRL_LEN]; }; /* A websocket connection with en- and decoder that treat frames @@ -130,172 +125,174 @@ struct websocket { size_t sendbuf_payload; /* number of payload bytes in sendbuf */ }; - -static const char *ws_frame_name_of_op(unsigned char firstbyte) +#ifdef CURLVERBOSE +static const char *ws_frame_name_of_op(uint8_t firstbyte) { switch(firstbyte & WSBIT_OPCODE_MASK) { - case WSBIT_OPCODE_CONT: - return "CONT"; - case WSBIT_OPCODE_TEXT: - return "TEXT"; - case WSBIT_OPCODE_BIN: - return "BIN"; - case WSBIT_OPCODE_CLOSE: - return "CLOSE"; - case WSBIT_OPCODE_PING: - return "PING"; - case WSBIT_OPCODE_PONG: - return "PONG"; - default: - return "???"; + case WSBIT_OPCODE_CONT: + return "CONT"; + case WSBIT_OPCODE_TEXT: + return "TEXT"; + case WSBIT_OPCODE_BIN: + return "BIN"; + case WSBIT_OPCODE_CLOSE: + return "CLOSE"; + case WSBIT_OPCODE_PING: + return "PING"; + case WSBIT_OPCODE_PONG: + return "PONG"; + default: + return "???"; } } +#endif static int ws_frame_firstbyte2flags(struct Curl_easy *data, - unsigned char firstbyte, int cont_flags) + uint8_t firstbyte, int cont_flags) { switch(firstbyte) { - /* 0x00 - intermediate TEXT/BINARY fragment */ - case WSBIT_OPCODE_CONT: - if(!(cont_flags & CURLWS_CONT)) { - failf(data, "[WS] no ongoing fragmented message to resume"); - return 0; - } - return cont_flags | CURLWS_CONT; - /* 0x80 - final TEXT/BIN fragment */ - case (WSBIT_OPCODE_CONT | WSBIT_FIN): - if(!(cont_flags & CURLWS_CONT)) { - failf(data, "[WS] no ongoing fragmented message to resume"); - return 0; - } - return cont_flags & ~CURLWS_CONT; - /* 0x01 - first TEXT fragment */ - case WSBIT_OPCODE_TEXT: - if(cont_flags & CURLWS_CONT) { - failf(data, "[WS] fragmented message interrupted by new TEXT msg"); - return 0; - } - return CURLWS_TEXT | CURLWS_CONT; - /* 0x81 - unfragmented TEXT msg */ - case (WSBIT_OPCODE_TEXT | WSBIT_FIN): - if(cont_flags & CURLWS_CONT) { - failf(data, "[WS] fragmented message interrupted by new TEXT msg"); - return 0; - } - return CURLWS_TEXT; - /* 0x02 - first BINARY fragment */ - case WSBIT_OPCODE_BIN: - if(cont_flags & CURLWS_CONT) { - failf(data, "[WS] fragmented message interrupted by new BINARY msg"); - return 0; - } - return CURLWS_BINARY | CURLWS_CONT; - /* 0x82 - unfragmented BINARY msg */ - case (WSBIT_OPCODE_BIN | WSBIT_FIN): - if(cont_flags & CURLWS_CONT) { - failf(data, "[WS] fragmented message interrupted by new BINARY msg"); - return 0; - } - return CURLWS_BINARY; - /* 0x08 - first CLOSE fragment */ - case WSBIT_OPCODE_CLOSE: - failf(data, "[WS] invalid fragmented CLOSE frame"); + /* 0x00 - intermediate TEXT/BINARY fragment */ + case WSBIT_OPCODE_CONT: + if(!(cont_flags & CURLWS_CONT)) { + failf(data, "[WS] no ongoing fragmented message to resume"); return 0; - /* 0x88 - unfragmented CLOSE */ - case (WSBIT_OPCODE_CLOSE | WSBIT_FIN): - return CURLWS_CLOSE; - /* 0x09 - first PING fragment */ - case WSBIT_OPCODE_PING: - failf(data, "[WS] invalid fragmented PING frame"); + } + return cont_flags | CURLWS_CONT; + /* 0x80 - final TEXT/BIN fragment */ + case (WSBIT_OPCODE_CONT | WSBIT_FIN): + if(!(cont_flags & CURLWS_CONT)) { + failf(data, "[WS] no ongoing fragmented message to resume"); return 0; - /* 0x89 - unfragmented PING */ - case (WSBIT_OPCODE_PING | WSBIT_FIN): - return CURLWS_PING; - /* 0x0a - first PONG fragment */ - case WSBIT_OPCODE_PONG: - failf(data, "[WS] invalid fragmented PONG frame"); + } + return cont_flags & ~CURLWS_CONT; + /* 0x01 - first TEXT fragment */ + case WSBIT_OPCODE_TEXT: + if(cont_flags & CURLWS_CONT) { + failf(data, "[WS] fragmented message interrupted by new TEXT msg"); return 0; - /* 0x8a - unfragmented PONG */ - case (WSBIT_OPCODE_PONG | WSBIT_FIN): - return CURLWS_PONG; - /* invalid first byte */ - default: - if(firstbyte & WSBIT_RSV_MASK) - /* any of the reserved bits 0x40/0x20/0x10 are set */ - failf(data, "[WS] invalid reserved bits: %02x", firstbyte); - else - /* any of the reserved opcodes 0x3-0x7 or 0xb-0xf is used */ - failf(data, "[WS] invalid opcode: %02x", firstbyte); + } + return CURLWS_TEXT | CURLWS_CONT; + /* 0x81 - unfragmented TEXT msg */ + case (WSBIT_OPCODE_TEXT | WSBIT_FIN): + if(cont_flags & CURLWS_CONT) { + failf(data, "[WS] fragmented message interrupted by new TEXT msg"); return 0; + } + return CURLWS_TEXT; + /* 0x02 - first BINARY fragment */ + case WSBIT_OPCODE_BIN: + if(cont_flags & CURLWS_CONT) { + failf(data, "[WS] fragmented message interrupted by new BINARY msg"); + return 0; + } + return CURLWS_BINARY | CURLWS_CONT; + /* 0x82 - unfragmented BINARY msg */ + case (WSBIT_OPCODE_BIN | WSBIT_FIN): + if(cont_flags & CURLWS_CONT) { + failf(data, "[WS] fragmented message interrupted by new BINARY msg"); + return 0; + } + return CURLWS_BINARY; + /* 0x08 - first CLOSE fragment */ + case WSBIT_OPCODE_CLOSE: + failf(data, "[WS] invalid fragmented CLOSE frame"); + return 0; + /* 0x88 - unfragmented CLOSE */ + case (WSBIT_OPCODE_CLOSE | WSBIT_FIN): + return CURLWS_CLOSE; + /* 0x09 - first PING fragment */ + case WSBIT_OPCODE_PING: + failf(data, "[WS] invalid fragmented PING frame"); + return 0; + /* 0x89 - unfragmented PING */ + case (WSBIT_OPCODE_PING | WSBIT_FIN): + return CURLWS_PING; + /* 0x0a - first PONG fragment */ + case WSBIT_OPCODE_PONG: + failf(data, "[WS] invalid fragmented PONG frame"); + return 0; + /* 0x8a - unfragmented PONG */ + case (WSBIT_OPCODE_PONG | WSBIT_FIN): + return CURLWS_PONG; + /* invalid first byte */ + default: + if(firstbyte & WSBIT_RSV_MASK) + /* any of the reserved bits 0x40/0x20/0x10 are set */ + failf(data, "[WS] invalid reserved bits: %02x", firstbyte); + else + /* any of the reserved opcodes 0x3-0x7 or 0xb-0xf is used */ + failf(data, "[WS] invalid opcode: %02x", firstbyte); + return 0; } } static CURLcode ws_frame_flags2firstbyte(struct Curl_easy *data, unsigned int flags, bool contfragment, - unsigned char *pfirstbyte) + uint8_t *pfirstbyte) { *pfirstbyte = 0; switch(flags & ~CURLWS_OFFSET) { - case 0: - if(contfragment) { - CURL_TRC_WS(data, "no flags given; interpreting as continuation " - "fragment for compatibility"); - *pfirstbyte = (WSBIT_OPCODE_CONT | WSBIT_FIN); - return CURLE_OK; - } - failf(data, "[WS] no flags given"); - return CURLE_BAD_FUNCTION_ARGUMENT; - case CURLWS_CONT: - if(contfragment) { - infof(data, "[WS] setting CURLWS_CONT flag without message type is " - "supported for compatibility but highly discouraged"); - *pfirstbyte = WSBIT_OPCODE_CONT; - return CURLE_OK; - } - failf(data, "[WS] No ongoing fragmented message to continue"); - return CURLE_BAD_FUNCTION_ARGUMENT; - case CURLWS_TEXT: - *pfirstbyte = contfragment ? (WSBIT_OPCODE_CONT | WSBIT_FIN) - : (WSBIT_OPCODE_TEXT | WSBIT_FIN); + case 0: + if(contfragment) { + CURL_TRC_WS(data, "no flags given; interpreting as continuation " + "fragment for compatibility"); + *pfirstbyte = (WSBIT_OPCODE_CONT | WSBIT_FIN); return CURLE_OK; - case (CURLWS_TEXT | CURLWS_CONT): - *pfirstbyte = contfragment ? WSBIT_OPCODE_CONT : WSBIT_OPCODE_TEXT; + } + failf(data, "[WS] no flags given"); + return CURLE_BAD_FUNCTION_ARGUMENT; + case CURLWS_CONT: + if(contfragment) { + infof(data, "[WS] setting CURLWS_CONT flag without message type is " + "supported for compatibility but highly discouraged"); + *pfirstbyte = WSBIT_OPCODE_CONT; return CURLE_OK; - case CURLWS_BINARY: - *pfirstbyte = contfragment ? (WSBIT_OPCODE_CONT | WSBIT_FIN) - : (WSBIT_OPCODE_BIN | WSBIT_FIN); - return CURLE_OK; - case (CURLWS_BINARY | CURLWS_CONT): - *pfirstbyte = contfragment ? WSBIT_OPCODE_CONT : WSBIT_OPCODE_BIN; - return CURLE_OK; - case CURLWS_CLOSE: - *pfirstbyte = WSBIT_OPCODE_CLOSE | WSBIT_FIN; - return CURLE_OK; - case (CURLWS_CLOSE | CURLWS_CONT): - failf(data, "[WS] CLOSE frame must not be fragmented"); - return CURLE_BAD_FUNCTION_ARGUMENT; - case CURLWS_PING: - *pfirstbyte = WSBIT_OPCODE_PING | WSBIT_FIN; - return CURLE_OK; - case (CURLWS_PING | CURLWS_CONT): - failf(data, "[WS] PING frame must not be fragmented"); - return CURLE_BAD_FUNCTION_ARGUMENT; - case CURLWS_PONG: - *pfirstbyte = WSBIT_OPCODE_PONG | WSBIT_FIN; - return CURLE_OK; - case (CURLWS_PONG | CURLWS_CONT): - failf(data, "[WS] PONG frame must not be fragmented"); - return CURLE_BAD_FUNCTION_ARGUMENT; - default: - failf(data, "[WS] unknown flags: %x", flags); - return CURLE_BAD_FUNCTION_ARGUMENT; + } + failf(data, "[WS] No ongoing fragmented message to continue"); + return CURLE_BAD_FUNCTION_ARGUMENT; + case CURLWS_TEXT: + *pfirstbyte = contfragment ? (WSBIT_OPCODE_CONT | WSBIT_FIN) + : (WSBIT_OPCODE_TEXT | WSBIT_FIN); + return CURLE_OK; + case (CURLWS_TEXT | CURLWS_CONT): + *pfirstbyte = contfragment ? WSBIT_OPCODE_CONT : WSBIT_OPCODE_TEXT; + return CURLE_OK; + case CURLWS_BINARY: + *pfirstbyte = contfragment ? (WSBIT_OPCODE_CONT | WSBIT_FIN) + : (WSBIT_OPCODE_BIN | WSBIT_FIN); + return CURLE_OK; + case (CURLWS_BINARY | CURLWS_CONT): + *pfirstbyte = contfragment ? WSBIT_OPCODE_CONT : WSBIT_OPCODE_BIN; + return CURLE_OK; + case CURLWS_CLOSE: + *pfirstbyte = WSBIT_OPCODE_CLOSE | WSBIT_FIN; + return CURLE_OK; + case (CURLWS_CLOSE | CURLWS_CONT): + failf(data, "[WS] CLOSE frame must not be fragmented"); + return CURLE_BAD_FUNCTION_ARGUMENT; + case CURLWS_PING: + *pfirstbyte = WSBIT_OPCODE_PING | WSBIT_FIN; + return CURLE_OK; + case (CURLWS_PING | CURLWS_CONT): + failf(data, "[WS] PING frame must not be fragmented"); + return CURLE_BAD_FUNCTION_ARGUMENT; + case CURLWS_PONG: + *pfirstbyte = WSBIT_OPCODE_PONG | WSBIT_FIN; + return CURLE_OK; + case (CURLWS_PONG | CURLWS_CONT): + failf(data, "[WS] PONG frame must not be fragmented"); + return CURLE_BAD_FUNCTION_ARGUMENT; + default: + failf(data, "[WS] unknown flags: %x", flags); + return CURLE_BAD_FUNCTION_ARGUMENT; } } 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; @@ -326,7 +323,7 @@ static CURLcode ws_send_raw_blocking(struct Curl_easy *data, struct websocket *ws, const char *buffer, size_t buflen); -typedef CURLcode ws_write_payload(const unsigned char *buf, size_t buflen, +typedef CURLcode ws_write_payload(const uint8_t *buf, size_t buflen, int frame_age, int frame_flags, curl_off_t payload_offset, curl_off_t payload_len, @@ -364,7 +361,7 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec, struct Curl_easy *data, struct bufq *inraw) { - const unsigned char *inbuf; + const uint8_t *inbuf; size_t inlen; while(Curl_bufq_peek(inraw, &inbuf, &inlen)) { @@ -386,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) { @@ -438,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; } } @@ -453,16 +454,17 @@ 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 64 signed not supported"); + failf(data, "[WS] frame length longer than 63 bits not supported"); return CURLE_RECV_ERROR; } - dec->payload_len = ((curl_off_t)dec->head[2] << 56) | + dec->payload_len = + (curl_off_t)dec->head[2] << 56 | (curl_off_t)dec->head[3] << 48 | (curl_off_t)dec->head[4] << 40 | (curl_off_t)dec->head[5] << 32 | (curl_off_t)dec->head[6] << 24 | (curl_off_t)dec->head[7] << 16 | - (curl_off_t)dec->head[8] << 8 | + (curl_off_t)dec->head[8] << 8 | dec->head[9]; break; default: @@ -486,16 +488,16 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec, ws_write_payload *write_cb, void *write_ctx) { - const unsigned char *inbuf; + const uint8_t *inbuf; size_t inlen; size_t nwritten; CURLcode result; - curl_off_t remain = dec->payload_len - dec->payload_offset; + 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((curl_off_t)inlen > remain) - inlen = (size_t)remain; + if(inlen > remain) + inlen = remain; result = write_cb(inbuf, inlen, dec->frame_age, dec->frame_flags, dec->payload_offset, dec->payload_len, write_ctx, &nwritten); @@ -503,9 +505,10 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec, return result; Curl_bufq_skip(inraw, nwritten); dec->payload_offset += nwritten; - remain = dec->payload_len - dec->payload_offset; - CURL_TRC_WS(data, "passed %zu bytes payload, %" - FMT_OFF_T " remain", nwritten, remain); + remain = curlx_sotouz_range(dec->payload_len - dec->payload_offset, + 0, SIZE_MAX); + CURL_TRC_WS(data, "passed %zu bytes payload, %zu remain", + nwritten, remain); } return remain ? CURLE_AGAIN : CURLE_OK; @@ -542,10 +545,10 @@ static CURLcode ws_dec_pass(struct ws_decoder *dec, dec->state = WS_DEC_PAYLOAD; if(dec->payload_len == 0) { size_t nwritten; - const unsigned char tmp = '\0'; + const uint8_t tmp = '\0'; /* special case of a 0 length frame, need to write once */ result = write_cb(&tmp, 0, dec->frame_age, dec->frame_flags, - 0, 0, write_ctx, &nwritten); + 0, 0, write_ctx, &nwritten); if(result) return result; dec->state = WS_DEC_INIT; @@ -582,7 +585,7 @@ static void update_meta(struct websocket *ws, ws->recvframe.bytesleft = bytesleft; } -/* WebSockets decoding client writer */ +/* WebSocket decoding client writer */ struct ws_cw_ctx { struct Curl_cwriter super; struct bufq buf; @@ -615,17 +618,17 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, bool blocking); static CURLcode ws_enc_send(struct Curl_easy *data, struct websocket *ws, - const unsigned char *buffer, + const uint8_t *buffer, 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); static CURLcode ws_enc_add_cntrl(struct Curl_easy *data, struct websocket *ws, - const unsigned char *payload, + const uint8_t *payload, size_t plen, unsigned int frame_type) { @@ -648,7 +651,20 @@ static CURLcode ws_enc_add_cntrl(struct Curl_easy *data, return CURLE_OK; } -static CURLcode ws_cw_dec_next(const unsigned char *buf, size_t buflen, +static curl_off_t ws_payload_remain(curl_off_t payload_total, + curl_off_t payload_offset, + size_t payload_buffered) +{ + curl_off_t buffered, remain = payload_total - payload_offset; + if((payload_total < 0) || (payload_offset < 0) || (remain < 0)) + return -1; + buffered = curlx_uztoso(payload_buffered); + if(remain < buffered) + return -1; + return remain - buffered; +} + +static CURLcode ws_cw_dec_next(const uint8_t *buf, size_t buflen, int frame_age, int frame_flags, curl_off_t payload_offset, curl_off_t payload_len, @@ -659,11 +675,16 @@ static CURLcode ws_cw_dec_next(const unsigned char *buf, size_t buflen, struct Curl_easy *data = ctx->data; struct websocket *ws = ctx->ws; bool auto_pong = !data->set.ws_no_auto_pong; - curl_off_t remain = (payload_len - (payload_offset + buflen)); + curl_off_t remain; CURLcode result; (void)frame_age; *pnwritten = 0; + remain = ws_payload_remain(payload_len, payload_offset, buflen); + if(remain < 0) { + DEBUGASSERT(0); /* parameter mismatch */ + return CURLE_BAD_FUNCTION_ARGUMENT; + } if(auto_pong && (frame_flags & CURLWS_PING) && !remain) { /* auto-respond to PINGs, only works for single-frame payloads atm */ @@ -680,8 +701,8 @@ static CURLcode ws_cw_dec_next(const unsigned char *buf, size_t buflen, payload_len, buflen); result = Curl_cwriter_write(data, ctx->next_writer, - (ctx->cw_type | CLIENTWRITE_0LEN), - (const char *)buf, buflen); + (ctx->cw_type | CLIENTWRITE_0LEN), + (const char *)buf, buflen); if(result) return result; } @@ -709,7 +730,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data, if(nbytes) { size_t nwritten; - result = Curl_bufq_write(&ctx->buf, (const unsigned char *)buf, + result = Curl_bufq_write(&ctx->buf, (const uint8_t *)buf, nbytes, &nwritten); if(result) { infof(data, "[WS] error adding data to buffer %d", result); @@ -755,10 +776,11 @@ static const struct Curl_cwtype ws_cw_decode = { sizeof(struct ws_cw_ctx) }; - 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), @@ -807,8 +829,8 @@ static CURLcode ws_enc_add_frame(struct Curl_easy *data, curl_off_t payload_len, struct bufq *out) { - unsigned char firstb = 0; - unsigned char head[14]; + uint8_t firstb = 0; + uint8_t head[14]; CURLcode result; size_t hlen, nwritten; @@ -820,19 +842,20 @@ static CURLcode ws_enc_add_frame(struct Curl_easy *data, if(enc->payload_remain > 0) { /* trying to write a new frame before the previous one is finished */ - failf(data, "[WS] starting new frame with %zd bytes from last one " - "remaining to be sent", (ssize_t)enc->payload_remain); + failf(data, "[WS] starting new frame with %" FMT_OFF_T " bytes " + "from last one remaining to be sent", enc->payload_remain); 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) { @@ -851,24 +874,24 @@ static CURLcode ws_enc_add_frame(struct Curl_easy *data, head[0] = enc->firstbyte = firstb; if(payload_len > 65535) { head[1] = 127 | WSBIT_MASK; - head[2] = (unsigned char)((payload_len >> 56) & 0xff); - head[3] = (unsigned char)((payload_len >> 48) & 0xff); - head[4] = (unsigned char)((payload_len >> 40) & 0xff); - head[5] = (unsigned char)((payload_len >> 32) & 0xff); - head[6] = (unsigned char)((payload_len >> 24) & 0xff); - head[7] = (unsigned char)((payload_len >> 16) & 0xff); - head[8] = (unsigned char)((payload_len >> 8) & 0xff); - head[9] = (unsigned char)(payload_len & 0xff); + head[2] = (uint8_t)((payload_len >> 56) & 0xff); + head[3] = (uint8_t)((payload_len >> 48) & 0xff); + head[4] = (uint8_t)((payload_len >> 40) & 0xff); + head[5] = (uint8_t)((payload_len >> 32) & 0xff); + head[6] = (uint8_t)((payload_len >> 24) & 0xff); + head[7] = (uint8_t)((payload_len >> 16) & 0xff); + head[8] = (uint8_t)((payload_len >> 8) & 0xff); + head[9] = (uint8_t)(payload_len & 0xff); hlen = 10; } else if(payload_len >= 126) { head[1] = 126 | WSBIT_MASK; - head[2] = (unsigned char)((payload_len >> 8) & 0xff); - head[3] = (unsigned char)(payload_len & 0xff); + head[2] = (uint8_t)((payload_len >> 8) & 0xff); + head[3] = (uint8_t)(payload_len & 0xff); hlen = 4; } else { - head[1] = (unsigned char)payload_len | WSBIT_MASK; + head[1] = (uint8_t)payload_len | WSBIT_MASK; hlen = 2; } @@ -877,7 +900,7 @@ static CURLcode ws_enc_add_frame(struct Curl_easy *data, /* 4 bytes random */ - result = Curl_rand(data, (unsigned char *)&enc->mask, sizeof(enc->mask)); + result = Curl_rand(data, (uint8_t *)&enc->mask, sizeof(enc->mask)); if(result) return result; @@ -923,11 +946,11 @@ static CURLcode ws_enc_write_head(struct Curl_easy *data, static CURLcode ws_enc_write_payload(struct ws_encoder *enc, struct Curl_easy *data, - const unsigned char *buf, size_t buflen, + const uint8_t *buf, size_t buflen, struct bufq *out, size_t *pnwritten) { CURLcode result; - size_t i, len, n; + size_t i, len, n, remain; *pnwritten = 0; if(Curl_bufq_is_full(out)) @@ -935,11 +958,12 @@ static CURLcode ws_enc_write_payload(struct ws_encoder *enc, /* not the most performant way to do this */ len = buflen; - if((curl_off_t)len > enc->payload_remain) - len = (size_t)enc->payload_remain; + remain = curlx_sotouz_range(enc->payload_remain, 0, SIZE_MAX); + if(remain < len) + len = remain; for(i = 0; i < len; ++i) { - unsigned char c = buf[i] ^ enc->mask[enc->xori]; + uint8_t c = buf[i] ^ enc->mask[enc->xori]; result = Curl_bufq_write(out, &c, 1, &n); if(result) { if((result != CURLE_AGAIN) || !i) @@ -970,7 +994,7 @@ static CURLcode ws_enc_add_pending(struct Curl_easy *data, (curl_off_t)ws->pending.payload_len, &ws->sendbuf); if(result) { - CURL_TRC_WS(data, "ws_enc_cntrl(), error addiong head: %d", + CURL_TRC_WS(data, "ws_enc_cntrl(), error adding head: %d", result); goto out; } @@ -982,18 +1006,24 @@ static CURLcode ws_enc_add_pending(struct Curl_easy *data, result); goto out; } - /* our buffer should always be able to take in a control frame */ - DEBUGASSERT(n == ws->pending.payload_len); + if(n != ws->pending.payload_len) { + DEBUGASSERT(0); /* buffer should always be able to take all */ + CURL_TRC_WS(data, "ws_enc_cntrl(), error added only %zu/%zu payload,", + n, ws->pending.payload_len); + result = CURLE_SEND_ERROR; + goto out; + } + /* the frame should be complete now */ DEBUGASSERT(!ws->enc.payload_remain); + memset(&ws->pending, 0, sizeof(ws->pending)); out: - memset(&ws->pending, 0, sizeof(ws->pending)); return result; } static CURLcode ws_enc_send(struct Curl_easy *data, struct websocket *ws, - const unsigned char *buffer, + const uint8_t *buffer, size_t buflen, curl_off_t fragsize, unsigned int flags, @@ -1010,7 +1040,7 @@ static CURLcode ws_enc_send(struct Curl_easy *data, * that needs to be encoded into the buffer */ if(buflen < ws->sendbuf_payload) { /* We have been called with LESS buffer data than before. This - * is not how it's supposed too work. */ + * is not how it is supposed too work. */ failf(data, "[WS] curl_ws_send() called with smaller 'buflen' than " "bytes already buffered in previous call, %zu vs %zu", buflen, ws->sendbuf_payload); @@ -1141,8 +1171,7 @@ static CURLcode cr_ws_read(struct Curl_easy *data, if(ws->enc.payload_remain) { CURL_TRC_WS(data, "current frame, %" FMT_OFF_T " remaining", ws->enc.payload_remain); - if(ws->enc.payload_remain < (curl_off_t)blen) - blen = (size_t)ws->enc.payload_remain; + blen = curlx_sotouz_range(ws->enc.payload_remain, 0, blen); } result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos); @@ -1160,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; } @@ -1172,7 +1201,7 @@ static CURLcode cr_ws_read(struct Curl_easy *data, goto out; } - result = ws_enc_write_payload(&ws->enc, data, (unsigned char *)buf, + result = ws_enc_write_payload(&ws->enc, data, (uint8_t *)buf, nread, &ws->sendbuf, &n); if(result) goto out; @@ -1208,7 +1237,6 @@ static const struct Curl_crtype ws_cr_encode = { sizeof(struct cr_ws_ctx) }; - struct wsfield { const char *name; const char *val; @@ -1218,12 +1246,12 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req) { unsigned int i; CURLcode result = CURLE_OK; - unsigned char rand[16]; + uint8_t rand[16]; char *randstr; size_t randlen; char keyval[40]; struct SingleRequest *k = &data->req; - struct wsfield heads[]= { + struct wsfield heads[] = { { /* The request MUST contain an |Upgrade| header field whose value MUST include the "websocket" keyword. */ @@ -1247,27 +1275,27 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req) heads[2].val = &keyval[0]; /* 16 bytes random */ - result = Curl_rand(data, (unsigned char *)rand, sizeof(rand)); + result = Curl_rand(data, rand, sizeof(rand)); if(result) return result; - result = curlx_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen); + result = curlx_base64_encode(rand, sizeof(rand), &randstr, &randlen); if(result) return result; DEBUGASSERT(randlen < sizeof(keyval)); if(randlen >= sizeof(keyval)) { - free(randstr); + curlx_free(randstr); return CURLE_FAILED_INIT; } - strcpy(keyval, randstr); - free(randstr); + curlx_strcopy(keyval, sizeof(keyval), randstr, randlen); + curlx_free(randstr); for(i = 0; !result && (i < CURL_ARRAYSIZE(heads)); i++) { if(!Curl_checkheaders(data, heads[i].name, strlen(heads[i].name))) { - result = curlx_dyn_addf(req, "%s: %s\r\n", heads[i].name, - heads[i].val); + result = curlx_dyn_addf(req, "%s: %s\r\n", heads[i].name, heads[i].val); } } data->state.http_hd_upgrade = TRUE; k->upgr101 = UPGR101_WS; + data->conn->bits.upgrade_in_progress = TRUE; return result; } @@ -1278,7 +1306,7 @@ static void ws_conn_dtor(void *key, size_t klen, void *entry) (void)klen; Curl_bufq_free(&ws->recvbuf); Curl_bufq_free(&ws->sendbuf); - free(ws); + curlx_free(ws); } /* @@ -1298,7 +1326,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); if(!ws) { size_t chunk_size = WS_CHUNK_SIZE; - ws = calloc(1, sizeof(*ws)); + ws = curlx_calloc(1, sizeof(*ws)); if(!ws) return CURLE_OUT_OF_MEMORY; #ifdef DEBUGBUILD @@ -1306,7 +1334,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, const char *p = getenv("CURL_WS_CHUNK_SIZE"); if(p) { curl_off_t l; - if(!curlx_str_number(&p, &l, 1*1024*1024)) + if(!curlx_str_number(&p, &l, 1 * 1024 * 1024)) chunk_size = (size_t)l; } } @@ -1359,17 +1387,19 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, goto out; ws_dec_writer = NULL; /* owned by transfer now */ + k->header = FALSE; /* we will not get more response headers */ + if(data->set.connect_only) { size_t nwritten; /* In CONNECT_ONLY setup, the payloads from `mem` need to be received * when using `curl_ws_recv` later on after this transfer is already * marked as DONE. */ - result = Curl_bufq_write(&ws->recvbuf, (const unsigned char *)mem, + result = Curl_bufq_write(&ws->recvbuf, (const uint8_t *)mem, nread, &nwritten); if(result) goto out; DEBUGASSERT(nread == nwritten); - k->keepon &= ~KEEP_RECV; /* read no more content */ + CURL_REQ_CLEAR_RECV(data); /* read no more content */ } else { /* !connect_only */ if(data->set.method == HTTPREQ_PUT) { @@ -1393,10 +1423,10 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, /* start over with sending */ data->req.eos_read = FALSE; data->req.upload_done = FALSE; - k->keepon |= KEEP_SEND; + CURL_REQ_SET_SEND(data); } - /* And pass any additional data to the writers */ + /* Then pass any additional data to the writers */ if(nread) { result = Curl_client_write(data, CLIENTWRITE_BODY, mem, nread); if(result) @@ -1423,7 +1453,7 @@ out: struct ws_collect { struct Curl_easy *data; struct websocket *ws; - unsigned char *buffer; + uint8_t *buffer; size_t buflen; size_t bufidx; int frame_age; @@ -1433,7 +1463,7 @@ struct ws_collect { bool written; }; -static CURLcode ws_client_collect(const unsigned char *buf, size_t buflen, +static CURLcode ws_client_collect(const uint8_t *buf, size_t buflen, int frame_age, int frame_flags, curl_off_t payload_offset, curl_off_t payload_len, @@ -1443,10 +1473,16 @@ static CURLcode ws_client_collect(const unsigned char *buf, size_t buflen, struct ws_collect *ctx = userp; struct Curl_easy *data = ctx->data; bool auto_pong = !data->set.ws_no_auto_pong; - curl_off_t remain = (payload_len - (payload_offset + buflen)); + curl_off_t remain; CURLcode result = CURLE_OK; *pnwritten = 0; + remain = ws_payload_remain(payload_len, payload_offset, buflen); + if(remain < 0) { + DEBUGASSERT(0); /* parameter mismatch */ + return CURLE_BAD_FUNCTION_ARGUMENT; + } + if(!ctx->bufidx) { /* first write */ ctx->frame_age = frame_age; @@ -1484,25 +1520,25 @@ static CURLcode ws_client_collect(const unsigned char *buf, size_t buflen, } static CURLcode nw_in_recv(void *reader_ctx, - unsigned char *buf, size_t buflen, + uint8_t *buf, size_t buflen, size_t *pnread) { struct Curl_easy *data = reader_ctx; return curl_easy_recv(data, buf, buflen, pnread); } -CURLcode curl_ws_recv(CURL *d, void *buffer, - size_t buflen, size_t *nread, +CURLcode curl_ws_recv(CURL *curl, void *buffer, + size_t buflen, size_t *recv, const struct curl_ws_frame **metap) { - struct Curl_easy *data = d; + struct Curl_easy *data = curl; struct connectdata *conn; struct websocket *ws; struct ws_collect ctx; - *nread = 0; + *recv = 0; *metap = NULL; - if(!GOOD_EASY_HANDLE(data)) + if(!GOOD_EASY_HANDLE(data) || (buflen && !buffer)) return CURLE_BAD_FUNCTION_ARGUMENT; conn = data->conn; @@ -1525,7 +1561,6 @@ CURLcode curl_ws_recv(CURL *d, void *buffer, return CURLE_BAD_FUNCTION_ARGUMENT; } - memset(&ctx, 0, sizeof(ctx)); ctx.data = data; ctx.ws = ws; @@ -1574,11 +1609,11 @@ CURLcode curl_ws_recv(CURL *d, void *buffer, update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset, ctx.payload_len, ctx.bufidx); *metap = &ws->recvframe; - *nread = ws->recvframe.len; + *recv = ws->recvframe.len; CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" - FMT_OFF_T ", %" FMT_OFF_T " left)", - buflen, *nread, ws->recvframe.offset, - ws->recvframe.bytesleft); + FMT_OFF_T ", %" FMT_OFF_T " left)", + buflen, *recv, ws->recvframe.offset, + ws->recvframe.bytesleft); /* all's well, try to send any pending control. we do not know * when the application will call `curl_ws_send()` again. */ if(!data->set.ws_raw_mode && ws->pending.type) { @@ -1594,7 +1629,7 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, { if(!Curl_bufq_is_empty(&ws->sendbuf)) { CURLcode result; - const unsigned char *out; + const uint8_t *out; size_t outlen, n; #ifdef DEBUGBUILD /* Simulate a blocking send after this chunk has been sent */ @@ -1603,7 +1638,7 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, const char *p = getenv("CURL_WS_CHUNK_EAGAIN"); if(p) { curl_off_t l; - if(!curlx_str_number(&p, &l, 1*1024*1024)) + if(!curlx_str_number(&p, &l, 1 * 1024 * 1024)) chunk_egain = (size_t)l; } #endif @@ -1654,6 +1689,9 @@ static CURLcode ws_send_raw_blocking(struct Curl_easy *data, CURLcode result = CURLE_OK; size_t nwritten; + if(!data) + return result; + (void)ws; while(buflen) { result = Curl_xfer_send(data, buffer, buflen, FALSE, &nwritten); @@ -1669,7 +1707,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(data, NULL, FALSE); + left_ms = Curl_timeleft_ms(data); if(left_ms < 0) { failf(data, "[WS] Timeout waiting for socket becoming writable"); return CURLE_SEND_ERROR; @@ -1678,8 +1716,7 @@ static CURLcode ws_send_raw_blocking(struct Curl_easy *data, /* POLLOUT socket */ if(sock == CURL_SOCKET_BAD) return CURLE_SEND_ERROR; - ev = Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, sock, - left_ms ? left_ms : 500); + ev = SOCKET_WRITABLE(sock, left_ms ? left_ms : 500); if(ev < 0) { failf(data, "[WS] Error while waiting for socket becoming writable"); return CURLE_SEND_ERROR; @@ -1726,15 +1763,15 @@ static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer, return result; } -CURLcode curl_ws_send(CURL *d, const void *buffer_arg, +CURLcode curl_ws_send(CURL *curl, const void *buffer_arg, size_t buflen, size_t *sent, curl_off_t fragsize, unsigned int flags) { struct websocket *ws; - const unsigned char *buffer = buffer_arg; + const uint8_t *buffer = buffer_arg; CURLcode result = CURLE_OK; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; size_t ndummy; size_t *pnsent = sent ? sent : &ndummy; @@ -1806,7 +1843,7 @@ out: static CURLcode ws_setup_conn(struct Curl_easy *data, struct connectdata *conn) { - /* WebSockets is 1.1 only (for now) */ + /* WebSocket is 1.1 only (for now) */ data->state.http_neg.accept_09 = FALSE; data->state.http_neg.only_10 = FALSE; data->state.http_neg.wanted = CURL_HTTP_V1x; @@ -1814,30 +1851,28 @@ static CURLcode ws_setup_conn(struct Curl_easy *data, return Curl_http_setup_conn(data, conn); } - -const struct curl_ws_frame *curl_ws_meta(CURL *d) +const struct curl_ws_frame *curl_ws_meta(CURL *curl) { /* we only return something for websocket, called from within the callback when not using raw mode */ - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->conn && !data->set.ws_raw_mode) { struct websocket *ws; ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); if(ws) return &ws->recvframe; - } return NULL; } -CURL_EXTERN CURLcode curl_ws_start_frame(CURL *d, +CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl, unsigned int flags, curl_off_t frame_len) { struct websocket *ws; CURLcode result = CURLE_OK; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1876,78 +1911,43 @@ CURL_EXTERN CURLcode curl_ws_start_frame(CURL *d, result = ws_enc_write_head(data, ws, &ws->enc, flags, frame_len, &ws->sendbuf); if(result) - CURL_TRC_WS(data, "curl_start_frame(), error adding frame head %d", + CURL_TRC_WS(data, "curl_start_frame(), error adding frame head %d", result); out: return result; } -const struct Curl_handler Curl_handler_ws = { - "WS", /* scheme */ +const struct Curl_protocol Curl_protocol_ws = { ws_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ - Curl_http_connect, /* connect_it */ + ZERO_NULL, /* connect_it */ ZERO_NULL, /* connecting */ ZERO_NULL, /* doing */ ZERO_NULL, /* proto_pollset */ - Curl_http_do_pollset, /* doing_pollset */ + Curl_http_doing_pollset, /* doing_pollset */ ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_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, /* connection_is_dead */ 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 */ - Curl_http_connect, /* connect_it */ - NULL, /* connecting */ - ZERO_NULL, /* doing */ - NULL, /* proto_pollset */ - Curl_http_do_pollset, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* 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, - size_t *nread, + size_t *recv, const struct curl_ws_frame **metap) { (void)curl; (void)buffer; (void)buflen; - (void)nread; + (void)recv; (void)metap; return CURLE_NOT_BUILT_IN; } diff --git a/lib/ws.h b/lib/ws.h index b7655abbce..71b5b3fff0 100644 --- a/lib/ws.h +++ b/lib/ws.h @@ -27,21 +27,18 @@ #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) +extern const struct Curl_protocol Curl_protocol_ws; + /* 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 -#define Curl_ws_free(x) Curl_nop_stmt +#define Curl_ws_request(x, y) CURLE_OK +#define Curl_ws_free(x) Curl_nop_stmt #endif #endif /* HEADER_CURL_WS_H */ diff --git a/m4/curl-amissl.m4 b/m4/curl-amissl.m4 index a70a536632..da90cc412d 100644 --- a/m4/curl-amissl.m4 +++ b/m4/curl-amissl.m4 @@ -25,7 +25,7 @@ AC_DEFUN([CURL_WITH_AMISSL], [ AC_MSG_CHECKING([whether to enable Amiga native SSL/TLS (AmiSSL v5)]) if test "$HAVE_PROTO_BSDSOCKET_H" = "1"; then - if test "x$OPT_AMISSL" != xno; then + if test "x$OPT_AMISSL" != "xno"; then ssl_msg= AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ @@ -33,8 +33,7 @@ if test "$HAVE_PROTO_BSDSOCKET_H" = "1"; then #include ]],[[ #if defined(AMISSL_CURRENT_VERSION) && defined(AMISSL_V3xx) && \ - (OPENSSL_VERSION_NUMBER >= 0x30000000L) && \ - defined(PROTO_AMISSL_H) + (OPENSSL_VERSION_NUMBER >= 0x30000000L) && defined(PROTO_AMISSL_H) return 0; #else #error not AmiSSL v5 / OpenSSL 3 @@ -43,18 +42,17 @@ if test "$HAVE_PROTO_BSDSOCKET_H" = "1"; then ],[ AC_MSG_RESULT([yes]) ssl_msg="AmiSSL" - test amissl != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + test "amissl" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes AMISSL_ENABLED=1 OPENSSL_ENABLED=1 - # Use AmiSSL's built-in ca bundle + dnl Use AmiSSL's built-in ca bundle check_for_ca_bundle=1 with_ca_fallback=yes LIBS="-lamisslstubs -lamisslauto $LIBS" CURL_NETWORK_AND_TIME_LIBS="-lamisslstubs -lamisslauto $CURL_NETWORK_AND_TIME_LIBS" AC_DEFINE(USE_AMISSL, 1, [if AmiSSL is in use]) AC_DEFINE(USE_OPENSSL, 1, [if OpenSSL is in use]) - AC_CHECK_HEADERS(openssl/x509.h openssl/rsa.h openssl/crypto.h \ - openssl/pem.h openssl/ssl.h openssl/err.h) + AC_CHECK_HEADERS(openssl/rsa.h openssl/crypto.h openssl/pem.h openssl/ssl.h openssl/err.h) ],[ AC_MSG_RESULT([no]) ]) diff --git a/m4/curl-apple-sectrust.m4 b/m4/curl-apple-sectrust.m4 new file mode 100644 index 0000000000..c70b7ac8cd --- /dev/null +++ b/m4/curl-apple-sectrust.m4 @@ -0,0 +1,61 @@ +#*************************************************************************** +# _ _ ____ _ +# 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 +# +#*************************************************************************** + +AC_DEFUN([CURL_WITH_APPLE_SECTRUST], [ +AC_MSG_CHECKING([whether to enable Apple OS native certificate validation]) +if test "x$OPT_APPLE_SECTRUST" = "xyes"; then + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ + #include + #include + ]],[[ + #if TARGET_OS_MAC + return 0; + #else + #error Not macOS + #endif + ]]) + ],[ + build_for_apple="yes" + ],[ + build_for_apple="no" + ]) + if test "$build_for_apple" = "no"; then + AC_MSG_ERROR([Apple SecTrust can only be enabled for Apple OS targets]) + fi + if test "$OPENSSL_ENABLED" = "1" || test "$GNUTLS_ENABLED" = "1"; then + AC_MSG_RESULT(yes) + AC_DEFINE(USE_APPLE_SECTRUST, 1, [enable Apple OS certificate validation]) + APPLE_SECTRUST_ENABLED=1 + APPLE_SECTRUST_LDFLAGS='-framework CoreFoundation -framework CoreServices -framework Security' + LDFLAGS="$LDFLAGS $APPLE_SECTRUST_LDFLAGS" + LDFLAGSPC="$LDFLAGSPC $APPLE_SECTRUST_LDFLAGS" + else + AC_MSG_ERROR([Apple SecTrust is only supported for OpenSSL/GnuTLS builds]) + fi +else + AC_MSG_RESULT(no) +fi + +]) diff --git a/m4/curl-compilers.m4 b/m4/curl-compilers.m4 index dc6d3aa4cf..4bb7196eae 100644 --- a/m4/curl-compilers.m4 +++ b/m4/curl-compilers.m4 @@ -22,27 +22,26 @@ # #*************************************************************************** -# File version for 'aclocal' use. Keep it a single number. -# serial 67 - +dnl File version for 'aclocal' use. Keep it a single number. +dnl serial 67 dnl CURL_CHECK_COMPILER dnl ------------------------------------------------- dnl Verify if the C compiler being used is known. AC_DEFUN([CURL_CHECK_COMPILER], [ - # + compiler_id="unknown" compiler_ver="" compiler_num="0" - # + flags_dbg_yes="unknown" flags_opt_all="unknown" flags_opt_yes="unknown" flags_opt_off="unknown" - # + flags_prefer_cppflags="no" - # + CURL_CHECK_COMPILER_DEC_C CURL_CHECK_COMPILER_HPUX_C CURL_CHECK_COMPILER_IBM_C @@ -57,7 +56,7 @@ AC_DEFUN([CURL_CHECK_COMPILER], [ esac CURL_CHECK_COMPILER_SUNPRO_C CURL_CHECK_COMPILER_TINY_C - # + if test "$compiler_id" = "unknown"; then cat <<_EOF 1>&2 *** @@ -81,14 +80,14 @@ dnl ------------------------------------------------- dnl Verify if compiler being used is clang. AC_DEFUN([CURL_CHECK_COMPILER_CLANG], [ - AC_BEFORE([$0],[CURL_CHECK_COMPILER_GNU_C])dnl + AC_BEFORE([$0],[CURL_CHECK_COMPILER_GNU_C]) AC_MSG_CHECKING([if compiler is clang]) CURL_CHECK_DEF([__clang__], [], [silent]) if test "$curl_cv_have_def___clang__" = "yes"; then AC_MSG_RESULT([yes]) AC_MSG_CHECKING([if compiler is xlclang]) CURL_CHECK_DEF([__ibmxl__], [], [silent]) - if test "$curl_cv_have_def___ibmxl__" = "yes" ; then + if test "$curl_cv_have_def___ibmxl__" = "yes"; then dnl IBM's almost-compatible clang version AC_MSG_RESULT([yes]) compiler_id="XLCLANG" @@ -118,18 +117,21 @@ AC_DEFUN([CURL_CHECK_COMPILER_CLANG], [ clangvlo=`echo $clangver | cut -d . -f2` compiler_ver="$clangver" compiler_num=`(expr $clangvhi "*" 100 + $clangvlo) 2>/dev/null` - if test "$appleclang" = '1' && test "$oldapple" = '0'; then - dnl Starting with Xcode 7 / clang 3.7, Apple clang won't tell its upstream version - if test "$compiler_num" -ge '1300'; then compiler_num='1200' - elif test "$compiler_num" -ge '1205'; then compiler_num='1101' - elif test "$compiler_num" -ge '1204'; then compiler_num='1000' - elif test "$compiler_num" -ge '1107'; then compiler_num='900' - elif test "$compiler_num" -ge '1103'; then compiler_num='800' - elif test "$compiler_num" -ge '1003'; then compiler_num='700' - elif test "$compiler_num" -ge '1001'; then compiler_num='600' - elif test "$compiler_num" -ge '904'; then compiler_num='500' - elif test "$compiler_num" -ge '902'; then compiler_num='400' - elif test "$compiler_num" -ge '803'; then compiler_num='309' + if test "$appleclang" = "1" && test "$oldapple" = "0"; then + dnl Starting with Xcode 7 / clang 3.7, Apple clang does not tell its upstream version + if test "$compiler_num" -ge '2604'; then compiler_num='2101' + elif test "$compiler_num" -ge '1700'; then compiler_num='1901' + elif test "$compiler_num" -ge '1600'; then compiler_num='1700' + elif test "$compiler_num" -ge '1500'; then compiler_num='1600' + elif test "$compiler_num" -ge '1400'; then compiler_num='1400' + elif test "$compiler_num" -ge '1301'; then compiler_num='1300' + elif test "$compiler_num" -ge '1300'; then compiler_num='1200' + elif test "$compiler_num" -ge '1200'; then compiler_num='1000' + elif test "$compiler_num" -ge '1100'; then compiler_num='800' + elif test "$compiler_num" -ge '1000'; then compiler_num='600' + elif test "$compiler_num" -ge '901'; then compiler_num='500' + elif test "$compiler_num" -ge '900'; then compiler_num='400' + elif test "$compiler_num" -ge '801'; then compiler_num='309' elif test "$compiler_num" -ge '703'; then compiler_num='308' else compiler_num='307' fi @@ -181,8 +183,8 @@ dnl Version 4.7 => 407 dnl Version 9.2.1 => 900 dnl AC_DEFUN([CURL_CHECK_COMPILER_GNU_C], [ - AC_REQUIRE([CURL_CHECK_COMPILER_INTEL_C])dnl - AC_REQUIRE([CURL_CHECK_COMPILER_CLANG])dnl + AC_REQUIRE([CURL_CHECK_COMPILER_INTEL_C]) + AC_REQUIRE([CURL_CHECK_COMPILER_CLANG]) AC_MSG_CHECKING([if compiler is GNU C]) CURL_CHECK_DEF([__GNUC__], [], [silent]) if test "$curl_cv_have_def___GNUC__" = "yes" && @@ -190,7 +192,7 @@ AC_DEFUN([CURL_CHECK_COMPILER_GNU_C], [ AC_MSG_RESULT([yes]) compiler_id="GNU_C" AC_MSG_CHECKING([compiler version]) - # strip '-suffix' parts, e.g. Ubuntu Windows cross-gcc returns '10-win32' + dnl strip '-suffix' parts, e.g. Ubuntu Windows cross-gcc returns '10-win32' gccver=`$CC -dumpversion | "$SED" 's/-.\{1,\}$//'` gccvhi=`echo $gccver | cut -d . -f1` if echo $gccver | grep -F '.' >/dev/null; then @@ -264,7 +266,7 @@ dnl ------------------------------------------------- dnl Verify if compiler being used is Intel C. AC_DEFUN([CURL_CHECK_COMPILER_INTEL_C], [ - AC_BEFORE([$0],[CURL_CHECK_COMPILER_GNU_C])dnl + AC_BEFORE([$0],[CURL_CHECK_COMPILER_GNU_C]) AC_MSG_CHECKING([if compiler is Intel C]) CURL_CHECK_DEF([__INTEL_COMPILER], [], [silent]) if test "$curl_cv_have_def___INTEL_COMPILER" = "yes"; then @@ -298,7 +300,7 @@ dnl ------------------------------------------------- dnl Verify if compiler being used is SGI MIPS C. AC_DEFUN([CURL_CHECK_COMPILER_SGI_MIPS_C], [ - AC_REQUIRE([CURL_CHECK_COMPILER_SGI_MIPSPRO_C])dnl + AC_REQUIRE([CURL_CHECK_COMPILER_SGI_MIPSPRO_C]) AC_MSG_CHECKING([if compiler is SGI MIPS C]) CURL_CHECK_DEF([__GNUC__], [], [silent]) CURL_CHECK_DEF([__sgi], [], [silent]) @@ -322,7 +324,7 @@ dnl ------------------------------------------------- dnl Verify if compiler being used is SGI MIPSpro C. AC_DEFUN([CURL_CHECK_COMPILER_SGI_MIPSPRO_C], [ - AC_BEFORE([$0],[CURL_CHECK_COMPILER_SGI_MIPS_C])dnl + AC_BEFORE([$0],[CURL_CHECK_COMPILER_SGI_MIPS_C]) AC_MSG_CHECKING([if compiler is SGI MIPSpro C]) CURL_CHECK_DEF([__GNUC__], [], [silent]) CURL_CHECK_DEF([_COMPILER_VERSION], [], [silent]) @@ -390,11 +392,12 @@ dnl headers from these locations, although on ancient dnl GNUC versions these warnings are not silenced. AC_DEFUN([CURL_CONVERT_INCLUDE_TO_ISYSTEM], [ - AC_REQUIRE([CURL_SHFUNC_SQUEEZE])dnl - AC_REQUIRE([CURL_CHECK_COMPILER])dnl + AC_REQUIRE([CURL_SHFUNC_SQUEEZE]) + AC_REQUIRE([CURL_CHECK_COMPILER]) AC_MSG_CHECKING([convert -I options to -isystem]) if test "$compiler_id" = "GNU_C" || - test "$compiler_id" = "CLANG" -o "$compiler_id" = "APPLECLANG"; then + test "$compiler_id" = "CLANG" || + test "$compiler_id" = "APPLECLANG"; then AC_MSG_RESULT([yes]) tmp_has_include="no" tmp_chg_FLAGS="$CFLAGS" @@ -473,7 +476,7 @@ AC_DEFUN([CURL_COMPILER_WORKS_IFELSE], [ ]) fi dnl only do runtime verification when not cross-compiling - if test "x$cross_compiling" != "xyes" && + if test "$cross_compiling" != "yes" && test "$tmp_compiler_works" = "yes"; then CURL_RUN_IFELSE([ AC_LANG_PROGRAM([[ @@ -509,29 +512,29 @@ dnl depend on configure's debug, optimize or warnings dnl options. AC_DEFUN([CURL_SET_COMPILER_BASIC_OPTS], [ - AC_REQUIRE([CURL_CHECK_COMPILER])dnl - AC_REQUIRE([CURL_SHFUNC_SQUEEZE])dnl - # + AC_REQUIRE([CURL_CHECK_COMPILER]) + AC_REQUIRE([CURL_SHFUNC_SQUEEZE]) + if test "$compiler_id" != "unknown"; then - # + tmp_save_CPPFLAGS="$CPPFLAGS" tmp_save_CFLAGS="$CFLAGS" tmp_CPPFLAGS="" tmp_CFLAGS="" - # + case "$compiler_id" in - # + CLANG|APPLECLANG) - # + dnl Disable warnings for unused arguments, otherwise clang will dnl warn about compile-time arguments used during link-time, like dnl -O and -g and -pedantic. tmp_CFLAGS="$tmp_CFLAGS -Qunused-arguments" tmp_CFLAGS="$tmp_CFLAGS -Werror-implicit-function-declaration" ;; - # + DEC_C) - # + dnl Select strict ANSI C compiler mode tmp_CFLAGS="$tmp_CFLAGS -std1" dnl Turn off optimizer ANSI C aliasing rules @@ -541,28 +544,28 @@ AC_DEFUN([CURL_SET_COMPILER_BASIC_OPTS], [ dnl Change some warnings into fatal errors tmp_CFLAGS="$tmp_CFLAGS -msg_fatal toofewargs,toomanyargs" ;; - # + GNU_C) - # + dnl turn implicit-function-declaration warning into error, dnl at least gcc 2.95 and later support this if test "$compiler_num" -ge "295"; then tmp_CFLAGS="$tmp_CFLAGS -Werror-implicit-function-declaration" fi ;; - # + HP_UX_C) - # - dnl Disallow run-time dereferencing of null pointers + + dnl Disallow runtime dereferencing of null pointers tmp_CFLAGS="$tmp_CFLAGS -z" dnl Disable some remarks dnl #4227: padding struct with n bytes to align member dnl #4255: padding size of struct with n bytes to alignment boundary tmp_CFLAGS="$tmp_CFLAGS +W 4227,4255" ;; - # + IBM_C) - # + dnl Ensure that compiler optimizations are always thread-safe. tmp_CPPFLAGS="$tmp_CPPFLAGS -qthreaded" dnl Disable type based strict aliasing optimizations, using worst @@ -574,9 +577,9 @@ AC_DEFUN([CURL_SET_COMPILER_BASIC_OPTS], [ dnl generating an object code file when compilation has errors. tmp_CPPFLAGS="$tmp_CPPFLAGS -qhalt=e" ;; - # + INTEL_UNIX_C) - # + dnl On Unix this compiler uses gcc's header files, so dnl we select ANSI C89 dialect plus GNU extensions. tmp_CFLAGS="$tmp_CFLAGS -std=gnu89" @@ -594,43 +597,43 @@ AC_DEFUN([CURL_SET_COMPILER_BASIC_OPTS], [ dnl #2259: non-pointer conversion from X to Y may lose significant bits tmp_CPPFLAGS="$tmp_CPPFLAGS -diag-disable 279,981,1025,1469,2259" ;; - # + INTEL_WINDOWS_C) - # + dnl Placeholder tmp_CFLAGS="$tmp_CFLAGS" ;; - # + SGI_MIPS_C) - # + dnl Placeholder tmp_CFLAGS="$tmp_CFLAGS" ;; - # + SGI_MIPSPRO_C) - # + dnl Placeholder tmp_CFLAGS="$tmp_CFLAGS" ;; - # + SUNPRO_C) - # + dnl Placeholder tmp_CFLAGS="$tmp_CFLAGS" ;; - # + TINY_C) - # + dnl Placeholder tmp_CFLAGS="$tmp_CFLAGS" ;; - # + esac - # + squeeze tmp_CPPFLAGS squeeze tmp_CFLAGS - # - if test ! -z "$tmp_CFLAGS" || test ! -z "$tmp_CPPFLAGS"; then + + if test -n "$tmp_CFLAGS" || test -n "$tmp_CPPFLAGS"; then AC_MSG_CHECKING([if compiler accepts some basic options]) CPPFLAGS="$tmp_save_CPPFLAGS $tmp_CPPFLAGS" CFLAGS="$tmp_save_CFLAGS $tmp_CFLAGS" @@ -647,7 +650,6 @@ AC_DEFUN([CURL_SET_COMPILER_BASIC_OPTS], [ CFLAGS="$tmp_save_CFLAGS" ]) fi - # fi ]) @@ -658,24 +660,24 @@ dnl Sets compiler specific options/flags which depend dnl on configure's debug option. AC_DEFUN([CURL_SET_COMPILER_DEBUG_OPTS], [ - AC_REQUIRE([CURL_CHECK_OPTION_DEBUG])dnl - AC_REQUIRE([CURL_CHECK_COMPILER])dnl - AC_REQUIRE([CURL_SHFUNC_SQUEEZE])dnl - # + AC_REQUIRE([CURL_CHECK_OPTION_DEBUG]) + AC_REQUIRE([CURL_CHECK_COMPILER]) + AC_REQUIRE([CURL_SHFUNC_SQUEEZE]) + if test "$compiler_id" != "unknown"; then - # + tmp_save_CFLAGS="$CFLAGS" tmp_save_CPPFLAGS="$CPPFLAGS" - # + tmp_options="" tmp_CFLAGS="$CFLAGS" tmp_CPPFLAGS="$CPPFLAGS" - # + if test "$want_debug" = "yes"; then AC_MSG_CHECKING([if compiler accepts debug enabling options]) tmp_options="$flags_dbg_yes" fi - # + if test "$flags_prefer_cppflags" = "yes"; then CPPFLAGS="$tmp_CPPFLAGS $tmp_options" CFLAGS="$tmp_CFLAGS" @@ -695,26 +697,26 @@ dnl Sets compiler specific options/flags which depend dnl on configure's optimize option. AC_DEFUN([CURL_SET_COMPILER_OPTIMIZE_OPTS], [ - AC_REQUIRE([CURL_CHECK_OPTION_OPTIMIZE])dnl - AC_REQUIRE([CURL_CHECK_COMPILER])dnl - AC_REQUIRE([CURL_SHFUNC_SQUEEZE])dnl - # + AC_REQUIRE([CURL_CHECK_OPTION_OPTIMIZE]) + AC_REQUIRE([CURL_CHECK_COMPILER]) + AC_REQUIRE([CURL_SHFUNC_SQUEEZE]) + if test "$compiler_id" != "unknown"; then - # + tmp_save_CFLAGS="$CFLAGS" tmp_save_CPPFLAGS="$CPPFLAGS" - # + tmp_options="" tmp_CFLAGS="$CFLAGS" tmp_CPPFLAGS="$CPPFLAGS" honor_optimize_option="yes" - # + dnl If optimization request setting has not been explicitly specified, dnl it has been derived from the debug setting and initially assumed. dnl This initially assumed optimizer setting will finally be ignored dnl if CFLAGS or CPPFLAGS already hold optimizer flags. This implies dnl that an initially assumed optimizer setting might not be honored. - # + if test "$want_optimize" = "assume_no" || test "$want_optimize" = "assume_yes"; then AC_MSG_CHECKING([if compiler optimizer assumed setting might be used]) @@ -734,7 +736,7 @@ AC_DEFUN([CURL_SET_COMPILER_OPTIMIZE_OPTS], [ fi fi fi - # + if test "$honor_optimize_option" = "yes"; then CURL_VAR_STRIP([tmp_CFLAGS],[$flags_opt_all]) CURL_VAR_STRIP([tmp_CPPFLAGS],[$flags_opt_all]) @@ -766,7 +768,6 @@ AC_DEFUN([CURL_SET_COMPILER_OPTIMIZE_OPTS], [ CFLAGS="$tmp_save_CFLAGS" ]) fi - # fi ]) @@ -777,23 +778,27 @@ dnl Sets compiler options/flags which depend on dnl configure's warnings given option. AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ - AC_REQUIRE([CURL_CHECK_OPTION_WARNINGS])dnl - AC_REQUIRE([CURL_CHECK_COMPILER])dnl - AC_REQUIRE([CURL_SHFUNC_SQUEEZE])dnl - # + AC_REQUIRE([CURL_CHECK_OPTION_WARNINGS]) + AC_REQUIRE([CURL_CHECK_COMPILER]) + AC_REQUIRE([CURL_SHFUNC_SQUEEZE]) + if test "$compiler_id" != "unknown"; then - # + tmp_save_CPPFLAGS="$CPPFLAGS" tmp_save_CFLAGS="$CFLAGS" tmp_CPPFLAGS="" tmp_CFLAGS="" - # + case "$compiler_id" in - # + CLANG|APPLECLANG) - # + if test "$want_warnings" = "yes"; then - tmp_CFLAGS="$tmp_CFLAGS -pedantic" + if test "$compiler_num" -ge "302"; then + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [pedantic]) + else + tmp_CFLAGS="$tmp_CFLAGS -pedantic" + fi CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [all extra]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [pointer-arith write-strings]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [shadow]) @@ -805,57 +810,65 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [sign-compare]) tmp_CFLAGS="$tmp_CFLAGS -Wno-multichar" CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [undef]) - tmp_CFLAGS="$tmp_CFLAGS -Wno-format-nonliteral" CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [endif-labels strict-prototypes]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [declaration-after-statement]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [cast-align]) tmp_CFLAGS="$tmp_CFLAGS -Wno-system-headers" CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [shorten-64-to-32]) - # + dnl Only clang 1.1 or later if test "$compiler_num" -ge "101"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unused]) fi - # + dnl Only clang 2.7 or later if test "$compiler_num" -ge "207"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [address]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [attributes]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [bad-function-cast]) + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [cast-qual]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [conversion]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [div-by-zero format-security]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [empty-body]) + tmp_CFLAGS="$tmp_CFLAGS -Wformat=2" CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [missing-field-initializers]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [missing-noreturn]) + tmp_CFLAGS="$tmp_CFLAGS -Wno-switch-default" + tmp_CFLAGS="$tmp_CFLAGS -Wno-switch-enum" # Not used because this basically disallows default case CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [old-style-definition]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [redundant-decls]) - # CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [switch-enum]) # Not used because this basically disallows default case CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [type-limits]) # CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unused-macros]) # Not practical # tmp_CFLAGS="$tmp_CFLAGS -Wno-error=unused-macros" CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unreachable-code unused-parameter]) fi - # + dnl Only clang 2.8 or later if test "$compiler_num" -ge "208"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [ignored-qualifiers]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [vla]) fi - # + dnl Only clang 2.9 or later if test "$compiler_num" -ge "209"; then tmp_CFLAGS="$tmp_CFLAGS -Wno-sign-conversion" + tmp_CFLAGS="$tmp_CFLAGS -Wno-padded" # Not used because we cannot change public structs + tmp_CFLAGS="$tmp_CFLAGS -Wno-used-but-marked-unused" # for typecheck-gcc.h with clang 14+, dependency headers CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [shift-sign-overflow]) - # CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [padded]) # Not used because we cannot change public structs fi - # + dnl Only clang 3.0 or later if test "$compiler_num" -ge "300"; then - CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [cast-qual]) + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [conditional-uninitialized]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [language-extension-token]) - tmp_CFLAGS="$tmp_CFLAGS -Wformat=2" fi - # + dnl Only clang 3.1 or later + if test "$compiler_num" -ge "301"; then + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [format-non-iso]) + tmp_CFLAGS="$tmp_CFLAGS -Wno-covered-switch-default" # Annoying to fix or silence + tmp_CFLAGS="$tmp_CFLAGS -Wno-disabled-macro-expansion" # for std headers, and curl/curl.h (rare combos) + fi + dnl Only clang 3.2 or later if test "$compiler_num" -ge "302"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [enum-conversion]) @@ -870,29 +883,33 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ ;; esac fi - # + dnl Only clang 3.3 or later + if test "$compiler_num" -ge "303"; then + tmp_CFLAGS="$tmp_CFLAGS -Wno-documentation-unknown-command" + fi + dnl Only clang 3.4 or later if test "$compiler_num" -ge "304"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [header-guard]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unused-const-variable]) fi - # + dnl Only clang 3.5 or later if test "$compiler_num" -ge "305"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [pragmas]) # CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unreachable-code-break]) # Not used: Silent in "unity" builds fi - # + dnl Only clang 3.6 or later if test "$compiler_num" -ge "306"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [double-promotion]) fi - # + dnl Only clang 3.9 or later if test "$compiler_num" -ge "309"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [comma]) - # avoid the varargs warning, fixed in 4.0 - # https://bugs.llvm.org/show_bug.cgi?id=29140 + dnl avoid the varargs warning, fixed in 4.0 + dnl https://bugs.llvm.org/show_bug.cgi?id=29140 if test "$compiler_num" -lt "400"; then tmp_CFLAGS="$tmp_CFLAGS -Wno-varargs" fi @@ -907,59 +924,110 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ tmp_CFLAGS="$tmp_CFLAGS -Wimplicit-fallthrough" # we have silencing markup for clang 10.0 and above only CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [xor-used-as-pow]) fi + dnl clang 13 or later + if test "$compiler_num" -ge "1300"; then + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [cast-function-type]) + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [reserved-identifier]) # Keep it before -Wno-reserved-macro-identifier + tmp_CFLAGS="$tmp_CFLAGS -Wno-reserved-macro-identifier" # Sometimes such external macros need to be set + fi + dnl clang 16 or later + if test "$compiler_num" -ge "1600"; then + tmp_CFLAGS="$tmp_CFLAGS -Wno-unsafe-buffer-usage" + fi + dnl clang 17 or later + if test "$compiler_num" -ge "1700"; then + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [cast-function-type-strict]) # with Apple clang it requires 16.0 or above + fi + dnl clang 19 or later + if test "$compiler_num" -ge "1901"; then + tmp_CFLAGS="$tmp_CFLAGS -Wno-format-signedness" + fi + dnl clang 20 or later + if test "$compiler_num" -ge "2001"; then + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [array-compare]) + fi + dnl clang 21 or later + if test "$compiler_num" -ge "2101"; then + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [c++-hidden-decl]) + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [implicit-int-enum-cast]) + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [jump-misses-init]) + tmp_CFLAGS="$tmp_CFLAGS -Wno-implicit-void-ptr-cast" + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [tentative-definition-compat]) + if test "$curl_cv_native_windows" = "yes"; then + tmp_CFLAGS="$tmp_CFLAGS -Wno-c++-keyword" # `wchar_t` triggers it on Windows + else + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [c++-keyword]) + fi + fi + + case "$CFLAGS" in + *-std=c89*|*-std=c90*|*-std=gnu89*|*-std=gnu90*) + if test "$compiler_num" -ge "300"; then + tmp_CFLAGS="$tmp_CFLAGS -Wno-c99-extensions" # Avoid: warning: '_Bool' is a C99 extension + fi + if test "$compiler_num" -ge "309"; then + tmp_CFLAGS="$tmp_CFLAGS -Wno-comma" # Just silly + fi + ;; + esac fi ;; - # + DEC_C) - # + if test "$want_warnings" = "yes"; then dnl Select a higher warning level than default level2 tmp_CFLAGS="$tmp_CFLAGS -msg_enable level3" fi ;; - # + GNU_C) - # - if test "$want_warnings" = "yes"; then - # + + dnl Leave disabled for GCC <4.6, because they lack #pragma features to silence locally. + if test "$want_warnings" = "yes" && test "$compiler_num" -ge "406"; then + dnl Do not enable -pedantic when cross-compiling with a gcc older dnl than 3.0, to avoid warnings from third party system headers. - if test "x$cross_compiling" != "xyes" || - test "$compiler_num" -ge "300"; then - tmp_CFLAGS="$tmp_CFLAGS -pedantic" + if test "$cross_compiling" != "yes" || + test "$compiler_num" -ge "300"; then + if test "$compiler_num" -ge "408"; then + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [pedantic]) + else + tmp_CFLAGS="$tmp_CFLAGS -pedantic" + fi fi - # + dnl Set of options we believe *ALL* gcc versions support: CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [all]) tmp_CFLAGS="$tmp_CFLAGS -W" - # + dnl Only gcc 1.4 or later if test "$compiler_num" -ge "104"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [pointer-arith write-strings]) dnl If not cross-compiling with a gcc older than 3.0 - if test "x$cross_compiling" != "xyes" || + if test "$cross_compiling" != "yes" || test "$compiler_num" -ge "300"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unused shadow]) fi fi - # + dnl Only gcc 2.7 or later if test "$compiler_num" -ge "207"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [nested-externs]) dnl If not cross-compiling with a gcc older than 3.0 - if test "x$cross_compiling" != "xyes" || + if test "$cross_compiling" != "yes" || test "$compiler_num" -ge "300"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [missing-declarations]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [missing-prototypes]) fi fi - # + dnl Only gcc 2.95 or later if test "$compiler_num" -ge "295"; then tmp_CFLAGS="$tmp_CFLAGS -Wno-long-long" CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [bad-function-cast]) fi - # + dnl Only gcc 2.96 or later if test "$compiler_num" -ge "296"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [float-equal]) @@ -970,12 +1038,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ dnl headers with gcc 2.95.4 on FreeBSD 4.9 CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [undef]) fi - # - dnl Only gcc 2.97 or later - if test "$compiler_num" -ge "297"; then - tmp_CFLAGS="$tmp_CFLAGS -Wno-format-nonliteral" - fi - # + dnl Only gcc 3.0 or later if test "$compiler_num" -ge "300"; then dnl -Wunreachable-code seems totally unreliable on my gcc 3.3.2 on @@ -984,24 +1047,24 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ dnl over making it unusable for generic purposes. Let's not use it. tmp_CFLAGS="$tmp_CFLAGS" fi - # + dnl Only gcc 3.3 or later if test "$compiler_num" -ge "303"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [endif-labels strict-prototypes]) fi - # + dnl Only gcc 3.4 or later if test "$compiler_num" -ge "304"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [declaration-after-statement]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [old-style-definition]) fi - # + dnl Only gcc 4.0 or later if test "$compiler_num" -ge "400"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [cast-qual]) tmp_CFLAGS="$tmp_CFLAGS -Wstrict-aliasing=3" fi - # + dnl Only gcc 4.1 or later if test "$compiler_num" -ge "401"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [attributes]) @@ -1015,19 +1078,20 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ ;; esac CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unreachable-code unused-parameter]) - # CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [padded]) # Not used because we cannot change public structs + tmp_CFLAGS="$tmp_CFLAGS -Wno-padded" # Not used because we cannot change public structs + tmp_CFLAGS="$tmp_CFLAGS -Wno-switch-default" + tmp_CFLAGS="$tmp_CFLAGS -Wno-switch-enum" # Not used because this basically disallows default case CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [pragmas]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [redundant-decls]) - # CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [switch-enum]) # Not used because this basically disallows default case # CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unused-macros]) # Not practical # tmp_CFLAGS="$tmp_CFLAGS -Wno-error=unused-macros" fi - # + dnl Only gcc 4.2 or later if test "$compiler_num" -ge "402"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [cast-align]) fi - # + dnl Only gcc 4.3 or later if test "$compiler_num" -ge "403"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [address]) @@ -1040,7 +1104,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ dnl required for -Warray-bounds, included in -Wall tmp_CFLAGS="$tmp_CFLAGS -ftree-vrp" fi - # + dnl Only gcc 4.5 or later if test "$compiler_num" -ge "405"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [jump-misses-init]) @@ -1055,23 +1119,24 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ ;; esac fi - # + dnl Only gcc 4.6 or later if test "$compiler_num" -ge "406"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [double-promotion]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [trampolines]) fi - # + dnl only gcc 4.8 or later if test "$compiler_num" -ge "408"; then tmp_CFLAGS="$tmp_CFLAGS -Wformat=2" fi - # + dnl Only gcc 5 or later if test "$compiler_num" -ge "500"; then tmp_CFLAGS="$tmp_CFLAGS -Warray-bounds=2" + tmp_CFLAGS="$tmp_CFLAGS -Wno-format-signedness" fi - # + dnl Only gcc 6 or later if test "$compiler_num" -ge "600"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [shift-negative-value]) @@ -1081,7 +1146,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [duplicated-cond]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unused-const-variable]) fi - # + dnl Only gcc 7 or later if test "$compiler_num" -ge "700"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [duplicated-branches]) @@ -1090,40 +1155,39 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ tmp_CFLAGS="$tmp_CFLAGS -Wformat-truncation=2" tmp_CFLAGS="$tmp_CFLAGS -Wimplicit-fallthrough" fi - # + dnl Only gcc 10 or later if test "$compiler_num" -ge "1000"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [arith-conversion]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [enum-conversion]) fi - # + dnl Only gcc 12 or later if test "$compiler_num" -ge "1200"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [array-compare]) fi - # + dnl Only gcc 13 or later if test "$compiler_num" -ge "1300"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [enum-int-mismatch]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [xor-used-as-pow]) fi - # + dnl Only gcc 15 or later if test "$compiler_num" -ge "1500"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [leading-whitespace=spaces]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [trailing-whitespace=any]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unterminated-string-initialization]) fi - # fi - # + dnl Do not issue warnings for code in system include paths. if test "$compiler_num" -ge "300"; then tmp_CFLAGS="$tmp_CFLAGS -Wno-system-headers" else dnl When cross-compiling with a gcc older than 3.0, disable dnl some warnings triggered on third party system headers. - if test "x$cross_compiling" = "xyes"; then + if test "$cross_compiling" = "yes"; then if test "$compiler_num" -ge "104"; then dnl gcc 1.4 or later tmp_CFLAGS="$tmp_CFLAGS -Wno-unused -Wno-shadow" @@ -1135,45 +1199,36 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ fi fi fi - if test "$compiler_num" -lt "405"; then - dnl Avoid false positives - tmp_CFLAGS="$tmp_CFLAGS -Wno-shadow" - tmp_CFLAGS="$tmp_CFLAGS -Wno-unreachable-code" - fi - if test "$compiler_num" -ge "402" -a "$compiler_num" -lt "406"; then - dnl GCC <4.6 do not support #pragma to suppress warnings locally. Disable globally instead. - tmp_CFLAGS="$tmp_CFLAGS -Wno-overlength-strings" - fi - if test "$compiler_num" -ge "400" -a "$compiler_num" -lt "407"; then + if test "$compiler_num" -ge "400" && test "$compiler_num" -lt "407"; then dnl https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84685 tmp_CFLAGS="$tmp_CFLAGS -Wno-missing-field-initializers" fi - if test "$compiler_num" -ge "403" -a "$compiler_num" -lt "408"; then + if test "$compiler_num" -ge "403" && test "$compiler_num" -lt "408"; then dnl Avoid false positives tmp_CFLAGS="$tmp_CFLAGS -Wno-type-limits" fi - if test "$compiler_num" -ge "501" -a "$compiler_num" -lt "505"; then + if test "$compiler_num" -ge "501" && test "$compiler_num" -lt "505"; then dnl Avoid false positives tmp_CFLAGS="$tmp_CFLAGS -Wno-conversion" fi ;; - # + HP_UX_C) - # + if test "$want_warnings" = "yes"; then dnl Issue all warnings tmp_CFLAGS="$tmp_CFLAGS +w1" fi ;; - # + IBM_C) - # + dnl Placeholder tmp_CFLAGS="$tmp_CFLAGS" ;; - # + INTEL_UNIX_C) - # + if test "$want_warnings" = "yes"; then if test "$compiler_num" -gt "600"; then dnl Show errors, warnings, and remarks @@ -1207,23 +1262,23 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ dnl Value-safe optimizations on floating-point data tmp_CFLAGS="$tmp_CFLAGS -fp-model precise" ;; - # + INTEL_WINDOWS_C) - # + dnl Placeholder tmp_CFLAGS="$tmp_CFLAGS" ;; - # + SGI_MIPS_C) - # + if test "$want_warnings" = "yes"; then dnl Perform stricter semantic and lint-like checks tmp_CFLAGS="$tmp_CFLAGS -fullwarn" fi ;; - # + SGI_MIPSPRO_C) - # + if test "$want_warnings" = "yes"; then dnl Perform stricter semantic and lint-like checks tmp_CFLAGS="$tmp_CFLAGS -fullwarn" @@ -1232,17 +1287,17 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ tmp_CFLAGS="$tmp_CFLAGS -woff 1209" fi ;; - # + SUNPRO_C) - # + if test "$want_warnings" = "yes"; then dnl Perform stricter semantic and lint-like checks tmp_CFLAGS="$tmp_CFLAGS -v" fi ;; - # + TINY_C) - # + if test "$want_warnings" = "yes"; then dnl Activate all warnings CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [all]) @@ -1252,13 +1307,13 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unsupported]) fi ;; - # + esac - # + squeeze tmp_CPPFLAGS squeeze tmp_CFLAGS - # - if test ! -z "$tmp_CFLAGS" || test ! -z "$tmp_CPPFLAGS"; then + + if test -n "$tmp_CFLAGS" || test -n "$tmp_CPPFLAGS"; then AC_MSG_CHECKING([if compiler accepts strict warning options]) CPPFLAGS="$tmp_save_CPPFLAGS $tmp_CPPFLAGS" CFLAGS="$tmp_save_CFLAGS $tmp_CFLAGS" @@ -1275,7 +1330,6 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ CFLAGS="$tmp_save_CFLAGS" ]) fi - # fi ]) @@ -1332,7 +1386,7 @@ dnl code file, when the source code tries to define a dnl type for a constant array with negative dimension. AC_DEFUN([CURL_CHECK_COMPILER_ARRAY_SIZE_NEGATIVE], [ - AC_REQUIRE([CURL_CHECK_COMPILER_HALT_ON_ERROR])dnl + AC_REQUIRE([CURL_CHECK_COMPILER_HALT_ON_ERROR]) AC_MSG_CHECKING([if compiler halts on negative sized arrays]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ @@ -1358,7 +1412,7 @@ dnl result, as a compilation-time condition inside the dnl type definition of a constant array. AC_DEFUN([CURL_CHECK_COMPILER_STRUCT_MEMBER_SIZE], [ - AC_REQUIRE([CURL_CHECK_COMPILER_ARRAY_SIZE_NEGATIVE])dnl + AC_REQUIRE([CURL_CHECK_COMPILER_ARRAY_SIZE_NEGATIVE]) AC_MSG_CHECKING([if compiler struct member size checking works]) tst_compiler_check_one_works="unknown" AC_COMPILE_IFELSE([ @@ -1423,8 +1477,8 @@ dnl shell variable supports_symbol_hiding value as appropriate, as well as dnl variables symbol_hiding_CFLAGS and symbol_hiding_EXTERN when supported. AC_DEFUN([CURL_CHECK_COMPILER_SYMBOL_HIDING], [ - AC_REQUIRE([CURL_CHECK_COMPILER])dnl - AC_BEFORE([$0],[CURL_CONFIGURE_SYMBOL_HIDING])dnl + AC_REQUIRE([CURL_CHECK_COMPILER]) + AC_BEFORE([$0],[CURL_CONFIGURE_SYMBOL_HIDING]) AC_MSG_CHECKING([if compiler supports hiding library internal symbols]) supports_symbol_hiding="no" symbol_hiding_CFLAGS="" @@ -1441,7 +1495,7 @@ AC_DEFUN([CURL_CHECK_COMPILER_SYMBOL_HIDING], [ GNU_C) dnl Only gcc 3.4 or later if test "$compiler_num" -ge "304"; then - if $CC --help --verbose 2>/dev/null | grep fvisibility= >/dev/null ; then + if $CC --help --verbose 2>/dev/null | grep fvisibility= >/dev/null; then tmp_EXTERN="__attribute__((__visibility__(\"default\")))" tmp_CFLAGS="-fvisibility=hidden" supports_symbol_hiding="yes" @@ -1451,7 +1505,7 @@ AC_DEFUN([CURL_CHECK_COMPILER_SYMBOL_HIDING], [ INTEL_UNIX_C) dnl Only icc 9.0 or later if test "$compiler_num" -ge "900"; then - if $CC --help --verbose 2>&1 | grep fvisibility= > /dev/null ; then + if $CC --help --verbose 2>&1 | grep fvisibility= > /dev/null; then tmp_save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fvisibility=hidden" AC_LINK_IFELSE([ @@ -1470,7 +1524,7 @@ AC_DEFUN([CURL_CHECK_COMPILER_SYMBOL_HIDING], [ fi ;; SUNPRO_C) - if $CC 2>&1 | grep flags >/dev/null && $CC -flags | grep xldscope= >/dev/null ; then + if $CC 2>&1 | grep flags >/dev/null && $CC -flags | grep xldscope= >/dev/null; then tmp_EXTERN="__global" tmp_CFLAGS="-xldscope=hidden" supports_symbol_hiding="yes" @@ -1532,7 +1586,7 @@ dnl code file, when the source code tries to redefine dnl a prototype which does not match previous one. AC_DEFUN([CURL_CHECK_COMPILER_PROTOTYPE_MISMATCH], [ - AC_REQUIRE([CURL_CHECK_COMPILER_HALT_ON_ERROR])dnl + AC_REQUIRE([CURL_CHECK_COMPILER_HALT_ON_ERROR]) AC_MSG_CHECKING([if compiler halts on function prototype mismatch]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ @@ -1546,7 +1600,7 @@ AC_DEFUN([CURL_CHECK_COMPILER_PROTOTYPE_MISMATCH], [ return n; } ]],[[ - int i[2]={0,0}; + int i[2] ={ 0, 0 }; int j = rand(i[0]); if(j) return j; @@ -1604,7 +1658,7 @@ dnl as whitespace separated lists of words. Each word dnl from VALUE is removed from VARNAME when present. AC_DEFUN([CURL_VAR_STRIP], [ - AC_REQUIRE([CURL_SHFUNC_SQUEEZE])dnl + AC_REQUIRE([CURL_SHFUNC_SQUEEZE]) ac_var_stripped="" for word1 in $[$1]; do ac_var_strip_word="no" @@ -1630,7 +1684,7 @@ dnl Add each compiler warning from NEW-WARNINGS that has not dnl been disabled via CFLAGS to WARNING-LIST. AC_DEFUN([CURL_ADD_COMPILER_WARNINGS], [ - AC_REQUIRE([CURL_SHFUNC_SQUEEZE])dnl + AC_REQUIRE([CURL_SHFUNC_SQUEEZE]) ac_var_added_warnings="" for warning in [$2]; do CURL_VAR_MATCH(CFLAGS, [-Wno-$warning -W$warning]) diff --git a/m4/curl-confopts.m4 b/m4/curl-confopts.m4 index 5149e8a34a..fe20155564 100644 --- a/m4/curl-confopts.m4 +++ b/m4/curl-confopts.m4 @@ -22,8 +22,8 @@ # #*************************************************************************** -# File version for 'aclocal' use. Keep it a single number. -# serial 19 +dnl File version for 'aclocal' use. Keep it a single number. +dnl serial 19 dnl CURL_CHECK_OPTION_THREADED_RESOLVER dnl ------------------------------------------------- @@ -73,8 +73,7 @@ dnl --enable-ares or --disable-ares, and dnl set shell variable want_ares as appropriate. AC_DEFUN([CURL_CHECK_OPTION_ARES], [ -dnl AC_BEFORE([$0],[CURL_CHECK_OPTION_THREADS])dnl - AC_BEFORE([$0],[CURL_CHECK_LIB_ARES])dnl + AC_BEFORE([$0],[CURL_CHECK_LIB_ARES]) AC_MSG_CHECKING([whether to enable c-ares for DNS lookups]) OPT_ARES="default" AC_ARG_ENABLE(ares, @@ -93,7 +92,7 @@ AS_HELP_STRING([--disable-ares],[Disable c-ares for DNS lookups]), *) dnl --enable-ares option used want_ares="yes" - if test -n "$enableval" && test "$enableval" != "yes"; then + if test -n "$enableval" && test "x$enableval" != "xyes"; then want_ares_path="$enableval" fi ;; @@ -102,57 +101,6 @@ AS_HELP_STRING([--disable-ares],[Disable c-ares for DNS lookups]), ]) -dnl CURL_CHECK_OPTION_CURLDEBUG -dnl ------------------------------------------------- -dnl Verify if configure has been invoked with option -dnl --enable-curldebug or --disable-curldebug, and set -dnl shell variable want_curldebug value as appropriate. - -AC_DEFUN([CURL_CHECK_OPTION_CURLDEBUG], [ - AC_BEFORE([$0],[CURL_CHECK_CURLDEBUG])dnl - AC_MSG_CHECKING([whether to enable curl debug memory tracking]) - OPT_CURLDEBUG_BUILD="default" - AC_ARG_ENABLE(curldebug, -AS_HELP_STRING([--enable-curldebug],[Enable curl debug memory tracking]) -AS_HELP_STRING([--disable-curldebug],[Disable curl debug memory tracking]), - OPT_CURLDEBUG_BUILD=$enableval) - case "$OPT_CURLDEBUG_BUILD" in - no) - dnl --disable-curldebug option used - want_curldebug="no" - AC_MSG_RESULT([no]) - ;; - default) - dnl configure's curldebug option not specified. Initially we will - dnl handle this as a request to use the same setting as option - dnl --enable-debug. IOW, initially, for debug-enabled builds - dnl this will be handled as a request to enable curldebug if - dnl possible, and for debug-disabled builds this will be handled - dnl as a request to disable curldebug. - if test "$want_debug" = "yes"; then - AC_MSG_RESULT([(assumed) yes]) - else - AC_MSG_RESULT([no]) - fi - want_curldebug_assumed="yes" - want_curldebug="$want_debug" - ;; - *) - dnl --enable-curldebug option used. - dnl The use of this option value is a request to enable curl's - dnl debug memory tracking for the libcurl library. This can only - dnl be done when some requisites are simultaneously satisfied. - dnl Later on, these requisites are verified and if they are not - dnl fully satisfied the option will be ignored and act as if - dnl --disable-curldebug had been given setting shell variable - dnl want_curldebug to 'no'. - want_curldebug="yes" - AC_MSG_RESULT([yes]) - ;; - esac -]) - - dnl CURL_CHECK_OPTION_DEBUG dnl ------------------------------------------------- dnl Verify if configure has been invoked with option @@ -160,9 +108,8 @@ dnl --enable-debug or --disable-debug, and set shell dnl variable want_debug value as appropriate. AC_DEFUN([CURL_CHECK_OPTION_DEBUG], [ - AC_BEFORE([$0],[CURL_CHECK_OPTION_WARNINGS])dnl - AC_BEFORE([$0],[CURL_CHECK_OPTION_CURLDEBUG])dnl - AC_BEFORE([$0],[XC_CHECK_PROG_CC])dnl + AC_BEFORE([$0],[CURL_CHECK_OPTION_WARNINGS]) + AC_BEFORE([$0],[XC_CHECK_PROG_CC]) AC_MSG_CHECKING([whether to enable debug build options]) OPT_DEBUG_BUILD="default" AC_ARG_ENABLE(debug, @@ -193,8 +140,8 @@ dnl --enable-optimize or --disable-optimize, and set dnl shell variable want_optimize value as appropriate. AC_DEFUN([CURL_CHECK_OPTION_OPTIMIZE], [ - AC_REQUIRE([CURL_CHECK_OPTION_DEBUG])dnl - AC_BEFORE([$0],[XC_CHECK_PROG_CC])dnl + AC_REQUIRE([CURL_CHECK_OPTION_DEBUG]) + AC_BEFORE([$0],[XC_CHECK_PROG_CC]) AC_MSG_CHECKING([whether to enable compiler optimizer]) OPT_COMPILER_OPTIMIZE="default" AC_ARG_ENABLE(optimize, @@ -249,7 +196,7 @@ dnl --enable-symbol-hiding or --disable-symbol-hiding, dnl setting shell variable want_symbol_hiding value. AC_DEFUN([CURL_CHECK_OPTION_SYMBOL_HIDING], [ - AC_BEFORE([$0],[CURL_CHECK_COMPILER_SYMBOL_HIDING])dnl + AC_BEFORE([$0],[CURL_CHECK_COMPILER_SYMBOL_HIDING]) AC_MSG_CHECKING([whether to enable hiding of library internal symbols]) OPT_SYMBOL_HIDING="default" AC_ARG_ENABLE(symbol-hiding, @@ -289,11 +236,11 @@ dnl --disable-rt and set shell variable dontwant_rt dnl as appropriate. AC_DEFUN([CURL_CHECK_OPTION_RT], [ - AC_BEFORE([$0], [CURL_CHECK_LIB_THREADS])dnl + AC_BEFORE([$0], [CURL_CHECK_LIB_THREADS]) AC_MSG_CHECKING([whether to disable dependency on -lrt]) OPT_RT="default" AC_ARG_ENABLE(rt, - AS_HELP_STRING([--disable-rt],[disable dependency on -lrt]), +AS_HELP_STRING([--disable-rt],[disable dependency on -lrt]), OPT_RT=$enableval) case "$OPT_RT" in no) @@ -321,9 +268,9 @@ dnl --enable-warnings or --disable-warnings, and set dnl shell variable want_warnings as appropriate. AC_DEFUN([CURL_CHECK_OPTION_WARNINGS], [ - AC_REQUIRE([CURL_CHECK_OPTION_DEBUG])dnl - AC_BEFORE([$0],[CURL_CHECK_OPTION_WERROR])dnl - AC_BEFORE([$0],[XC_CHECK_PROG_CC])dnl + AC_REQUIRE([CURL_CHECK_OPTION_DEBUG]) + AC_BEFORE([$0],[CURL_CHECK_OPTION_WERROR]) + AC_BEFORE([$0],[XC_CHECK_PROG_CC]) AC_MSG_CHECKING([whether to enable strict compiler warnings]) OPT_COMPILER_WARNINGS="default" AC_ARG_ENABLE(warnings, @@ -355,7 +302,7 @@ dnl --enable-werror or --disable-werror, and set dnl shell variable want_werror as appropriate. AC_DEFUN([CURL_CHECK_OPTION_WERROR], [ - AC_BEFORE([$0],[CURL_CHECK_COMPILER])dnl + AC_BEFORE([$0],[CURL_CHECK_COMPILER]) AC_MSG_CHECKING([whether to enable compiler warnings as errors]) OPT_COMPILER_WERROR="default" AC_ARG_ENABLE(werror, @@ -385,22 +332,22 @@ dnl ------------------------------------------------- dnl Check for how to set a socket into non-blocking state. AC_DEFUN([CURL_CHECK_NONBLOCKING_SOCKET], [ - AC_REQUIRE([CURL_CHECK_FUNC_FCNTL])dnl - AC_REQUIRE([CURL_CHECK_FUNC_IOCTLSOCKET])dnl - AC_REQUIRE([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL])dnl - # + AC_REQUIRE([CURL_CHECK_FUNC_FCNTL]) + AC_REQUIRE([CURL_CHECK_FUNC_IOCTLSOCKET]) + AC_REQUIRE([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL]) + tst_method="unknown" AC_MSG_CHECKING([how to set a socket into non-blocking mode]) - if test "x$curl_cv_func_fcntl_o_nonblock" = "xyes"; then + if test "$curl_cv_func_fcntl_o_nonblock" = "yes"; then tst_method="fcntl O_NONBLOCK" - elif test "x$curl_cv_func_ioctl_fionbio" = "xyes"; then + elif test "$curl_cv_func_ioctl_fionbio" = "yes"; then tst_method="ioctl FIONBIO" - elif test "x$curl_cv_func_ioctlsocket_fionbio" = "xyes"; then + elif test "$curl_cv_func_ioctlsocket_fionbio" = "yes"; then tst_method="ioctlsocket FIONBIO" - elif test "x$curl_cv_func_ioctlsocket_camel_fionbio" = "xyes"; then + elif test "$curl_cv_func_ioctlsocket_camel_fionbio" = "yes"; then tst_method="IoctlSocket FIONBIO" - elif test "x$curl_cv_func_setsockopt_so_nonblock" = "xyes"; then + elif test "$curl_cv_func_setsockopt_so_nonblock" = "yes"; then tst_method="setsockopt SO_NONBLOCK" fi AC_MSG_RESULT([$tst_method]) @@ -433,7 +380,7 @@ AC_DEFUN([CURL_CONFIGURE_SYMBOL_HIDING], [ else AC_MSG_RESULT([no]) fi - AM_CONDITIONAL(DOING_CURL_SYMBOL_HIDING, test x$doing_symbol_hiding = xyes) + AM_CONDITIONAL(DOING_CURL_SYMBOL_HIDING, test "$doing_symbol_hiding" = "yes") AC_SUBST(CFLAG_CURL_SYMBOL_HIDING) ]) @@ -444,7 +391,7 @@ dnl When c-ares library support has been requested, performs necessary checks dnl and adjustments needed to enable support of this library. AC_DEFUN([CURL_CHECK_LIB_ARES], [ - # + if test "$want_ares" = "yes"; then dnl c-ares library support has been requested clean_CPPFLAGS="$CPPFLAGS" @@ -456,7 +403,7 @@ AC_DEFUN([CURL_CHECK_LIB_ARES], [ dnl c-ares library path has been specified ARES_PCDIR="$want_ares_path/lib/pkgconfig" CURL_CHECK_PKGCONFIG(libcares, [$ARES_PCDIR]) - if test "$PKGCONFIG" != "no" ; then + if test "$PKGCONFIG" != "no"; then ares_LIBS=`CURL_EXPORT_PCDIR([$ARES_PCDIR]) $PKGCONFIG --libs-only-l libcares` ares_LDFLAGS=`CURL_EXPORT_PCDIR([$ARES_PCDIR]) @@ -475,7 +422,7 @@ AC_DEFUN([CURL_CHECK_LIB_ARES], [ else dnl c-ares path not specified, use defaults CURL_CHECK_PKGCONFIG(libcares) - if test "$PKGCONFIG" != "no" ; then + if test "$PKGCONFIG" != "no"; then ares_LIBS=`$PKGCONFIG --libs-only-l libcares` ares_LDFLAGS=`$PKGCONFIG --libs-only-L libcares` ares_CPPFLAGS=`$PKGCONFIG --cflags-only-I libcares` @@ -488,41 +435,23 @@ AC_DEFUN([CURL_CHECK_LIB_ARES], [ ares_LIBS="-lcares" fi fi - # + CPPFLAGS="$clean_CPPFLAGS $ares_CPPFLAGS" LDFLAGS="$clean_LDFLAGS $ares_LDFLAGS" LDFLAGSPC="$clean_LDFLAGSPC $ares_LDFLAGS" LIBS="$ares_LIBS $clean_LIBS" - # - dnl check if c-ares new enough - AC_MSG_CHECKING([that c-ares is good and recent enough]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ - #include - /* set of dummy functions in case c-ares was built with debug */ - void curl_dofree(void); void curl_dofree(void) {} - void curl_sclose(void); void curl_sclose(void) {} - void curl_domalloc(void); void curl_domalloc(void) {} - void curl_docalloc(void); void curl_docalloc(void) {} - void curl_socket(void); void curl_socket(void) {} - ]],[[ - ares_channel channel; - ares_cancel(channel); /* added in 1.2.0 */ - ares_process_fd(channel, 0, 0); /* added in 1.4.0 */ - ares_dup(&channel, channel); /* added in 1.6.0 */ - ]]) + dnl check if c-ares new enough, 1.16.0 or newer + AC_CHECK_FUNC([ares_getaddrinfo], + [ ],[ - AC_MSG_RESULT([yes]) - ],[ - AC_MSG_RESULT([no]) - AC_MSG_ERROR([c-ares library defective or too old]) + AC_MSG_ERROR([c-ares library is defective or too old]) dnl restore initial settings CPPFLAGS="$clean_CPPFLAGS" LDFLAGS="$clean_LDFLAGS" LDFLAGSPC="$clean_LDFLAGSPC" LIBS="$clean_LIBS" - # prevent usage + dnl prevent usage want_ares="no" ]) diff --git a/m4/curl-functions.m4 b/m4/curl-functions.m4 index 83206dbc82..9d80f2f538 100644 --- a/m4/curl-functions.m4 +++ b/m4/curl-functions.m4 @@ -22,9 +22,8 @@ # #*************************************************************************** -# File version for 'aclocal' use. Keep it a single number. -# serial 73 - +dnl File version for 'aclocal' use. Keep it a single number. +dnl serial 73 dnl CURL_INCLUDES_ARPA_INET dnl ------------------------------------------------- @@ -419,7 +418,7 @@ struct SocketIFace *ISocket = NULL; # else struct Library *SocketBase = NULL; # endif -# define select(a,b,c,d,e) WaitSelect(a,b,c,d,e,0) +# define select(a, b, c, d, e) WaitSelect(a, b, c, d, e, 0) #endif /* includes end */" AC_CHECK_HEADERS( @@ -471,13 +470,13 @@ dnl shell variable curl_disallow_alarm, then dnl HAVE_ALARM will be defined. AC_DEFUN([CURL_CHECK_FUNC_ALARM], [ - AC_REQUIRE([CURL_INCLUDES_UNISTD])dnl - # + AC_REQUIRE([CURL_INCLUDES_UNISTD]) + tst_links_alarm="unknown" tst_proto_alarm="unknown" tst_compi_alarm="unknown" tst_allow_alarm="unknown" - # + AC_MSG_CHECKING([if alarm can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([alarm]) @@ -488,7 +487,7 @@ AC_DEFUN([CURL_CHECK_FUNC_ALARM], [ AC_MSG_RESULT([no]) tst_links_alarm="no" ]) - # + if test "$tst_links_alarm" = "yes"; then AC_MSG_CHECKING([if alarm is prototyped]) AC_EGREP_CPP([alarm],[ @@ -501,14 +500,14 @@ AC_DEFUN([CURL_CHECK_FUNC_ALARM], [ tst_proto_alarm="no" ]) fi - # + if test "$tst_proto_alarm" = "yes"; then AC_MSG_CHECKING([if alarm is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_unistd ]],[[ - if(0 != alarm(0)) + if(alarm(0) != 0) return 1; ]]) ],[ @@ -519,7 +518,7 @@ AC_DEFUN([CURL_CHECK_FUNC_ALARM], [ tst_compi_alarm="no" ]) fi - # + if test "$tst_compi_alarm" = "yes"; then AC_MSG_CHECKING([if alarm usage allowed]) if test "x$curl_disallow_alarm" != "xyes"; then @@ -530,7 +529,7 @@ AC_DEFUN([CURL_CHECK_FUNC_ALARM], [ tst_allow_alarm="no" fi fi - # + AC_MSG_CHECKING([if alarm might be used]) if test "$tst_links_alarm" = "yes" && test "$tst_proto_alarm" = "yes" && @@ -556,15 +555,15 @@ dnl shell variable curl_disallow_basename, then dnl HAVE_BASENAME will be defined. AC_DEFUN([CURL_CHECK_FUNC_BASENAME], [ - AC_REQUIRE([CURL_INCLUDES_STRING])dnl - AC_REQUIRE([CURL_INCLUDES_LIBGEN])dnl - AC_REQUIRE([CURL_INCLUDES_UNISTD])dnl - # + AC_REQUIRE([CURL_INCLUDES_STRING]) + AC_REQUIRE([CURL_INCLUDES_LIBGEN]) + AC_REQUIRE([CURL_INCLUDES_UNISTD]) + tst_links_basename="unknown" tst_proto_basename="unknown" tst_compi_basename="unknown" tst_allow_basename="unknown" - # + AC_MSG_CHECKING([if basename can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([basename]) @@ -575,7 +574,7 @@ AC_DEFUN([CURL_CHECK_FUNC_BASENAME], [ AC_MSG_RESULT([no]) tst_links_basename="no" ]) - # + if test "$tst_links_basename" = "yes"; then AC_MSG_CHECKING([if basename is prototyped]) AC_EGREP_CPP([basename],[ @@ -590,7 +589,7 @@ AC_DEFUN([CURL_CHECK_FUNC_BASENAME], [ tst_proto_basename="no" ]) fi - # + if test "$tst_proto_basename" = "yes"; then AC_MSG_CHECKING([if basename is compilable]) AC_COMPILE_IFELSE([ @@ -599,7 +598,7 @@ AC_DEFUN([CURL_CHECK_FUNC_BASENAME], [ $curl_includes_libgen $curl_includes_unistd ]],[[ - if(0 != basename(0)) + if(basename(0) != 0) return 1; ]]) ],[ @@ -610,7 +609,7 @@ AC_DEFUN([CURL_CHECK_FUNC_BASENAME], [ tst_compi_basename="no" ]) fi - # + if test "$tst_compi_basename" = "yes"; then AC_MSG_CHECKING([if basename usage allowed]) if test "x$curl_disallow_basename" != "xyes"; then @@ -621,7 +620,7 @@ AC_DEFUN([CURL_CHECK_FUNC_BASENAME], [ tst_allow_basename="no" fi fi - # + AC_MSG_CHECKING([if basename might be used]) if test "$tst_links_basename" = "yes" && test "$tst_proto_basename" = "yes" && @@ -647,19 +646,19 @@ dnl shell variable curl_disallow_closesocket, then dnl HAVE_CLOSESOCKET will be defined. AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ - AC_REQUIRE([CURL_INCLUDES_WINSOCK2])dnl - # + AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) + tst_links_closesocket="unknown" tst_proto_closesocket="unknown" tst_compi_closesocket="unknown" tst_allow_closesocket="unknown" - # + AC_MSG_CHECKING([if closesocket can be linked]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_winsock2 ]],[[ - if(0 != closesocket(0)) + if(closesocket(0) != 0) return 1; ]]) ],[ @@ -669,7 +668,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ AC_MSG_RESULT([no]) tst_links_closesocket="no" ]) - # + if test "$tst_links_closesocket" = "yes"; then AC_MSG_CHECKING([if closesocket is prototyped]) AC_EGREP_CPP([closesocket],[ @@ -682,14 +681,14 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ tst_proto_closesocket="no" ]) fi - # + if test "$tst_proto_closesocket" = "yes"; then AC_MSG_CHECKING([if closesocket is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_winsock2 ]],[[ - if(0 != closesocket(0)) + if(closesocket(0) != 0) return 1; ]]) ],[ @@ -700,7 +699,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ tst_compi_closesocket="no" ]) fi - # + if test "$tst_compi_closesocket" = "yes"; then AC_MSG_CHECKING([if closesocket usage allowed]) if test "x$curl_disallow_closesocket" != "xyes"; then @@ -711,7 +710,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ tst_allow_closesocket="no" fi fi - # + AC_MSG_CHECKING([if closesocket might be used]) if test "$tst_links_closesocket" = "yes" && test "$tst_proto_closesocket" = "yes" && @@ -737,20 +736,20 @@ dnl shell variable curl_disallow_closesocket_camel, dnl then HAVE_CLOSESOCKET_CAMEL will be defined. AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET_CAMEL], [ - AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET])dnl - AC_REQUIRE([CURL_INCLUDES_BSDSOCKET])dnl - # + AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET]) + AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) + tst_links_closesocket_camel="unknown" tst_compi_closesocket_camel="unknown" tst_allow_closesocket_camel="unknown" - # + AC_MSG_CHECKING([if CloseSocket can be linked]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(0 != CloseSocket(0)) + if(CloseSocket(0) != 0) return 1; ]]) ],[ @@ -760,7 +759,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET_CAMEL], [ AC_MSG_RESULT([no]) tst_links_closesocket_camel="no" ]) - # + if test "$tst_links_closesocket_camel" = "yes"; then AC_MSG_CHECKING([if CloseSocket is compilable]) AC_COMPILE_IFELSE([ @@ -768,7 +767,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET_CAMEL], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(0 != CloseSocket(0)) + if(CloseSocket(0) != 0) return 1; ]]) ],[ @@ -779,7 +778,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET_CAMEL], [ tst_compi_closesocket_camel="no" ]) fi - # + if test "$tst_compi_closesocket_camel" = "yes"; then AC_MSG_CHECKING([if CloseSocket usage allowed]) if test "x$curl_disallow_closesocket_camel" != "xyes"; then @@ -790,7 +789,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET_CAMEL], [ tst_allow_closesocket_camel="no" fi fi - # + AC_MSG_CHECKING([if CloseSocket might be used]) if test "$tst_links_closesocket_camel" = "yes" && test "$tst_compi_closesocket_camel" = "yes" && @@ -814,13 +813,13 @@ dnl shell variable curl_disallow_fcntl, then dnl HAVE_FCNTL will be defined. AC_DEFUN([CURL_CHECK_FUNC_FCNTL], [ - AC_REQUIRE([CURL_INCLUDES_FCNTL])dnl - # + AC_REQUIRE([CURL_INCLUDES_FCNTL]) + tst_links_fcntl="unknown" tst_proto_fcntl="unknown" tst_compi_fcntl="unknown" tst_allow_fcntl="unknown" - # + AC_MSG_CHECKING([if fcntl can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([fcntl]) @@ -831,7 +830,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FCNTL], [ AC_MSG_RESULT([no]) tst_links_fcntl="no" ]) - # + if test "$tst_links_fcntl" = "yes"; then AC_MSG_CHECKING([if fcntl is prototyped]) AC_EGREP_CPP([fcntl],[ @@ -844,14 +843,14 @@ AC_DEFUN([CURL_CHECK_FUNC_FCNTL], [ tst_proto_fcntl="no" ]) fi - # + if test "$tst_proto_fcntl" = "yes"; then AC_MSG_CHECKING([if fcntl is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_fcntl ]],[[ - if(0 != fcntl(0, 0, 0)) + if(fcntl(0, 0, 0) != 0) return 1; ]]) ],[ @@ -862,7 +861,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FCNTL], [ tst_compi_fcntl="no" ]) fi - # + if test "$tst_compi_fcntl" = "yes"; then AC_MSG_CHECKING([if fcntl usage allowed]) if test "x$curl_disallow_fcntl" != "xyes"; then @@ -873,7 +872,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FCNTL], [ tst_allow_fcntl="no" fi fi - # + AC_MSG_CHECKING([if fcntl might be used]) if test "$tst_links_fcntl" = "yes" && test "$tst_proto_fcntl" = "yes" && @@ -899,17 +898,17 @@ dnl all of these are true, then HAVE_FCNTL_O_NONBLOCK dnl will be defined. AC_DEFUN([CURL_CHECK_FUNC_FCNTL_O_NONBLOCK], [ - # + tst_compi_fcntl_o_nonblock="unknown" tst_allow_fcntl_o_nonblock="unknown" - # + case $host_os in sunos4* | aix3*) dnl O_NONBLOCK does not work on these platforms curl_disallow_fcntl_o_nonblock="yes" ;; esac - # + if test "$curl_cv_func_fcntl" = "yes"; then AC_MSG_CHECKING([if fcntl O_NONBLOCK is compilable]) AC_COMPILE_IFELSE([ @@ -917,7 +916,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FCNTL_O_NONBLOCK], [ $curl_includes_fcntl ]],[[ int flags = 0; - if(0 != fcntl(0, F_SETFL, flags | O_NONBLOCK)) + if(fcntl(0, F_SETFL, flags | O_NONBLOCK) != 0) return 1; ]]) ],[ @@ -928,7 +927,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FCNTL_O_NONBLOCK], [ tst_compi_fcntl_o_nonblock="no" ]) fi - # + if test "$tst_compi_fcntl_o_nonblock" = "yes"; then AC_MSG_CHECKING([if fcntl O_NONBLOCK usage allowed]) if test "x$curl_disallow_fcntl_o_nonblock" != "xyes"; then @@ -939,7 +938,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FCNTL_O_NONBLOCK], [ tst_allow_fcntl_o_nonblock="no" fi fi - # + AC_MSG_CHECKING([if fcntl O_NONBLOCK might be used]) if test "$tst_compi_fcntl_o_nonblock" = "yes" && test "$tst_allow_fcntl_o_nonblock" = "yes"; then @@ -963,15 +962,15 @@ dnl shell variable curl_disallow_freeaddrinfo, then dnl HAVE_FREEADDRINFO will be defined. AC_DEFUN([CURL_CHECK_FUNC_FREEADDRINFO], [ - AC_REQUIRE([CURL_INCLUDES_WS2TCPIP])dnl - AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET])dnl - AC_REQUIRE([CURL_INCLUDES_NETDB])dnl - # + AC_REQUIRE([CURL_INCLUDES_WS2TCPIP]) + AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET]) + AC_REQUIRE([CURL_INCLUDES_NETDB]) + tst_links_freeaddrinfo="unknown" tst_proto_freeaddrinfo="unknown" tst_compi_freeaddrinfo="unknown" tst_allow_freeaddrinfo="unknown" - # + AC_MSG_CHECKING([if freeaddrinfo can be linked]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ @@ -988,7 +987,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FREEADDRINFO], [ AC_MSG_RESULT([no]) tst_links_freeaddrinfo="no" ]) - # + if test "$tst_links_freeaddrinfo" = "yes"; then AC_MSG_CHECKING([if freeaddrinfo is prototyped]) AC_EGREP_CPP([freeaddrinfo],[ @@ -1003,7 +1002,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FREEADDRINFO], [ tst_proto_freeaddrinfo="no" ]) fi - # + if test "$tst_proto_freeaddrinfo" = "yes"; then AC_MSG_CHECKING([if freeaddrinfo is compilable]) AC_COMPILE_IFELSE([ @@ -1022,7 +1021,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FREEADDRINFO], [ tst_compi_freeaddrinfo="no" ]) fi - # + if test "$tst_compi_freeaddrinfo" = "yes"; then AC_MSG_CHECKING([if freeaddrinfo usage allowed]) if test "x$curl_disallow_freeaddrinfo" != "xyes"; then @@ -1033,7 +1032,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FREEADDRINFO], [ tst_allow_freeaddrinfo="no" fi fi - # + AC_MSG_CHECKING([if freeaddrinfo might be used]) if test "$tst_links_freeaddrinfo" = "yes" && test "$tst_proto_freeaddrinfo" = "yes" && @@ -1059,14 +1058,14 @@ dnl shell variable curl_disallow_fsetxattr, then dnl HAVE_FSETXATTR will be defined. AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ - AC_REQUIRE([CURL_INCLUDES_SYS_XATTR])dnl - # + AC_REQUIRE([CURL_INCLUDES_SYS_XATTR]) + tst_links_fsetxattr="unknown" tst_proto_fsetxattr="unknown" tst_compi_fsetxattr="unknown" tst_allow_fsetxattr="unknown" tst_nargs_fsetxattr="unknown" - # + AC_MSG_CHECKING([if fsetxattr can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([fsetxattr]) @@ -1077,7 +1076,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ AC_MSG_RESULT([no]) tst_links_fsetxattr="no" ]) - # + if test "$tst_links_fsetxattr" = "yes"; then AC_MSG_CHECKING([if fsetxattr is prototyped]) AC_EGREP_CPP([fsetxattr],[ @@ -1090,7 +1089,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ tst_proto_fsetxattr="no" ]) fi - # + if test "$tst_proto_fsetxattr" = "yes"; then if test "$tst_nargs_fsetxattr" = "unknown"; then AC_MSG_CHECKING([if fsetxattr takes 5 args.]) @@ -1098,7 +1097,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ AC_LANG_PROGRAM([[ $curl_includes_sys_xattr ]],[[ - if(0 != fsetxattr(0, "", 0, 0, 0)) + if(fsetxattr(0, "", 0, 0, 0) != 0) return 1; ]]) ],[ @@ -1116,7 +1115,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ AC_LANG_PROGRAM([[ $curl_includes_sys_xattr ]],[[ - if(0 != fsetxattr(0, 0, 0, 0, 0, 0)) + if(fsetxattr(0, 0, 0, 0, 0, 0) != 0) return 1; ]]) ],[ @@ -1135,7 +1134,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ AC_MSG_RESULT([no]) fi fi - # + if test "$tst_compi_fsetxattr" = "yes"; then AC_MSG_CHECKING([if fsetxattr usage allowed]) if test "x$curl_disallow_fsetxattr" != "xyes"; then @@ -1146,7 +1145,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ tst_allow_fsetxattr="no" fi fi - # + AC_MSG_CHECKING([if fsetxattr might be used]) if test "$tst_links_fsetxattr" = "yes" && test "$tst_proto_fsetxattr" = "yes" && @@ -1157,13 +1156,13 @@ AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ [Define to 1 if you have the fsetxattr function.]) dnl AC_DEFINE_UNQUOTED(FSETXATTR_ARGS, $tst_nargs_fsetxattr, dnl [Specifies the number of arguments to fsetxattr]) - # + if test "$tst_nargs_fsetxattr" -eq "5"; then AC_DEFINE(HAVE_FSETXATTR_5, 1, [fsetxattr() takes 5 args]) elif test "$tst_nargs_fsetxattr" -eq "6"; then AC_DEFINE(HAVE_FSETXATTR_6, 1, [fsetxattr() takes 6 args]) fi - # + curl_cv_func_fsetxattr="yes" else AC_MSG_RESULT([no]) @@ -1172,91 +1171,6 @@ AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ ]) -dnl CURL_CHECK_FUNC_FTRUNCATE -dnl ------------------------------------------------- -dnl Verify if ftruncate is available, prototyped, and -dnl can be compiled. If all of these are true, and -dnl usage has not been previously disallowed with -dnl shell variable curl_disallow_ftruncate, then -dnl HAVE_FTRUNCATE will be defined. - -AC_DEFUN([CURL_CHECK_FUNC_FTRUNCATE], [ - AC_REQUIRE([CURL_INCLUDES_UNISTD])dnl - # - tst_links_ftruncate="unknown" - tst_proto_ftruncate="unknown" - tst_compi_ftruncate="unknown" - tst_allow_ftruncate="unknown" - # - AC_MSG_CHECKING([if ftruncate can be linked]) - AC_LINK_IFELSE([ - AC_LANG_FUNC_LINK_TRY([ftruncate]) - ],[ - AC_MSG_RESULT([yes]) - tst_links_ftruncate="yes" - ],[ - AC_MSG_RESULT([no]) - tst_links_ftruncate="no" - ]) - # - if test "$tst_links_ftruncate" = "yes"; then - AC_MSG_CHECKING([if ftruncate is prototyped]) - AC_EGREP_CPP([ftruncate],[ - $curl_includes_unistd - ],[ - AC_MSG_RESULT([yes]) - tst_proto_ftruncate="yes" - ],[ - AC_MSG_RESULT([no]) - tst_proto_ftruncate="no" - ]) - fi - # - if test "$tst_proto_ftruncate" = "yes"; then - AC_MSG_CHECKING([if ftruncate is compilable]) - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - $curl_includes_unistd - ]],[[ - if(0 != ftruncate(0, 0)) - return 1; - ]]) - ],[ - AC_MSG_RESULT([yes]) - tst_compi_ftruncate="yes" - ],[ - AC_MSG_RESULT([no]) - tst_compi_ftruncate="no" - ]) - fi - # - if test "$tst_compi_ftruncate" = "yes"; then - AC_MSG_CHECKING([if ftruncate usage allowed]) - if test "x$curl_disallow_ftruncate" != "xyes"; then - AC_MSG_RESULT([yes]) - tst_allow_ftruncate="yes" - else - AC_MSG_RESULT([no]) - tst_allow_ftruncate="no" - fi - fi - # - AC_MSG_CHECKING([if ftruncate might be used]) - if test "$tst_links_ftruncate" = "yes" && - test "$tst_proto_ftruncate" = "yes" && - test "$tst_compi_ftruncate" = "yes" && - test "$tst_allow_ftruncate" = "yes"; then - AC_MSG_RESULT([yes]) - AC_DEFINE_UNQUOTED(HAVE_FTRUNCATE, 1, - [Define to 1 if you have the ftruncate function.]) - curl_cv_func_ftruncate="yes" - else - AC_MSG_RESULT([no]) - curl_cv_func_ftruncate="no" - fi -]) - - dnl CURL_CHECK_FUNC_GETADDRINFO dnl ------------------------------------------------- dnl Verify if getaddrinfo is available, prototyped, can @@ -1265,24 +1179,24 @@ dnl true, and usage has not been previously disallowed dnl with shell variable curl_disallow_getaddrinfo, then dnl HAVE_GETADDRINFO will be defined. Additionally when dnl HAVE_GETADDRINFO gets defined this will also attempt -dnl to find out if getaddrinfo happens to be threadsafe, +dnl to find out if getaddrinfo happens to be thread-safe, dnl defining HAVE_GETADDRINFO_THREADSAFE when true. AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ - AC_REQUIRE([CURL_INCLUDES_WS2TCPIP])dnl - AC_REQUIRE([CURL_INCLUDES_STDLIB])dnl - AC_REQUIRE([CURL_INCLUDES_STRING])dnl - AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET])dnl - AC_REQUIRE([CURL_INCLUDES_NETDB])dnl - AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS])dnl - # + AC_REQUIRE([CURL_INCLUDES_WS2TCPIP]) + AC_REQUIRE([CURL_INCLUDES_STDLIB]) + AC_REQUIRE([CURL_INCLUDES_STRING]) + AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET]) + AC_REQUIRE([CURL_INCLUDES_NETDB]) + AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS]) + tst_links_getaddrinfo="unknown" tst_proto_getaddrinfo="unknown" tst_compi_getaddrinfo="unknown" tst_works_getaddrinfo="unknown" tst_allow_getaddrinfo="unknown" tst_tsafe_getaddrinfo="unknown" - # + AC_MSG_CHECKING([if getaddrinfo can be linked]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ @@ -1291,7 +1205,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ $curl_includes_netdb ]],[[ struct addrinfo *ai = 0; - if(0 != getaddrinfo(0, 0, 0, &ai)) + if(getaddrinfo(0, 0, 0, &ai) != 0) return 1; ]]) ],[ @@ -1301,7 +1215,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ AC_MSG_RESULT([no]) tst_links_getaddrinfo="no" ]) - # + if test "$tst_links_getaddrinfo" = "yes"; then AC_MSG_CHECKING([if getaddrinfo is prototyped]) AC_EGREP_CPP([getaddrinfo],[ @@ -1316,7 +1230,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ tst_proto_getaddrinfo="no" ]) fi - # + if test "$tst_proto_getaddrinfo" = "yes"; then AC_MSG_CHECKING([if getaddrinfo is compilable]) AC_COMPILE_IFELSE([ @@ -1326,7 +1240,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ $curl_includes_netdb ]],[[ struct addrinfo *ai = 0; - if(0 != getaddrinfo(0, 0, 0, &ai)) + if(getaddrinfo(0, 0, 0, &ai) != 0) return 1; ]]) ],[ @@ -1337,9 +1251,9 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ tst_compi_getaddrinfo="no" ]) fi - # + dnl only do runtime verification when not cross-compiling - if test "x$cross_compiling" != "xyes" && + if test "$cross_compiling" != "yes" && test "$tst_compi_getaddrinfo" = "yes"; then AC_MSG_CHECKING([if getaddrinfo seems to work]) CURL_RUN_IFELSE([ @@ -1385,7 +1299,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ tst_works_getaddrinfo="no" ]) fi - # + if test "$tst_compi_getaddrinfo" = "yes" && test "$tst_works_getaddrinfo" != "no"; then AC_MSG_CHECKING([if getaddrinfo usage allowed]) @@ -1397,7 +1311,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ tst_allow_getaddrinfo="no" fi fi - # + AC_MSG_CHECKING([if getaddrinfo might be used]) if test "$tst_proto_getaddrinfo" = "yes" && test "$tst_compi_getaddrinfo" = "yes" && @@ -1412,10 +1326,10 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ curl_cv_func_getaddrinfo="no" curl_cv_func_getaddrinfo_threadsafe="no" fi - # + if test "$curl_cv_func_getaddrinfo" = "yes"; then - AC_MSG_CHECKING([if getaddrinfo is threadsafe]) - if test "$curl_cv_apple" = 'yes'; then + AC_MSG_CHECKING([if getaddrinfo is thread-safe]) + if test "$curl_cv_apple" = "yes"; then dnl Darwin 6.0 and macOS 10.2.X and newer tst_tsafe_getaddrinfo="yes" fi @@ -1479,7 +1393,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ AC_MSG_RESULT([$tst_tsafe_getaddrinfo]) if test "$tst_tsafe_getaddrinfo" = "yes"; then AC_DEFINE_UNQUOTED(HAVE_GETADDRINFO_THREADSAFE, 1, - [Define to 1 if the getaddrinfo function is threadsafe.]) + [Define to 1 if the getaddrinfo function is thread-safe.]) curl_cv_func_getaddrinfo_threadsafe="yes" else curl_cv_func_getaddrinfo_threadsafe="no" @@ -1497,14 +1411,14 @@ dnl shell variable curl_disallow_gethostbyname_r, then dnl HAVE_GETHOSTBYNAME_R will be defined. AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ - AC_REQUIRE([CURL_INCLUDES_NETDB])dnl - # + AC_REQUIRE([CURL_INCLUDES_NETDB]) + tst_links_gethostbyname_r="unknown" tst_proto_gethostbyname_r="unknown" tst_compi_gethostbyname_r="unknown" tst_allow_gethostbyname_r="unknown" tst_nargs_gethostbyname_r="unknown" - # + AC_MSG_CHECKING([if gethostbyname_r can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([gethostbyname_r]) @@ -1515,7 +1429,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ AC_MSG_RESULT([no]) tst_links_gethostbyname_r="no" ]) - # + if test "$tst_links_gethostbyname_r" = "yes"; then AC_MSG_CHECKING([if gethostbyname_r is prototyped]) AC_EGREP_CPP([gethostbyname_r],[ @@ -1528,7 +1442,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ tst_proto_gethostbyname_r="no" ]) fi - # + if test "$tst_proto_gethostbyname_r" = "yes"; then if test "$tst_nargs_gethostbyname_r" = "unknown"; then AC_MSG_CHECKING([if gethostbyname_r takes 3 args.]) @@ -1537,7 +1451,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ $curl_includes_netdb $curl_includes_bsdsocket ]],[[ - if(0 != gethostbyname_r(0, 0, 0)) + if(gethostbyname_r(0, 0, 0) != 0) return 1; ]]) ],[ @@ -1556,7 +1470,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ $curl_includes_netdb $curl_includes_bsdsocket ]],[[ - if(0 != gethostbyname_r(0, 0, 0, 0, 0)) + if(gethostbyname_r(0, 0, 0, 0, 0) != 0) return 1; ]]) ],[ @@ -1575,7 +1489,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ $curl_includes_netdb $curl_includes_bsdsocket ]],[[ - if(0 != gethostbyname_r(0, 0, 0, 0, 0, 0)) + if(gethostbyname_r(0, 0, 0, 0, 0, 0) != 0) return 1; ]]) ],[ @@ -1594,7 +1508,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ AC_MSG_RESULT([no]) fi fi - # + if test "$tst_compi_gethostbyname_r" = "yes"; then AC_MSG_CHECKING([if gethostbyname_r usage allowed]) if test "x$curl_disallow_gethostbyname_r" != "xyes"; then @@ -1605,7 +1519,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ tst_allow_gethostbyname_r="no" fi fi - # + AC_MSG_CHECKING([if gethostbyname_r might be used]) if test "$tst_links_gethostbyname_r" = "yes" && test "$tst_proto_gethostbyname_r" = "yes" && @@ -1616,7 +1530,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ [Define to 1 if you have the gethostbyname_r function.]) dnl AC_DEFINE_UNQUOTED(GETHOSTBYNAME_R_ARGS, $tst_nargs_gethostbyname_r, dnl [Specifies the number of arguments to gethostbyname_r]) - # + if test "$tst_nargs_gethostbyname_r" -eq "3"; then AC_DEFINE(HAVE_GETHOSTBYNAME_R_3, 1, [gethostbyname_r() takes 3 args]) elif test "$tst_nargs_gethostbyname_r" -eq "5"; then @@ -1624,7 +1538,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ elif test "$tst_nargs_gethostbyname_r" -eq "6"; then AC_DEFINE(HAVE_GETHOSTBYNAME_R_6, 1, [gethostbyname_r() takes 6 args]) fi - # + curl_cv_func_gethostbyname_r="yes" else AC_MSG_RESULT([no]) @@ -1642,16 +1556,16 @@ dnl shell variable curl_disallow_gethostname, then dnl HAVE_GETHOSTNAME will be defined. AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ - AC_REQUIRE([CURL_INCLUDES_WINSOCK2])dnl - AC_REQUIRE([CURL_INCLUDES_BSDSOCKET])dnl - AC_REQUIRE([CURL_INCLUDES_UNISTD])dnl - AC_REQUIRE([CURL_PREPROCESS_CALLCONV])dnl - # + AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) + AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) + AC_REQUIRE([CURL_INCLUDES_UNISTD]) + AC_REQUIRE([CURL_PREPROCESS_CALLCONV]) + tst_links_gethostname="unknown" tst_proto_gethostname="unknown" tst_compi_gethostname="unknown" tst_allow_gethostname="unknown" - # + AC_MSG_CHECKING([if gethostname can be linked]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ @@ -1660,7 +1574,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ $curl_includes_bsdsocket ]],[[ char s[1]; - if(0 != gethostname((void *)s, 0)) + if(gethostname((void *)s, 0) != 0) return 1; ]]) ],[ @@ -1670,7 +1584,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ AC_MSG_RESULT([no]) tst_links_gethostname="no" ]) - # + if test "$tst_links_gethostname" = "yes"; then AC_MSG_CHECKING([if gethostname is prototyped]) AC_EGREP_CPP([gethostname],[ @@ -1685,7 +1599,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ tst_proto_gethostname="no" ]) fi - # + if test "$tst_proto_gethostname" = "yes"; then AC_MSG_CHECKING([if gethostname is compilable]) AC_COMPILE_IFELSE([ @@ -1695,7 +1609,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ $curl_includes_bsdsocket ]],[[ char s[1]; - if(0 != gethostname((void *)s, 0)) + if(gethostname((void *)s, 0) != 0) return 1; ]]) ],[ @@ -1706,7 +1620,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ tst_compi_gethostname="no" ]) fi - # + if test "$tst_compi_gethostname" = "yes"; then AC_MSG_CHECKING([for gethostname arg 2 data type]) tst_gethostname_type_arg2="unknown" @@ -1727,7 +1641,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ int FUNCALLCONV gethostname($tst_arg1, $tst_arg2); ]],[[ char s[1]; - if(0 != gethostname(($tst_arg1)s, 0)) + if(gethostname(($tst_arg1)s, 0) != 0) return 1; ]]) ],[ @@ -1742,7 +1656,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ [Define to the type of arg 2 for gethostname.]) fi fi - # + if test "$tst_compi_gethostname" = "yes"; then AC_MSG_CHECKING([if gethostname usage allowed]) if test "x$curl_disallow_gethostname" != "xyes"; then @@ -1753,7 +1667,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ tst_allow_gethostname="no" fi fi - # + AC_MSG_CHECKING([if gethostname might be used]) if test "$tst_links_gethostname" = "yes" && test "$tst_proto_gethostname" = "yes" && @@ -1778,16 +1692,16 @@ dnl shell variable curl_disallow_getpeername, then dnl HAVE_GETPEERNAME will be defined. AC_DEFUN([CURL_CHECK_FUNC_GETPEERNAME], [ - AC_REQUIRE([CURL_INCLUDES_WINSOCK2])dnl - AC_REQUIRE([CURL_INCLUDES_UNISTD])dnl - AC_REQUIRE([CURL_PREPROCESS_CALLCONV])dnl - AC_REQUIRE([CURL_INCLUDES_BSDSOCKET])dnl - # + AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) + AC_REQUIRE([CURL_INCLUDES_UNISTD]) + AC_REQUIRE([CURL_PREPROCESS_CALLCONV]) + AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) + tst_links_getpeername="unknown" tst_proto_getpeername="unknown" tst_compi_getpeername="unknown" tst_allow_getpeername="unknown" - # + AC_MSG_CHECKING([if getpeername can be linked]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ @@ -1795,7 +1709,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETPEERNAME], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(0 != getpeername(0, (void *)0, (void *)0)) + if(getpeername(0, (void *)0, (void *)0) != 0) return 1; ]]) ],[ @@ -1805,7 +1719,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETPEERNAME], [ AC_MSG_RESULT([no]) tst_links_getpeername="no" ]) - # + if test "$tst_links_getpeername" = "yes"; then AC_MSG_CHECKING([if getpeername is prototyped]) AC_EGREP_CPP([getpeername],[ @@ -1820,7 +1734,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETPEERNAME], [ tst_proto_getpeername="no" ]) fi - # + if test "$tst_proto_getpeername" = "yes"; then AC_MSG_CHECKING([if getpeername is compilable]) AC_COMPILE_IFELSE([ @@ -1829,7 +1743,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETPEERNAME], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(0 != getpeername(0, (void *)0, (void *)0)) + if(getpeername(0, (void *)0, (void *)0) != 0) return 1; ]]) ],[ @@ -1840,7 +1754,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETPEERNAME], [ tst_compi_getpeername="no" ]) fi - # + if test "$tst_compi_getpeername" = "yes"; then AC_MSG_CHECKING([if getpeername usage allowed]) if test "x$curl_disallow_getpeername" != "xyes"; then @@ -1851,7 +1765,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETPEERNAME], [ tst_allow_getpeername="no" fi fi - # + AC_MSG_CHECKING([if getpeername might be used]) if test "$tst_links_getpeername" = "yes" && test "$tst_proto_getpeername" = "yes" && @@ -1876,16 +1790,16 @@ dnl shell variable curl_disallow_getsockname, then dnl HAVE_GETSOCKNAME will be defined. AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ - AC_REQUIRE([CURL_INCLUDES_WINSOCK2])dnl - AC_REQUIRE([CURL_INCLUDES_UNISTD])dnl - AC_REQUIRE([CURL_PREPROCESS_CALLCONV])dnl - AC_REQUIRE([CURL_INCLUDES_BSDSOCKET])dnl - # + AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) + AC_REQUIRE([CURL_INCLUDES_UNISTD]) + AC_REQUIRE([CURL_PREPROCESS_CALLCONV]) + AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) + tst_links_getsockname="unknown" tst_proto_getsockname="unknown" tst_compi_getsockname="unknown" tst_allow_getsockname="unknown" - # + AC_MSG_CHECKING([if getsockname can be linked]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ @@ -1893,7 +1807,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(0 != getsockname(0, (void *)0, (void *)0)) + if(getsockname(0, (void *)0, (void *)0) != 0) return 1; ]]) ],[ @@ -1903,7 +1817,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ AC_MSG_RESULT([no]) tst_links_getsockname="no" ]) - # + if test "$tst_links_getsockname" = "yes"; then AC_MSG_CHECKING([if getsockname is prototyped]) AC_EGREP_CPP([getsockname],[ @@ -1918,7 +1832,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ tst_proto_getsockname="no" ]) fi - # + if test "$tst_proto_getsockname" = "yes"; then AC_MSG_CHECKING([if getsockname is compilable]) AC_COMPILE_IFELSE([ @@ -1927,7 +1841,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(0 != getsockname(0, (void *)0, (void *)0)) + if(getsockname(0, (void *)0, (void *)0) != 0) return 1; ]]) ],[ @@ -1938,7 +1852,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ tst_compi_getsockname="no" ]) fi - # + if test "$tst_compi_getsockname" = "yes"; then AC_MSG_CHECKING([if getsockname usage allowed]) if test "x$curl_disallow_getsockname" != "xyes"; then @@ -1949,7 +1863,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ tst_allow_getsockname="no" fi fi - # + AC_MSG_CHECKING([if getsockname might be used]) if test "$tst_links_getsockname" = "yes" && test "$tst_proto_getsockname" = "yes" && @@ -1975,15 +1889,15 @@ dnl with shell variable curl_disallow_getifaddrs, then dnl HAVE_GETIFADDRS will be defined. AC_DEFUN([CURL_CHECK_FUNC_GETIFADDRS], [ - AC_REQUIRE([CURL_INCLUDES_STDLIB])dnl - AC_REQUIRE([CURL_INCLUDES_IFADDRS])dnl - # + AC_REQUIRE([CURL_INCLUDES_STDLIB]) + AC_REQUIRE([CURL_INCLUDES_IFADDRS]) + tst_links_getifaddrs="unknown" tst_proto_getifaddrs="unknown" tst_compi_getifaddrs="unknown" tst_works_getifaddrs="unknown" tst_allow_getifaddrs="unknown" - # + AC_MSG_CHECKING([if getifaddrs can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([getifaddrs]) @@ -1994,7 +1908,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETIFADDRS], [ AC_MSG_RESULT([no]) tst_links_getifaddrs="no" ]) - # + if test "$tst_links_getifaddrs" = "yes"; then AC_MSG_CHECKING([if getifaddrs is prototyped]) AC_EGREP_CPP([getifaddrs],[ @@ -2007,14 +1921,14 @@ AC_DEFUN([CURL_CHECK_FUNC_GETIFADDRS], [ tst_proto_getifaddrs="no" ]) fi - # + if test "$tst_proto_getifaddrs" = "yes"; then AC_MSG_CHECKING([if getifaddrs is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_ifaddrs ]],[[ - if(0 != getifaddrs(0)) + if(getifaddrs(0) != 0) return 1; ]]) ],[ @@ -2025,9 +1939,9 @@ AC_DEFUN([CURL_CHECK_FUNC_GETIFADDRS], [ tst_compi_getifaddrs="no" ]) fi - # + dnl only do runtime verification when not cross-compiling - if test "x$cross_compiling" != "xyes" && + if test "$cross_compiling" != "yes" && test "$tst_compi_getifaddrs" = "yes"; then AC_MSG_CHECKING([if getifaddrs seems to work]) CURL_RUN_IFELSE([ @@ -2054,7 +1968,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETIFADDRS], [ tst_works_getifaddrs="no" ]) fi - # + if test "$tst_compi_getifaddrs" = "yes" && test "$tst_works_getifaddrs" != "no"; then AC_MSG_CHECKING([if getifaddrs usage allowed]) @@ -2066,7 +1980,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETIFADDRS], [ tst_allow_getifaddrs="no" fi fi - # + AC_MSG_CHECKING([if getifaddrs might be used]) if test "$tst_links_getifaddrs" = "yes" && test "$tst_proto_getifaddrs" = "yes" && @@ -2093,15 +2007,15 @@ dnl with shell variable curl_disallow_gmtime_r, then dnl HAVE_GMTIME_R will be defined. AC_DEFUN([CURL_CHECK_FUNC_GMTIME_R], [ - AC_REQUIRE([CURL_INCLUDES_STDLIB])dnl - AC_REQUIRE([CURL_INCLUDES_TIME])dnl - # + AC_REQUIRE([CURL_INCLUDES_STDLIB]) + AC_REQUIRE([CURL_INCLUDES_TIME]) + tst_links_gmtime_r="unknown" tst_proto_gmtime_r="unknown" tst_compi_gmtime_r="unknown" tst_works_gmtime_r="unknown" tst_allow_gmtime_r="unknown" - # + AC_MSG_CHECKING([if gmtime_r can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([gmtime_r]) @@ -2112,7 +2026,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GMTIME_R], [ AC_MSG_RESULT([no]) tst_links_gmtime_r="no" ]) - # + if test "$tst_links_gmtime_r" = "yes"; then AC_MSG_CHECKING([if gmtime_r is prototyped]) AC_EGREP_CPP([gmtime_r],[ @@ -2125,7 +2039,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GMTIME_R], [ tst_proto_gmtime_r="no" ]) fi - # + if test "$tst_proto_gmtime_r" = "yes"; then AC_MSG_CHECKING([if gmtime_r is compilable]) AC_COMPILE_IFELSE([ @@ -2134,7 +2048,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GMTIME_R], [ ]],[[ time_t tm = 1170352587; struct tm result; - if(0 != gmtime_r(&tm, &result)) + if(gmtime_r(&tm, &result) == 0) return 1; (void)result; ]]) @@ -2146,9 +2060,9 @@ AC_DEFUN([CURL_CHECK_FUNC_GMTIME_R], [ tst_compi_gmtime_r="no" ]) fi - # + dnl only do runtime verification when not cross-compiling - if test "x$cross_compiling" != "xyes" && + if test "$cross_compiling" != "yes" && test "$tst_compi_gmtime_r" = "yes"; then AC_MSG_CHECKING([if gmtime_r seems to work]) CURL_RUN_IFELSE([ @@ -2174,7 +2088,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GMTIME_R], [ tst_works_gmtime_r="no" ]) fi - # + if test "$tst_compi_gmtime_r" = "yes" && test "$tst_works_gmtime_r" != "no"; then AC_MSG_CHECKING([if gmtime_r usage allowed]) @@ -2186,7 +2100,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GMTIME_R], [ tst_allow_gmtime_r="no" fi fi - # + AC_MSG_CHECKING([if gmtime_r might be used]) if test "$tst_links_gmtime_r" = "yes" && test "$tst_proto_gmtime_r" = "yes" && @@ -2204,6 +2118,126 @@ AC_DEFUN([CURL_CHECK_FUNC_GMTIME_R], [ ]) +dnl CURL_CHECK_FUNC_LOCALTIME_R +dnl ------------------------------------------------- +dnl Verify if localtime_r is available, prototyped, can +dnl be compiled and seems to work. If all of these are +dnl true, and usage has not been previously disallowed +dnl with shell variable curl_disallow_localtime_r, then +dnl HAVE_LOCALTIME_R will be defined. + +AC_DEFUN([CURL_CHECK_FUNC_LOCALTIME_R], [ + AC_REQUIRE([CURL_INCLUDES_STDLIB]) + AC_REQUIRE([CURL_INCLUDES_TIME]) + + tst_links_localtime_r="unknown" + tst_proto_localtime_r="unknown" + tst_compi_localtime_r="unknown" + tst_works_localtime_r="unknown" + tst_allow_localtime_r="unknown" + + AC_MSG_CHECKING([if localtime_r can be linked]) + AC_LINK_IFELSE([ + AC_LANG_FUNC_LINK_TRY([localtime_r]) + ],[ + AC_MSG_RESULT([yes]) + tst_links_localtime_r="yes" + ],[ + AC_MSG_RESULT([no]) + tst_links_localtime_r="no" + ]) + + if test "$tst_links_localtime_r" = "yes"; then + AC_MSG_CHECKING([if localtime_r is prototyped]) + AC_EGREP_CPP([localtime_r],[ + $curl_includes_time + ],[ + AC_MSG_RESULT([yes]) + tst_proto_localtime_r="yes" + ],[ + AC_MSG_RESULT([no]) + tst_proto_localtime_r="no" + ]) + fi + + if test "$tst_proto_localtime_r" = "yes"; then + AC_MSG_CHECKING([if localtime_r is compilable]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ + $curl_includes_time + ]],[[ + time_t clock = 1170352587; + struct tm result; + if(localtime_r(&clock, &result) != 0) + return 1; + (void)result; + ]]) + ],[ + AC_MSG_RESULT([yes]) + tst_compi_localtime_r="yes" + ],[ + AC_MSG_RESULT([no]) + tst_compi_localtime_r="no" + ]) + fi + + dnl only do runtime verification when not cross-compiling + if test "$cross_compiling" != "yes" && + test "$tst_compi_localtime_r" = "yes"; then + AC_MSG_CHECKING([if localtime_r seems to work]) + CURL_RUN_IFELSE([ + AC_LANG_PROGRAM([[ + $curl_includes_stdlib + $curl_includes_time + ]],[[ + time_t clock = 1170352587; + struct tm *tmp = 0; + struct tm result; + tmp = localtime_r(&clock, &result); + (void)result; + if(tmp) + return 0; + else + return 1; + ]]) + ],[ + AC_MSG_RESULT([yes]) + tst_works_localtime_r="yes" + ],[ + AC_MSG_RESULT([no]) + tst_works_localtime_r="no" + ]) + fi + + if test "$tst_compi_localtime_r" = "yes" && + test "$tst_works_localtime_r" != "no"; then + AC_MSG_CHECKING([if localtime_r usage allowed]) + if test "x$curl_disallow_localtime_r" != "xyes"; then + AC_MSG_RESULT([yes]) + tst_allow_localtime_r="yes" + else + AC_MSG_RESULT([no]) + tst_allow_localtime_r="no" + fi + fi + + AC_MSG_CHECKING([if localtime_r might be used]) + if test "$tst_links_localtime_r" = "yes" && + test "$tst_proto_localtime_r" = "yes" && + test "$tst_compi_localtime_r" = "yes" && + test "$tst_allow_localtime_r" = "yes" && + test "$tst_works_localtime_r" != "no"; then + AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(HAVE_LOCALTIME_R, 1, + [Define to 1 if you have a working localtime_r function.]) + curl_cv_func_localtime_r="yes" + else + AC_MSG_RESULT([no]) + curl_cv_func_localtime_r="no" + fi +]) + + dnl CURL_CHECK_FUNC_INET_NTOP dnl ------------------------------------------------- dnl Verify if inet_ntop is available, prototyped, can @@ -2213,16 +2247,16 @@ dnl with shell variable curl_disallow_inet_ntop, then dnl HAVE_INET_NTOP will be defined. AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ - AC_REQUIRE([CURL_INCLUDES_STDLIB])dnl - AC_REQUIRE([CURL_INCLUDES_ARPA_INET])dnl - AC_REQUIRE([CURL_INCLUDES_STRING])dnl - # + AC_REQUIRE([CURL_INCLUDES_STDLIB]) + AC_REQUIRE([CURL_INCLUDES_ARPA_INET]) + AC_REQUIRE([CURL_INCLUDES_STRING]) + tst_links_inet_ntop="unknown" tst_proto_inet_ntop="unknown" tst_compi_inet_ntop="unknown" tst_works_inet_ntop="unknown" tst_allow_inet_ntop="unknown" - # + AC_MSG_CHECKING([if inet_ntop can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([inet_ntop]) @@ -2233,7 +2267,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ AC_MSG_RESULT([no]) tst_links_inet_ntop="no" ]) - # + if test "$tst_links_inet_ntop" = "yes"; then AC_MSG_CHECKING([if inet_ntop is prototyped]) AC_EGREP_CPP([inet_ntop],[ @@ -2246,7 +2280,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ tst_proto_inet_ntop="no" ]) fi - # + if test "$tst_proto_inet_ntop" = "yes"; then AC_MSG_CHECKING([if inet_ntop is compilable]) AC_COMPILE_IFELSE([ @@ -2255,7 +2289,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ ]],[[ char ipv4res[sizeof("255.255.255.255")]; unsigned char ipv4a[5] = ""; - if(0 != inet_ntop(0, ipv4a, ipv4res, 0)) + if(inet_ntop(0, ipv4a, ipv4res, 0) != 0) return 1; ]]) ],[ @@ -2266,9 +2300,9 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ tst_compi_inet_ntop="no" ]) fi - # + dnl only do runtime verification when not cross-compiling - if test "x$cross_compiling" != "xyes" && + if test "$cross_compiling" != "yes" && test "$tst_compi_inet_ntop" = "yes"; then AC_MSG_CHECKING([if inet_ntop seems to work]) CURL_RUN_IFELSE([ @@ -2335,7 +2369,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ tst_works_inet_ntop="no" ]) fi - # + if test "$tst_compi_inet_ntop" = "yes" && test "$tst_works_inet_ntop" != "no"; then AC_MSG_CHECKING([if inet_ntop usage allowed]) @@ -2347,7 +2381,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ tst_allow_inet_ntop="no" fi fi - # + AC_MSG_CHECKING([if inet_ntop might be used]) if test "$tst_links_inet_ntop" = "yes" && test "$tst_proto_inet_ntop" = "yes" && @@ -2374,16 +2408,16 @@ dnl with shell variable curl_disallow_inet_pton, then dnl HAVE_INET_PTON will be defined. AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ - AC_REQUIRE([CURL_INCLUDES_STDLIB])dnl - AC_REQUIRE([CURL_INCLUDES_ARPA_INET])dnl - AC_REQUIRE([CURL_INCLUDES_STRING])dnl - # + AC_REQUIRE([CURL_INCLUDES_STDLIB]) + AC_REQUIRE([CURL_INCLUDES_ARPA_INET]) + AC_REQUIRE([CURL_INCLUDES_STRING]) + tst_links_inet_pton="unknown" tst_proto_inet_pton="unknown" tst_compi_inet_pton="unknown" tst_works_inet_pton="unknown" tst_allow_inet_pton="unknown" - # + AC_MSG_CHECKING([if inet_pton can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([inet_pton]) @@ -2394,7 +2428,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ AC_MSG_RESULT([no]) tst_links_inet_pton="no" ]) - # + if test "$tst_links_inet_pton" = "yes"; then AC_MSG_CHECKING([if inet_pton is prototyped]) AC_EGREP_CPP([inet_pton],[ @@ -2407,16 +2441,16 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ tst_proto_inet_pton="no" ]) fi - # + if test "$tst_proto_inet_pton" = "yes"; then AC_MSG_CHECKING([if inet_pton is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_arpa_inet ]],[[ - unsigned char ipv4a[4+1] = ""; + unsigned char ipv4a[4 + 1] = ""; const char *ipv4src = "192.168.100.1"; - if(0 != inet_pton(0, ipv4src, ipv4a)) + if(inet_pton(0, ipv4src, ipv4a) != 0) return 1; ]]) ],[ @@ -2427,9 +2461,9 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ tst_compi_inet_pton="no" ]) fi - # + dnl only do runtime verification when not cross-compiling - if test "x$cross_compiling" != "xyes" && + if test "$cross_compiling" != "yes" && test "$tst_compi_inet_pton" = "yes"; then AC_MSG_CHECKING([if inet_pton seems to work]) CURL_RUN_IFELSE([ @@ -2438,45 +2472,45 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ $curl_includes_arpa_inet $curl_includes_string ]],[[ - unsigned char ipv6a[16+1]; - unsigned char ipv4a[4+1]; + unsigned char ipv6a[16 + 1]; + unsigned char ipv4a[4 + 1]; const char *ipv6src = "fe80::214:4fff:fe0b:76c8"; const char *ipv4src = "192.168.100.1"; /* - */ memset(ipv4a, 1, sizeof(ipv4a)); - if(1 != inet_pton(AF_INET, ipv4src, ipv4a)) + if(inet_pton(AF_INET, ipv4src, ipv4a) != 1) return 1; /* fail */ /* - */ - if( (ipv4a[0] != 0xc0) || - (ipv4a[1] != 0xa8) || - (ipv4a[2] != 0x64) || - (ipv4a[3] != 0x01) || - (ipv4a[4] != 0x01) ) + if((ipv4a[0] != 0xc0) || + (ipv4a[1] != 0xa8) || + (ipv4a[2] != 0x64) || + (ipv4a[3] != 0x01) || + (ipv4a[4] != 0x01)) return 1; /* fail */ /* - */ memset(ipv6a, 1, sizeof(ipv6a)); - if(1 != inet_pton(AF_INET6, ipv6src, ipv6a)) + if(inet_pton(AF_INET6, ipv6src, ipv6a) != 1) return 1; /* fail */ /* - */ - if( (ipv6a[0] != 0xfe) || - (ipv6a[1] != 0x80) || - (ipv6a[8] != 0x02) || - (ipv6a[9] != 0x14) || - (ipv6a[10] != 0x4f) || - (ipv6a[11] != 0xff) || - (ipv6a[12] != 0xfe) || - (ipv6a[13] != 0x0b) || - (ipv6a[14] != 0x76) || - (ipv6a[15] != 0xc8) || - (ipv6a[16] != 0x01) ) + if((ipv6a[0] != 0xfe) || + (ipv6a[1] != 0x80) || + (ipv6a[8] != 0x02) || + (ipv6a[9] != 0x14) || + (ipv6a[10] != 0x4f) || + (ipv6a[11] != 0xff) || + (ipv6a[12] != 0xfe) || + (ipv6a[13] != 0x0b) || + (ipv6a[14] != 0x76) || + (ipv6a[15] != 0xc8) || + (ipv6a[16] != 0x01)) return 1; /* fail */ /* - */ - if( (ipv6a[2] != 0x0) || - (ipv6a[3] != 0x0) || - (ipv6a[4] != 0x0) || - (ipv6a[5] != 0x0) || - (ipv6a[6] != 0x0) || - (ipv6a[7] != 0x0) ) + if((ipv6a[2] != 0x0) || + (ipv6a[3] != 0x0) || + (ipv6a[4] != 0x0) || + (ipv6a[5] != 0x0) || + (ipv6a[6] != 0x0) || + (ipv6a[7] != 0x0)) return 1; /* fail */ /* - */ return 0; @@ -2489,7 +2523,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ tst_works_inet_pton="no" ]) fi - # + if test "$tst_compi_inet_pton" = "yes" && test "$tst_works_inet_pton" != "no"; then AC_MSG_CHECKING([if inet_pton usage allowed]) @@ -2501,7 +2535,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ tst_allow_inet_pton="no" fi fi - # + AC_MSG_CHECKING([if inet_pton might be used]) if test "$tst_links_inet_pton" = "yes" && test "$tst_proto_inet_pton" = "yes" && @@ -2528,13 +2562,13 @@ dnl shell variable curl_disallow_ioctl, then dnl curl_cv_func_ioctl is set to "yes". AC_DEFUN([CURL_CHECK_FUNC_IOCTL], [ - AC_REQUIRE([CURL_INCLUDES_STROPTS])dnl - # + AC_REQUIRE([CURL_INCLUDES_STROPTS]) + tst_links_ioctl="unknown" tst_proto_ioctl="unknown" tst_compi_ioctl="unknown" tst_allow_ioctl="unknown" - # + AC_MSG_CHECKING([if ioctl can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([ioctl]) @@ -2545,7 +2579,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL], [ AC_MSG_RESULT([no]) tst_links_ioctl="no" ]) - # + if test "$tst_links_ioctl" = "yes"; then AC_MSG_CHECKING([if ioctl is prototyped]) AC_EGREP_CPP([ioctl],[ @@ -2558,14 +2592,14 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL], [ tst_proto_ioctl="no" ]) fi - # + if test "$tst_proto_ioctl" = "yes"; then AC_MSG_CHECKING([if ioctl is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_stropts ]],[[ - if(0 != ioctl(0, 0, 0)) + if(ioctl(0, 0, 0) != 0) return 1; ]]) ],[ @@ -2576,7 +2610,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL], [ tst_compi_ioctl="no" ]) fi - # + if test "$tst_compi_ioctl" = "yes"; then AC_MSG_CHECKING([if ioctl usage allowed]) if test "x$curl_disallow_ioctl" != "xyes"; then @@ -2587,7 +2621,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL], [ tst_allow_ioctl="no" fi fi - # + AC_MSG_CHECKING([if ioctl might be used]) if test "$tst_links_ioctl" = "yes" && test "$tst_proto_ioctl" = "yes" && @@ -2612,10 +2646,10 @@ dnl all of these are true, then HAVE_IOCTL_FIONBIO dnl will be defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTL_FIONBIO], [ - # + tst_compi_ioctl_fionbio="unknown" tst_allow_ioctl_fionbio="unknown" - # + if test "$curl_cv_func_ioctl" = "yes"; then AC_MSG_CHECKING([if ioctl FIONBIO is compilable]) AC_COMPILE_IFELSE([ @@ -2623,7 +2657,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL_FIONBIO], [ $curl_includes_stropts ]],[[ int flags = 0; - if(0 != ioctl(0, FIONBIO, &flags)) + if(ioctl(0, FIONBIO, &flags) != 0) return 1; ]]) ],[ @@ -2634,7 +2668,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL_FIONBIO], [ tst_compi_ioctl_fionbio="no" ]) fi - # + if test "$tst_compi_ioctl_fionbio" = "yes"; then AC_MSG_CHECKING([if ioctl FIONBIO usage allowed]) if test "x$curl_disallow_ioctl_fionbio" != "xyes"; then @@ -2645,7 +2679,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL_FIONBIO], [ tst_allow_ioctl_fionbio="no" fi fi - # + AC_MSG_CHECKING([if ioctl FIONBIO might be used]) if test "$tst_compi_ioctl_fionbio" = "yes" && test "$tst_allow_ioctl_fionbio" = "yes"; then @@ -2668,10 +2702,10 @@ dnl work. If all of these are true, then HAVE_IOCTL_SIOCGIFADDR dnl will be defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTL_SIOCGIFADDR], [ - # + tst_compi_ioctl_siocgifaddr="unknown" tst_allow_ioctl_siocgifaddr="unknown" - # + if test "$curl_cv_func_ioctl" = "yes"; then AC_MSG_CHECKING([if ioctl SIOCGIFADDR is compilable]) AC_COMPILE_IFELSE([ @@ -2680,7 +2714,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL_SIOCGIFADDR], [ #include ]],[[ struct ifreq ifr; - if(0 != ioctl(0, SIOCGIFADDR, &ifr)) + if(ioctl(0, SIOCGIFADDR, &ifr) != 0) return 1; ]]) ],[ @@ -2691,7 +2725,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL_SIOCGIFADDR], [ tst_compi_ioctl_siocgifaddr="no" ]) fi - # + if test "$tst_compi_ioctl_siocgifaddr" = "yes"; then AC_MSG_CHECKING([if ioctl SIOCGIFADDR usage allowed]) if test "x$curl_disallow_ioctl_siocgifaddr" != "xyes"; then @@ -2702,7 +2736,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL_SIOCGIFADDR], [ tst_allow_ioctl_siocgifaddr="no" fi fi - # + AC_MSG_CHECKING([if ioctl SIOCGIFADDR might be used]) if test "$tst_compi_ioctl_siocgifaddr" = "yes" && test "$tst_allow_ioctl_siocgifaddr" = "yes"; then @@ -2726,19 +2760,19 @@ dnl shell variable curl_disallow_ioctlsocket, then dnl HAVE_IOCTLSOCKET will be defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET], [ - AC_REQUIRE([CURL_INCLUDES_WINSOCK2])dnl - # + AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) + tst_links_ioctlsocket="unknown" tst_proto_ioctlsocket="unknown" tst_compi_ioctlsocket="unknown" tst_allow_ioctlsocket="unknown" - # + AC_MSG_CHECKING([if ioctlsocket can be linked]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_winsock2 ]],[[ - if(0 != ioctlsocket(0, 0, 0)) + if(ioctlsocket(0, 0, 0) != 0) return 1; ]]) ],[ @@ -2748,7 +2782,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET], [ AC_MSG_RESULT([no]) tst_links_ioctlsocket="no" ]) - # + if test "$tst_links_ioctlsocket" = "yes"; then AC_MSG_CHECKING([if ioctlsocket is prototyped]) AC_EGREP_CPP([ioctlsocket],[ @@ -2761,14 +2795,14 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET], [ tst_proto_ioctlsocket="no" ]) fi - # + if test "$tst_proto_ioctlsocket" = "yes"; then AC_MSG_CHECKING([if ioctlsocket is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_winsock2 ]],[[ - if(0 != ioctlsocket(0, 0, 0)) + if(ioctlsocket(0, 0, 0) != 0) return 1; ]]) ],[ @@ -2779,7 +2813,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET], [ tst_compi_ioctlsocket="no" ]) fi - # + if test "$tst_compi_ioctlsocket" = "yes"; then AC_MSG_CHECKING([if ioctlsocket usage allowed]) if test "x$curl_disallow_ioctlsocket" != "xyes"; then @@ -2790,7 +2824,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET], [ tst_allow_ioctlsocket="no" fi fi - # + AC_MSG_CHECKING([if ioctlsocket might be used]) if test "$tst_links_ioctlsocket" = "yes" && test "$tst_proto_ioctlsocket" = "yes" && @@ -2816,10 +2850,10 @@ dnl all of these are true, then HAVE_IOCTLSOCKET_FIONBIO dnl will be defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_FIONBIO], [ - # + tst_compi_ioctlsocket_fionbio="unknown" tst_allow_ioctlsocket_fionbio="unknown" - # + if test "$curl_cv_func_ioctlsocket" = "yes"; then AC_MSG_CHECKING([if ioctlsocket FIONBIO is compilable]) AC_COMPILE_IFELSE([ @@ -2827,7 +2861,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_FIONBIO], [ $curl_includes_winsock2 ]],[[ unsigned long flags = 0; - if(0 != ioctlsocket(0, FIONBIO, &flags)) + if(ioctlsocket(0, FIONBIO, &flags) != 0) return 1; ]]) ],[ @@ -2838,7 +2872,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_FIONBIO], [ tst_compi_ioctlsocket_fionbio="no" ]) fi - # + if test "$tst_compi_ioctlsocket_fionbio" = "yes"; then AC_MSG_CHECKING([if ioctlsocket FIONBIO usage allowed]) if test "x$curl_disallow_ioctlsocket_fionbio" != "xyes"; then @@ -2849,7 +2883,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_FIONBIO], [ tst_allow_ioctlsocket_fionbio="no" fi fi - # + AC_MSG_CHECKING([if ioctlsocket FIONBIO might be used]) if test "$tst_compi_ioctlsocket_fionbio" = "yes" && test "$tst_allow_ioctlsocket_fionbio" = "yes"; then @@ -2873,18 +2907,18 @@ dnl shell variable curl_disallow_ioctlsocket_camel, dnl then HAVE_IOCTLSOCKET_CAMEL will be defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL], [ - AC_REQUIRE([CURL_INCLUDES_BSDSOCKET])dnl - # + AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) + tst_links_ioctlsocket_camel="unknown" tst_compi_ioctlsocket_camel="unknown" tst_allow_ioctlsocket_camel="unknown" - # + AC_MSG_CHECKING([if IoctlSocket can be linked]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_bsdsocket ]],[[ - if(0 != IoctlSocket(0, 0, 0)) + if(IoctlSocket(0, 0, 0) != 0) return 1; ]]) ],[ @@ -2894,14 +2928,14 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL], [ AC_MSG_RESULT([no]) tst_links_ioctlsocket_camel="no" ]) - # + if test "$tst_links_ioctlsocket_camel" = "yes"; then AC_MSG_CHECKING([if IoctlSocket is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_bsdsocket ]],[[ - if(0 != IoctlSocket(0, 0, 0)) + if(IoctlSocket(0, 0, 0) != 0) return 1; ]]) ],[ @@ -2912,7 +2946,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL], [ tst_compi_ioctlsocket_camel="no" ]) fi - # + if test "$tst_compi_ioctlsocket_camel" = "yes"; then AC_MSG_CHECKING([if IoctlSocket usage allowed]) if test "x$curl_disallow_ioctlsocket_camel" != "xyes"; then @@ -2923,7 +2957,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL], [ tst_allow_ioctlsocket_camel="no" fi fi - # + AC_MSG_CHECKING([if IoctlSocket might be used]) if test "$tst_links_ioctlsocket_camel" = "yes" && test "$tst_compi_ioctlsocket_camel" = "yes" && @@ -2947,11 +2981,11 @@ dnl can be compiled, and seems to work. If all of these are dnl true, then HAVE_IOCTLSOCKET_CAMEL_FIONBIO will be defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL_FIONBIO], [ - AC_REQUIRE([CURL_INCLUDES_BSDSOCKET])dnl - # + AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) + tst_compi_ioctlsocket_camel_fionbio="unknown" tst_allow_ioctlsocket_camel_fionbio="unknown" - # + if test "$curl_cv_func_ioctlsocket_camel" = "yes"; then AC_MSG_CHECKING([if IoctlSocket FIONBIO is compilable]) AC_COMPILE_IFELSE([ @@ -2959,7 +2993,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL_FIONBIO], [ $curl_includes_bsdsocket ]],[[ long flags = 0; - if(0 != IoctlSocket(0, FIONBIO, &flags)) + if(IoctlSocket(0, FIONBIO, &flags) != 0) return 1; ]]) ],[ @@ -2970,7 +3004,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL_FIONBIO], [ tst_compi_ioctlsocket_camel_fionbio="no" ]) fi - # + if test "$tst_compi_ioctlsocket_camel_fionbio" = "yes"; then AC_MSG_CHECKING([if IoctlSocket FIONBIO usage allowed]) if test "x$curl_disallow_ioctlsocket_camel_fionbio" != "xyes"; then @@ -2981,7 +3015,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL_FIONBIO], [ tst_allow_ioctlsocket_camel_fionbio="no" fi fi - # + AC_MSG_CHECKING([if IoctlSocket FIONBIO might be used]) if test "$tst_compi_ioctlsocket_camel_fionbio" = "yes" && test "$tst_allow_ioctlsocket_camel_fionbio" = "yes"; then @@ -3005,14 +3039,14 @@ dnl shell variable curl_disallow_memrchr, then dnl HAVE_MEMRCHR will be defined. AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ - AC_REQUIRE([CURL_INCLUDES_STRING])dnl - # + AC_REQUIRE([CURL_INCLUDES_STRING]) + tst_links_memrchr="unknown" tst_macro_memrchr="unknown" tst_proto_memrchr="unknown" tst_compi_memrchr="unknown" tst_allow_memrchr="unknown" - # + AC_MSG_CHECKING([if memrchr can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([memrchr]) @@ -3023,14 +3057,14 @@ AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ AC_MSG_RESULT([no]) tst_links_memrchr="no" ]) - # + if test "$tst_links_memrchr" = "no"; then AC_MSG_CHECKING([if memrchr seems a macro]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(0 != memrchr("", 0, 0)) + if(memrchr("", 0, 0) != 0) return 1; ]]) ],[ @@ -3041,7 +3075,7 @@ AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ tst_macro_memrchr="no" ]) fi - # + if test "$tst_links_memrchr" = "yes"; then AC_MSG_CHECKING([if memrchr is prototyped]) AC_EGREP_CPP([memrchr],[ @@ -3054,7 +3088,7 @@ AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ tst_proto_memrchr="no" ]) fi - # + if test "$tst_proto_memrchr" = "yes" || test "$tst_macro_memrchr" = "yes"; then AC_MSG_CHECKING([if memrchr is compilable]) @@ -3062,7 +3096,7 @@ AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(0 != memrchr("", 0, 0)) + if(memrchr("", 0, 0) != 0) return 1; ]]) ],[ @@ -3073,7 +3107,7 @@ AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ tst_compi_memrchr="no" ]) fi - # + if test "$tst_compi_memrchr" = "yes"; then AC_MSG_CHECKING([if memrchr usage allowed]) if test "x$curl_disallow_memrchr" != "xyes"; then @@ -3084,7 +3118,7 @@ AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ tst_allow_memrchr="no" fi fi - # + AC_MSG_CHECKING([if memrchr might be used]) if (test "$tst_proto_memrchr" = "yes" || test "$tst_macro_memrchr" = "yes") && @@ -3110,13 +3144,13 @@ dnl shell variable curl_disallow_sigaction, then dnl HAVE_SIGACTION will be defined. AC_DEFUN([CURL_CHECK_FUNC_SIGACTION], [ - AC_REQUIRE([CURL_INCLUDES_SIGNAL])dnl - # + AC_REQUIRE([CURL_INCLUDES_SIGNAL]) + tst_links_sigaction="unknown" tst_proto_sigaction="unknown" tst_compi_sigaction="unknown" tst_allow_sigaction="unknown" - # + AC_MSG_CHECKING([if sigaction can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([sigaction]) @@ -3127,7 +3161,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGACTION], [ AC_MSG_RESULT([no]) tst_links_sigaction="no" ]) - # + if test "$tst_links_sigaction" = "yes"; then AC_MSG_CHECKING([if sigaction is prototyped]) AC_EGREP_CPP([sigaction],[ @@ -3140,14 +3174,14 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGACTION], [ tst_proto_sigaction="no" ]) fi - # + if test "$tst_proto_sigaction" = "yes"; then AC_MSG_CHECKING([if sigaction is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_signal ]],[[ - if(0 != sigaction(0, 0, 0)) + if(sigaction(0, 0, 0) != 0) return 1; ]]) ],[ @@ -3158,7 +3192,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGACTION], [ tst_compi_sigaction="no" ]) fi - # + if test "$tst_compi_sigaction" = "yes"; then AC_MSG_CHECKING([if sigaction usage allowed]) if test "x$curl_disallow_sigaction" != "xyes"; then @@ -3169,7 +3203,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGACTION], [ tst_allow_sigaction="no" fi fi - # + AC_MSG_CHECKING([if sigaction might be used]) if test "$tst_links_sigaction" = "yes" && test "$tst_proto_sigaction" = "yes" && @@ -3195,13 +3229,13 @@ dnl shell variable curl_disallow_siginterrupt, then dnl HAVE_SIGINTERRUPT will be defined. AC_DEFUN([CURL_CHECK_FUNC_SIGINTERRUPT], [ - AC_REQUIRE([CURL_INCLUDES_SIGNAL])dnl - # + AC_REQUIRE([CURL_INCLUDES_SIGNAL]) + tst_links_siginterrupt="unknown" tst_proto_siginterrupt="unknown" tst_compi_siginterrupt="unknown" tst_allow_siginterrupt="unknown" - # + AC_MSG_CHECKING([if siginterrupt can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([siginterrupt]) @@ -3212,7 +3246,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGINTERRUPT], [ AC_MSG_RESULT([no]) tst_links_siginterrupt="no" ]) - # + if test "$tst_links_siginterrupt" = "yes"; then AC_MSG_CHECKING([if siginterrupt is prototyped]) AC_EGREP_CPP([siginterrupt],[ @@ -3225,14 +3259,14 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGINTERRUPT], [ tst_proto_siginterrupt="no" ]) fi - # + if test "$tst_proto_siginterrupt" = "yes"; then AC_MSG_CHECKING([if siginterrupt is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_signal ]],[[ - if(0 != siginterrupt(0, 0)) + if(siginterrupt(0, 0) != 0) return 1; ]]) ],[ @@ -3243,7 +3277,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGINTERRUPT], [ tst_compi_siginterrupt="no" ]) fi - # + if test "$tst_compi_siginterrupt" = "yes"; then AC_MSG_CHECKING([if siginterrupt usage allowed]) if test "x$curl_disallow_siginterrupt" != "xyes"; then @@ -3254,7 +3288,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGINTERRUPT], [ tst_allow_siginterrupt="no" fi fi - # + AC_MSG_CHECKING([if siginterrupt might be used]) if test "$tst_links_siginterrupt" = "yes" && test "$tst_proto_siginterrupt" = "yes" && @@ -3280,13 +3314,13 @@ dnl shell variable curl_disallow_signal, then dnl HAVE_SIGNAL will be defined. AC_DEFUN([CURL_CHECK_FUNC_SIGNAL], [ - AC_REQUIRE([CURL_INCLUDES_SIGNAL])dnl - # + AC_REQUIRE([CURL_INCLUDES_SIGNAL]) + tst_links_signal="unknown" tst_proto_signal="unknown" tst_compi_signal="unknown" tst_allow_signal="unknown" - # + AC_MSG_CHECKING([if signal can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([signal]) @@ -3297,7 +3331,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGNAL], [ AC_MSG_RESULT([no]) tst_links_signal="no" ]) - # + if test "$tst_links_signal" = "yes"; then AC_MSG_CHECKING([if signal is prototyped]) AC_EGREP_CPP([signal],[ @@ -3310,14 +3344,14 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGNAL], [ tst_proto_signal="no" ]) fi - # + if test "$tst_proto_signal" = "yes"; then AC_MSG_CHECKING([if signal is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_signal ]],[[ - if(0 != signal(0, 0)) + if(signal(0, 0) != 0) return 1; ]]) ],[ @@ -3328,7 +3362,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGNAL], [ tst_compi_signal="no" ]) fi - # + if test "$tst_compi_signal" = "yes"; then AC_MSG_CHECKING([if signal usage allowed]) if test "x$curl_disallow_signal" != "xyes"; then @@ -3339,7 +3373,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGNAL], [ tst_allow_signal="no" fi fi - # + AC_MSG_CHECKING([if signal might be used]) if test "$tst_links_signal" = "yes" && test "$tst_proto_signal" = "yes" && @@ -3365,14 +3399,14 @@ dnl shell variable curl_disallow_sigsetjmp, then dnl HAVE_SIGSETJMP will be defined. AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ - AC_REQUIRE([CURL_INCLUDES_SETJMP])dnl - # + AC_REQUIRE([CURL_INCLUDES_SETJMP]) + tst_links_sigsetjmp="unknown" tst_macro_sigsetjmp="unknown" tst_proto_sigsetjmp="unknown" tst_compi_sigsetjmp="unknown" tst_allow_sigsetjmp="unknown" - # + AC_MSG_CHECKING([if sigsetjmp can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([sigsetjmp]) @@ -3383,7 +3417,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ AC_MSG_RESULT([no]) tst_links_sigsetjmp="no" ]) - # + if test "$tst_links_sigsetjmp" = "no"; then AC_MSG_CHECKING([if sigsetjmp seems a macro]) AC_LINK_IFELSE([ @@ -3391,7 +3425,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ $curl_includes_setjmp ]],[[ sigjmp_buf env; - if(0 != sigsetjmp(env, 0)) + if(sigsetjmp(env, 0) != 0) return 1; ]]) ],[ @@ -3402,7 +3436,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ tst_macro_sigsetjmp="no" ]) fi - # + if test "$tst_links_sigsetjmp" = "yes"; then AC_MSG_CHECKING([if sigsetjmp is prototyped]) AC_EGREP_CPP([sigsetjmp],[ @@ -3415,7 +3449,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ tst_proto_sigsetjmp="no" ]) fi - # + if test "$tst_proto_sigsetjmp" = "yes" || test "$tst_macro_sigsetjmp" = "yes"; then AC_MSG_CHECKING([if sigsetjmp is compilable]) @@ -3424,7 +3458,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ $curl_includes_setjmp ]],[[ sigjmp_buf env; - if(0 != sigsetjmp(env, 0)) + if(sigsetjmp(env, 0) != 0) return 1; ]]) ],[ @@ -3435,7 +3469,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ tst_compi_sigsetjmp="no" ]) fi - # + if test "$tst_compi_sigsetjmp" = "yes"; then AC_MSG_CHECKING([if sigsetjmp usage allowed]) if test "x$curl_disallow_sigsetjmp" != "xyes"; then @@ -3446,7 +3480,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ tst_allow_sigsetjmp="no" fi fi - # + AC_MSG_CHECKING([if sigsetjmp might be used]) if (test "$tst_proto_sigsetjmp" = "yes" || test "$tst_macro_sigsetjmp" = "yes") && @@ -3472,14 +3506,14 @@ dnl shell variable curl_disallow_socket, then dnl HAVE_SOCKET will be defined. AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ - AC_REQUIRE([CURL_INCLUDES_WINSOCK2])dnl - AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET])dnl - # + AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) + AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET]) + tst_links_socket="unknown" tst_proto_socket="unknown" tst_compi_socket="unknown" tst_allow_socket="unknown" - # + AC_MSG_CHECKING([if socket can be linked]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ @@ -3487,7 +3521,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(0 != socket(0, 0, 0)) + if(socket(0, 0, 0) != 0) return 1; ]]) ],[ @@ -3497,7 +3531,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ AC_MSG_RESULT([no]) tst_links_socket="no" ]) - # + if test "$tst_links_socket" = "yes"; then AC_MSG_CHECKING([if socket is prototyped]) AC_EGREP_CPP([socket],[ @@ -3512,7 +3546,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ tst_proto_socket="no" ]) fi - # + if test "$tst_proto_socket" = "yes"; then AC_MSG_CHECKING([if socket is compilable]) AC_COMPILE_IFELSE([ @@ -3521,7 +3555,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(0 != socket(0, 0, 0)) + if(socket(0, 0, 0) != 0) return 1; ]]) ],[ @@ -3532,7 +3566,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ tst_compi_socket="no" ]) fi - # + if test "$tst_compi_socket" = "yes"; then AC_MSG_CHECKING([if socket usage allowed]) if test "x$curl_disallow_socket" != "xyes"; then @@ -3543,7 +3577,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ tst_allow_socket="no" fi fi - # + AC_MSG_CHECKING([if socket might be used]) if test "$tst_links_socket" = "yes" && test "$tst_proto_socket" = "yes" && @@ -3569,13 +3603,13 @@ dnl shell variable curl_disallow_socketpair, then dnl HAVE_SOCKETPAIR will be defined. AC_DEFUN([CURL_CHECK_FUNC_SOCKETPAIR], [ - AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET])dnl - # + AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET]) + tst_links_socketpair="unknown" tst_proto_socketpair="unknown" tst_compi_socketpair="unknown" tst_allow_socketpair="unknown" - # + AC_MSG_CHECKING([if socketpair can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([socketpair]) @@ -3586,7 +3620,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKETPAIR], [ AC_MSG_RESULT([no]) tst_links_socketpair="no" ]) - # + if test "$tst_links_socketpair" = "yes"; then AC_MSG_CHECKING([if socketpair is prototyped]) AC_EGREP_CPP([socketpair],[ @@ -3599,7 +3633,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKETPAIR], [ tst_proto_socketpair="no" ]) fi - # + if test "$tst_proto_socketpair" = "yes"; then AC_MSG_CHECKING([if socketpair is compilable]) AC_COMPILE_IFELSE([ @@ -3607,7 +3641,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKETPAIR], [ $curl_includes_sys_socket ]],[[ int sv[2]; - if(0 != socketpair(0, 0, 0, sv)) + if(socketpair(0, 0, 0, sv) != 0) return 1; ]]) ],[ @@ -3618,7 +3652,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKETPAIR], [ tst_compi_socketpair="no" ]) fi - # + if test "$tst_compi_socketpair" = "yes"; then AC_MSG_CHECKING([if socketpair usage allowed]) if test "x$curl_disallow_socketpair" != "xyes"; then @@ -3629,7 +3663,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKETPAIR], [ tst_allow_socketpair="no" fi fi - # + AC_MSG_CHECKING([if socketpair might be used]) if test "$tst_links_socketpair" = "yes" && test "$tst_proto_socketpair" = "yes" && @@ -3655,13 +3689,13 @@ dnl shell variable curl_disallow_strcasecmp, then dnl HAVE_STRCASECMP will be defined. AC_DEFUN([CURL_CHECK_FUNC_STRCASECMP], [ - AC_REQUIRE([CURL_INCLUDES_STRING])dnl - # + AC_REQUIRE([CURL_INCLUDES_STRING]) + tst_links_strcasecmp="unknown" tst_proto_strcasecmp="unknown" tst_compi_strcasecmp="unknown" tst_allow_strcasecmp="unknown" - # + AC_MSG_CHECKING([if strcasecmp can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([strcasecmp]) @@ -3672,7 +3706,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRCASECMP], [ AC_MSG_RESULT([no]) tst_links_strcasecmp="no" ]) - # + if test "$tst_links_strcasecmp" = "yes"; then AC_MSG_CHECKING([if strcasecmp is prototyped]) AC_EGREP_CPP([strcasecmp],[ @@ -3685,14 +3719,14 @@ AC_DEFUN([CURL_CHECK_FUNC_STRCASECMP], [ tst_proto_strcasecmp="no" ]) fi - # + if test "$tst_proto_strcasecmp" = "yes"; then AC_MSG_CHECKING([if strcasecmp is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(0 != strcasecmp("", "")) + if(strcasecmp("", "") != 0) return 1; ]]) ],[ @@ -3703,7 +3737,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRCASECMP], [ tst_compi_strcasecmp="no" ]) fi - # + if test "$tst_compi_strcasecmp" = "yes"; then AC_MSG_CHECKING([if strcasecmp usage allowed]) if test "x$curl_disallow_strcasecmp" != "xyes"; then @@ -3714,7 +3748,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRCASECMP], [ tst_allow_strcasecmp="no" fi fi - # + AC_MSG_CHECKING([if strcasecmp might be used]) if test "$tst_links_strcasecmp" = "yes" && test "$tst_proto_strcasecmp" = "yes" && @@ -3739,13 +3773,13 @@ dnl shell variable curl_disallow_strcmpi, then dnl HAVE_STRCMPI will be defined. AC_DEFUN([CURL_CHECK_FUNC_STRCMPI], [ - AC_REQUIRE([CURL_INCLUDES_STRING])dnl - # + AC_REQUIRE([CURL_INCLUDES_STRING]) + tst_links_strcmpi="unknown" tst_proto_strcmpi="unknown" tst_compi_strcmpi="unknown" tst_allow_strcmpi="unknown" - # + AC_MSG_CHECKING([if strcmpi can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([strcmpi]) @@ -3756,7 +3790,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRCMPI], [ AC_MSG_RESULT([no]) tst_links_strcmpi="no" ]) - # + if test "$tst_links_strcmpi" = "yes"; then AC_MSG_CHECKING([if strcmpi is prototyped]) AC_EGREP_CPP([strcmpi],[ @@ -3769,14 +3803,14 @@ AC_DEFUN([CURL_CHECK_FUNC_STRCMPI], [ tst_proto_strcmpi="no" ]) fi - # + if test "$tst_proto_strcmpi" = "yes"; then AC_MSG_CHECKING([if strcmpi is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(0 != strcmpi(0, 0)) + if(strcmpi(0, 0) != 0) return 1; ]]) ],[ @@ -3787,7 +3821,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRCMPI], [ tst_compi_strcmpi="no" ]) fi - # + if test "$tst_compi_strcmpi" = "yes"; then AC_MSG_CHECKING([if strcmpi usage allowed]) if test "x$curl_disallow_strcmpi" != "xyes"; then @@ -3798,7 +3832,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRCMPI], [ tst_allow_strcmpi="no" fi fi - # + AC_MSG_CHECKING([if strcmpi might be used]) if test "$tst_links_strcmpi" = "yes" && test "$tst_proto_strcmpi" = "yes" && @@ -3815,92 +3849,6 @@ AC_DEFUN([CURL_CHECK_FUNC_STRCMPI], [ ]) -dnl CURL_CHECK_FUNC_STRDUP -dnl ------------------------------------------------- -dnl Verify if strdup is available, prototyped, and -dnl can be compiled. If all of these are true, and -dnl usage has not been previously disallowed with -dnl shell variable curl_disallow_strdup, then -dnl HAVE_STRDUP will be defined. - -AC_DEFUN([CURL_CHECK_FUNC_STRDUP], [ - AC_REQUIRE([CURL_INCLUDES_STRING])dnl - AC_REQUIRE([CURL_INCLUDES_STDLIB])dnl - # - tst_links_strdup="unknown" - tst_proto_strdup="unknown" - tst_compi_strdup="unknown" - tst_allow_strdup="unknown" - # - AC_MSG_CHECKING([if strdup can be linked]) - AC_LINK_IFELSE([ - AC_LANG_FUNC_LINK_TRY([strdup]) - ],[ - AC_MSG_RESULT([yes]) - tst_links_strdup="yes" - ],[ - AC_MSG_RESULT([no]) - tst_links_strdup="no" - ]) - # - if test "$tst_links_strdup" = "yes"; then - AC_MSG_CHECKING([if strdup is prototyped]) - AC_EGREP_CPP([strdup],[ - $curl_includes_string - ],[ - AC_MSG_RESULT([yes]) - tst_proto_strdup="yes" - ],[ - AC_MSG_RESULT([no]) - tst_proto_strdup="no" - ]) - fi - # - if test "$tst_proto_strdup" = "yes"; then - AC_MSG_CHECKING([if strdup is compilable]) - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - $curl_includes_string - $curl_includes_stdlib - ]],[[ - free(strdup("")); - ]]) - ],[ - AC_MSG_RESULT([yes]) - tst_compi_strdup="yes" - ],[ - AC_MSG_RESULT([no]) - tst_compi_strdup="no" - ]) - fi - # - if test "$tst_compi_strdup" = "yes"; then - AC_MSG_CHECKING([if strdup usage allowed]) - if test "x$curl_disallow_strdup" != "xyes"; then - AC_MSG_RESULT([yes]) - tst_allow_strdup="yes" - else - AC_MSG_RESULT([no]) - tst_allow_strdup="no" - fi - fi - # - AC_MSG_CHECKING([if strdup might be used]) - if test "$tst_links_strdup" = "yes" && - test "$tst_proto_strdup" = "yes" && - test "$tst_compi_strdup" = "yes" && - test "$tst_allow_strdup" = "yes"; then - AC_MSG_RESULT([yes]) - AC_DEFINE_UNQUOTED(HAVE_STRDUP, 1, - [Define to 1 if you have the strdup function.]) - curl_cv_func_strdup="yes" - else - AC_MSG_RESULT([no]) - curl_cv_func_strdup="no" - fi -]) - - dnl CURL_CHECK_FUNC_STRERROR_R dnl ------------------------------------------------- dnl Verify if strerror_r is available, prototyped, can be compiled and @@ -3915,7 +3863,7 @@ dnl char *strerror_r(int errnum, char *workbuf, size_t bufsize); dnl dnl glibc-style strerror_r returns a pointer to the error string, dnl and might use the provided workbuf as a scratch area if needed. A -dnl quick test on a few systems shows that it's usually not used at all. +dnl quick test on a few systems shows that it is usually not used at all. dnl dnl POSIX-style strerror_r: dnl @@ -3926,9 +3874,9 @@ dnl error string in the provided resultbuf. dnl AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ - AC_REQUIRE([CURL_INCLUDES_STDLIB])dnl - AC_REQUIRE([CURL_INCLUDES_STRING])dnl - # + AC_REQUIRE([CURL_INCLUDES_STDLIB]) + AC_REQUIRE([CURL_INCLUDES_STRING]) + tst_links_strerror_r="unknown" tst_proto_strerror_r="unknown" tst_compi_strerror_r="unknown" @@ -3939,7 +3887,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ tst_works_posix_strerror_r="unknown" tst_glibc_strerror_r_type_arg3="unknown" tst_posix_strerror_r_type_arg3="unknown" - # + AC_MSG_CHECKING([if strerror_r can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([strerror_r]) @@ -3950,7 +3898,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ AC_MSG_RESULT([no]) tst_links_strerror_r="no" ]) - # + if test "$tst_links_strerror_r" = "yes"; then AC_MSG_CHECKING([if strerror_r is prototyped]) AC_EGREP_CPP([strerror_r],[ @@ -3963,7 +3911,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ tst_proto_strerror_r="no" ]) fi - # + if test "$tst_proto_strerror_r" = "yes"; then AC_MSG_CHECKING([if strerror_r is compilable]) AC_COMPILE_IFELSE([ @@ -3971,7 +3919,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ $curl_includes_string ]],[[ char s[1]; - if(0 != strerror_r(0, s, 0)) + if(strerror_r(0, s, 0) != 0) return 1; ]]) ],[ @@ -3982,7 +3930,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ tst_compi_strerror_r="no" ]) fi - # + if test "$tst_compi_strerror_r" = "yes"; then AC_MSG_CHECKING([if strerror_r is glibc like]) tst_glibc_strerror_r_type_arg3="unknown" @@ -3994,7 +3942,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ char *strerror_r(int errnum, char *workbuf, $arg3 bufsize); ]],[[ char s[1]; - if(0 != strerror_r(0, s, 0)) + if(strerror_r(0, s, 0) != 0) return 1; (void)s; ]]) @@ -4014,9 +3962,9 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ ;; esac fi - # + dnl only do runtime verification when not cross-compiling - if test "x$cross_compiling" != "xyes" && + if test "$cross_compiling" != "yes" && test "$tst_glibc_strerror_r" = "yes"; then AC_MSG_CHECKING([if strerror_r seems to work]) CURL_RUN_IFELSE([ @@ -4044,7 +3992,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ tst_works_glibc_strerror_r="no" ]) fi - # + if test "$tst_compi_strerror_r" = "yes" && test "$tst_works_glibc_strerror_r" != "yes"; then AC_MSG_CHECKING([if strerror_r is POSIX like]) @@ -4057,7 +4005,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ int strerror_r(int errnum, char *resultbuf, $arg3 bufsize); ]],[[ char s[1]; - if(0 != strerror_r(0, s, 0)) + if(strerror_r(0, s, 0) != 0) return 1; (void)s; ]]) @@ -4077,9 +4025,9 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ ;; esac fi - # + dnl only do runtime verification when not cross-compiling - if test "x$cross_compiling" != "xyes" && + if test "$cross_compiling" != "yes" && test "$tst_posix_strerror_r" = "yes"; then AC_MSG_CHECKING([if strerror_r seems to work]) CURL_RUN_IFELSE([ @@ -4107,7 +4055,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ tst_works_posix_strerror_r="no" ]) fi - # + if test "$tst_works_glibc_strerror_r" = "yes"; then tst_posix_strerror_r="no" fi @@ -4134,7 +4082,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ tst_allow_strerror_r="no" fi fi - # + AC_MSG_CHECKING([if strerror_r might be used]) if test "$tst_links_strerror_r" = "yes" && test "$tst_proto_strerror_r" = "yes" && @@ -4158,12 +4106,12 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ AC_MSG_RESULT([no]) curl_cv_func_strerror_r="no" fi - # + if test "$tst_compi_strerror_r" = "yes" && test "$tst_allow_strerror_r" = "unknown"; then AC_MSG_WARN([cannot determine strerror_r() style: edit lib/curl_config.h manually.]) fi - # + ]) @@ -4176,13 +4124,13 @@ dnl shell variable curl_disallow_stricmp, then dnl HAVE_STRICMP will be defined. AC_DEFUN([CURL_CHECK_FUNC_STRICMP], [ - AC_REQUIRE([CURL_INCLUDES_STRING])dnl - # + AC_REQUIRE([CURL_INCLUDES_STRING]) + tst_links_stricmp="unknown" tst_proto_stricmp="unknown" tst_compi_stricmp="unknown" tst_allow_stricmp="unknown" - # + AC_MSG_CHECKING([if stricmp can be linked]) AC_LINK_IFELSE([ AC_LANG_FUNC_LINK_TRY([stricmp]) @@ -4193,7 +4141,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRICMP], [ AC_MSG_RESULT([no]) tst_links_stricmp="no" ]) - # + if test "$tst_links_stricmp" = "yes"; then AC_MSG_CHECKING([if stricmp is prototyped]) AC_EGREP_CPP([stricmp],[ @@ -4206,14 +4154,14 @@ AC_DEFUN([CURL_CHECK_FUNC_STRICMP], [ tst_proto_stricmp="no" ]) fi - # + if test "$tst_proto_stricmp" = "yes"; then AC_MSG_CHECKING([if stricmp is compilable]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(0 != stricmp(0, 0)) + if(stricmp(0, 0) != 0) return 1; ]]) ],[ @@ -4224,7 +4172,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRICMP], [ tst_compi_stricmp="no" ]) fi - # + if test "$tst_compi_stricmp" = "yes"; then AC_MSG_CHECKING([if stricmp usage allowed]) if test "x$curl_disallow_stricmp" != "xyes"; then @@ -4235,7 +4183,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRICMP], [ tst_allow_stricmp="no" fi fi - # + AC_MSG_CHECKING([if stricmp might be used]) if test "$tst_links_stricmp" = "yes" && test "$tst_proto_stricmp" = "yes" && @@ -4259,7 +4207,7 @@ dnl CURL_LIBRARY_PATH variable. It keeps the LD_LIBRARY_PATH dnl changes contained within this macro. AC_DEFUN([CURL_RUN_IFELSE], [ - if test "$curl_cv_apple" = 'yes'; then + if test "$curl_cv_apple" = "yes"; then AC_RUN_IFELSE([AC_LANG_SOURCE([$1])], $2, $3, $4) else oldcc=$CC @@ -4268,7 +4216,8 @@ AC_DEFUN([CURL_RUN_IFELSE], [ LD_LIBRARY_PATH=$CURL_LIBRARY_PATH:$old export LD_LIBRARY_PATH AC_RUN_IFELSE([AC_LANG_SOURCE([$1])], $2, $3, $4) - LD_LIBRARY_PATH=$old # restore + # restore + LD_LIBRARY_PATH=$old CC=$oldcc fi ]) @@ -4290,25 +4239,31 @@ AC_DEFUN([CURL_COVERAGE],[ AS_HELP_STRING([--enable-code-coverage], [Provide code coverage]), coverage="$enableval") - dnl if not gcc switch off again - AS_IF([ test "$GCC" != "yes" ], coverage="no" ) + dnl if not gcc or clang switch off again + AS_IF([test "$compiler_id" != "GNU_C" && test "$compiler_id" != "CLANG" && test "$compiler_id" != "APPLECLANG"], coverage="no" ) AC_MSG_RESULT($coverage) if test "x$coverage" = "xyes"; then curl_coverage_msg="enabled" - AC_CHECK_TOOL([GCOV], [gcov], [gcov]) - if test -z "$GCOV"; then - AC_MSG_ERROR([needs gcov for code coverage]) - fi - AC_CHECK_PROG([LCOV], [lcov], [lcov]) - if test -z "$LCOV"; then - AC_MSG_ERROR([needs lcov for code coverage]) - fi - CPPFLAGS="$CPPFLAGS -DNDEBUG" - CFLAGS="$CFLAGS -O0 -g -fprofile-arcs -ftest-coverage" - LIBS="$LIBS -lgcov" + CFLAGS="$CFLAGS -O0 -g" + + if test "$compiler_id" = "GNU_C"; then + AC_CHECK_TOOL([GCOV], [gcov], [gcov]) + if test -z "$GCOV"; then + AC_MSG_ERROR([needs gcov for code coverage]) + fi + AC_CHECK_PROG([LCOV], [lcov], [lcov]) + if test -z "$LCOV"; then + AC_MSG_ERROR([needs lcov for code coverage]) + fi + CFLAGS="$CFLAGS -ftest-coverage -fprofile-arcs" + LIBS="$LIBS -lgcov" + else + CFLAGS="$CFLAGS -fprofile-instr-generate -fcoverage-mapping" + LDFLAGS="$LDFLAGS -fprofile-instr-generate -fcoverage-mapping" + fi fi ]) @@ -4339,30 +4294,30 @@ AC_DEFUN([CURL_ATOMIC],[ ]) ]) -# Rewrite inspired by the functionality once provided by -# AX_COMPILE_CHECK_SIZEOF. Uses the switch() "trick" to find the size of the -# given type. -# -# This code fails to compile: -# -# switch() { case 0: case 0: } -# -# By making the second case number a boolean check, it fails to compile the -# test code when the boolean is false and thus creating a zero, making it a -# duplicated case label. If the boolean equals true, it becomes a one, the -# code compiles and we know it was a match. -# -# The check iterates over all possible sizes and stops as soon it compiles -# error-free. -# -# Usage: -# -# CURL_SIZEOF(TYPE, [HEADERS]) -# +dnl Rewrite inspired by the functionality once provided by +dnl AX_COMPILE_CHECK_SIZEOF. Uses the switch() "trick" to find the size of the +dnl given type. +dnl +dnl This code fails to compile: +dnl +dnl switch() { case 0: case 0: } +dnl +dnl By making the second case number a boolean check, it fails to compile the +dnl test code when the boolean is false and thus creating a zero, making it a +dnl duplicated case label. If the boolean equals true, it becomes a one, the +dnl code compiles and we know it was a match. +dnl +dnl The check iterates over all possible sizes and stops as soon it compiles +dnl error-free. +dnl +dnl Usage: +dnl +dnl CURL_SIZEOF(TYPE, [HEADERS]) +dnl AC_DEFUN([CURL_SIZEOF], [ dnl The #define name to make autoheader put the name in curl_config.h.in - define(TYPE, translit(sizeof_$1, [a-z *], [A-Z_P]))dnl + define(TYPE, translit(sizeof_$1, [a-z *], [A-Z_P])) AC_MSG_CHECKING(size of $1) r=0 @@ -4374,8 +4329,8 @@ AC_DEFUN([CURL_SIZEOF], [ $2 ]],[ switch(0) { - case 0: - case (sizeof($1) == $typesize):; + case 0: + case (sizeof($1) == $typesize):; } ]) ],[ @@ -4384,11 +4339,11 @@ AC_DEFUN([CURL_SIZEOF], [ r=0 ]) dnl get out of the loop once matched - if test $r -gt 0; then + if test "$r" -gt 0; then break; fi done - if test $r -eq 0; then + if test "$r" -eq 0; then AC_MSG_ERROR([Failed to find size of $1]) fi AC_MSG_RESULT($r) diff --git a/m4/curl-gnutls.m4 b/m4/curl-gnutls.m4 index fb98f8d8fb..b8ee3f5780 100644 --- a/m4/curl-gnutls.m4 +++ b/m4/curl-gnutls.m4 @@ -27,10 +27,10 @@ dnl check for GnuTLS dnl ---------------------------------------------------- AC_DEFUN([CURL_WITH_GNUTLS], [ -if test "x$OPT_GNUTLS" != xno; then +if test "x$OPT_GNUTLS" != "xno"; then ssl_msg= - if test X"$OPT_GNUTLS" != Xno; then + if test "x$OPT_GNUTLS" != "xno"; then addld="" addlib="" @@ -42,7 +42,7 @@ if test "x$OPT_GNUTLS" != xno; then dnl this is with no particular path given CURL_CHECK_PKGCONFIG(gnutls) - if test "$PKGCONFIG" != "no" ; then + if test "$PKGCONFIG" != "no"; then addlib=`$PKGCONFIG --libs-only-l gnutls` addld=`$PKGCONFIG --libs-only-L gnutls` addcflags=`$PKGCONFIG --cflags-only-I gnutls` @@ -60,7 +60,7 @@ if test "x$OPT_GNUTLS" != xno; then fi fi else - dnl this is with a given path, first check if there's a libgnutls-config + dnl this is with a given path, first check if there is a libgnutls-config dnl there and if not, make an educated guess cfg=$OPT_GNUTLS/bin/libgnutls-config check=`$cfg --version 2>/dev/null` @@ -74,7 +74,8 @@ if test "x$OPT_GNUTLS" != xno; then addlib=-lgnutls addld=-L$OPT_GNUTLS/lib$libsuff addcflags=-I$OPT_GNUTLS/include - version="" # we just don't know + dnl we just do not know + version="" gtlslib=$OPT_GNUTLS/lib$libsuff fi fi @@ -100,36 +101,37 @@ if test "x$OPT_GNUTLS" != xno; then dnl this function is selected since it was introduced in 3.1.10 AC_CHECK_LIB(gnutls, gnutls_x509_crt_get_dn2, - [ + [ AC_DEFINE(USE_GNUTLS, 1, [if GnuTLS is enabled]) GNUTLS_ENABLED=1 USE_GNUTLS="yes" ssl_msg="GnuTLS" QUIC_ENABLED=yes - test gnutls != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes - ], - [ - LIBS="$CLEANLIBS" - CPPFLAGS="$CLEANCPPFLAGS" - ]) + test "gnutls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + ], + [ + LIBS="$CLEANLIBS" + CPPFLAGS="$CLEANCPPFLAGS" + LDFLAGS="$CLEANLDFLAGS" + LDFLAGSPC="$CLEANLDFLAGSPC" + ]) - if test "x$USE_GNUTLS" = "xyes"; then + if test "$USE_GNUTLS" = "yes"; then AC_MSG_NOTICE([detected GnuTLS version $version]) check_for_ca_bundle=1 if test -n "$gtlslib"; then - dnl when shared libs were found in a path that the run-time - dnl linker doesn't search through, we need to add it to + dnl when shared libs were found in a path that the runtime + dnl linker does not search through, we need to add it to dnl CURL_LIBRARY_PATH to prevent further configure tests to fail dnl due to this - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$gtlslib" export CURL_LIBRARY_PATH AC_MSG_NOTICE([Added $gtlslib to CURL_LIBRARY_PATH]) fi fi - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE gnutls nettle" + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE gnutls" fi - fi fi dnl GNUTLS not disabled @@ -137,34 +139,81 @@ if test "x$OPT_GNUTLS" != xno; then test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" fi -dnl --- +dnl dnl Check which crypto backend GnuTLS uses -dnl --- - +dnl if test "$GNUTLS_ENABLED" = "1"; then USE_GNUTLS_NETTLE= - # First check if we can detect either crypto library via transitive linking + dnl First check if we can detect either crypto library via transitive linking AC_CHECK_LIB(gnutls, nettle_MD5Init, [ USE_GNUTLS_NETTLE=1 ]) - # If not, try linking directly to both of them to see if they are available - if test "$USE_GNUTLS_NETTLE" = ""; then - AC_CHECK_LIB(nettle, nettle_MD5Init, [ USE_GNUTLS_NETTLE=1 ]) - fi - if test "$USE_GNUTLS_NETTLE" = ""; then - AC_MSG_ERROR([GnuTLS found, but nettle was not found]) - fi - LIBS="-lnettle $LIBS" -fi + dnl If not, try linking directly to both of them to see if they are available + if test -z "$USE_GNUTLS_NETTLE"; then -dnl --- -dnl We require GnuTLS with SRP support. -dnl --- -if test "$GNUTLS_ENABLED" = "1"; then + dnl this is with no particular path given + CURL_CHECK_PKGCONFIG(nettle) + + if test "$PKGCONFIG" != "no"; then + addlib=`$PKGCONFIG --libs-only-l nettle` + addld=`$PKGCONFIG --libs-only-L nettle` + addcflags=`$PKGCONFIG --cflags-only-I nettle` + version=`$PKGCONFIG --modversion nettle` + gtlslib=`echo $addld | $SED -e 's/^-L//'` + + if test -n "$addlib"; then + + CLEANLIBS="$LIBS" + CLEANCPPFLAGS="$CPPFLAGS" + CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" + + LIBS="$addlib $LIBS" + LDFLAGS="$LDFLAGS $addld" + LDFLAGSPC="$LDFLAGSPC $addld" + if test "$addcflags" != "-I/usr/include"; then + CPPFLAGS="$CPPFLAGS $addcflags" + fi + + AC_CHECK_LIB(nettle, nettle_MD5Init, + [ + USE_GNUTLS_NETTLE=1 + ], + [ + LIBS="$CLEANLIBS" + CPPFLAGS="$CLEANCPPFLAGS" + LDFLAGS="$CLEANLDFLAGS" + LDFLAGSPC="$CLEANLDFLAGSPC" + ]) + + if test "$USE_GNUTLS_NETTLE" = "1"; then + if test -z "$version"; then + version="unknown" + fi + AC_MSG_NOTICE([detected nettle version $version]) + fi + fi + fi + if test -z "$USE_GNUTLS_NETTLE"; then + AC_MSG_ERROR([GnuTLS found, but nettle was not found]) + fi + else + LIBS="-lnettle $LIBS" + fi + + if test "$USE_GNUTLS_NETTLE" = "1"; then + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE nettle" + fi + + dnl --- + dnl We require GnuTLS with SRP support. + dnl + dnl In GnuTLS 3.8.0 (2023-02-10) and upper, this check always succeeds. + dnl Detecting actual TLS-SRP support needs poking the API at runtime. + dnl --- AC_CHECK_LIB(gnutls, gnutls_srp_verifier, [ AC_DEFINE(HAVE_GNUTLS_SRP, 1, [if you have the function gnutls_srp_verifier]) HAVE_GNUTLS_SRP=1 ]) fi - ]) diff --git a/m4/curl-mbedtls.m4 b/m4/curl-mbedtls.m4 index 519c29d79a..7c5bccd229 100644 --- a/m4/curl-mbedtls.m4 +++ b/m4/curl-mbedtls.m4 @@ -27,19 +27,19 @@ dnl check for mbedTLS dnl ---------------------------------------------------- AC_DEFUN([CURL_WITH_MBEDTLS], [ -if test "x$OPT_MBEDTLS" != xno; then +if test "x$OPT_MBEDTLS" != "xno"; then _cppflags=$CPPFLAGS _ldflags=$LDFLAGS _ldflagspc=$LDFLAGSPC ssl_msg= - if test X"$OPT_MBEDTLS" != Xno; then + if test "x$OPT_MBEDTLS" != "xno"; then - if test "$OPT_MBEDTLS" = "yes"; then + if test "x$OPT_MBEDTLS" = "xyes"; then OPT_MBEDTLS="" fi - if test -z "$OPT_MBEDTLS" ; then + if test -z "$OPT_MBEDTLS"; then dnl check for lib first without setting any new path AC_CHECK_LIB(mbedtls, mbedtls_havege_init, @@ -49,7 +49,7 @@ if test "x$OPT_MBEDTLS" != xno; then MBEDTLS_ENABLED=1 USE_MBEDTLS="yes" ssl_msg="mbedTLS" - test mbedtls != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + test "mbedtls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes ], [], -lmbedx509 -lmbedcrypto) fi @@ -58,7 +58,7 @@ if test "x$OPT_MBEDTLS" != xno; then addcflags="" mbedtlslib="" - if test "x$USE_MBEDTLS" != "xyes"; then + if test "$USE_MBEDTLS" != "yes"; then dnl add the path and test again addld=-L$OPT_MBEDTLS/lib$libsuff addcflags=-I$OPT_MBEDTLS/include @@ -76,7 +76,7 @@ if test "x$OPT_MBEDTLS" != xno; then MBEDTLS_ENABLED=1 USE_MBEDTLS="yes" ssl_msg="mbedTLS" - test mbedtls != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + test "mbedtls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes ], [ CPPFLAGS=$_cppflags @@ -85,18 +85,18 @@ if test "x$OPT_MBEDTLS" != xno; then ], -lmbedx509 -lmbedcrypto) fi - if test "x$USE_MBEDTLS" = "xyes"; then + if test "$USE_MBEDTLS" = "yes"; then AC_MSG_NOTICE([detected mbedTLS]) check_for_ca_bundle=1 LIBS="-lmbedtls -lmbedx509 -lmbedcrypto $LIBS" if test -n "$mbedtlslib"; then - dnl when shared libs were found in a path that the run-time - dnl linker doesn't search through, we need to add it to + dnl when shared libs were found in a path that the runtime + dnl linker does not search through, we need to add it to dnl CURL_LIBRARY_PATH to prevent further configure tests to fail dnl due to this - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$mbedtlslib" export CURL_LIBRARY_PATH AC_MSG_NOTICE([Added $mbedtlslib to CURL_LIBRARY_PATH]) @@ -106,6 +106,12 @@ if test "x$OPT_MBEDTLS" != xno; then if false; then LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE mbedtls mbedx509 mbedcrypto" fi + + dnl Check DES support in mbedTLS <4. + AC_CHECK_FUNCS(mbedtls_des_crypt_ecb) + if test "$ac_cv_func_mbedtls_des_crypt_ecb" = 'yes'; then + HAVE_MBEDTLS_DES_CRYPT_ECB=1 + fi fi fi dnl mbedTLS not disabled diff --git a/m4/curl-openssl.m4 b/m4/curl-openssl.m4 index c421fbae88..256037b19a 100644 --- a/m4/curl-openssl.m4 +++ b/m4/curl-openssl.m4 @@ -22,15 +22,15 @@ # #*************************************************************************** -# File version for 'aclocal' use. Keep it a single number. -# serial 5 +dnl File version for 'aclocal' use. Keep it a single number. +dnl serial 5 dnl ********************************************************************** dnl Check for OpenSSL libraries and headers dnl ********************************************************************** AC_DEFUN([CURL_WITH_OPENSSL], [ -if test "x$OPT_OPENSSL" != xno; then +if test "x$OPT_OPENSSL" != "xno"; then ssl_msg= dnl backup the pre-ssl variables @@ -76,7 +76,7 @@ if test "x$OPT_OPENSSL" != xno; then PREFIX_OPENSSL=$OPT_OPENSSL dnl Try pkg-config even when cross-compiling. Since we - dnl specify PKG_CONFIG_LIBDIR we're only looking where + dnl specify PKG_CONFIG_LIBDIR we are only looking where dnl the user told us to look OPENSSL_PCDIR="$OPT_OPENSSL/lib/pkgconfig" if test -f "$OPENSSL_PCDIR/openssl.pc"; then @@ -85,7 +85,7 @@ if test "x$OPT_OPENSSL" != xno; then fi if test "$PKGTEST" != "yes"; then - # try lib64 instead + dnl try lib64 instead OPENSSL_PCDIR="$OPT_OPENSSL/lib64/pkgconfig" if test -f "$OPENSSL_PCDIR/openssl.pc"; then AC_MSG_NOTICE([PKG_CONFIG_LIBDIR will be set to "$OPENSSL_PCDIR"]) @@ -102,7 +102,7 @@ if test "x$OPT_OPENSSL" != xno; then dnl in case pkg-config comes up empty, use what we got dnl via --with-openssl LIB_OPENSSL="$PREFIX_OPENSSL/lib$libsuff" - if test "$PREFIX_OPENSSL" != "/usr" ; then + if test "$PREFIX_OPENSSL" != "/usr"; then SSL_LDFLAGS="-L$LIB_OPENSSL" SSL_CPPFLAGS="-I$PREFIX_OPENSSL/include" fi @@ -113,14 +113,14 @@ if test "x$OPT_OPENSSL" != xno; then CURL_CHECK_PKGCONFIG(openssl, [$OPENSSL_PCDIR]) - if test "$PKGCONFIG" != "no" ; then - SSL_LIBS=`CURL_EXPORT_PCDIR([$OPENSSL_PCDIR]) dnl + if test "$PKGCONFIG" != "no"; then + SSL_LIBS=`CURL_EXPORT_PCDIR([$OPENSSL_PCDIR]) $PKGCONFIG --libs-only-l --libs-only-other openssl 2>/dev/null` - SSL_LDFLAGS=`CURL_EXPORT_PCDIR([$OPENSSL_PCDIR]) dnl + SSL_LDFLAGS=`CURL_EXPORT_PCDIR([$OPENSSL_PCDIR]) $PKGCONFIG --libs-only-L openssl 2>/dev/null` - SSL_CPPFLAGS=`CURL_EXPORT_PCDIR([$OPENSSL_PCDIR]) dnl + SSL_CPPFLAGS=`CURL_EXPORT_PCDIR([$OPENSSL_PCDIR]) $PKGCONFIG --cflags-only-I openssl 2>/dev/null` AC_MSG_NOTICE([pkg-config: SSL_LIBS: "$SSL_LIBS"]) @@ -132,9 +132,9 @@ if test "x$OPT_OPENSSL" != xno; then dnl use the values pkg-config reported. This is here dnl instead of below with CPPFLAGS and LDFLAGS because we only dnl learn about this via pkg-config. If we only have - dnl the argument to --with-openssl we don't know what + dnl the argument to --with-openssl we do not know what dnl additional libs may be necessary. Hope that we - dnl don't need any. + dnl do not need any. LIBS="$SSL_LIBS $LIBS" fi fi @@ -148,17 +148,17 @@ if test "x$OPT_OPENSSL" != xno; then HAVECRYPTO="yes" LIBS="-lcrypto $LIBS" ],[ - if test -n "$LIB_OPENSSL" ; then + if test -n "$LIB_OPENSSL"; then LDFLAGS="$CLEANLDFLAGS -L$LIB_OPENSSL" LDFLAGSPC="$CLEANLDFLAGSPC -L$LIB_OPENSSL" fi - if test "$PKGCONFIG" = "no" -a -n "$PREFIX_OPENSSL" ; then - # only set this if pkg-config wasn't used + if test "$PKGCONFIG" = "no" && test -n "$PREFIX_OPENSSL"; then + dnl only set this if pkg-config was not used CPPFLAGS="$CLEANCPPFLAGS -I$PREFIX_OPENSSL/include" fi - # Linking previously failed, try extra paths from --with-openssl or - # pkg-config. Use a different function name to avoid reusing the earlier - # cached result. + dnl Linking previously failed, try extra paths from --with-openssl or + dnl pkg-config. Use a different function name to avoid reusing the earlier + dnl cached result. AC_CHECK_LIB(crypto, HMAC_Init_ex,[ HAVECRYPTO="yes" LIBS="-lcrypto $LIBS"], [ @@ -203,48 +203,32 @@ if test "x$OPT_OPENSSL" != xno; then ]) ]) - if test X"$HAVECRYPTO" = X"yes"; then + if test "$HAVECRYPTO" = "yes"; then dnl This is only reasonable to do if crypto actually is there: check for dnl SSL libs NOTE: it is important to do this AFTER the crypto lib AC_CHECK_LIB(ssl, SSL_connect) - if test "$ac_cv_lib_ssl_SSL_connect" != yes; then - dnl we didn't find the SSL lib, try the RSAglue/rsaref stuff - AC_MSG_CHECKING(for ssl with RSAglue/rsaref libs in use); - OLIBS=$LIBS - LIBS="-lRSAglue -lrsaref $LIBS" - AC_CHECK_LIB(ssl, SSL_connect) - if test "$ac_cv_lib_ssl_SSL_connect" != yes; then - dnl still no SSL_connect - AC_MSG_RESULT(no) - LIBS=$OLIBS - else - AC_MSG_RESULT(yes) - fi - - else - + if test "$ac_cv_lib_ssl_SSL_connect" = "yes"; then dnl Have the libraries--check for OpenSSL headers - AC_CHECK_HEADERS(openssl/x509.h openssl/rsa.h openssl/crypto.h \ - openssl/pem.h openssl/ssl.h openssl/err.h, + AC_CHECK_HEADERS(openssl/rsa.h openssl/crypto.h openssl/pem.h openssl/ssl.h openssl/err.h, ssl_msg="OpenSSL" - test openssl != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + test "openssl" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes OPENSSL_ENABLED=1 AC_DEFINE(USE_OPENSSL, 1, [if OpenSSL is in use])) fi - if test X"$OPENSSL_ENABLED" != X"1"; then + if test "$OPENSSL_ENABLED" != "1"; then LIBS="$CLEANLIBS" fi - if test X"$OPT_OPENSSL" != Xoff && + if test "x$OPT_OPENSSL" != "xoff" && test "$OPENSSL_ENABLED" != "1"; then AC_MSG_ERROR([OpenSSL libs and/or directories were not found where specified!]) fi fi - if test X"$OPENSSL_ENABLED" = X"1"; then + if test "$OPENSSL_ENABLED" = "1"; then dnl These can only exist if OpenSSL exists AC_MSG_CHECKING([for BoringSSL]) @@ -276,7 +260,7 @@ if test "x$OPT_OPENSSL" != xno; then ],[ AC_MSG_RESULT([yes]) ssl_msg="AWS-LC" - OPENSSL_IS_BORINGSSL=1 + OPENSSL_IS_AWSLC=1 ],[ AC_MSG_RESULT([no]) ]) @@ -297,23 +281,22 @@ if test "x$OPT_OPENSSL" != xno; then AC_MSG_RESULT([no]) ]) - AC_MSG_CHECKING([for OpenSSL >= v3]) - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - #include - ]],[[ - #if (OPENSSL_VERSION_NUMBER >= 0x30000000L) - return 0; - #else - #error older than 3 - #endif - ]]) - ],[ - AC_MSG_RESULT([yes]) - ssl_msg="OpenSSL v3+" - ],[ - AC_MSG_RESULT([no]) - ]) + if test "$ssl_msg" = 'OpenSSL'; then + AC_MSG_CHECKING([for OpenSSL >= v3]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ + #include + ]],[[ + #if (OPENSSL_VERSION_NUMBER >= 0x30000000L) + return 0; + #else + #error older than 3 + #endif + ]]) + ],[],[ + AC_MSG_ERROR([OpenSSL 3.0.0 or upper required.]) + ]) + fi fi dnl is this OpenSSL (fork) providing the original QUIC API? @@ -332,33 +315,56 @@ if test "x$OPT_OPENSSL" != xno; then if test "$OPENSSL_ENABLED" = "1"; then if test -n "$LIB_OPENSSL"; then - dnl when the ssl shared libs were found in a path that the run-time - dnl linker doesn't search through, we need to add it to CURL_LIBRARY_PATH + dnl when the ssl shared libs were found in a path that the runtime + dnl linker does not search through, we need to add it to CURL_LIBRARY_PATH dnl to prevent further configure tests to fail due to this - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$LIB_OPENSSL" export CURL_LIBRARY_PATH AC_MSG_NOTICE([Added $LIB_OPENSSL to CURL_LIBRARY_PATH]) fi fi check_for_ca_bundle=1 - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE openssl" + if test "$OPENSSL_IS_BORINGSSL" != "1"; then dnl BoringSSL does not provide openssl.pc + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE openssl" + fi fi test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" fi -if test X"$OPT_OPENSSL" != Xno && +if test "x$OPT_OPENSSL" != "xno" && test "$OPENSSL_ENABLED" != "1"; then AC_MSG_NOTICE([OPT_OPENSSL: $OPT_OPENSSL]) AC_MSG_NOTICE([OPENSSL_ENABLED: $OPENSSL_ENABLED]) AC_MSG_ERROR([--with-openssl was given but OpenSSL could not be detected]) fi -dnl --- -dnl We require OpenSSL with SRP support. -dnl --- if test "$OPENSSL_ENABLED" = "1"; then + dnl --- + dnl We check OpenSSL for DES support. + dnl --- + AC_MSG_CHECKING([for DES support in OpenSSL]) + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[ + #ifndef OPENSSL_SUPPRESS_DEPRECATED + #define OPENSSL_SUPPRESS_DEPRECATED + #endif + #include + ]],[[ + DES_ecb_encrypt(0, 0, 0, DES_ENCRYPT); + ]]) + ],[ + AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_DES_ECB_ENCRYPT, 1, [if you have the function DES_ecb_encrypt]) + HAVE_DES_ECB_ENCRYPT=1 + ],[ + AC_MSG_RESULT([no]) + ]) + + dnl --- + dnl We require OpenSSL with SRP support. + dnl --- AC_MSG_CHECKING([for SRP support in OpenSSL]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ @@ -377,41 +383,18 @@ if test "$OPENSSL_ENABLED" = "1"; then ],[ AC_MSG_RESULT([no]) ]) -fi -dnl --- -dnl Whether the OpenSSL configuration will be loaded automatically -dnl --- -if test X"$OPENSSL_ENABLED" = X"1"; then + dnl --- + dnl Whether the OpenSSL configuration will be loaded automatically + dnl --- AC_ARG_ENABLE(openssl-auto-load-config, AS_HELP_STRING([--enable-openssl-auto-load-config],[Enable automatic loading of OpenSSL configuration]) AS_HELP_STRING([--disable-openssl-auto-load-config],[Disable automatic loading of OpenSSL configuration]), - [ if test X"$enableval" = X"no"; then + [ if test "x$enableval" = "xno"; then AC_MSG_NOTICE([automatic loading of OpenSSL configuration disabled]) - AC_DEFINE(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG, 1, [if the OpenSSL configuration won't be loaded automatically]) + AC_DEFINE(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG, 1, [if the OpenSSL configuration will not be loaded automatically]) fi ]) -fi -dnl --- -dnl We may use OpenSSL QUIC. -dnl --- -if test "$OPENSSL_ENABLED" = "1"; then - AC_MSG_CHECKING([for QUIC support and OpenSSL >= 3.3]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ - #include - ]],[[ - #if (OPENSSL_VERSION_NUMBER < 0x30300000L) - #error need at least version 3.3.0 - #endif - OSSL_QUIC_client_method(); - ]]) - ],[ - AC_MSG_RESULT([yes]) - have_openssl_quic=1 - ],[ - AC_MSG_RESULT([no]) - ]) fi ]) diff --git a/m4/curl-override.m4 b/m4/curl-override.m4 index e36c0c6c74..79b24567ac 100644 --- a/m4/curl-override.m4 +++ b/m4/curl-override.m4 @@ -20,12 +20,10 @@ # # SPDX-License-Identifier: curl # -########################################################################### -#*************************************************************************** #*************************************************************************** -# File version for 'aclocal' use. Keep it a single number. -# serial 7 +dnl File version for 'aclocal' use. Keep it a single number. +dnl serial 7 dnl CURL_OVERRIDE_AUTOCONF dnl ------------------------------------------------- @@ -42,7 +40,7 @@ AC_BEFORE([$0],[AC_PROG_LIBTOOL]) dnl Override Autoconf's AC_LANG_PROGRAM (C) dnl ------------------------------------------------- dnl This is done to prevent compiler warning -dnl 'function declaration isn't a prototype' +dnl 'function declaration is not a prototype' dnl in function main. This requires at least dnl a C89 compiler and does not support K&R. @@ -82,9 +80,9 @@ m4_define([AC_LANG_FUNC_LINK_TRY(C)], [ #define $1 innocuous_$1 #ifdef __STDC__ -# include +# include #else -# include +# include #endif #undef $1 #ifdef __cplusplus diff --git a/m4/curl-reentrant.m4 b/m4/curl-reentrant.m4 index 95e539c152..6ac731bed9 100644 --- a/m4/curl-reentrant.m4 +++ b/m4/curl-reentrant.m4 @@ -22,8 +22,8 @@ # #*************************************************************************** -# File version for 'aclocal' use. Keep it a single number. -# serial 10 +dnl File version for 'aclocal' use. Keep it a single number. +dnl serial 10 dnl Note 1 dnl ------ @@ -184,6 +184,7 @@ AC_DEFUN([CURL_CHECK_NEED_REENTRANT_STRERROR_R], [ fi ]) + dnl CURL_CHECK_NEED_REENTRANT_GETHOSTBYNAME_R dnl ------------------------------------------------- dnl Checks if the preprocessor _REENTRANT definition @@ -359,7 +360,7 @@ _EOF dnl CURL_CONFIGURE_REENTRANT dnl ------------------------------------------------- dnl This first checks if the preprocessor _REENTRANT -dnl symbol is already defined. If it isn't currently +dnl symbol is already defined. If it is not currently dnl defined a set of checks are performed to verify dnl if its definition is required to make visible to dnl the compiler a set of *_r functions. Finally, if @@ -369,8 +370,8 @@ dnl that it is defined equally for further configure dnl tests and generated config file. AC_DEFUN([CURL_CONFIGURE_REENTRANT], [ - AC_PREREQ([2.50])dnl - # + AC_PREREQ([2.50]) + AC_MSG_CHECKING([if _REENTRANT is already defined]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ @@ -389,7 +390,7 @@ AC_DEFUN([CURL_CONFIGURE_REENTRANT], [ AC_MSG_RESULT([no]) tmp_reentrant_initially_defined="no" ]) - # + if test "$tmp_reentrant_initially_defined" = "no"; then AC_MSG_CHECKING([if _REENTRANT is actually needed]) CURL_CHECK_NEED_REENTRANT_SYSTEM @@ -405,7 +406,7 @@ AC_DEFUN([CURL_CONFIGURE_REENTRANT], [ AC_MSG_RESULT([no]) fi fi - # + AC_MSG_CHECKING([if _REENTRANT is onwards defined]) if test "$tmp_reentrant_initially_defined" = "yes" || test "$tmp_need_reentrant" = "yes"; then @@ -414,14 +415,13 @@ AC_DEFUN([CURL_CONFIGURE_REENTRANT], [ else AC_MSG_RESULT([no]) fi - # ]) dnl CURL_CONFIGURE_THREAD_SAFE dnl ------------------------------------------------- dnl This first checks if the preprocessor _THREAD_SAFE -dnl symbol is already defined. If it isn't currently +dnl symbol is already defined. If it is not currently dnl defined a set of checks are performed to verify dnl if its definition is required. Finally, if dnl _THREAD_SAFE is already defined or needed it takes @@ -430,8 +430,8 @@ dnl that it is defined equally for further configure dnl tests and generated config file. AC_DEFUN([CURL_CONFIGURE_THREAD_SAFE], [ - AC_PREREQ([2.50])dnl - # + AC_PREREQ([2.50]) + AC_MSG_CHECKING([if _THREAD_SAFE is already defined]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ @@ -450,7 +450,7 @@ AC_DEFUN([CURL_CONFIGURE_THREAD_SAFE], [ AC_MSG_RESULT([no]) tmp_thread_safe_initially_defined="no" ]) - # + if test "$tmp_thread_safe_initially_defined" = "no"; then AC_MSG_CHECKING([if _THREAD_SAFE is actually needed]) CURL_CHECK_NEED_THREAD_SAFE_SYSTEM @@ -460,7 +460,7 @@ AC_DEFUN([CURL_CONFIGURE_THREAD_SAFE], [ AC_MSG_RESULT([no]) fi fi - # + AC_MSG_CHECKING([if _THREAD_SAFE is onwards defined]) if test "$tmp_thread_safe_initially_defined" = "yes" || test "$tmp_need_thread_safe" = "yes"; then @@ -469,5 +469,4 @@ AC_DEFUN([CURL_CONFIGURE_THREAD_SAFE], [ else AC_MSG_RESULT([no]) fi - # ]) diff --git a/m4/curl-rustls.m4 b/m4/curl-rustls.m4 index 13022f3963..2a035680ff 100644 --- a/m4/curl-rustls.m4 +++ b/m4/curl-rustls.m4 @@ -27,7 +27,7 @@ dnl ---------------------------------------------------- dnl check for Rustls dnl ---------------------------------------------------- -if test "x$OPT_RUSTLS" != xno; then +if test "x$OPT_RUSTLS" != "xno"; then ssl_msg= dnl backup the pre-ssl variables @@ -35,7 +35,7 @@ if test "x$OPT_RUSTLS" != xno; then CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" - ## NEW CODE + dnl NEW CODE dnl use pkg-config unless we have been given a path dnl even then, try pkg-config first @@ -62,7 +62,7 @@ if test "x$OPT_RUSTLS" != xno; then fi if test "$PKGTEST" != "yes"; then - # try lib64 instead + dnl try lib64 instead RUSTLS_PCDIR="$PREFIX_RUSTLS/lib64/pkgconfig" if test -f "$RUSTLS_PCDIR/rustls.pc"; then AC_MSG_NOTICE([PKG_CONFIG_LIBDIR will be set to "$RUSTLS_PCDIR"]) @@ -83,14 +83,14 @@ if test "x$OPT_RUSTLS" != xno; then CPPFLAGS="$CPPFLAGS $addcflags" fi - if test "$curl_cv_apple" = 'yes'; then + if test "$curl_cv_apple" = "yes"; then RUSTLS_LDFLAGS="-framework Security -framework Foundation" else RUSTLS_LDFLAGS="-lpthread -ldl -lm" fi LIB_RUSTLS="$PREFIX_RUSTLS/lib$libsuff" - if test "$PREFIX_RUSTLS" != "/usr" ; then + if test "$PREFIX_RUSTLS" != "/usr"; then SSL_LDFLAGS="-L$LIB_RUSTLS $RUSTLS_LDFLAGS" SSL_CPPFLAGS="-I$PREFIX_RUSTLS/include" fi @@ -108,14 +108,14 @@ if test "x$OPT_RUSTLS" != xno; then CURL_CHECK_PKGCONFIG(rustls, [$RUSTLS_PCDIR]) - if test "$PKGCONFIG" != "no" ; then - SSL_LIBS=`CURL_EXPORT_PCDIR([$RUSTLS_PCDIR]) dnl + if test "$PKGCONFIG" != "no"; then + SSL_LIBS=`CURL_EXPORT_PCDIR([$RUSTLS_PCDIR]) $PKGCONFIG --libs-only-l --libs-only-other rustls 2>/dev/null` - SSL_LDFLAGS=`CURL_EXPORT_PCDIR([$RUSTLS_PCDIR]) dnl + SSL_LDFLAGS=`CURL_EXPORT_PCDIR([$RUSTLS_PCDIR]) $PKGCONFIG --libs-only-L rustls 2>/dev/null` - SSL_CPPFLAGS=`CURL_EXPORT_PCDIR([$RUSTLS_PCDIR]) dnl + SSL_CPPFLAGS=`CURL_EXPORT_PCDIR([$RUSTLS_PCDIR]) $PKGCONFIG --cflags-only-I rustls 2>/dev/null` AC_MSG_NOTICE([pkg-config: SSL_LIBS: "$SSL_LIBS"]) @@ -127,16 +127,16 @@ if test "x$OPT_RUSTLS" != xno; then dnl use the values pkg-config reported. This is here dnl instead of below with CPPFLAGS and LDFLAGS because we only dnl learn about this via pkg-config. If we only have - dnl the argument to --with-rustls we don't know what + dnl the argument to --with-rustls we do not know what dnl additional libs may be necessary. Hope that we - dnl don't need any. + dnl do not need any. LIBS="$SSL_LIBS $LIBS" link_pkgconfig=1 - ssl_msg="rustls" + ssl_msg="Rustls" AC_DEFINE(USE_RUSTLS, 1, [if Rustls is enabled]) USE_RUSTLS="yes" RUSTLS_ENABLED=1 - test rustls != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + test "rustls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes else AC_MSG_ERROR([pkg-config: Could not find Rustls]) fi @@ -152,16 +152,16 @@ if test "x$OPT_RUSTLS" != xno; then LDFLAGS="$CLEANLDFLAGS $SSL_LDFLAGS" LDFLAGSPC="$CLEANLDFLAGSPC $SSL_LDFLAGS" - if test "x$USE_RUSTLS" = "xyes"; then + if test "$USE_RUSTLS" = "yes"; then AC_MSG_NOTICE([detected Rustls]) check_for_ca_bundle=1 if test -n "$LIB_RUSTLS"; then - dnl when shared libs were found in a path that the run-time + dnl when shared libs were found in a path that the runtime dnl linker does not search through, we need to add it to dnl CURL_LIBRARY_PATH so that further configure tests do not dnl fail due to this - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$LIB_RUSTLS" export CURL_LIBRARY_PATH AC_MSG_NOTICE([Added $LIB_RUSTLS to CURL_LIBRARY_PATH]) @@ -176,8 +176,8 @@ if test "x$OPT_RUSTLS" != xno; then AC_DEFINE(USE_RUSTLS, 1, [if Rustls is enabled]) RUSTLS_ENABLED=1 USE_RUSTLS="yes" - ssl_msg="rustls" - test rustls != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + ssl_msg="Rustls" + test "rustls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes ], AC_MSG_ERROR([--with-rustls was specified but could not find compatible Rustls.]), $RUSTLS_LDFLAGS) @@ -185,7 +185,7 @@ if test "x$OPT_RUSTLS" != xno; then test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" - if test X"$OPT_RUSTLS" != Xno && + if test "x$OPT_RUSTLS" != "xno" && test "$RUSTLS_ENABLED" != "1"; then AC_MSG_NOTICE([OPT_RUSTLS: $OPT_RUSTLS]) AC_MSG_NOTICE([RUSTLS_ENABLED: $RUSTLS_ENABLED]) diff --git a/m4/curl-schannel.m4 b/m4/curl-schannel.m4 index 1aa66dc2c8..3d0385347c 100644 --- a/m4/curl-schannel.m4 +++ b/m4/curl-schannel.m4 @@ -24,19 +24,19 @@ AC_DEFUN([CURL_WITH_SCHANNEL], [ AC_MSG_CHECKING([whether to enable Windows native SSL/TLS]) -if test "x$OPT_SCHANNEL" != xno; then +if test "x$OPT_SCHANNEL" != "xno"; then ssl_msg= if test "x$OPT_SCHANNEL" != "xno" && - test "x$curl_cv_native_windows" = "xyes"; then - if test "$curl_cv_winuwp" = 'yes'; then + test "$curl_cv_native_windows" = "yes"; then + if test "$curl_cv_winuwp" = "yes"; then AC_MSG_ERROR([UWP does not support Schannel.]) fi AC_MSG_RESULT(yes) AC_DEFINE(USE_SCHANNEL, 1, [to enable Windows native SSL/TLS support]) ssl_msg="Schannel" - test schannel != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + test "schannel" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes SCHANNEL_ENABLED=1 - # --with-schannel implies --enable-sspi + dnl --with-schannel implies --enable-sspi AC_DEFINE(USE_WINDOWS_SSPI, 1, [to enable SSPI support]) USE_WINDOWS_SSPI=1 curl_sspi_msg="enabled" diff --git a/m4/curl-sysconfig.m4 b/m4/curl-sysconfig.m4 index f5ebe0391b..7ad1285ef2 100644 --- a/m4/curl-sysconfig.m4 +++ b/m4/curl-sysconfig.m4 @@ -40,7 +40,7 @@ AC_MSG_CHECKING([whether to link macOS CoreFoundation, CoreServices, and SystemC ],[ build_for_macos="no" ]) - if test "x$build_for_macos" != xno; then + if test "$build_for_macos" != "no"; then AC_MSG_RESULT(yes) SYSCONFIG_LDFLAGS='-framework CoreFoundation -framework CoreServices -framework SystemConfiguration' LDFLAGS="$LDFLAGS $SYSCONFIG_LDFLAGS" diff --git a/m4/curl-wolfssl.m4 b/m4/curl-wolfssl.m4 index e2c58a94af..1d7b46721b 100644 --- a/m4/curl-wolfssl.m4 +++ b/m4/curl-wolfssl.m4 @@ -32,18 +32,18 @@ case "$OPT_WOLFSSL" in wolfpkg="" ;; *) - wolfpkg="$withval/lib/pkgconfig" + wolfpkg="$OPT_WOLFSSL/lib/pkgconfig" ;; esac -if test "x$OPT_WOLFSSL" != xno; then +if test "$OPT_WOLFSSL" != "no"; then _cppflags=$CPPFLAGS _ldflags=$LDFLAGS _ldflagspc=$LDFLAGSPC ssl_msg= - if test X"$OPT_WOLFSSL" != Xno; then + if test "$OPT_WOLFSSL" != "no"; then if test "$OPT_WOLFSSL" = "yes"; then OPT_WOLFSSL="" @@ -51,12 +51,12 @@ if test "x$OPT_WOLFSSL" != xno; then dnl try pkg-config magic CURL_CHECK_PKGCONFIG(wolfssl, [$wolfpkg]) - AC_MSG_NOTICE([Check dir $wolfpkg]) + AC_MSG_NOTICE([Check directory $wolfpkg]) addld="" addlib="" addcflags="" - if test "$PKGCONFIG" != "no" ; then + if test "$PKGCONFIG" != "no"; then addlib=`CURL_EXPORT_PCDIR([$wolfpkg]) $PKGCONFIG --libs-only-l wolfssl` addld=`CURL_EXPORT_PCDIR([$wolfpkg]) @@ -76,13 +76,14 @@ if test "x$OPT_WOLFSSL" != xno; then fi fi - if test "$curl_cv_apple" = 'yes'; then + if test "$curl_cv_apple" = "yes"; then addlib="$addlib -framework Security -framework CoreFoundation" else addlib="$addlib -lm" fi - if test "x$USE_WOLFSSL" != "xyes"; then + if test "$USE_WOLFSSL" != "yes"; then + CPPFLAGS="$CPPFLAGS -DWOLFSSL_OPTIONS_IGNORE_SYS" LDFLAGS="$LDFLAGS $addld" LDFLAGSPC="$LDFLAGSPC $addld" @@ -99,10 +100,6 @@ if test "x$OPT_WOLFSSL" != xno; then AC_MSG_CHECKING([for wolfSSL_Init in -lwolfssl]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ - /* These are not needed for detection and confuse wolfSSL. - They are set up properly later if it is detected. */ - #undef SIZEOF_LONG - #undef SIZEOF_LONG_LONG #include #include ]],[[ @@ -114,7 +111,7 @@ if test "x$OPT_WOLFSSL" != xno; then WOLFSSL_ENABLED=1 USE_WOLFSSL="yes" ssl_msg="wolfSSL" - test wolfssl != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + test "wolfssl" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes ], [ AC_MSG_RESULT(no) @@ -126,45 +123,41 @@ if test "x$OPT_WOLFSSL" != xno; then LIBS="$my_ac_save_LIBS" fi - if test "x$USE_WOLFSSL" = "xyes"; then + if test "$USE_WOLFSSL" = "yes"; then AC_MSG_NOTICE([detected wolfSSL]) check_for_ca_bundle=1 - dnl wolfssl/ctaocrypt/types.h needs SIZEOF_LONG_LONG defined! - CURL_SIZEOF(long long) - LIBS="$addlib $LIBS" dnl is this wolfSSL providing the original QUIC API? AC_CHECK_FUNCS([wolfSSL_set_quic_use_legacy_codepoint], [QUIC_ENABLED=yes]) dnl wolfSSL needs configure --enable-opensslextra to have *get_peer* - dnl DES* is needed for NTLM support and lives in the OpenSSL compatibility - dnl layer + dnl wc_Des_EcbEncrypt is needed for NTLM support. dnl if wolfSSL_BIO_set_shutdown is present, we have the full BIO feature set AC_CHECK_FUNCS(wolfSSL_get_peer_certificate \ wolfSSL_UseALPN \ - wolfSSL_DES_ecb_encrypt \ wolfSSL_BIO_new \ - wolfSSL_BIO_set_shutdown) + wolfSSL_BIO_set_shutdown \ + wc_Des_EcbEncrypt) dnl if this symbol is present, we want the include path to include the dnl OpenSSL API root as well - if test "x$ac_cv_func_wolfSSL_DES_ecb_encrypt" = 'xyes'; then - HAVE_WOLFSSL_DES_ECB_ENCRYPT=1 + if test "$ac_cv_func_wc_Des_EcbEncrypt" = "yes"; then + HAVE_WC_DES_ECBENCRYPT=1 fi dnl if this symbol is present, we can make use of BIO filter chains - if test "x$ac_cv_func_wolfSSL_BIO_new" = 'xyes'; then + if test "$ac_cv_func_wolfSSL_BIO_new" = "yes"; then HAVE_WOLFSSL_BIO_NEW=1 fi if test -n "$wolfssllibpath"; then - dnl when shared libs were found in a path that the run-time - dnl linker doesn't search through, we need to add it to + dnl when shared libs were found in a path that the runtime + dnl linker does not search through, we need to add it to dnl CURL_LIBRARY_PATH to prevent further configure tests to fail dnl due to this - if test "x$cross_compiling" != "xyes"; then + if test "$cross_compiling" != "yes"; then CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$wolfssllibpath" export CURL_LIBRARY_PATH AC_MSG_NOTICE([Added $wolfssllibpath to CURL_LIBRARY_PATH]) @@ -172,7 +165,7 @@ if test "x$OPT_WOLFSSL" != xno; then fi LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE wolfssl" else - AC_MSG_ERROR([--with-wolfssl but wolfSSL was not found or doesn't work]) + AC_MSG_ERROR([--with-wolfssl but wolfSSL was not found or does not work]) fi fi dnl wolfSSL not disabled diff --git a/m4/xc-am-iface.m4 b/m4/xc-am-iface.m4 index e85e6d4246..069490159b 100644 --- a/m4/xc-am-iface.m4 +++ b/m4/xc-am-iface.m4 @@ -20,7 +20,7 @@ # #--------------------------------------------------------------------------- -# serial 1 +dnl serial 1 dnl _XC_AUTOMAKE_BODY @@ -39,21 +39,20 @@ dnl is used to differentiate automake version 1.14 from older dnl ones which lack this macro. m4_define([_XC_AUTOMAKE_BODY], -[dnl -## --------------------------------------- ## -## Start of automake initialization code ## -## --------------------------------------- ## +[ +dnl --------------------------------------- +dnl Start of automake initialization code +dnl --------------------------------------- m4_ifdef([_AM_PROG_CC_C_O], [ AM_INIT_AUTOMAKE([subdir-objects]) ],[ AM_INIT_AUTOMAKE -])dnl -## ------------------------------------- ## -## End of automake initialization code ## -## ------------------------------------- ## -dnl -m4_define([$0], [])[]dnl +]) +dnl ------------------------------------- +dnl End of automake initialization code +dnl ------------------------------------- +m4_define([$0], [])[] ]) @@ -71,15 +70,15 @@ dnl generate the configure script, otherwise this option dnl is not used. AC_DEFUN([XC_AUTOMAKE], -[dnl -AC_PREREQ([2.50])dnl -dnl -AC_BEFORE([$0],[AM_INIT_AUTOMAKE])dnl -dnl +[ +AC_PREREQ([2.50]) + +AC_BEFORE([$0],[AM_INIT_AUTOMAKE]) + _XC_AUTOMAKE_BODY -dnl + m4_ifdef([AM_INIT_AUTOMAKE], - [m4_undefine([AM_INIT_AUTOMAKE])])dnl -dnl -m4_define([$0], [])[]dnl + [m4_undefine([AM_INIT_AUTOMAKE])]) + +m4_define([$0], [])[] ]) diff --git a/m4/xc-cc-check.m4 b/m4/xc-cc-check.m4 index 5cf2af7bee..c3294e87f6 100644 --- a/m4/xc-cc-check.m4 +++ b/m4/xc-cc-check.m4 @@ -20,7 +20,7 @@ # #--------------------------------------------------------------------------- -# serial 1 +dnl serial 1 dnl _XC_PROG_CC_PREAMBLE @@ -46,12 +46,12 @@ AC_DEFUN([_XC_PROG_CC_POSTLUDE], [ CFLAGS=$xc_prog_cc_prev_CFLAGS LDFLAGS=$xc_prog_cc_prev_LDFLAGS CPPFLAGS=$xc_prog_cc_prev_CPPFLAGS - AC_SUBST([CC])dnl - AC_SUBST([CPP])dnl - AC_SUBST([LIBS])dnl - AC_SUBST([CFLAGS])dnl - AC_SUBST([LDFLAGS])dnl - AC_SUBST([CPPFLAGS])dnl + AC_SUBST([CC]) + AC_SUBST([CPP]) + AC_SUBST([LIBS]) + AC_SUBST([CFLAGS]) + AC_SUBST([LDFLAGS]) + AC_SUBST([CPPFLAGS]) ]) @@ -60,13 +60,13 @@ dnl ------------------------------------------------- dnl Private macro. AC_DEFUN([_XC_PROG_CC], [ - AC_REQUIRE([_XC_PROG_CC_PREAMBLE])dnl - AC_REQUIRE([XC_CHECK_BUILD_FLAGS])dnl - AC_REQUIRE([AC_PROG_INSTALL])dnl - AC_REQUIRE([AC_PROG_CC])dnl - AC_REQUIRE([AM_PROG_CC_C_O])dnl - AC_REQUIRE([AC_PROG_CPP])dnl - AC_REQUIRE([_XC_PROG_CC_POSTLUDE])dnl + AC_REQUIRE([_XC_PROG_CC_PREAMBLE]) + AC_REQUIRE([XC_CHECK_BUILD_FLAGS]) + AC_REQUIRE([AC_PROG_INSTALL]) + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AM_PROG_CC_C_O]) + AC_REQUIRE([AC_PROG_CPP]) + AC_REQUIRE([_XC_PROG_CC_POSTLUDE]) ]) @@ -84,14 +84,14 @@ dnl LIBS, LDFLAGS, CFLAGS, CPPFLAGS and IFS from being dnl unexpectedly changed by underlying macros. AC_DEFUN([XC_CHECK_PROG_CC], [ - AC_PREREQ([2.50])dnl - AC_BEFORE([$0],[_XC_PROG_CC_PREAMBLE])dnl - AC_BEFORE([$0],[AC_PROG_INSTALL])dnl - AC_BEFORE([$0],[AC_PROG_CC])dnl - AC_BEFORE([$0],[AM_PROG_CC_C_O])dnl - AC_BEFORE([$0],[AC_PROG_CPP])dnl - AC_BEFORE([$0],[AC_PROG_LIBTOOL])dnl - AC_BEFORE([$0],[AM_INIT_AUTOMAKE])dnl - AC_BEFORE([$0],[_XC_PROG_CC_POSTLUDE])dnl - AC_REQUIRE([_XC_PROG_CC])dnl + AC_PREREQ([2.50]) + AC_BEFORE([$0],[_XC_PROG_CC_PREAMBLE]) + AC_BEFORE([$0],[AC_PROG_INSTALL]) + AC_BEFORE([$0],[AC_PROG_CC]) + AC_BEFORE([$0],[AM_PROG_CC_C_O]) + AC_BEFORE([$0],[AC_PROG_CPP]) + AC_BEFORE([$0],[AC_PROG_LIBTOOL]) + AC_BEFORE([$0],[AM_INIT_AUTOMAKE]) + AC_BEFORE([$0],[_XC_PROG_CC_POSTLUDE]) + AC_REQUIRE([_XC_PROG_CC]) ]) diff --git a/m4/xc-lt-iface.m4 b/m4/xc-lt-iface.m4 index a408800353..0d8b0ef31f 100644 --- a/m4/xc-lt-iface.m4 +++ b/m4/xc-lt-iface.m4 @@ -20,7 +20,7 @@ # #--------------------------------------------------------------------------- -# serial 1 +dnl serial 1 dnl _XC_LIBTOOL_PREAMBLE @@ -32,23 +32,23 @@ dnl libtool and customizes its default behavior before dnl libtool code is actually used in script. m4_define([_XC_LIBTOOL_PREAMBLE], -[dnl -# ------------------------------------ # -# Determine libtool default behavior # -# ------------------------------------ # +[ +dnl ------------------------------------ +dnl Determine libtool default behavior +dnl ------------------------------------ -# -# Default behavior is to enable shared and static libraries on systems -# where libtool knows how to build both library versions, and does not -# require separate configuration and build runs for each flavor. -# +dnl +dnl Default behavior is to enable shared and static libraries on systems +dnl where libtool knows how to build both library versions, and does not +dnl require separate configuration and build runs for each flavor. +dnl xc_lt_want_enable_shared='yes' xc_lt_want_enable_static='yes' -# -# User may have disabled shared or static libraries. -# +dnl +dnl User may have disabled shared or static libraries. +dnl case "x$enable_shared" in @%:@ ( xno) xc_lt_want_enable_shared='no' @@ -59,46 +59,46 @@ case "x$enable_static" in @%:@ ( xc_lt_want_enable_static='no' ;; esac -if test "x$xc_lt_want_enable_shared" = 'xno' && - test "x$xc_lt_want_enable_static" = 'xno'; then +if test "$xc_lt_want_enable_shared" = "no" && + test "$xc_lt_want_enable_static" = "no"; then AC_MSG_ERROR([can not disable shared and static libraries simultaneously]) fi -# -# Default behavior on systems that require independent configuration -# and build runs for shared and static is to enable shared libraries -# and disable static ones. On these systems option '--disable-shared' -# must be used in order to build a proper static library. -# +dnl +dnl Default behavior on systems that require independent configuration +dnl and build runs for shared and static is to enable shared libraries +dnl and disable static ones. On these systems option '--disable-shared' +dnl must be used in order to build a proper static library. +dnl -if test "x$xc_lt_want_enable_shared" = 'xyes' && - test "x$xc_lt_want_enable_static" = 'xyes'; then +if test "$xc_lt_want_enable_shared" = "yes" && + test "$xc_lt_want_enable_static" = "yes"; then case $host_os in @%:@ ( - cegcc* | os2* | aix*) + os2* | aix*) xc_lt_want_enable_static='no' ;; esac fi -# -# Make libtool aware of current shared and static library preferences -# taking in account that, depending on host characteristics, libtool -# may modify these option preferences later in this configure script. -# +dnl +dnl Make libtool aware of current shared and static library preferences +dnl taking in account that, depending on host characteristics, libtool +dnl may modify these option preferences later in this configure script. +dnl enable_shared=$xc_lt_want_enable_shared enable_static=$xc_lt_want_enable_static -# -# Default behavior is to build PIC objects for shared libraries and -# non-PIC objects for static libraries. -# +dnl +dnl Default behavior is to build PIC objects for shared libraries and +dnl non-PIC objects for static libraries. +dnl xc_lt_want_with_pic='default' -# -# User may have specified PIC preference. -# +dnl +dnl User may have specified PIC preference. +dnl case "x$with_pic" in @%:@ (( xno) @@ -109,15 +109,15 @@ case "x$with_pic" in @%:@ (( ;; esac -# -# Default behavior on some systems where building a shared library out -# of non-PIC compiled objects will fail with following linker error -# "relocation R_X86_64_32 can not be used when making a shared object" -# is to build PIC objects even for static libraries. This behavior may -# be overridden using 'configure --disable-shared --without-pic'. -# +dnl +dnl Default behavior on some systems where building a shared library out +dnl of non-PIC compiled objects will fail with following linker error +dnl "relocation R_X86_64_32 can not be used when making a shared object" +dnl is to build PIC objects even for static libraries. This behavior may +dnl be overridden using 'configure --disable-shared --without-pic'. +dnl -if test "x$xc_lt_want_with_pic" = 'xdefault'; then +if test "$xc_lt_want_with_pic" = "default"; then case $host_cpu in @%:@ ( x86_64 | amd64 | ia64) case $host_os in @%:@ ( @@ -136,8 +136,8 @@ fi # with_pic=$xc_lt_want_with_pic -dnl -m4_define([$0],[])dnl + +m4_define([$0],[]) ]) @@ -150,22 +150,22 @@ dnl configure script, regardless of libtool version in dnl use when generating configure script. m4_define([_XC_LIBTOOL_BODY], -[dnl -## ----------------------- ## -## Start of libtool code ## -## ----------------------- ## +[ +dnl ----------------------- +dnl Start of libtool code +dnl ----------------------- m4_ifdef([LT_INIT], -[dnl +[ LT_INIT([win32-dll]) -],[dnl +],[ AC_LIBTOOL_WIN32_DLL AC_PROG_LIBTOOL -])dnl -## --------------------- ## -## End of libtool code ## -## --------------------- ## +]) +dnl --------------------- +dnl End of libtool code +dnl --------------------- dnl -m4_define([$0], [])[]dnl +m4_define([$0], [])[] ]) @@ -182,7 +182,7 @@ dnl xc_lt_build_shared dnl xc_lt_build_static m4_define([_XC_CHECK_LT_BUILD_LIBRARIES], -[dnl +[ # # Verify if finally libtool shared libraries will be built # @@ -208,8 +208,8 @@ case "x$enable_static" in @%:@ (( AC_MSG_ERROR([unexpected libtool enable_static value: $enable_static]) ;; esac -dnl -m4_define([$0],[])dnl + +m4_define([$0],[]) ]) @@ -222,14 +222,14 @@ dnl provided when building libtool shared libraries. dnl Result stored in xc_lt_shlib_use_version_info. m4_define([_XC_CHECK_LT_SHLIB_USE_VERSION_INFO], -[dnl +[ # # Verify if libtool shared libraries should be linked using flag -version-info # AC_MSG_CHECKING([whether to build shared libraries with -version-info]) xc_lt_shlib_use_version_info='yes' -if test "x$version_type" = 'xnone'; then +if test "$version_type" = "none"; then xc_lt_shlib_use_version_info='no' fi case $host_os in @%:@ ( @@ -238,8 +238,8 @@ case $host_os in @%:@ ( ;; esac AC_MSG_RESULT([$xc_lt_shlib_use_version_info]) -dnl -m4_define([$0], [])[]dnl + +m4_define([$0], [])[] ]) @@ -252,26 +252,26 @@ dnl provided when building libtool shared libraries. dnl Result stored in xc_lt_shlib_use_no_undefined. m4_define([_XC_CHECK_LT_SHLIB_USE_NO_UNDEFINED], -[dnl +[ # # Verify if libtool shared libraries should be linked using flag -no-undefined # AC_MSG_CHECKING([whether to build shared libraries with -no-undefined]) xc_lt_shlib_use_no_undefined='no' -if test "x$allow_undefined" = 'xno'; then +if test "x$allow_undefined" = "xno"; then xc_lt_shlib_use_no_undefined='yes' -elif test "x$allow_undefined_flag" = 'xunsupported'; then +elif test "x$allow_undefined_flag" = "xunsupported"; then xc_lt_shlib_use_no_undefined='yes' fi case $host_os in @%:@ ( - cygwin* | mingw* | cegcc* | os2* | aix*) + cygwin* | mingw* | os2* | aix*) xc_lt_shlib_use_no_undefined='yes' ;; esac AC_MSG_RESULT([$xc_lt_shlib_use_no_undefined]) -dnl -m4_define([$0], [])[]dnl + +m4_define([$0], [])[] ]) @@ -284,7 +284,7 @@ dnl provided when building libtool shared libraries. dnl Result stored in xc_lt_shlib_use_mimpure_text. m4_define([_XC_CHECK_LT_SHLIB_USE_MIMPURE_TEXT], -[dnl +[ # # Verify if libtool shared libraries should be linked using flag -mimpure-text # @@ -293,14 +293,14 @@ AC_MSG_CHECKING([whether to build shared libraries with -mimpure-text]) xc_lt_shlib_use_mimpure_text='no' case $host_os in @%:@ ( solaris2*) - if test "x$GCC" = 'xyes'; then + if test "x$GCC" = "xyes"; then xc_lt_shlib_use_mimpure_text='yes' fi ;; esac AC_MSG_RESULT([$xc_lt_shlib_use_mimpure_text]) -dnl -m4_define([$0], [])[]dnl + +m4_define([$0], [])[] ]) @@ -317,7 +317,7 @@ dnl xc_lt_build_shared_with_pic dnl xc_lt_build_static_with_pic m4_define([_XC_CHECK_LT_BUILD_WITH_PIC], -[dnl +[ # # Find out whether libtool libraries would be built with PIC # @@ -345,8 +345,8 @@ AC_MSG_CHECKING([whether to build shared libraries with PIC]) AC_MSG_RESULT([$xc_lt_build_shared_with_pic]) AC_MSG_CHECKING([whether to build static libraries with PIC]) AC_MSG_RESULT([$xc_lt_build_static_with_pic]) -dnl -m4_define([$0],[])dnl + +m4_define([$0],[]) ]) @@ -361,14 +361,14 @@ dnl xc_lt_build_shared_only dnl xc_lt_build_static_only m4_define([_XC_CHECK_LT_BUILD_SINGLE_VERSION], -[dnl +[ # # Verify if libtool shared libraries will be built while static not built # AC_MSG_CHECKING([whether to build shared libraries only]) -if test "$xc_lt_build_shared" = 'yes' && - test "$xc_lt_build_static" = 'no'; then +if test "$xc_lt_build_shared" = "yes" && + test "$xc_lt_build_static" = "no"; then xc_lt_build_shared_only='yes' else xc_lt_build_shared_only='no' @@ -380,15 +380,15 @@ AC_MSG_RESULT([$xc_lt_build_shared_only]) # AC_MSG_CHECKING([whether to build static libraries only]) -if test "$xc_lt_build_static" = 'yes' && - test "$xc_lt_build_shared" = 'no'; then +if test "$xc_lt_build_static" = "yes" && + test "$xc_lt_build_shared" = "no"; then xc_lt_build_static_only='yes' else xc_lt_build_static_only='no' fi AC_MSG_RESULT([$xc_lt_build_static_only]) -dnl -m4_define([$0],[])dnl + +m4_define([$0],[]) ]) @@ -402,15 +402,15 @@ dnl been executed. See individual check descriptions dnl for further info. m4_define([_XC_LIBTOOL_POSTLUDE], -[dnl +[ _XC_CHECK_LT_BUILD_LIBRARIES _XC_CHECK_LT_SHLIB_USE_VERSION_INFO _XC_CHECK_LT_SHLIB_USE_NO_UNDEFINED _XC_CHECK_LT_SHLIB_USE_MIMPURE_TEXT _XC_CHECK_LT_BUILD_WITH_PIC _XC_CHECK_LT_BUILD_SINGLE_VERSION -dnl -m4_define([$0],[])dnl + +m4_define([$0],[]) ]) @@ -440,27 +440,27 @@ dnl xc_lt_build_shared_only dnl xc_lt_build_static_only AC_DEFUN([XC_LIBTOOL], -[dnl -AC_PREREQ([2.50])dnl -dnl -AC_BEFORE([$0],[LT_INIT])dnl -AC_BEFORE([$0],[AC_PROG_LIBTOOL])dnl -AC_BEFORE([$0],[AC_LIBTOOL_WIN32_DLL])dnl -dnl -AC_REQUIRE([XC_CHECK_PATH_SEPARATOR])dnl -AC_REQUIRE([AC_CANONICAL_HOST])dnl -AC_REQUIRE([AC_PROG_CC])dnl -dnl +[ +AC_PREREQ([2.50]) + +AC_BEFORE([$0],[LT_INIT]) +AC_BEFORE([$0],[AC_PROG_LIBTOOL]) +AC_BEFORE([$0],[AC_LIBTOOL_WIN32_DLL]) + +AC_REQUIRE([XC_CHECK_PATH_SEPARATOR]) +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_REQUIRE([AC_PROG_CC]) + _XC_LIBTOOL_PREAMBLE _XC_LIBTOOL_BODY _XC_LIBTOOL_POSTLUDE -dnl + m4_ifdef([AC_LIBTOOL_WIN32_DLL], - [m4_undefine([AC_LIBTOOL_WIN32_DLL])])dnl + [m4_undefine([AC_LIBTOOL_WIN32_DLL])]) m4_ifdef([AC_PROG_LIBTOOL], - [m4_undefine([AC_PROG_LIBTOOL])])dnl + [m4_undefine([AC_PROG_LIBTOOL])]) m4_ifdef([LT_INIT], - [m4_undefine([LT_INIT])])dnl -dnl -m4_define([$0],[])dnl + [m4_undefine([LT_INIT])]) + +m4_define([$0],[]) ]) diff --git a/m4/xc-val-flgs.m4 b/m4/xc-val-flgs.m4 index e2225c2e57..7d3322b512 100644 --- a/m4/xc-val-flgs.m4 +++ b/m4/xc-val-flgs.m4 @@ -20,7 +20,7 @@ # #--------------------------------------------------------------------------- -# serial 1 +dnl serial 1 dnl _XC_CHECK_VAR_LIBS @@ -39,7 +39,7 @@ AC_DEFUN([_XC_CHECK_VAR_LIBS], [ ;; esac done - if test $xc_bad_var_libs = yes; then + if test "$xc_bad_var_libs" = "yes"; then AC_MSG_NOTICE([using LIBS: $LIBS]) AC_MSG_NOTICE([LIBS note: LIBS should only be used to specify libraries (-lname).]) fi @@ -68,7 +68,7 @@ AC_DEFUN([_XC_CHECK_VAR_LDFLAGS], [ ;; esac done - if test $xc_bad_var_ldflags = yes; then + if test "$xc_bad_var_ldflags" = "yes"; then AC_MSG_NOTICE([using LDFLAGS: $LDFLAGS]) xc_bad_var_msg="LDFLAGS note: LDFLAGS should only be used to specify linker flags, not" for xc_word in $LDFLAGS; do @@ -110,7 +110,7 @@ AC_DEFUN([_XC_CHECK_VAR_CPPFLAGS], [ ;; esac done - if test $xc_bad_var_cppflags = yes; then + if test "$xc_bad_var_cppflags" = "yes"; then AC_MSG_NOTICE([using CPPFLAGS: $CPPFLAGS]) xc_bad_var_msg="CPPFLAGS note: CPPFLAGS should only be used to specify C preprocessor flags, not" for xc_word in $CPPFLAGS; do @@ -158,7 +158,7 @@ AC_DEFUN([_XC_CHECK_VAR_CFLAGS], [ ;; esac done - if test $xc_bad_var_cflags = yes; then + if test "$xc_bad_var_cflags" = "yes"; then AC_MSG_NOTICE([using CFLAGS: $CFLAGS]) xc_bad_var_msg="CFLAGS note: CFLAGS should only be used to specify C compiler flags, not" for xc_word in $CFLAGS; do @@ -200,17 +200,17 @@ dnl dnl Intended to be used early in configure script. AC_DEFUN([XC_CHECK_USER_FLAGS], [ - AC_PREREQ([2.50])dnl - AC_BEFORE([$0],[XC_CHECK_PROG_CC])dnl + AC_PREREQ([2.50]) + AC_BEFORE([$0],[XC_CHECK_PROG_CC]) dnl check order below matters _XC_CHECK_VAR_LIBS _XC_CHECK_VAR_LDFLAGS _XC_CHECK_VAR_CPPFLAGS _XC_CHECK_VAR_CFLAGS - if test $xc_bad_var_libs = yes || - test $xc_bad_var_cflags = yes || - test $xc_bad_var_ldflags = yes || - test $xc_bad_var_cppflags = yes; then + if test "$xc_bad_var_libs" = "yes" || + test "$xc_bad_var_cflags" = "yes" || + test "$xc_bad_var_ldflags" = "yes" || + test "$xc_bad_var_cppflags" = "yes"; then AC_MSG_ERROR([Can not continue. Fix errors mentioned immediately above this line.]) fi ]) @@ -226,19 +226,19 @@ dnl script might have set. When checks fails, user dnl is noticed about errors detected in all of them dnl but script continues execution. dnl -dnl Intended to be used very late in configure script. +dnl Intended to be used late in configure script. AC_DEFUN([XC_CHECK_BUILD_FLAGS], [ - AC_PREREQ([2.50])dnl + AC_PREREQ([2.50]) dnl check order below matters _XC_CHECK_VAR_LIBS _XC_CHECK_VAR_LDFLAGS _XC_CHECK_VAR_CPPFLAGS _XC_CHECK_VAR_CFLAGS - if test $xc_bad_var_libs = yes || - test $xc_bad_var_cflags = yes || - test $xc_bad_var_ldflags = yes || - test $xc_bad_var_cppflags = yes; then + if test "$xc_bad_var_libs" = "yes" || + test "$xc_bad_var_cflags" = "yes" || + test "$xc_bad_var_ldflags" = "yes" || + test "$xc_bad_var_cppflags" = "yes"; then AC_MSG_WARN([Continuing even with errors mentioned immediately above this line.]) fi ]) diff --git a/m4/zz40-xc-ovr.m4 b/m4/zz40-xc-ovr.m4 index 6faa94e43e..5d2b2d0b72 100644 --- a/m4/zz40-xc-ovr.m4 +++ b/m4/zz40-xc-ovr.m4 @@ -31,8 +31,8 @@ dnl Version macros dnl ------------------------------------------------- dnl Public macros. -m4_define([XC_CONFIGURE_PREAMBLE_VER_MAJOR],[1])dnl -m4_define([XC_CONFIGURE_PREAMBLE_VER_MINOR],[0])dnl +m4_define([XC_CONFIGURE_PREAMBLE_VER_MAJOR],[1]) +m4_define([XC_CONFIGURE_PREAMBLE_VER_MINOR],[0]) dnl _XC_CFG_PRE_PREAMBLE @@ -50,9 +50,9 @@ XC_CONFIGURE_PREAMBLE_VER_MINOR ## xc_configure_preamble_ver_major='XC_CONFIGURE_PREAMBLE_VER_MAJOR' xc_configure_preamble_ver_minor='XC_CONFIGURE_PREAMBLE_VER_MINOR' -# -# Set IFS to space, tab and newline. -# +dnl +dnl Set IFS to space, tab and newline. +dnl xc_space=' ' xc_tab=' ' @@ -60,9 +60,9 @@ xc_newline=' ' IFS="$xc_space$xc_tab$xc_newline" -# -# Set internationalization behavior variables. -# +dnl +dnl Set internationalization behavior variables. +dnl LANG='C' LC_ALL='C' @@ -71,9 +71,9 @@ export LANG export LC_ALL export LANGUAGE -# -# Some useful variables. -# +dnl +dnl Some useful variables. +dnl xc_msg_warn='configure: WARNING:' xc_msg_abrt='Can not continue.' @@ -89,11 +89,11 @@ dnl Emits shell code that verifies that 'echo' command dnl is available, otherwise aborts execution. AC_DEFUN([_XC_CFG_PRE_BASIC_CHK_CMD_ECHO], -[dnl -AC_REQUIRE([_XC_CFG_PRE_PREAMBLE])dnl -# -# Verify that 'echo' command is available, otherwise abort. -# +[ +AC_REQUIRE([_XC_CFG_PRE_PREAMBLE]) +dnl +dnl Verify that 'echo' command is available, otherwise abort. +dnl xc_tst_str='unknown' (`echo "$xc_tst_str" >/dev/null 2>&1`) && xc_tst_str='success' @@ -102,7 +102,7 @@ case "x$xc_tst_str" in @%:@ (( : ;; *) - # Try built-in echo, and fail. + dnl Try built-in echo, and fail. echo "$xc_msg_err 'echo' command not found. $xc_msg_abrt" >&2 exit 1 ;; @@ -118,11 +118,11 @@ dnl Emits shell code that verifies that 'test' command dnl is available, otherwise aborts execution. AC_DEFUN([_XC_CFG_PRE_BASIC_CHK_CMD_TEST], -[dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_ECHO])dnl -# -# Verify that 'test' command is available, otherwise abort. -# +[ +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_ECHO]) +dnl +dnl Verify that 'test' command is available, otherwise abort. +dnl xc_tst_str='unknown' (`test -n "$xc_tst_str" >/dev/null 2>&1`) && xc_tst_str='success' @@ -146,11 +146,11 @@ dnl Emits shell code that verifies that 'PATH' variable dnl is set, otherwise aborts execution. AC_DEFUN([_XC_CFG_PRE_BASIC_CHK_VAR_PATH], -[dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_TEST])dnl -# -# Verify that 'PATH' variable is set, otherwise abort. -# +[ +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_TEST]) +dnl +dnl Verify that 'PATH' variable is set, otherwise abort. +dnl xc_tst_str='unknown' (`test -n "$PATH" >/dev/null 2>&1`) && xc_tst_str='success' @@ -174,11 +174,11 @@ dnl Emits shell code that verifies that 'expr' command dnl is available, otherwise aborts execution. AC_DEFUN([_XC_CFG_PRE_BASIC_CHK_CMD_EXPR], -[dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_VAR_PATH])dnl -# -# Verify that 'expr' command is available, otherwise abort. -# +[ +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_VAR_PATH]) +dnl +dnl Verify that 'expr' command is available, otherwise abort. +dnl xc_tst_str='unknown' xc_tst_str=`expr "$xc_tst_str" : '.*' 2>/dev/null` @@ -206,11 +206,11 @@ dnl script bootstrapping itself. No fancy testing for a dnl proper 'sed' this early, that should be done later. AC_DEFUN([_XC_CFG_PRE_BASIC_CHK_UTIL_SED], -[dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_VAR_PATH])dnl -# -# Verify that 'sed' utility is found within 'PATH', otherwise abort. -# +[ +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_VAR_PATH]) +dnl +dnl Verify that 'sed' utility is found within 'PATH', otherwise abort. +dnl xc_tst_str='unknown' xc_tst_str=`echo "$xc_tst_str" 2>/dev/null \ @@ -239,11 +239,11 @@ dnl script bootstrapping itself. No fancy testing for a dnl proper 'grep' this early, that should be done later. AC_DEFUN([_XC_CFG_PRE_BASIC_CHK_UTIL_GREP], -[dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_VAR_PATH])dnl -# -# Verify that 'grep' utility is found within 'PATH', otherwise abort. -# +[ +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_VAR_PATH]) +dnl +dnl Verify that 'grep' utility is found within 'PATH', otherwise abort. +dnl xc_tst_str='unknown' (`echo "$xc_tst_str" 2>/dev/null \ @@ -268,11 +268,11 @@ dnl Emits shell code that verifies that 'tr' utility dnl is found within 'PATH', otherwise aborts execution. AC_DEFUN([_XC_CFG_PRE_BASIC_CHK_UTIL_TR], -[dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_VAR_PATH])dnl -# -# Verify that 'tr' utility is found within 'PATH', otherwise abort. -# +[ +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_VAR_PATH]) +dnl +dnl Verify that 'tr' utility is found within 'PATH', otherwise abort. +dnl xc_tst_str="${xc_tab}98s7u6c5c4e3s2s10" xc_tst_str=`echo "$xc_tst_str" 2>/dev/null \ @@ -297,11 +297,11 @@ dnl Emits shell code that verifies that 'wc' utility dnl is found within 'PATH', otherwise aborts execution. AC_DEFUN([_XC_CFG_PRE_BASIC_CHK_UTIL_WC], -[dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_TR])dnl -# -# Verify that 'wc' utility is found within 'PATH', otherwise abort. -# +[ +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_TR]) +dnl +dnl Verify that 'wc' utility is found within 'PATH', otherwise abort. +dnl xc_tst_str='unknown unknown unknown unknown' xc_tst_str=`echo "$xc_tst_str" 2>/dev/null \ @@ -326,11 +326,11 @@ dnl Emits shell code that verifies that 'cat' utility dnl is found within 'PATH', otherwise aborts execution. AC_DEFUN([_XC_CFG_PRE_BASIC_CHK_UTIL_CAT], -[dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_WC])dnl -# -# Verify that 'cat' utility is found within 'PATH', otherwise abort. -# +[ +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_WC]) +dnl +dnl Verify that 'cat' utility is found within 'PATH', otherwise abort. +dnl xc_tst_str='unknown' xc_tst_str=`cat <<_EOT 2>/dev/null \ @@ -367,13 +367,13 @@ dnl Non-empty user provided 'PATH_SEPARATOR' always dnl overrides the auto-detected one. AC_DEFUN([_XC_CFG_PRE_CHECK_PATH_SEPARATOR], -[dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_EXPR])dnl -# -# Auto-detect and set 'PATH_SEPARATOR', unless it is already non-empty set. -# +[ +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_EXPR]) +dnl +dnl Auto-detect and set 'PATH_SEPARATOR', unless it is already non-empty set. +dnl -# Directory count in 'PATH' when using a colon separator. +dnl Directory count in 'PATH' when using a colon separator. xc_tst_dirs_col='x' xc_tst_prev_IFS=$IFS; IFS=':' for xc_tst_dir in $PATH; do @@ -383,7 +383,7 @@ done IFS=$xc_tst_prev_IFS xc_tst_dirs_col=`expr "$xc_tst_dirs_col" : '.*'` -# Directory count in 'PATH' when using a semicolon separator. +dnl Directory count in 'PATH' when using a semicolon separator. xc_tst_dirs_sem='x' xc_tst_prev_IFS=$IFS; IFS=';' for xc_tst_dir in $PATH; do @@ -393,31 +393,31 @@ done IFS=$xc_tst_prev_IFS xc_tst_dirs_sem=`expr "$xc_tst_dirs_sem" : '.*'` -if test $xc_tst_dirs_sem -eq $xc_tst_dirs_col; then - # When both counting methods give the same result we do not want to - # chose one over the other, and consider auto-detection not possible. +if test "$xc_tst_dirs_sem" -eq "$xc_tst_dirs_col"; then + dnl When both counting methods give the same result we do not want to + dnl chose one over the other, and consider auto-detection not possible. if test -z "$PATH_SEPARATOR"; then - # User should provide the correct 'PATH_SEPARATOR' definition. - # Until then, guess that it is colon! + dnl User should provide the correct 'PATH_SEPARATOR' definition. + dnl Until then, guess that it is colon! echo "$xc_msg_warn path separator not determined, guessing colon" >&2 PATH_SEPARATOR=':' fi else - # Separator with the greater directory count is the auto-detected one. - if test $xc_tst_dirs_sem -gt $xc_tst_dirs_col; then + dnl Separator with the greater directory count is the auto-detected one. + if test "$xc_tst_dirs_sem" -gt "$xc_tst_dirs_col"; then xc_tst_auto_separator=';' else xc_tst_auto_separator=':' fi if test -z "$PATH_SEPARATOR"; then - # Simply use the auto-detected one when not already set. + dnl Use the auto-detected one when not already set. PATH_SEPARATOR=$xc_tst_auto_separator elif test "x$PATH_SEPARATOR" != "x$xc_tst_auto_separator"; then echo "$xc_msg_warn 'PATH_SEPARATOR' does not match auto-detected one." >&2 fi fi xc_PATH_SEPARATOR=$PATH_SEPARATOR -AC_SUBST([PATH_SEPARATOR])dnl +AC_SUBST([PATH_SEPARATOR]) ]) @@ -426,19 +426,19 @@ dnl ------------------------------------------------- dnl Private macro. AC_DEFUN([_XC_CFG_PRE_POSTLUDE], -[dnl -AC_REQUIRE([_XC_CFG_PRE_PREAMBLE])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_ECHO])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_TEST])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_VAR_PATH])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_EXPR])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_SED])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_GREP])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_TR])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_WC])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_CAT])dnl -AC_REQUIRE([_XC_CFG_PRE_CHECK_PATH_SEPARATOR])dnl -dnl +[ +AC_REQUIRE([_XC_CFG_PRE_PREAMBLE]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_ECHO]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_TEST]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_VAR_PATH]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_EXPR]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_SED]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_GREP]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_TR]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_WC]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_CAT]) +AC_REQUIRE([_XC_CFG_PRE_CHECK_PATH_SEPARATOR]) + xc_configure_preamble_result='yes' ]) @@ -448,7 +448,7 @@ dnl ------------------------------------------------- dnl Public macro. dnl dnl This macro emits shell code which does some -dnl very basic checks related with the availability +dnl basic checks related with the availability dnl of some commands and utilities needed to allow dnl configure script bootstrapping itself when using dnl these to figure out other settings. Also emits @@ -471,58 +471,58 @@ dnl script is making it override autoconf and libtool dnl PATH_SEPARATOR check. AC_DEFUN([XC_CONFIGURE_PREAMBLE], -[dnl -AC_PREREQ([2.50])dnl -dnl -AC_BEFORE([$0],[_XC_CFG_PRE_PREAMBLE])dnl -AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_CMD_ECHO])dnl -AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_CMD_TEST])dnl -AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_VAR_PATH])dnl -AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_CMD_EXPR])dnl -AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_UTIL_SED])dnl -AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_UTIL_GREP])dnl -AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_UTIL_TR])dnl -AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_UTIL_WC])dnl -AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_UTIL_CAT])dnl -AC_BEFORE([$0],[_XC_CFG_PRE_CHECK_PATH_SEPARATOR])dnl -AC_BEFORE([$0],[_XC_CFG_PRE_POSTLUDE])dnl -dnl -AC_BEFORE([$0],[AC_CHECK_TOOL])dnl -AC_BEFORE([$0],[AC_CHECK_PROG])dnl -AC_BEFORE([$0],[AC_CHECK_TOOLS])dnl -AC_BEFORE([$0],[AC_CHECK_PROGS])dnl -dnl -AC_BEFORE([$0],[AC_PATH_TOOL])dnl -AC_BEFORE([$0],[AC_PATH_PROG])dnl -AC_BEFORE([$0],[AC_PATH_PROGS])dnl -dnl -AC_BEFORE([$0],[AC_PROG_SED])dnl -AC_BEFORE([$0],[AC_PROG_GREP])dnl -AC_BEFORE([$0],[AC_PROG_LN_S])dnl -AC_BEFORE([$0],[AC_PROG_MKDIR_P])dnl -AC_BEFORE([$0],[AC_PROG_INSTALL])dnl -AC_BEFORE([$0],[AC_PROG_MAKE_SET])dnl -AC_BEFORE([$0],[AC_PROG_LIBTOOL])dnl -dnl -AC_BEFORE([$0],[LT_INIT])dnl -AC_BEFORE([$0],[AM_INIT_AUTOMAKE])dnl -AC_BEFORE([$0],[AC_LIBTOOL_WIN32_DLL])dnl -dnl -AC_REQUIRE([_XC_CFG_PRE_PREAMBLE])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_ECHO])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_TEST])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_VAR_PATH])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_EXPR])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_SED])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_GREP])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_TR])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_WC])dnl -AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_CAT])dnl -AC_REQUIRE([_XC_CFG_PRE_CHECK_PATH_SEPARATOR])dnl -AC_REQUIRE([_XC_CFG_PRE_POSTLUDE])dnl -dnl -m4_pattern_forbid([^_*XC])dnl -m4_define([$0],[])dnl +[ +AC_PREREQ([2.50]) + +AC_BEFORE([$0],[_XC_CFG_PRE_PREAMBLE]) +AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_CMD_ECHO]) +AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_CMD_TEST]) +AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_VAR_PATH]) +AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_CMD_EXPR]) +AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_UTIL_SED]) +AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_UTIL_GREP]) +AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_UTIL_TR]) +AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_UTIL_WC]) +AC_BEFORE([$0],[_XC_CFG_PRE_BASIC_CHK_UTIL_CAT]) +AC_BEFORE([$0],[_XC_CFG_PRE_CHECK_PATH_SEPARATOR]) +AC_BEFORE([$0],[_XC_CFG_PRE_POSTLUDE]) + +AC_BEFORE([$0],[AC_CHECK_TOOL]) +AC_BEFORE([$0],[AC_CHECK_PROG]) +AC_BEFORE([$0],[AC_CHECK_TOOLS]) +AC_BEFORE([$0],[AC_CHECK_PROGS]) + +AC_BEFORE([$0],[AC_PATH_TOOL]) +AC_BEFORE([$0],[AC_PATH_PROG]) +AC_BEFORE([$0],[AC_PATH_PROGS]) + +AC_BEFORE([$0],[AC_PROG_SED]) +AC_BEFORE([$0],[AC_PROG_GREP]) +AC_BEFORE([$0],[AC_PROG_LN_S]) +AC_BEFORE([$0],[AC_PROG_MKDIR_P]) +AC_BEFORE([$0],[AC_PROG_INSTALL]) +AC_BEFORE([$0],[AC_PROG_MAKE_SET]) +AC_BEFORE([$0],[AC_PROG_LIBTOOL]) + +AC_BEFORE([$0],[LT_INIT]) +AC_BEFORE([$0],[AM_INIT_AUTOMAKE]) +AC_BEFORE([$0],[AC_LIBTOOL_WIN32_DLL]) + +AC_REQUIRE([_XC_CFG_PRE_PREAMBLE]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_ECHO]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_TEST]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_VAR_PATH]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_CMD_EXPR]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_SED]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_GREP]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_TR]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_WC]) +AC_REQUIRE([_XC_CFG_PRE_BASIC_CHK_UTIL_CAT]) +AC_REQUIRE([_XC_CFG_PRE_CHECK_PATH_SEPARATOR]) +AC_REQUIRE([_XC_CFG_PRE_POSTLUDE]) + +m4_pattern_forbid([^_*XC]) +m4_define([$0],[]) ]) @@ -540,13 +540,13 @@ dnl Override when using autoconf 2.53 and newer. dnl m4_ifdef([_AS_PATH_SEPARATOR_PREPARE], -[dnl -m4_undefine([_AS_PATH_SEPARATOR_PREPARE])dnl +[ +m4_undefine([_AS_PATH_SEPARATOR_PREPARE]) m4_defun([_AS_PATH_SEPARATOR_PREPARE], -[dnl -AC_REQUIRE([XC_CONFIGURE_PREAMBLE])dnl -m4_define([$0],[])dnl -])dnl +[ +AC_REQUIRE([XC_CONFIGURE_PREAMBLE]) +m4_define([$0],[]) +]) ]) dnl @@ -554,14 +554,14 @@ dnl Override when using autoconf 2.50 to 2.52 dnl m4_ifdef([_AC_INIT_PREPARE_FS_SEPARATORS], -[dnl -m4_undefine([_AC_INIT_PREPARE_FS_SEPARATORS])dnl +[ +m4_undefine([_AC_INIT_PREPARE_FS_SEPARATORS]) m4_defun([_AC_INIT_PREPARE_FS_SEPARATORS], -[dnl -AC_REQUIRE([XC_CONFIGURE_PREAMBLE])dnl +[ +AC_REQUIRE([XC_CONFIGURE_PREAMBLE]) ac_path_separator=$PATH_SEPARATOR -m4_define([$0],[])dnl -])dnl +m4_define([$0],[]) +]) ]) dnl @@ -569,14 +569,14 @@ dnl Override when using libtool 1.4.2 dnl m4_ifdef([_LT_AC_LIBTOOL_SYS_PATH_SEPARATOR], -[dnl -m4_undefine([_LT_AC_LIBTOOL_SYS_PATH_SEPARATOR])dnl +[ +m4_undefine([_LT_AC_LIBTOOL_SYS_PATH_SEPARATOR]) m4_defun([_LT_AC_LIBTOOL_SYS_PATH_SEPARATOR], -[dnl -AC_REQUIRE([XC_CONFIGURE_PREAMBLE])dnl +[ +AC_REQUIRE([XC_CONFIGURE_PREAMBLE]) lt_cv_sys_path_separator=$PATH_SEPARATOR -m4_define([$0],[])dnl -])dnl +m4_define([$0],[]) +]) ]) @@ -602,7 +602,7 @@ dnl overrides the auto-detected one. dnl dnl Strictly speaking the check is done in two steps. The dnl first, which does the actual check, takes place in -dnl XC_CONFIGURE_PREAMBLE macro and happens very early in +dnl XC_CONFIGURE_PREAMBLE macro and happens early in dnl generated configure script. The second one shows and dnl logs the result of the check into config.log at a later dnl configure stage. Placement of this second stage in @@ -610,43 +610,43 @@ dnl generated configure script will be done where first dnl direct or indirect usage of this macro happens. AC_DEFUN([XC_CHECK_PATH_SEPARATOR], -[dnl -AC_PREREQ([2.50])dnl +[ +AC_PREREQ([2.50]) + +AC_BEFORE([$0],[AC_CHECK_TOOL]) +AC_BEFORE([$0],[AC_CHECK_PROG]) +AC_BEFORE([$0],[AC_CHECK_TOOLS]) +AC_BEFORE([$0],[AC_CHECK_PROGS]) + +AC_BEFORE([$0],[AC_PATH_TOOL]) +AC_BEFORE([$0],[AC_PATH_PROG]) +AC_BEFORE([$0],[AC_PATH_PROGS]) + +AC_BEFORE([$0],[AC_PROG_SED]) +AC_BEFORE([$0],[AC_PROG_GREP]) +AC_BEFORE([$0],[AC_PROG_LN_S]) +AC_BEFORE([$0],[AC_PROG_MKDIR_P]) +AC_BEFORE([$0],[AC_PROG_INSTALL]) +AC_BEFORE([$0],[AC_PROG_MAKE_SET]) +AC_BEFORE([$0],[AC_PROG_LIBTOOL]) + +AC_BEFORE([$0],[LT_INIT]) +AC_BEFORE([$0],[AM_INIT_AUTOMAKE]) +AC_BEFORE([$0],[AC_LIBTOOL_WIN32_DLL]) + +AC_REQUIRE([XC_CONFIGURE_PREAMBLE]) + dnl -AC_BEFORE([$0],[AC_CHECK_TOOL])dnl -AC_BEFORE([$0],[AC_CHECK_PROG])dnl -AC_BEFORE([$0],[AC_CHECK_TOOLS])dnl -AC_BEFORE([$0],[AC_CHECK_PROGS])dnl +dnl Check that 'XC_CONFIGURE_PREAMBLE' has already run. dnl -AC_BEFORE([$0],[AC_PATH_TOOL])dnl -AC_BEFORE([$0],[AC_PATH_PROG])dnl -AC_BEFORE([$0],[AC_PATH_PROGS])dnl -dnl -AC_BEFORE([$0],[AC_PROG_SED])dnl -AC_BEFORE([$0],[AC_PROG_GREP])dnl -AC_BEFORE([$0],[AC_PROG_LN_S])dnl -AC_BEFORE([$0],[AC_PROG_MKDIR_P])dnl -AC_BEFORE([$0],[AC_PROG_INSTALL])dnl -AC_BEFORE([$0],[AC_PROG_MAKE_SET])dnl -AC_BEFORE([$0],[AC_PROG_LIBTOOL])dnl -dnl -AC_BEFORE([$0],[LT_INIT])dnl -AC_BEFORE([$0],[AM_INIT_AUTOMAKE])dnl -AC_BEFORE([$0],[AC_LIBTOOL_WIN32_DLL])dnl -dnl -AC_REQUIRE([XC_CONFIGURE_PREAMBLE])dnl -dnl -# -# Check that 'XC_CONFIGURE_PREAMBLE' has already run. -# if test -z "$xc_configure_preamble_result"; then AC_MSG_ERROR([xc_configure_preamble_result not set (internal problem)]) fi -# -# Check that 'PATH_SEPARATOR' has already been set. -# +dnl +dnl Check that 'PATH_SEPARATOR' has already been set. +dnl if test -z "$xc_PATH_SEPARATOR"; then AC_MSG_ERROR([xc_PATH_SEPARATOR not set (internal problem)]) @@ -661,7 +661,7 @@ if test "x$PATH_SEPARATOR" != "x$xc_PATH_SEPARATOR"; then AC_MSG_RESULT([$xc_PATH_SEPARATOR]) AC_MSG_ERROR([path separator mismatch (internal or config.site problem)]) fi -dnl -m4_pattern_forbid([^_*XC])dnl -m4_define([$0],[])dnl + +m4_pattern_forbid([^_*XC]) +m4_define([$0],[]) ]) diff --git a/m4/zz50-xc-ovr.m4 b/m4/zz50-xc-ovr.m4 index 735df2f59b..155eb7f9b4 100644 --- a/m4/zz50-xc-ovr.m4 +++ b/m4/zz50-xc-ovr.m4 @@ -20,7 +20,7 @@ # #--------------------------------------------------------------------------- -# serial 1 +dnl serial 1 dnl The funny name of this file is intentional in order to make it @@ -35,7 +35,7 @@ dnl ------------------------------------------------- dnl This is done to prevent Libtool 1.5.X from doing dnl unnecessary C++, Fortran and Java tests when only dnl using C language and reduce resulting configure -dnl script by nearly 300 Kb. +dnl script by nearly 300 KB. m4_ifdef([AC_LIBTOOL_LANG_CXX_CONFIG], [m4_undefine([AC_LIBTOOL_LANG_CXX_CONFIG])]) diff --git a/m4/zz60-xc-ovr.m4 b/m4/zz60-xc-ovr.m4 deleted file mode 100644 index d1d3a2fd50..0000000000 --- a/m4/zz60-xc-ovr.m4 +++ /dev/null @@ -1,65 +0,0 @@ -#--------------------------------------------------------------------------- -# -# zz60-xc-ovr.m4 -# -# Copyright (C) Daniel Stenberg, -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -# -# SPDX-License-Identifier: ISC -# -#--------------------------------------------------------------------------- - -# serial 1 - - -dnl The funny name of this file is intentional in order to make it -dnl sort alphabetically after any libtool, autoconf or automake -dnl provided .m4 macro file that might get copied into this same -dnl subdirectory. This allows that macro (re)definitions from this -dnl file may override those provided in other files. - - -dnl Override an autoconf provided macro -dnl ------------------------------------------------- -dnl This macro overrides the one provided by autoconf -dnl 2.58 or newer, and provides macro definition for -dnl autoconf 2.57 or older which lack it. This allows -dnl using libtool 2.2 or newer, which requires that -dnl this macro is used in configure.ac, with autoconf -dnl 2.57 or older. - -m4_ifdef([AC_CONFIG_MACRO_DIR], -[dnl -m4_undefine([AC_CONFIG_MACRO_DIR])dnl -]) -m4_define([AC_CONFIG_MACRO_DIR],[]) - - -dnl XC_OVR_ZZ60 -dnl ------------------------------------------------- -dnl Placing a call to this macro in configure.ac will -dnl make macros in this file visible to other macros -dnl used for same configure script, overriding those -dnl provided elsewhere. - -AC_DEFUN([XC_OVR_ZZ60], -[dnl -AC_BEFORE([$0],[LT_INIT])dnl -AC_BEFORE([$0],[AM_INIT_AUTOMAKE])dnl -AC_BEFORE([$0],[AC_LIBTOOL_WIN32_DLL])dnl -AC_BEFORE([$0],[AC_PROG_LIBTOOL])dnl -dnl -AC_BEFORE([$0],[AC_CONFIG_MACRO_DIR])dnl -AC_BEFORE([$0],[AC_CONFIG_MACRO_DIRS])dnl -]) diff --git a/packages/README.md b/packages/README.md deleted file mode 100644 index f52f8e477f..0000000000 --- a/packages/README.md +++ /dev/null @@ -1,12 +0,0 @@ - - -# Packages - - This directory and all its subdirectories are for special package -information, templates, scripts and docs. The files herein should be of use -for those of you who want to package curl in a binary or source format for -these platforms. diff --git a/packages/vms/curlmsg.h b/packages/vms/curlmsg.h deleted file mode 100644 index 9b5c4c76d1..0000000000 --- a/packages/vms/curlmsg.h +++ /dev/null @@ -1,143 +0,0 @@ -#ifndef HEADER_CURLMSG_H -#define HEADER_CURLMSG_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 - * - ***************************************************************************/ - -#pragma __member_alignment __save -#pragma __nomember_alignment - -/* */ -/* CURLMSG.H */ -/* */ -/* SDL File Generated by VAX-11 Message V04-00 on 3-SEP-2008 13:33:54.09 */ -/* */ -/* THESE VMS ERROR CODES ARE GENERATED BY TAKING APART THE CURL.H */ -/* FILE AND PUTTING ALL THE CURLE_* ENUM STUFF INTO THIS FILE, */ -/* CURLMSG.MSG. AN .SDL FILE IS CREATED FROM THIS FILE WITH */ -/* MESSAGE/SDL. THE .H FILE IS CREATED USING THE FREEWARE SDL TOOL */ -/* AGAINST THE .SDL FILE WITH SDL/ALPHA/LANG=CC COMMAND. */ -/* */ -/* WITH THE EXCEPTION OF CURLE_OK, ALL OF THE MESSAGES ARE AT */ -/* THE ERROR SEVERITY LEVEL. WITH THE EXCEPTION OF */ -/* PEER_FAILED_VERIF, WHICH IS A SHORTENED FORM OF */ -/* PEER_FAILED_VERIFICATION, THESE ARE THE SAME NAMES AS THE */ -/* CURLE_ ONES IN INCLUDE/CURL.H. THE MESSAGE UTILITY MANUAL STATES */ -/* "THE COMBINED LENGTH OF THE PREFIX AND THE MESSAGE SYMBOL NAME CANNOT */ -/* EXCEED 31 CHARACTERS." WITH A PREFIX OF FIVE THAT LEAVES US WITH 26 */ -/* FOR THE MESSAGE NAME. */ -/* */ -/* IF YOU UPDATE THIS FILE, UPDATE CURLMSG_VMS.H SO THAT THEY ARE IN SYNC */ -/* */ - -#define CURL_FACILITY 3841 -#define CURL_OK 251756553 -#define CURL_UNSUPPORTED_PROTOCOL 251756562 -#define CURL_FAILED_INIT 251756570 -#define CURL_URL_MALFORMAT 251756578 -#define CURL_OBSOLETE4 251756586 -#define CURL_COULDNT_RESOLVE_PROXY 251756594 -#define CURL_COULDNT_RESOLVE_HOST 251756602 -#define CURL_COULDNT_CONNECT 251756610 -#define CURL_WEIRD_SERVER_REPLY 251756618 -#define CURL_FTP_WEIRD_SERVER_REPLY CURL_WEIRD_SERVER_REPLY -#define CURL_FTP_ACCESS_DENIED 251756626 -#define CURL_OBSOLETE10 251756634 -#define CURL_FTP_WEIRD_PASS_REPLY 251756642 -#define CURL_OBSOLETE12 251756650 -#define CURL_FTP_WEIRD_PASV_REPLY 251756658 -#define CURL_FTP_WEIRD_227_FORMAT 251756666 -#define CURL_FTP_CANT_GET_HOST 251756674 -#define CURL_OBSOLETE16 251756682 -#define CURL_FTP_COULDNT_SET_TYPE 251756690 -#define CURL_PARTIAL_FILE 251756698 -#define CURL_FTP_COULDNT_RETR_FILE 251756706 -#define CURL_OBSOLETE20 251756714 -#define CURL_QUOTE_ERROR 251756722 -#define CURL_HTTP_RETURNED_ERROR 251756730 -#define CURL_WRITE_ERROR 251756738 -#define CURL_OBSOLETE24 251756746 -#define CURL_UPLOAD_FAILED 251756754 -#define CURL_READ_ERROR 251756762 -#define CURL_OUT_OF_MEMORY 251756770 -#define CURL_OPERATION_TIMEOUTED 251756778 -#define CURL_OBSOLETE29 251756786 -#define CURL_FTP_PORT_FAILED 251756794 -#define CURL_FTP_COULDNT_USE_REST 251756802 -#define CURL_OBSOLETE32 251756810 -#define CURL_RANGE_ERROR 251756818 -#define CURL_HTTP_POST_ERROR 251756826 -#define CURL_SSL_CONNECT_ERROR 251756834 -#define CURL_BAD_DOWNLOAD_RESUME 251756842 -#define CURL_FILE_COULDNT_READ_FILE 251756850 -#define CURL_LDAP_CANNOT_BIND 251756858 -#define CURL_LDAP_SEARCH_FAILED 251756866 -#define CURL_OBSOLETE40 251756874 -#define CURL_FUNCTION_NOT_FOUND 251756882 -#define CURL_ABORTED_BY_CALLBACK 251756890 -#define CURL_BAD_FUNCTION_ARGUMENT 251756898 -#define CURL_OBSOLETE44 251756906 -#define CURL_INTERFACE_FAILED 251756914 -#define CURL_OBSOLETE46 251756922 -#define CURL_TOO_MANY_REDIRECTS 251756930 -#define CURL_UNKNOWN_TELNET_OPTION 251756938 -#define CURL_TELNET_OPTION_SYNTAX 251756946 -#define CURL_OBSOLETE50 251756954 -#define CURL_PEER_FAILED_VERIF 251756962 -#define CURL_GOT_NOTHING 251756970 -#define CURL_SSL_ENGINE_NOTFOUND 251756978 -#define CURL_SSL_ENGINE_SETFAILED 251756986 -#define CURL_SEND_ERROR 251756994 -#define CURL_RECV_ERROR 251757002 -#define CURL_OBSOLETE57 251757010 -#define CURL_SSL_CERTPROBLEM 251757018 -#define CURL_SSL_CIPHER 251757026 -#define CURL_SSL_CACERT 251757034 -#define CURL_BAD_CONTENT_ENCODING 251757042 -#define CURL_LDAP_INVALID_URL 251757050 -#define CURL_FILESIZE_EXCEEDED 251757058 -#define CURL_USE_SSL_FAILED 251757066 -#define CURL_SEND_FAIL_REWIND 251757074 -#define CURL_SSL_ENGINE_INITFAILED 251757082 -#define CURL_LOGIN_DENIED 251757090 -#define CURL_TFTP_NOTFOUND 251757098 -#define CURL_TFTP_PERM 251757106 -#define CURL_REMOTE_DISK_FULL 251757114 -#define CURL_TFTP_ILLEGAL 251757122 -#define CURL_TFTP_UNKNOWNID 251757130 -#define CURL_REMOTE_FILE_EXISTS 251757138 -#define CURL_TFTP_NOSUCHUSER 251757146 -#define CURL_CONV_FAILED 251757154 -#define CURL_CONV_REQD 251757162 -#define CURL_SSL_CACERT_BADFILE 251757170 -#define CURL_REMOTE_FILE_NOT_FOUND 251757178 -#define CURL_SSH 251757186 -#define CURL_SSL_SHUTDOWN_FAILED 251757194 -#define CURL_AGAIN 251757202 -#define CURL_SSL_CRL_BADFILE 251757210 -#define CURL_SSL_ISSUER_ERROR 251757218 -#define CURL_CURL_LAST 251757226 - -#pragma __member_alignment __restore - -#endif /* HEADER_CURLMSG_H */ diff --git a/plan9/README b/plan9/README deleted file mode 100644 index 6df23d31a7..0000000000 --- a/plan9/README +++ /dev/null @@ -1,55 +0,0 @@ -Prerequirement -============== -This document describes how to compile, build and install curl and libcurl -from sources using mk. To build it, you will require to install latest -9legacy patches into Plan 9. Also Plan 9 still have no configuration option so -both zlib and libopenssl are required too. - -The zlib that is available on Plan 9 can be downloaded from: - - https://github.com/madler/zlib/pull/398 - -LibreSSL Portable can be downloaded from: - - https://github.com/libressl-portable/portable/pull/510 - -Instruction -=========== -First, you should construct namespace as like described below: - -% bind -ac ../lib lib -% bind -ac ../src src -% bind -ac ../include include -% bind -ac .. . - -Then you will see as shown below (excerpt): - - curl.git/ - |_plan9 - | |_BUILD.PLAN9.txt - | |_CHANGES - | |_CMake - | | : - | |_mkfile - | |_mkfile.proto - | |_include - | | |_Makefile.am - | | | : - | | |_mkfile - | |_lib - | | |_CMakeLists.txt - | | | : - | | |_mkfile - | | |_mkfile.inc - | |_src - | | |_CMakeLists.txt - | | | : - | | |_mkfile - | | |_mkfile.inc - |_lib - |_src - -After constructing namespace, you can run mk on plan9 directory. - -% mk -% mk install diff --git a/plan9/include/mkfile b/plan9/include/mkfile deleted file mode 100644 index a0970e932a..0000000000 --- a/plan9/include/mkfile +++ /dev/null @@ -1,36 +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 -# -########################################################################### - -DIR=/sys/include/ape/curl -HFILES=`{ls curl/*.h} - -all:V: $HFILES - -install:V: all - mkdir -p $DIR - cp curl/*.h $DIR/ - -clean:V: $HFILES # do nothing - -nuke:V: clean diff --git a/plan9/lib/mkfile b/plan9/lib/mkfile deleted file mode 100644 index 04b54a8378..0000000000 --- a/plan9/lib/mkfile +++ /dev/null @@ -1,41 +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 -# -########################################################################### - -<../mkfile.proto -<|mkfile.inc - -CFLAGS=$CFLAGS -I../include -I. -c - -OFILES=${CSOURCES:%.c=%.$O} -HFILES=$HHEADERS -LIB=/$objtype/lib/ape/libcurl.a - -CLEANFILES=\ - ${LIB_VAUTH_CFILES:%.c=%.$O}\ - ${LIB_VTLS_CFILES:%.c=%.$O}\ - -, 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 -# -########################################################################### - -# rename $(VAR) -> $VAR -sed 's/\$\(([A-Z_]+)\)/$\1/g' Makefile.inc diff --git a/plan9/mkfile b/plan9/mkfile deleted file mode 100644 index 2133a49702..0000000000 --- a/plan9/mkfile +++ /dev/null @@ -1,38 +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 -# -########################################################################### - -, 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 -# -########################################################################### - -, 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 -# -########################################################################### - -# rename $(VAR) -> $VAR -sed 's/\$\(([A-Z_]+)\)/$\1/g' Makefile.inc diff --git a/packages/Makefile.am b/projects/Makefile.am similarity index 97% rename from packages/Makefile.am rename to projects/Makefile.am index 43e544a019..bf7f3a05ab 100644 --- a/packages/Makefile.am +++ b/projects/Makefile.am @@ -24,6 +24,7 @@ SUBDIRS = vms EXTRA_DIST = README.md \ + OS400/.checksrc \ OS400/README.OS400 \ OS400/rpg-examples \ OS400/ccsidcurl.c \ @@ -49,7 +50,7 @@ CS_1 = CS_ = $(CS_0) checksrc: - $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) $(srcdir)/OS400/*.[ch]) + $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir)/OS400 $(srcdir)/OS400/*.[ch]) if NOT_CURL_CI if DEBUGBUILD diff --git a/projects/OS400/.checksrc b/projects/OS400/.checksrc new file mode 100644 index 0000000000..3bd88ff1e7 --- /dev/null +++ b/projects/OS400/.checksrc @@ -0,0 +1,9 @@ +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + +# Possibly not what we want, but cannot test, just silence the warnings +allowfunc calloc +allowfunc free +allowfunc malloc +allowfunc realloc diff --git a/packages/OS400/README.OS400 b/projects/OS400/README.OS400 similarity index 98% rename from packages/OS400/README.OS400 rename to projects/OS400/README.OS400 index e7699e1686..5d4b643a5e 100644 --- a/packages/OS400/README.OS400 +++ b/projects/OS400/README.OS400 @@ -225,7 +225,7 @@ must be released with curl_free(). Standard compilation environment does support neither autotools nor make; in fact, few common utilities are available. As a consequence, the config-os400.h has been coded manually and the compilation scripts are a set of shell scripts -stored in subdirectory packages/OS400. +stored in subdirectory projects/OS400. The test environment is currently not supported on OS/400. @@ -234,26 +234,20 @@ Protocols currently implemented on OS/400: _ DICT _ FILE _ FTP -_ FTPS -_ FTP with secure transmission _ GOPHER _ HTTP -_ HTTPS _ IMAP -_ IMAPS -_ IMAP with secure transmission _ LDAP +_ MQTT _ POP3 -_ POP3S -_ POP3 with secure transmission _ RTSP _ SCP if libssh2 is enabled _ SFTP if libssh2 is enabled +_ SMB _ SMTP -_ SMTPS -_ SMTP with secure transmission _ TELNET _ TFTP +_ WS Compiling on OS/400: @@ -273,7 +267,7 @@ _ Install the curl source directory in IFS. Do NOT install it in the installation target directory (which defaults to /curl). _ Enter Qshell (QSH, not PASE) _ Change current directory to the curl installation directory -_ Change current directory to ./packages/OS400 +_ Change current directory to ./projects/OS400 - If you want to change the default configuration parameters like debug info generation, optimization level, listing option, target library, ZLIB/LIBSSH2 availability and location, etc., copy file config400.default to diff --git a/packages/OS400/ccsidcurl.c b/projects/OS400/ccsidcurl.c similarity index 70% rename from packages/OS400/ccsidcurl.c rename to projects/OS400/ccsidcurl.c index a9c04d5a42..0982eed639 100644 --- a/packages/OS400/ccsidcurl.c +++ b/projects/OS400/ccsidcurl.c @@ -46,20 +46,17 @@ #include "os400sys.h" #ifndef SIZE_MAX -#define SIZE_MAX ((size_t) ~0) /* Is unsigned on OS/400. */ +#define SIZE_MAX ((size_t)~0) /* Is unsigned on OS/400. */ #endif +#define ASCII_CCSID 819 /* Use ISO-8859-1 as ASCII. */ +#define NOCONV_CCSID 65535 /* No conversion. */ +#define ICONV_ID_SIZE 32 /* Size of iconv_open() code identifier. */ +#define ICONV_OPEN_ERROR(t) ((t).return_value == -1) -#define ASCII_CCSID 819 /* Use ISO-8859-1 as ASCII. */ -#define NOCONV_CCSID 65535 /* No conversion. */ -#define ICONV_ID_SIZE 32 /* Size of iconv_open() code identifier. */ -#define ICONV_OPEN_ERROR(t) ((t).return_value == -1) +#define ALLOC_GRANULE 8 /* Alloc. granule for curl_formadd_ccsid(). */ -#define ALLOC_GRANULE 8 /* Alloc. granule for curl_formadd_ccsid(). */ - - -static void -makeOS400IconvCode(char buf[ICONV_ID_SIZE], unsigned int ccsid) +static void makeOS400IconvCode(char buf[ICONV_ID_SIZE], unsigned int ccsid) { /** *** Convert a CCSID to the corresponding IBM iconv_open() character @@ -78,10 +75,8 @@ makeOS400IconvCode(char buf[ICONV_ID_SIZE], unsigned int ccsid) curl_msprintf(buf, "IBMCCSID%05u0000000", ccsid); } - -static iconv_t -iconv_open_CCSID(unsigned int ccsidout, unsigned int ccsidin, - unsigned int cstr) +static iconv_t iconv_open_CCSID(unsigned int ccsidout, unsigned int ccsidin, + unsigned int cstr) { char fromcode[ICONV_ID_SIZE]; char tocode[ICONV_ID_SIZE]; @@ -95,18 +90,16 @@ iconv_open_CCSID(unsigned int ccsidout, unsigned int ccsidin, makeOS400IconvCode(fromcode, ccsidin); makeOS400IconvCode(tocode, ccsidout); - memset(tocode + 13, 0, sizeof(tocode) - 13); /* Dest. code id format. */ + memset(tocode + 13, 0, sizeof(tocode) - 13); /* Dest. code id format. */ if(cstr) - fromcode[18] = '1'; /* Set null-terminator flag. */ + fromcode[18] = '1'; /* Set null-terminator flag. */ return iconv_open(tocode, fromcode); } - -static int -convert(char *d, size_t dlen, int dccsid, - const char *s, int slen, int sccsid) +static int convert(char *d, size_t dlen, int dccsid, const char *s, int slen, + int sccsid) { int i; iconv_t cd; @@ -134,23 +127,23 @@ convert(char *d, size_t dlen, int dccsid, memcpy(d, s, i); return i; - } + } if(slen < 0) { lslen = 0; cd = iconv_open_CCSID(dccsid, sccsid, 1); - } + } else { - lslen = (size_t) slen; + lslen = (size_t)slen; cd = iconv_open_CCSID(dccsid, sccsid, 0); - } + } if(ICONV_OPEN_ERROR(cd)) return -1; i = dlen; - if((int) iconv(cd, (char * *) &s, &lslen, &d, &dlen) < 0) + if((int)iconv(cd, (char **)&s, &lslen, &d, &dlen) < 0) i = -1; else i -= dlen; @@ -159,8 +152,8 @@ convert(char *d, size_t dlen, int dccsid, return i; } - -static char *dynconvert(int dccsid, const char *s, int slen, int sccsid) +static char *dynconvert(int dccsid, const char *s, int slen, int sccsid, + int *olen) { char *d; char *cp; @@ -170,19 +163,19 @@ static char *dynconvert(int dccsid, const char *s, int slen, int sccsid) /* Like convert, but the destination is allocated and returned. */ - dlen = (size_t) (slen < 0 ? strlen(s) : slen) + 1; - dlen *= MAX_CONV_EXPANSION; /* Allow some expansion. */ + dlen = (size_t)(slen < 0 ? strlen(s) : slen) + 1; + dlen *= MAX_CONV_EXPANSION; /* Allow some expansion. */ d = malloc(dlen); if(!d) - return (char *) NULL; + return (char *)NULL; l = convert(d, dlen, dccsid, s, slen, sccsid); if(l < 0) { free(d); - return (char *) NULL; - } + return (char *)NULL; + } if(slen < 0) { /* Need to null-terminate even when source length is given. @@ -193,35 +186,36 @@ static char *dynconvert(int dccsid, const char *s, int slen, int sccsid) if(l2 < 0) { free(d); - return (char *) NULL; - } - - l += l2; + return (char *)NULL; } - if((size_t) l < dlen) { - cp = realloc(d, l); /* Shorten to minimum needed. */ + l += l2; + } + + if((size_t)l < dlen) { + cp = realloc(d, l); /* Shorten to minimum needed. */ if(cp) d = cp; - } + } + if(olen) + *olen = l; return d; } - -static struct curl_slist * -slist_convert(int dccsid, struct curl_slist *from, int sccsid) +static struct curl_slist *slist_convert(int dccsid, struct curl_slist *from, + int sccsid) { - struct curl_slist *to = (struct curl_slist *) NULL; + struct curl_slist *to = (struct curl_slist *)NULL; for(; from; from = from->next) { struct curl_slist *nl; - char *cp = dynconvert(dccsid, from->data, -1, sccsid); + char *cp = dynconvert(dccsid, from->data, -1, sccsid, NULL); if(!cp) { curl_slist_free_all(to); - return (struct curl_slist *) NULL; + return (struct curl_slist *)NULL; } nl = Curl_slist_append_nodup(to, cp); if(!nl) { @@ -234,15 +228,14 @@ slist_convert(int dccsid, struct curl_slist *from, int sccsid) return to; } - -static char * -keyed_string(localkey_t key, const char *ascii, unsigned int ccsid) +static char *keyed_string(localkey_t key, const char *ascii, + unsigned int ccsid) { int i; char *ebcdic; if(!ascii) - return (char *) NULL; + return (char *)NULL; i = MAX_CONV_EXPANSION * (strlen(ascii) + 1); @@ -251,40 +244,32 @@ keyed_string(localkey_t key, const char *ascii, unsigned int ccsid) return ebcdic; if(convert(ebcdic, i, ccsid, ascii, -1, ASCII_CCSID) < 0) - return (char *) NULL; + return (char *)NULL; return ebcdic; } - -const char * -curl_to_ccsid(const char *s, unsigned int ccsid) +const char *curl_to_ccsid(const char *s, unsigned int ccsid) { if(s) - s = dynconvert(ccsid, s, -1, ASCII_CCSID); + s = dynconvert(ccsid, s, -1, ASCII_CCSID, NULL); return s; } - -const char * -curl_from_ccsid(const char *s, unsigned int ccsid) +const char *curl_from_ccsid(const char *s, unsigned int ccsid) { if(s) - s = dynconvert(ASCII_CCSID, s, -1, ccsid); + s = dynconvert(ASCII_CCSID, s, -1, ccsid, NULL); return s; } - -char * -curl_version_ccsid(unsigned int ccsid) +char *curl_version_ccsid(unsigned int ccsid) { return keyed_string(LK_CURL_VERSION, curl_version(), ccsid); } - -char * -curl_easy_escape_ccsid(CURL *handle, const char *string, int length, - unsigned int sccsid, unsigned int dccsid) +char *curl_easy_escape_ccsid(CURL *handle, const char *string, int length, + unsigned int sccsid, unsigned int dccsid) { char *s; char *d; @@ -292,30 +277,28 @@ curl_easy_escape_ccsid(CURL *handle, const char *string, int length, if(!string) { /* !checksrc! disable ERRNOVAR 1 */ errno = EINVAL; - return (char *) NULL; - } + return (char *)NULL; + } - s = dynconvert(ASCII_CCSID, string, length ? length : -1, sccsid); + s = dynconvert(ASCII_CCSID, string, length ? length : -1, sccsid, NULL); if(!s) - return (char *) NULL; + return (char *)NULL; d = curl_easy_escape(handle, s, 0); free(s); if(!d) - return (char *) NULL; + return (char *)NULL; - s = dynconvert(dccsid, d, -1, ASCII_CCSID); + s = dynconvert(dccsid, d, -1, ASCII_CCSID, NULL); free(d); return s; } - -char * -curl_easy_unescape_ccsid(CURL *handle, const char *string, int length, - int *outlength, - unsigned int sccsid, unsigned int dccsid) +char *curl_easy_unescape_ccsid(CURL *handle, const char *string, int length, + int *outlength, unsigned int sccsid, + unsigned int dccsid) { char *s; char *d; @@ -323,21 +306,21 @@ curl_easy_unescape_ccsid(CURL *handle, const char *string, int length, if(!string) { /* !checksrc! disable ERRNOVAR 1 */ errno = EINVAL; - return (char *) NULL; - } + return (char *)NULL; + } - s = dynconvert(ASCII_CCSID, string, length ? length : -1, sccsid); + s = dynconvert(ASCII_CCSID, string, length ? length : -1, sccsid, NULL); if(!s) - return (char *) NULL; + return (char *)NULL; d = curl_easy_unescape(handle, s, 0, outlength); free(s); if(!d) - return (char *) NULL; + return (char *)NULL; - s = dynconvert(dccsid, d, -1, ASCII_CCSID); + s = dynconvert(dccsid, d, -1, ASCII_CCSID, NULL); free(d); if(s && outlength) @@ -346,31 +329,29 @@ curl_easy_unescape_ccsid(CURL *handle, const char *string, int length, return s; } - -struct curl_slist * -curl_slist_append_ccsid(struct curl_slist *list, - const char *data, unsigned int ccsid) +struct curl_slist *curl_slist_append_ccsid(struct curl_slist *list, + const char *data, + unsigned int ccsid) { char *s; - s = (char *) NULL; + s = (char *)NULL; if(!data) return curl_slist_append(list, data); - s = dynconvert(ASCII_CCSID, data, -1, ccsid); + s = dynconvert(ASCII_CCSID, data, -1, ccsid, NULL); if(!s) - return (struct curl_slist *) NULL; + return (struct curl_slist *)NULL; list = curl_slist_append(list, s); free(s); return list; } - -time_t -curl_getdate_ccsid(const char *p, const time_t *unused, unsigned int ccsid) +time_t curl_getdate_ccsid(const char *p, const time_t *unused, + unsigned int ccsid) { char *s; time_t t; @@ -378,20 +359,18 @@ curl_getdate_ccsid(const char *p, const time_t *unused, unsigned int ccsid) if(!p) return curl_getdate(p, unused); - s = dynconvert(ASCII_CCSID, p, -1, ccsid); + s = dynconvert(ASCII_CCSID, p, -1, ccsid, NULL); if(!s) - return (time_t) -1; + return (time_t)-1; t = curl_getdate(s, unused); free(s); return t; } - -static int -convert_version_info_string(const char **stringp, - char **bufp, int *left, unsigned int ccsid) +static int convert_version_info_string(const char **stringp, char **bufp, + int *left, unsigned int ccsid) { /* Helper for curl_version_info_ccsid(): convert a string if defined. Result is stored in the `*left'-byte buffer at `*bufp'. @@ -407,14 +386,13 @@ convert_version_info_string(const char **stringp, *stringp = *bufp; *bufp += l; *left -= l; - } + } return 0; } - -curl_version_info_data * -curl_version_info_ccsid(CURLversion stamp, unsigned int ccsid) +curl_version_info_data *curl_version_info_ccsid(CURLversion stamp, + unsigned int ccsid) { curl_version_info_data *p; char *cp; @@ -454,7 +432,7 @@ curl_version_info_ccsid(CURLversion stamp, unsigned int ccsid) /* If caller has been compiled with a newer version, error. */ if(stamp > CURLVERSION_NOW) - return (curl_version_info_data *) NULL; + return (curl_version_info_data *)NULL; p = curl_version_info(stamp); @@ -471,10 +449,10 @@ curl_version_info_ccsid(CURLversion stamp, unsigned int ccsid) n += strlen(p->protocols[nproto++]); n += nproto++; - } + } for(i = 0; i < sizeof(charfields) / sizeof(charfields[0]); i++) { - cpp = (const char **) ((char *) p + charfields[i]); + cpp = (const char **)((char *)p + charfields[i]); if(*cpp) n += strlen(*cpp) + 1; } @@ -487,70 +465,60 @@ curl_version_info_ccsid(CURLversion stamp, unsigned int ccsid) n += nproto * sizeof(const char *); cp = Curl_thread_buffer(LK_VERSION_INFO_DATA, n); - id = (curl_version_info_data *) Curl_thread_buffer(LK_VERSION_INFO, - sizeof(*id)); + id = (curl_version_info_data *)Curl_thread_buffer(LK_VERSION_INFO, + sizeof(*id)); if(!id || !cp) - return (curl_version_info_data *) NULL; + return (curl_version_info_data *)NULL; /* Copy data and convert strings. */ - memcpy((char *) id, (char *) p, sizeof(*p)); + memcpy((char *)id, (char *)p, sizeof(*p)); if(id->protocols) { i = nproto * sizeof(id->protocols[0]); - id->protocols = (const char * const *) cp; - memcpy(cp, (char *) p->protocols, i); + id->protocols = (const char * const *)cp; + memcpy(cp, (char *)p->protocols, i); cp += i; n -= i; for(i = 0; id->protocols[i]; i++) - if(convert_version_info_string(((const char * *) id->protocols) + i, - &cp, &n, ccsid)) - return (curl_version_info_data *) NULL; + if(convert_version_info_string(((const char **)id->protocols) + i, + &cp, &n, ccsid)) + return (curl_version_info_data *)NULL; } for(i = 0; i < sizeof(charfields) / sizeof(charfields[0]); i++) { - cpp = (const char **) ((char *) p + charfields[i]); + cpp = (const char **)((char *)p + charfields[i]); if(*cpp && convert_version_info_string(cpp, &cp, &n, ccsid)) - return (curl_version_info_data *) NULL; + return (curl_version_info_data *)NULL; } return id; } - -const char * -curl_easy_strerror_ccsid(CURLcode error, unsigned int ccsid) +const char *curl_easy_strerror_ccsid(CURLcode error, unsigned int ccsid) { return keyed_string(LK_EASY_STRERROR, curl_easy_strerror(error), ccsid); } - -const char * -curl_share_strerror_ccsid(CURLSHcode error, unsigned int ccsid) +const char *curl_share_strerror_ccsid(CURLSHcode error, unsigned int ccsid) { return keyed_string(LK_SHARE_STRERROR, curl_share_strerror(error), ccsid); } - -const char * -curl_multi_strerror_ccsid(CURLMcode error, unsigned int ccsid) +const char *curl_multi_strerror_ccsid(CURLMcode error, unsigned int ccsid) { return keyed_string(LK_MULTI_STRERROR, curl_multi_strerror(error), ccsid); } - -const char * -curl_url_strerror_ccsid(CURLUcode error, unsigned int ccsid) +const char *curl_url_strerror_ccsid(CURLUcode error, unsigned int ccsid) { return keyed_string(LK_URL_STRERROR, curl_url_strerror(error), ccsid); } - -void -curl_certinfo_free_all(struct curl_certinfo *info) +void curl_certinfo_free_all(struct curl_certinfo *info) { /* Free all memory used by certificate info. */ if(info) { @@ -559,15 +527,13 @@ curl_certinfo_free_all(struct curl_certinfo *info) for(i = 0; i < info->num_of_certs; i++) curl_slist_free_all(info->certinfo[i]); - free((char *) info->certinfo); + free((char *)info->certinfo); } - free((char *) info); + free((char *)info); } } - -CURLcode -curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) +CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) { va_list arg; void *paramp; @@ -577,7 +543,7 @@ curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) /* WARNING: unlike curl_easy_getinfo(), the strings returned by this procedure have to be free'ed. */ - data = (struct Curl_easy *) curl; + data = (struct Curl_easy *)curl; va_start(arg, info); paramp = va_arg(arg, void *); ret = Curl_getinfo(data, info, paramp); @@ -589,14 +555,14 @@ curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) struct curl_certinfo *cipf; struct curl_certinfo *cipt; - switch((int) info & CURLINFO_TYPEMASK) { + switch((int)info & CURLINFO_TYPEMASK) { case CURLINFO_STRING: ccsid = va_arg(arg, unsigned int); - cpp = (char * *) paramp; + cpp = (char **)paramp; if(*cpp) { - *cpp = dynconvert(ccsid, *cpp, -1, ASCII_CCSID); + *cpp = dynconvert(ccsid, *cpp, -1, ASCII_CCSID, NULL); if(!*cpp) ret = CURLE_OUT_OF_MEMORY; @@ -608,15 +574,15 @@ curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) ccsid = va_arg(arg, unsigned int); switch(info) { case CURLINFO_CERTINFO: - cipf = *(struct curl_certinfo * *) paramp; + cipf = *(struct curl_certinfo **)paramp; if(cipf) { - cipt = (struct curl_certinfo *) malloc(sizeof(*cipt)); + cipt = (struct curl_certinfo *)malloc(sizeof(*cipt)); if(!cipt) ret = CURLE_OUT_OF_MEMORY; else { - cipt->certinfo = (struct curl_slist **) - calloc(cipf->num_of_certs + - 1, sizeof(struct curl_slist *)); + cipt->certinfo = + (struct curl_slist **)calloc(cipf->num_of_certs + 1, + sizeof(struct curl_slist *)); if(!cipt->certinfo) ret = CURLE_OUT_OF_MEMORY; else { @@ -626,20 +592,20 @@ curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) for(i = 0; i < cipf->num_of_certs; i++) if(cipf->certinfo[i]) if(!(cipt->certinfo[i] = slist_convert(ccsid, - cipf->certinfo[i], - ASCII_CCSID))) { + cipf->certinfo[i], + ASCII_CCSID))) { ret = CURLE_OUT_OF_MEMORY; break; } - } } + } if(ret != CURLE_OK) { curl_certinfo_free_all(cipt); - cipt = (struct curl_certinfo *) NULL; + cipt = (struct curl_certinfo *)NULL; } - *(struct curl_certinfo * *) paramp = cipt; + *(struct curl_certinfo **)paramp = cipt; } break; @@ -650,7 +616,7 @@ curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) break; default: - slp = (struct curl_slist **) paramp; + slp = (struct curl_slist **)paramp; if(*slp) { *slp = slist_convert(ccsid, *slp, ASCII_CCSID); if(!*slp) @@ -665,9 +631,7 @@ curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) return ret; } - -static int -Curl_is_formadd_string(CURLformoption option) +static int Curl_is_formadd_string(CURLformoption option) { switch(option) { @@ -684,23 +648,20 @@ Curl_is_formadd_string(CURLformoption option) return 0; } - -static void -Curl_formadd_release_local(struct curl_forms *forms, int nargs, int skip) +static void Curl_formadd_release_local(struct curl_forms *forms, int nargs, + int skip) { while(nargs--) if(nargs != skip) if(Curl_is_formadd_string(forms[nargs].option)) if(forms[nargs].value) - free((char *) forms[nargs].value); + free((char *)forms[nargs].value); - free((char *) forms); + free((char *)forms); } - -static int -Curl_formadd_convert(struct curl_forms *forms, - int formx, int lengthx, unsigned int ccsid) +static int Curl_formadd_convert(struct curl_forms *forms, int formx, + int lengthx, unsigned int ccsid) { int l; char *cp; @@ -710,7 +671,7 @@ Curl_formadd_convert(struct curl_forms *forms, return 0; if(lengthx >= 0) - l = (int) forms[lengthx].value; + l = (int)forms[lengthx].value; else l = strlen(forms[formx].value) + 1; @@ -719,15 +680,15 @@ Curl_formadd_convert(struct curl_forms *forms, if(!cp) return -1; - l = convert(cp, MAX_CONV_EXPANSION * l, ASCII_CCSID, - forms[formx].value, l, ccsid); + l = convert(cp, MAX_CONV_EXPANSION * l, ASCII_CCSID, forms[formx].value, l, + ccsid); if(l < 0) { free(cp); return -1; - } + } - cp2 = realloc(cp, l); /* Shorten buffer to the string size. */ + cp2 = realloc(cp, l); /* Shorten buffer to the string size. */ if(cp2) cp = cp2; @@ -735,15 +696,13 @@ Curl_formadd_convert(struct curl_forms *forms, forms[formx].value = cp; if(lengthx >= 0) - forms[lengthx].value = (char *) l; /* Update length after conversion. */ + forms[lengthx].value = (char *)l; /* Update length after conversion. */ return l; } - -CURLFORMcode -curl_formadd_ccsid(struct curl_httppost **httppost, - struct curl_httppost **last_post, ...) +CURLFORMcode curl_formadd_ccsid(struct curl_httppost **httppost, + struct curl_httppost **last_post, ...) { va_list arg; CURLformoption option; @@ -794,7 +753,7 @@ curl_formadd_ccsid(struct curl_httppost **httppost, lengthx = -1; namex = -1; namelengthx = -1; - forms = (struct curl_forms *) NULL; + forms = (struct curl_forms *)NULL; va_start(arg, last_post); for(;;) { @@ -807,10 +766,10 @@ curl_formadd_ccsid(struct curl_httppost **httppost, if(!tforms) { result = CURL_FORMADD_MEMORY; break; - } + } lforms = tforms; - } + } /* Get next option. */ @@ -820,7 +779,7 @@ curl_formadd_ccsid(struct curl_httppost **httppost, option = forms->option; value = forms->value; forms++; - } + } else { /* Get option from arguments. */ @@ -828,27 +787,27 @@ curl_formadd_ccsid(struct curl_httppost **httppost, if(option == CURLFORM_END) break; - } + } /* Dispatch by option. */ switch(option) { case CURLFORM_END: - forms = (struct curl_forms *) NULL; /* Leave array mode. */ + forms = (struct curl_forms *)NULL; /* Leave array mode. */ continue; case CURLFORM_ARRAY: if(!forms) { forms = va_arg(arg, struct curl_forms *); continue; - } + } result = CURL_FORMADD_ILLEGAL_ARRAY; break; case CURLFORM_COPYNAME: - option = CURLFORM_PTRNAME; /* Static for now. */ + option = CURLFORM_PTRNAME; /* Static for now. */ case CURLFORM_PTRNAME: if(namex >= 0) @@ -858,12 +817,12 @@ curl_formadd_ccsid(struct curl_httppost **httppost, if(!forms) { value = va_arg(arg, char *); - nameccsid = (unsigned int) va_arg(arg, long); - } + nameccsid = (unsigned int)va_arg(arg, long); + } else { - nameccsid = (unsigned int) forms->value; + nameccsid = (unsigned int)forms->value; forms++; - } + } break; @@ -875,19 +834,19 @@ curl_formadd_ccsid(struct curl_httppost **httppost, if(!forms) { value = va_arg(arg, char *); - contentccsid = (unsigned int) va_arg(arg, long); - } + contentccsid = (unsigned int)va_arg(arg, long); + } else { - contentccsid = (unsigned int) forms->value; + contentccsid = (unsigned int)forms->value; forms++; - } + } break; case CURLFORM_PTRCONTENTS: case CURLFORM_BUFFERPTR: if(!forms) - value = va_arg(arg, char *); /* No conversion. */ + value = va_arg(arg, char *); /* No conversion. */ break; @@ -895,7 +854,7 @@ curl_formadd_ccsid(struct curl_httppost **httppost, lengthx = nargs; if(!forms) - value = (char *) va_arg(arg, long); + value = (char *)va_arg(arg, long); break; @@ -903,7 +862,7 @@ curl_formadd_ccsid(struct curl_httppost **httppost, lengthx = nargs; if(!forms) - value = (char *) va_arg(arg, curl_off_t); + value = (char *)va_arg(arg, curl_off_t); break; @@ -911,25 +870,25 @@ curl_formadd_ccsid(struct curl_httppost **httppost, namelengthx = nargs; if(!forms) - value = (char *) va_arg(arg, long); + value = (char *)va_arg(arg, long); break; case CURLFORM_BUFFERLENGTH: if(!forms) - value = (char *) va_arg(arg, long); + value = (char *)va_arg(arg, long); break; case CURLFORM_CONTENTHEADER: if(!forms) - value = (char *) va_arg(arg, struct curl_slist *); + value = (char *)va_arg(arg, struct curl_slist *); break; case CURLFORM_STREAM: if(!forms) - value = (char *) va_arg(arg, void *); + value = (char *)va_arg(arg, void *); break; @@ -939,7 +898,7 @@ curl_formadd_ccsid(struct curl_httppost **httppost, if(Curl_formadd_convert(lforms, contentx, lengthx, contentccsid) < 0) { result = CURL_FORMADD_MEMORY; break; - } + } contentx = -1; lengthx = -1; @@ -951,16 +910,16 @@ curl_formadd_ccsid(struct curl_httppost **httppost, if(!Curl_is_formadd_string(option)) { result = CURL_FORMADD_UNKNOWN_OPTION; break; - } + } if(!forms) { value = va_arg(arg, char *); - ccsid = (unsigned int) va_arg(arg, long); - } + ccsid = (unsigned int)va_arg(arg, long); + } else { - ccsid = (unsigned int) forms->value; + ccsid = (unsigned int)forms->value; forms++; - } + } /* Do the conversion. */ @@ -969,17 +928,17 @@ curl_formadd_ccsid(struct curl_httppost **httppost, if(Curl_formadd_convert(lforms, nargs, -1, ccsid) < 0) { result = CURL_FORMADD_MEMORY; break; - } + } value = lforms[nargs].value; - } + } if(result != CURL_FORMADD_OK) break; lforms[nargs].value = value; lforms[nargs++].option = option; - } + } va_end(arg); @@ -989,15 +948,15 @@ curl_formadd_ccsid(struct curl_httppost **httppost, if(Curl_formadd_convert(lforms, namex, namelengthx, nameccsid) < 0) result = CURL_FORMADD_MEMORY; else - lforms[namex].option = CURLFORM_COPYNAME; /* Force copy. */ - } + lforms[namex].option = CURLFORM_COPYNAME; /* Force copy. */ + } if(result == CURL_FORMADD_OK) { if(Curl_formadd_convert(lforms, contentx, lengthx, contentccsid) < 0) result = CURL_FORMADD_MEMORY; else contentx = -1; - } + } /* Do the formadd with our converted parameters. */ @@ -1005,7 +964,7 @@ curl_formadd_ccsid(struct curl_httppost **httppost, lforms[nargs].option = CURLFORM_END; result = curl_formadd(httppost, last_post, CURLFORM_ARRAY, lforms, CURLFORM_END); - } + } /* Terminate. */ @@ -1013,60 +972,54 @@ curl_formadd_ccsid(struct curl_httppost **httppost, return result; } - struct cfcdata { curl_formget_callback append; void * arg; unsigned int ccsid; }; - -static size_t -Curl_formget_callback_ccsid(void *arg, const char *buf, size_t len) +static size_t Curl_formget_callback_ccsid(void *arg, const char *buf, + size_t len) { struct cfcdata *p; char *b; int l; size_t ret; - p = (struct cfcdata *) arg; + p = (struct cfcdata *)arg; - if((long) len <= 0) + if((long)len <= 0) return (*p->append)(p->arg, buf, len); b = malloc(MAX_CONV_EXPANSION * len); if(!b) - return (size_t) -1; + return (size_t)-1; l = convert(b, MAX_CONV_EXPANSION * len, p->ccsid, buf, len, ASCII_CCSID); if(l < 0) { free(b); - return (size_t) -1; - } + return (size_t)-1; + } ret = (*p->append)(p->arg, b, l); free(b); return ret == l ? len : -1; } - -int -curl_formget_ccsid(struct curl_httppost *form, void *arg, - curl_formget_callback append, unsigned int ccsid) +int curl_formget_ccsid(struct curl_httppost *form, void *arg, + curl_formget_callback append, unsigned int ccsid) { struct cfcdata lcfc; lcfc.append = append; lcfc.arg = arg; lcfc.ccsid = ccsid; - return curl_formget(form, (void *) &lcfc, Curl_formget_callback_ccsid); + return curl_formget(form, (void *)&lcfc, Curl_formget_callback_ccsid); } - -CURLcode -curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) +CURLcode curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) { CURLcode result; va_list arg; @@ -1180,7 +1133,7 @@ curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) ccsid = va_arg(arg, unsigned int); if(s) { - s = dynconvert(ASCII_CCSID, s, -1, ccsid); + s = dynconvert(ASCII_CCSID, s, -1, ccsid, NULL); if(!s) { result = CURLE_OUT_OF_MEMORY; @@ -1208,13 +1161,13 @@ curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) if(pfsize == -1) { /* Data is null-terminated. */ - s = dynconvert(ASCII_CCSID, s, -1, ccsid); + s = dynconvert(ASCII_CCSID, s, -1, ccsid, NULL); if(!s) { result = CURLE_OUT_OF_MEMORY; break; - } } + } else { /* Data length specified. */ size_t len; @@ -1244,12 +1197,13 @@ curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) break; } - data->set.postfieldsize = pfsize; /* Replace data size. */ + data->set.postfieldsize = pfsize; /* Replace data size. */ s = cp; + cp = NULL; } result = curl_easy_setopt(easy, CURLOPT_POSTFIELDS, s); - data->set.str[STRING_COPYPOSTFIELDS] = s; /* Give to library. */ + data->set.str[STRING_COPYPOSTFIELDS] = s; /* Give to library. */ break; default: @@ -1261,7 +1215,7 @@ curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) if(bp && bp->data && bp->len && ccsid != NOCONV_CCSID && ccsid != ASCII_CCSID) { - pfsize = (curl_off_t) bp->len * MAX_CONV_EXPANSION; + pfsize = (curl_off_t)bp->len * MAX_CONV_EXPANSION; if(pfsize > SIZE_MAX) pfsize = SIZE_MAX; @@ -1285,11 +1239,11 @@ curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) blob.flags = bp->flags | CURL_BLOB_COPY; bp = &blob; } - result = curl_easy_setopt(easy, tag, &blob); + result = curl_easy_setopt(easy, tag, bp); break; } FALLTHROUGH(); - case CURLOPT_ERRORBUFFER: /* This is an output buffer. */ + case CURLOPT_ERRORBUFFER: /* This is an output buffer. */ result = Curl_vsetopt(easy, tag, arg); break; } @@ -1299,70 +1253,62 @@ curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) return result; } - /* ILE/RPG helper functions. */ -char * -curl_form_long_value(long value) +char *curl_form_long_value(long value) { /* ILE/RPG cannot cast an integer to a pointer. This procedure does it. */ - return (char *) value; + return (char *)value; } - -CURLcode -curl_easy_setopt_RPGnum_(CURL *easy, CURLoption tag, curl_off_t arg) +CURLcode curl_easy_setopt_RPGnum_(CURL *easy, CURLoption tag, curl_off_t arg) { /* ILE/RPG procedure overloading cannot discriminate between different size and/or signedness of format arguments. This provides a generic wrapper that adapts size to the given tag expectation. This procedure is not intended to be explicitly called from user code. */ if(tag / 10000 != CURLOPTTYPE_OFF_T) - return curl_easy_setopt(easy, tag, (long) arg); + return curl_easy_setopt(easy, tag, (long)arg); return curl_easy_setopt(easy, tag, arg); } - -CURLcode -curl_multi_setopt_RPGnum_(CURLM *multi, CURLMoption tag, curl_off_t arg) +CURLcode curl_multi_setopt_RPGnum_(CURLM *multi, CURLMoption tag, + curl_off_t arg) { /* Likewise, for multi handle. */ if(tag / 10000 != CURLOPTTYPE_OFF_T) - return curl_multi_setopt(multi, tag, (long) arg); + return curl_multi_setopt(multi, tag, (long)arg); return curl_multi_setopt(multi, tag, arg); } - -char * -curl_pushheader_bynum_cssid(struct curl_pushheaders *h, - size_t num, unsigned int ccsid) +char *curl_pushheader_bynum_cssid(struct curl_pushheaders *h, size_t num, + unsigned int ccsid) { - char *d = (char *) NULL; + char *d = (char *)NULL; char *s = curl_pushheader_bynum(h, num); if(s) - d = dynconvert(ccsid, s, -1, ASCII_CCSID); + d = dynconvert(ccsid, s, -1, ASCII_CCSID, NULL); return d; } - -char * -curl_pushheader_byname_ccsid(struct curl_pushheaders *h, const char *header, - unsigned int ccsidin, unsigned int ccsidout) +char *curl_pushheader_byname_ccsid(struct curl_pushheaders *h, + const char *header, unsigned int ccsidin, + unsigned int ccsidout) { - char *d = (char *) NULL; + char *d = (char *)NULL; if(header) { - header = dynconvert(ASCII_CCSID, header, -1, ccsidin); + header = dynconvert(ASCII_CCSID, header, -1, ccsidin, NULL); if(header) { char *s = curl_pushheader_byname(h, header); - free((char *) header); + free((char *)header); if(s) - d = dynconvert(ccsidout, s, -1, ASCII_CCSID); + d = dynconvert(ccsidout, s, -1, ASCII_CCSID, NULL); } } @@ -1373,12 +1319,12 @@ static CURLcode mime_string_call(curl_mimepart *part, const char *string, unsigned int ccsid, CURLcode (*mimefunc)(curl_mimepart *part, const char *string)) { - char *s = (char *) NULL; + char *s = (char *)NULL; CURLcode result; if(!string) return mimefunc(part, string); - s = dynconvert(ASCII_CCSID, string, -1, ccsid); + s = dynconvert(ASCII_CCSID, string, -1, ccsid, NULL); if(!s) return CURLE_OUT_OF_MEMORY; @@ -1387,61 +1333,56 @@ mime_string_call(curl_mimepart *part, const char *string, unsigned int ccsid, return result; } -CURLcode -curl_mime_name_ccsid(curl_mimepart *part, const char *name, unsigned int ccsid) +CURLcode curl_mime_name_ccsid(curl_mimepart *part, const char *name, + unsigned int ccsid) { return mime_string_call(part, name, ccsid, curl_mime_name); } -CURLcode -curl_mime_filename_ccsid(curl_mimepart *part, - const char *filename, unsigned int ccsid) +CURLcode curl_mime_filename_ccsid(curl_mimepart *part, const char *filename, + unsigned int ccsid) { return mime_string_call(part, filename, ccsid, curl_mime_filename); } -CURLcode -curl_mime_type_ccsid(curl_mimepart *part, - const char *mimetype, unsigned int ccsid) +CURLcode curl_mime_type_ccsid(curl_mimepart *part, const char *mimetype, + unsigned int ccsid) { return mime_string_call(part, mimetype, ccsid, curl_mime_type); } -CURLcode -curl_mime_encoder_ccsid(curl_mimepart *part, - const char *encoding, unsigned int ccsid) +CURLcode curl_mime_encoder_ccsid(curl_mimepart *part, const char *encoding, + unsigned int ccsid) { return mime_string_call(part, encoding, ccsid, curl_mime_encoder); } -CURLcode -curl_mime_filedata_ccsid(curl_mimepart *part, - const char *filename, unsigned int ccsid) +CURLcode curl_mime_filedata_ccsid(curl_mimepart *part, const char *filename, + unsigned int ccsid) { return mime_string_call(part, filename, ccsid, curl_mime_filedata); } -CURLcode -curl_mime_data_ccsid(curl_mimepart *part, - const char *data, size_t datasize, unsigned int ccsid) +CURLcode curl_mime_data_ccsid(curl_mimepart *part, const char *data, + size_t datasize, unsigned int ccsid) { - char *s = (char *) NULL; + char *s = (char *)NULL; CURLcode result; + int osize; if(!data) return curl_mime_data(part, data, datasize); - s = dynconvert(ASCII_CCSID, data, datasize, ccsid); + s = dynconvert(ASCII_CCSID, data, datasize, ccsid, &osize); if(!s) return CURLE_OUT_OF_MEMORY; - result = curl_mime_data(part, s, datasize); + result = curl_mime_data(part, s, osize); free(s); return result; } -CURLUcode -curl_url_get_ccsid(CURLU *handle, CURLUPart what, char **part, - unsigned int flags, unsigned int ccsid) +CURLUcode curl_url_get_ccsid(CURLU *handle, CURLUPart what, char **part, + unsigned int flags, unsigned int ccsid) { char *s = (char *)NULL; CURLUcode result; @@ -1453,7 +1394,7 @@ curl_url_get_ccsid(CURLU *handle, CURLUPart what, char **part, result = curl_url_get(handle, what, &s, flags); if(result == CURLUE_OK) { if(s) { - *part = dynconvert(ccsid, s, -1, ASCII_CCSID); + *part = dynconvert(ccsid, s, -1, ASCII_CCSID, NULL); if(!*part) result = CURLUE_OUT_OF_MEMORY; } @@ -1463,15 +1404,14 @@ curl_url_get_ccsid(CURLU *handle, CURLUPart what, char **part, return result; } -CURLUcode -curl_url_set_ccsid(CURLU *handle, CURLUPart what, const char *part, - unsigned int flags, unsigned int ccsid) +CURLUcode curl_url_set_ccsid(CURLU *handle, CURLUPart what, const char *part, + unsigned int flags, unsigned int ccsid) { char *s = (char *)NULL; CURLUcode result; if(part) { - s = dynconvert(ASCII_CCSID, part, -1, ccsid); + s = dynconvert(ASCII_CCSID, part, -1, ccsid, NULL); if(!s) return CURLUE_OUT_OF_MEMORY; } @@ -1487,7 +1427,7 @@ curl_easy_option_by_name_ccsid(const char *name, unsigned int ccsid) const struct curl_easyoption *option = NULL; if(name) { - char *s = dynconvert(ASCII_CCSID, name, -1, ccsid); + char *s = dynconvert(ASCII_CCSID, name, -1, ccsid, NULL); if(s) { option = curl_easy_option_by_name(s); @@ -1506,21 +1446,20 @@ curl_easy_option_get_name_ccsid(const struct curl_easyoption *option, char *name = NULL; if(option && option->name) - name = dynconvert(ccsid, option->name, -1, ASCII_CCSID); + name = dynconvert(ccsid, option->name, -1, ASCII_CCSID, NULL); - return (const char *) name; + return (const char *)name; } /* Header API CCSID support. */ -CURLHcode -curl_easy_header_ccsid(CURL *easy, const char *name, size_t index, - unsigned int origin, int request, - struct curl_header **hout, unsigned int ccsid) +CURLHcode curl_easy_header_ccsid(CURL *easy, const char *name, size_t index, + unsigned int origin, int request, + struct curl_header **hout, unsigned int ccsid) { CURLHcode result = CURLHE_BAD_ARGUMENT; if(name) { - char *s = dynconvert(ASCII_CCSID, name, -1, ccsid); + char *s = dynconvert(ASCII_CCSID, name, -1, ccsid, NULL); result = CURLHE_OUT_OF_MEMORY; if(s) { diff --git a/packages/OS400/ccsidcurl.h b/projects/OS400/ccsidcurl.h similarity index 99% rename from packages/OS400/ccsidcurl.h rename to projects/OS400/ccsidcurl.h index ab01d32b85..f9e667a9e4 100644 --- a/packages/OS400/ccsidcurl.h +++ b/projects/OS400/ccsidcurl.h @@ -28,7 +28,6 @@ #include "easy.h" #include "multi.h" - CURL_EXTERN char *curl_version_ccsid(unsigned int ccsid); CURL_EXTERN char *curl_easy_escape_ccsid(CURL *handle, const char *string, int length, diff --git a/packages/OS400/config400.default b/projects/OS400/config400.default similarity index 100% rename from packages/OS400/config400.default rename to projects/OS400/config400.default diff --git a/packages/OS400/curl.cmd b/projects/OS400/curl.cmd similarity index 97% rename from packages/OS400/curl.cmd rename to projects/OS400/curl.cmd index 822f4db207..fa6aaa59a5 100644 --- a/packages/OS400/curl.cmd +++ b/projects/OS400/curl.cmd @@ -29,4 +29,4 @@ PARM KWD(CMDARGS) TYPE(*CHAR) LEN(5000) VARY(*YES *INT2) + CASE(*MIXED) EXPR(*YES) MIN(1) + - PROMPT('Curl command arguments') + PROMPT('curl command arguments') diff --git a/packages/OS400/curl.inc.in b/projects/OS400/curl.inc.in similarity index 98% rename from packages/OS400/curl.inc.in rename to projects/OS400/curl.inc.in index 1fe5b07ebe..f97f26e012 100644 --- a/packages/OS400/curl.inc.in +++ b/projects/OS400/curl.inc.in @@ -117,7 +117,7 @@ d CURL_VERSION_CONV... d c X'00001000' d CURL_VERSION_CURLDEBUG... - d c X'00002000' + d c X'00002000' Deprecated d CURL_VERSION_TLSAUTH_SRP... d c X'00004000' d CURL_VERSION_NTLM_WB... @@ -366,6 +366,13 @@ * d CURL_PUSH_OK c 0 d CURL_PUSH_DENY c 1 + d CURL_PUSH_ERROROUT... + d c 2 + * + d CURLMNOTIFY_INFO_READ... + d c 0 + d CURLMNOTIFY_EASY_DONE... + d c 1 * d CURLPAUSE_RECV c X'00000001' d CURLPAUSE_RECV_CONT... @@ -1085,6 +1092,9 @@ d c X'10000000' d CURLPROTO_GOPHERS... d c X'20000000' + d CURLPROTO_MQTTS... + d c X'40000000' + d CURLPROTO_ALL c X'FFFFFFFF' * d CURLoption s 10i 0 based(######ptr######) Enum d CURLOPT_WRITEDATA... @@ -2201,6 +2211,33 @@ d c 10015 d CURLMOPT_MAX_CONCURRENT_STREAMS... d c 10016 + d CURLMOPT_NETWORK_CHANGED... + d c 10017 + d CURLMOPT_NOTIFYFUNCTION... + d c 20018 + d CURLMOPT_NOTIFYDATA... + d c 10019 + * + d CURLMinfo_offt s 10i 0 based(######ptr######) Enum + d CURLMINFO_NONE... + d c 0 + d CURLMINFO_XFERS_CURRENT... + d c 1 + d CURLMINFO_XFERS_RUNNING... + d c 2 + d CURLMINFO_XFERS_PENDING... + d c 3 + d CURLMINFO_XFERS_DONE... + d c 4 + d CURLMINFO_XFERS_ADDED... + d c 5 + * + * Definition of bits for the CURLMOPT_NETWORK_CHANGED argument. + * + d CURLMNWC_CLEAR_CONNS... + d c x'00000001' + d CURLMNWC_CLEAR_DNS... + d c x'00000002' * * Bitmask bits for CURLMOPT_PIPELINING. * @@ -2635,6 +2672,9 @@ d s * based(######ptr######) procptr * d curl_ws_write_callback... + d s * based(######ptr######) procptr + * + d curl_notify_callback... d s * based(######ptr######) procptr * ************************************************************************** @@ -3128,6 +3168,25 @@ d pr * extproc('curl_multi_get_handles') CURL ** d multi_handle * value CURLM * * + d curl_multi_get_offt... + d pr extproc('curl_multi_get_offt') + d like(CURLMcode) + d multi_handle * value CURLM * + d info value like(CURLMinfo_offt) + d pvalue like(curl_off_t) + * + d curl_multi_notify_disable... + d pr extproc('curl_multi_notify_disable') + d like(CURLMcode) + d multi_handle * value CURLM * + d notification 10i 0 value + * + d curl_multi_notify_enable... + d pr extproc('curl_multi_notify_enable') + d like(CURLMcode) + d multi_handle * value CURLM * + d notification 10i 0 value + * d curl_url pr * extproc('curl_url') CURLU * * d curl_url_cleanup... @@ -3187,6 +3246,12 @@ d curl_ws_meta pr * extproc('curl_ws_meta') curl_ws_frame * d curl * value CURL * * + d curl_ws_start pr extproc('curl_ws_start') + d like(CURLcode) + d curl * value CURL * + d flags 10u 0 value + d frame_len value like(curl_off_t) + * d curl_easy_header... d pr extproc('curl_easy_header') curl_header * d like(CURLHcode) diff --git a/packages/OS400/curlcl.c b/projects/OS400/curlcl.c similarity index 85% rename from packages/OS400/curlcl.c rename to projects/OS400/curlcl.c index 02edcd63c0..7a7f3c6459 100644 --- a/packages/OS400/curlcl.c +++ b/projects/OS400/curlcl.c @@ -48,16 +48,13 @@ struct arguments { struct vary2 *cmdargs; /* Command line arguments. */ }; -static int -is_ifs(char c) +static int is_ifs(char c) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } -static int -parse_command_line(const char *cmdargs, size_t len, - size_t *argc, char **argv, - size_t *argsize, char *argbuf) +static int parse_command_line(const char *cmdargs, size_t len, size_t *argc, + char **argv, size_t *argsize, char *argbuf) { const char *endline = cmdargs + len; char quote = '\0'; @@ -107,6 +104,7 @@ parse_command_line(const char *cmdargs, size_t len, } if(quote) { + /* !checksrc! disable BANNEDFUNC 1 */ fprintf(stderr, "Unterminated quote: %c\n", quote); return -1; } @@ -125,9 +123,7 @@ parse_command_line(const char *cmdargs, size_t len, return 0; } - -int -main(int argsc, struct arguments *args) +int main(int argsc, struct arguments *args) { size_t argc; char **argv; @@ -153,20 +149,20 @@ main(int argsc, struct arguments *args) if(!exitcode) { /* Allocate space for parsed arguments. */ - argv = (char **) malloc((argc + 1) * sizeof(*argv) + argsize); + argv = (char **)malloc((argc + 1) * sizeof(*argv) + argsize); if(!argv) { fputs("Memory allocation error\n", stderr); exitcode = -2; } else { - _SYSPTR pgmptr = rslvsp(WLI_PGM, (char *) CURLPGM, library, _AUTH_NONE); - _LU_Work_Area_T *luwrka = (_LU_Work_Area_T *) _LUWRKA(); + _SYSPTR pgmptr = rslvsp(WLI_PGM, (char *)CURLPGM, library, _AUTH_NONE); + _LU_Work_Area_T *luwrka = (_LU_Work_Area_T *)_LUWRKA(); - parse_command_line(args->cmdargs->string, args->cmdargs->len, - &argc, argv, &argsize, (char *) (argv + argc + 1)); + parse_command_line(args->cmdargs->string, args->cmdargs->len, &argc, + argv, &argsize, (char *)(argv + argc + 1)); /* Call program. */ - _CALLPGMV((void *) &pgmptr, argv, argc); + _CALLPGMV((void *)&pgmptr, argv, argc); exitcode = luwrka->LU_RC; free(argv); diff --git a/packages/OS400/curlmain.c b/projects/OS400/curlmain.c similarity index 91% rename from packages/OS400/curlmain.c rename to projects/OS400/curlmain.c index 079b9b0d9d..54ca865264 100644 --- a/packages/OS400/curlmain.c +++ b/projects/OS400/curlmain.c @@ -41,15 +41,13 @@ extern int QadrtFreeConversionTable(void); extern int QadrtFreeEnviron(void); extern char * setlocale_a(int, const char *); - /* The ASCII main program. */ -extern int main_a(int argc, char * * argv); +extern int main_a(int argc, char **argv); /* Global values of original EBCDIC arguments. */ int ebcdic_argc; char ** ebcdic_argv; - int main(int argc, char **argv) { int i; @@ -63,8 +61,8 @@ int main(int argc, char **argv) char dummybuf[128]; /* To/From codes are 32 byte long strings with reserved fields initialized to ZEROs */ - const char tocode[32] = {"IBMCCSID01208"}; /* Use UTF-8. */ - const char fromcode[32] = {"IBMCCSID000000000010"}; + const char tocode[32] = { "IBMCCSID01208" }; /* Use UTF-8. */ + const char fromcode[32] = { "IBMCCSID000000000010" }; ebcdic_argc = argc; ebcdic_argv = argv; @@ -85,13 +83,13 @@ int main(int argc, char **argv) /* Reset the shift state. */ iconv(cd, NULL, &inbytesleft, &outbuf, &outbytesleft); - } + } /* Allocate memory for the ASCII arguments and vector. */ - argv = (char **) malloc((argc + 1) * sizeof(*argv) + bytecount); + argv = (char **)malloc((argc + 1) * sizeof(*argv) + bytecount); /* Build the vector and convert argument encoding. */ - outbuf = (char *) (argv + argc + 1); + outbuf = (char *)(argv + argc + 1); outbytesleft = bytecount; for(i = 0; i < argc; i++) { @@ -112,7 +110,7 @@ int main(int argc, char **argv) i = main_a(argc, argv); /* Clean-up allocated items. */ - free((char *) argv); + free((char *)argv); QadrtFreeConversionTable(); QadrtFreeEnviron(); diff --git a/packages/OS400/initscript.sh b/projects/OS400/initscript.sh similarity index 98% rename from packages/OS400/initscript.sh rename to projects/OS400/initscript.sh index 69a354ab0d..dcdcfa8000 100755 --- a/packages/OS400/initscript.sh +++ b/projects/OS400/initscript.sh @@ -50,7 +50,7 @@ do case "${SCRIPTDIR}" in esac done -# The script directory is supposed to be in $TOPDIR/packages/os400. +# The script directory is supposed to be in $TOPDIR/projects/os400. TOPDIR=$(dirname "${SCRIPTDIR}") TOPDIR=$(dirname "${TOPDIR}") @@ -197,7 +197,7 @@ make_module() CMD="${CMD} LOCALETYPE(*LOCALE) FLAG(10)" CMD="${CMD} INCDIR('${QADRTDIR}/include'" CMD="${CMD} '${TOPDIR}/include/curl' '${TOPDIR}/include' '${SRCDIR}'" - CMD="${CMD} '${TOPDIR}/packages/OS400'" + CMD="${CMD} '${TOPDIR}/projects/OS400'" if [ "${WITH_ZLIB}" != "0" ] then CMD="${CMD} '${ZLIB_INCLUDE}'" diff --git a/packages/OS400/make-docs.sh b/projects/OS400/make-docs.sh similarity index 100% rename from packages/OS400/make-docs.sh rename to projects/OS400/make-docs.sh diff --git a/packages/OS400/make-include.sh b/projects/OS400/make-include.sh similarity index 100% rename from packages/OS400/make-include.sh rename to projects/OS400/make-include.sh diff --git a/packages/OS400/make-lib.sh b/projects/OS400/make-lib.sh similarity index 97% rename from packages/OS400/make-lib.sh rename to projects/OS400/make-lib.sh index e7b5b519e7..cd3f71897f 100755 --- a/packages/OS400/make-lib.sh +++ b/projects/OS400/make-lib.sh @@ -79,7 +79,7 @@ fi if [ -n "${LINK}" ] then rm -rf "${LIBIFSNAME}/${STATBNDDIR}.BNDDIR" CMD="CRTBNDDIR BNDDIR(${TARGETLIB}/${STATBNDDIR})" - CMD="${CMD} TEXT('LibCurl API static binding directory')" + CMD="${CMD} TEXT('libcurl API static binding directory')" CLcommand "${CMD}" for MODULE in ${MODULES} @@ -173,7 +173,7 @@ fi if [ -n "${LINK}" ] then rm -rf "${LIBIFSNAME}/${DYNBNDDIR}.BNDDIR" CMD="CRTBNDDIR BNDDIR(${TARGETLIB}/${DYNBNDDIR})" - CMD="${CMD} TEXT('LibCurl API dynamic binding directory')" + CMD="${CMD} TEXT('libcurl API dynamic binding directory')" CLcommand "${CMD}" CMD="ADDBNDDIRE BNDDIR(${TARGETLIB}/${DYNBNDDIR})" CMD="${CMD} OBJ((*LIBL/${SRVPGM} *SRVPGM))" diff --git a/packages/OS400/make-src.sh b/projects/OS400/make-src.sh similarity index 100% rename from packages/OS400/make-src.sh rename to projects/OS400/make-src.sh diff --git a/packages/OS400/make-tests.sh b/projects/OS400/make-tests.sh similarity index 98% rename from packages/OS400/make-tests.sh rename to projects/OS400/make-tests.sh index 3a2125965c..4ddc537487 100755 --- a/packages/OS400/make-tests.sh +++ b/projects/OS400/make-tests.sh @@ -58,7 +58,7 @@ build_all_programs() for FLAG in ${PGMCFLAGS} do case "${FLAG}" in - -D?*) # shellcheck disable=SC2001 + -D?*) DEFINE="$(echo "${FLAG}" | sed 's/^..//')" PGMDFNS="${PGMDFNS} '${DEFINE}'" ;; diff --git a/packages/OS400/makefile.sh b/projects/OS400/makefile.sh similarity index 95% rename from packages/OS400/makefile.sh rename to projects/OS400/makefile.sh index 7f75845215..2b688a4421 100755 --- a/packages/OS400/makefile.sh +++ b/projects/OS400/makefile.sh @@ -35,8 +35,10 @@ cd "${TOPDIR}" || exit 1 # Make sure all files are UTF8-encoded. +# Qshell does not support -print0. ls -S has a non-POSIX meaning. +# https://www.ibm.com/docs/en/i/7.1.0?topic=qshell-command-language # shellcheck disable=SC2038 -find "${TOPDIR}" -type f -print | xargs ls -S | while read -r CCSID FILE +find "${TOPDIR}" -type f | xargs ls -S -- | while read -r CCSID FILE do if [ "${CCSID}" != 1208 ] then CMD="CPY OBJ('${FILE}') TOOBJ('${FILE}') FROMCCSID(*OBJ)" CMD="${CMD} TOCCSID(1208) DTAFMT(*TEXT) REPLACE(*YES)" @@ -65,7 +67,7 @@ fi # Copy some documentation files if needed. for TEXT in "${TOPDIR}/COPYING" "${SCRIPTDIR}/README.OS400" \ - "${TOPDIR}/CHANGES.md" "${TOPDIR}/docs/THANKS" "${TOPDIR}/docs/FAQ" \ + "${TOPDIR}/CHANGES.md" "${TOPDIR}/docs/THANKS" "${TOPDIR}/docs/FAQ.md" \ "${TOPDIR}/docs/FEATURES" "${TOPDIR}/docs/SSLCERTS.md" \ "${TOPDIR}/docs/RESOURCES" "${TOPDIR}/docs/VERSIONS.md" \ "${TOPDIR}/docs/HISTORY.md" diff --git a/packages/OS400/os400sys.c b/projects/OS400/os400sys.c similarity index 70% rename from packages/OS400/os400sys.c rename to projects/OS400/os400sys.c index 7be7208549..36ed2480ea 100644 --- a/packages/OS400/os400sys.c +++ b/projects/OS400/os400sys.c @@ -65,14 +65,13 @@ #pragma convert(0) /* Restore EBCDIC. */ -#define MIN_BYTE_GAIN 1024 /* Minimum gain when shortening a buffer. */ +#define MIN_BYTE_GAIN 1024 /* Minimum gain when shortening a buffer. */ struct buffer_t { unsigned long size; /* Buffer size. */ char *buf; /* Buffer address. */ }; - static char *buffer_undef(localkey_t key, long size); static char *buffer_threaded(localkey_t key, long size); static char *buffer_unthreaded(localkey_t key, long size); @@ -86,10 +85,10 @@ char *(*Curl_thread_buffer)(localkey_t key, long size) = buffer_undef; static void thdbufdestroy(void *private) { if(private) { - struct buffer_t *p = (struct buffer_t *) private; + struct buffer_t *p = (struct buffer_t *)private; localkey_t i; - for(i = (localkey_t) 0; i < LK_LAST; i++) { + for(i = (localkey_t)0; i < LK_LAST; i++) { free(p->buf); p++; } @@ -98,27 +97,23 @@ static void thdbufdestroy(void *private) } } - -static void -terminate(void) +static void terminate(void) { if(Curl_thread_buffer == buffer_threaded) { locbufs = pthread_getspecific(thdkey); - pthread_setspecific(thdkey, (void *) NULL); + pthread_setspecific(thdkey, (void *)NULL); pthread_key_delete(thdkey); } if(Curl_thread_buffer != buffer_undef) { - thdbufdestroy((void *) locbufs); - locbufs = (struct buffer_t *) NULL; + thdbufdestroy((void *)locbufs); + locbufs = (struct buffer_t *)NULL; } Curl_thread_buffer = buffer_undef; } - -static char * -get_buffer(struct buffer_t *buf, long size) +static char *get_buffer(struct buffer_t *buf, long size) { char *cp; @@ -136,7 +131,7 @@ get_buffer(struct buffer_t *buf, long size) return buf->buf; } - if((unsigned long) size <= buf->size) { + if((unsigned long)size <= buf->size) { /* Shorten the buffer only if it frees a significant byte count. This avoids some realloc() overhead. */ @@ -157,16 +152,23 @@ get_buffer(struct buffer_t *buf, long size) return cp; } - -static char * -buffer_unthreaded(localkey_t key, long size) +/* + * Get buffer address for the given local key. + * This is always called though `Curl_thread_buffer' and when threads are + * NOT made available by the os, so no mutex lock/unlock occurs. + */ +static char *buffer_unthreaded(localkey_t key, long size) { return get_buffer(locbufs + key, size); } - -static char * -buffer_threaded(localkey_t key, long size) +/* + * Get buffer address for the given local key, taking care of + * concurrent threads. + * This is always called though `Curl_thread_buffer' and when threads are + * made available by the os. + */ +static char *buffer_threaded(localkey_t key, long size) { struct buffer_t *bufs; @@ -174,30 +176,28 @@ buffer_threaded(localkey_t key, long size) make sure it is at least `size'-byte long. Set `size' to < 0 to get its address only. */ - bufs = (struct buffer_t *) pthread_getspecific(thdkey); + bufs = (struct buffer_t *)pthread_getspecific(thdkey); if(!bufs) { if(size < 0) - return (char *) NULL; /* No buffer yet. */ + return (char *)NULL; /* No buffer yet. */ /* Allocate buffer descriptors for the current thread. */ - bufs = calloc((size_t) LK_LAST, sizeof(*bufs)); + bufs = calloc((size_t)LK_LAST, sizeof(*bufs)); if(!bufs) - return (char *) NULL; + return (char *)NULL; - if(pthread_setspecific(thdkey, (void *) bufs)) { + if(pthread_setspecific(thdkey, (void *)bufs)) { free(bufs); - return (char *) NULL; + return (char *)NULL; } } return get_buffer(bufs + key, size); } - -static char * -buffer_undef(localkey_t key, long size) +static char *buffer_undef(localkey_t key, long size) { /* Define the buffer system, get the buffer for the given local key in the current thread, and make sure it is at least `size'-byte long. @@ -207,17 +207,22 @@ buffer_undef(localkey_t key, long size) /* Determine if we can use pthread-specific data. */ - if(Curl_thread_buffer == buffer_undef) { /* If unchanged during lock. */ - if(!pthread_key_create(&thdkey, thdbufdestroy)) + if(Curl_thread_buffer == buffer_undef) { /* If unchanged during lock. */ + /* OS400 interactive jobs do not support threads: check here. */ + if(!pthread_key_create(&thdkey, thdbufdestroy)) { + /* Threads are supported: use the thread-aware buffer procedure. */ Curl_thread_buffer = buffer_threaded; + } else { - locbufs = calloc((size_t) LK_LAST, sizeof(*locbufs)); + /* No multi-threading available: allocate storage for single-thread + * buffer headers. */ + locbufs = calloc((size_t)LK_LAST, sizeof(*locbufs)); if(!locbufs) { - pthread_mutex_unlock(&mutex); - return (char *) NULL; + pthread_mutex_unlock(&mutex); /* For symmetry: will probably fail. */ + return (char *)NULL; } else - Curl_thread_buffer = buffer_unthreaded; + Curl_thread_buffer = buffer_unthreaded; /* Use unthreaded version. */ } atexit(terminate); @@ -227,15 +232,13 @@ buffer_undef(localkey_t key, long size) return Curl_thread_buffer(key, size); } - -static char * -set_thread_string(localkey_t key, const char *s) +static char *set_thread_string(localkey_t key, const char *s) { int i; char *cp; if(!s) - return (char *) NULL; + return (char *)NULL; i = strlen(s) + 1; cp = Curl_thread_buffer(key, MAX_CONV_EXPANSION * i + 1); @@ -248,12 +251,10 @@ set_thread_string(localkey_t key, const char *s) return cp; } - -int -Curl_getnameinfo_a(const struct sockaddr *sa, socklen_t salen, - char *nodename, socklen_t nodenamelen, - char *servname, socklen_t servnamelen, - int flags) +int Curl_getnameinfo_a(const struct sockaddr *sa, socklen_t salen, + char *nodename, socklen_t nodenamelen, + char *servname, socklen_t servnamelen, + int flags) { char *enodename = NULL; char *eservname = NULL; @@ -273,20 +274,20 @@ Curl_getnameinfo_a(const struct sockaddr *sa, socklen_t salen, } } - status = getnameinfo(sa, salen, enodename, nodenamelen, - eservname, servnamelen, flags); + status = getnameinfo(sa, salen, enodename, nodenamelen, eservname, + servnamelen, flags); if(!status) { int i; if(enodename) { - i = QadrtConvertE2A(nodename, enodename, - nodenamelen - 1, strlen(enodename)); + i = QadrtConvertE2A(nodename, enodename, nodenamelen - 1, + strlen(enodename)); nodename[i] = '\0'; } if(eservname) { - i = QadrtConvertE2A(servname, eservname, - servnamelen - 1, strlen(eservname)); + i = QadrtConvertE2A(servname, eservname, servnamelen - 1, + strlen(eservname)); servname[i] = '\0'; } } @@ -296,18 +297,16 @@ Curl_getnameinfo_a(const struct sockaddr *sa, socklen_t salen, return status; } -int -Curl_getaddrinfo_a(const char *nodename, const char *servname, - const struct addrinfo *hints, - struct addrinfo **res) +int Curl_getaddrinfo_a(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) { char *enodename; char *eservname; int status; int i; - enodename = (char *) NULL; - eservname = (char *) NULL; + enodename = (char *)NULL; + eservname = (char *)NULL; if(nodename) { i = strlen(nodename); @@ -333,6 +332,7 @@ Curl_getaddrinfo_a(const char *nodename, const char *servname, eservname[i] = '\0'; } + /* !checksrc! disable BANNEDFUNC 1 */ status = getaddrinfo(enodename, eservname, hints, res); free(enodename); free(eservname); @@ -343,8 +343,7 @@ Curl_getaddrinfo_a(const char *nodename, const char *servname, /* ASCII wrappers for the GSSAPI procedures. */ -static int -Curl_gss_convert_in_place(OM_uint32 *minor_status, gss_buffer_t buf) +static int Curl_gss_convert_in_place(OM_uint32 *minor_status, gss_buffer_t buf) { unsigned int i = buf->length; @@ -371,10 +370,8 @@ Curl_gss_convert_in_place(OM_uint32 *minor_status, gss_buffer_t buf) return 0; } - -OM_uint32 -Curl_gss_import_name_a(OM_uint32 *minor_status, gss_buffer_t in_name, - gss_OID in_name_type, gss_name_t *out_name) +OM_uint32 Curl_gss_import_name_a(OM_uint32 *minor_status, gss_buffer_t in_name, + gss_OID in_name_type, gss_name_t *out_name) { OM_uint32 rc; unsigned int i; @@ -383,7 +380,7 @@ Curl_gss_import_name_a(OM_uint32 *minor_status, gss_buffer_t in_name, if(!in_name || !in_name->value || !in_name->length) return gss_import_name(minor_status, in_name, in_name_type, out_name); - memcpy((char *) &in, (char *) in_name, sizeof(in)); + memcpy((char *)&in, (char *)in_name, sizeof(in)); i = in.length; in.value = malloc(i + 1); @@ -396,17 +393,17 @@ Curl_gss_import_name_a(OM_uint32 *minor_status, gss_buffer_t in_name, } QadrtConvertA2E(in.value, in_name->value, i, i); - ((char *) in.value)[i] = '\0'; + ((char *)in.value)[i] = '\0'; rc = gss_import_name(minor_status, &in, in_name_type, out_name); free(in.value); return rc; } -OM_uint32 -Curl_gss_display_status_a(OM_uint32 *minor_status, OM_uint32 status_value, - int status_type, gss_OID mech_type, - gss_msg_ctx_t *message_context, - gss_buffer_t status_string) +OM_uint32 Curl_gss_display_status_a(OM_uint32 *minor_status, + OM_uint32 status_value, int status_type, + gss_OID mech_type, + gss_msg_ctx_t *message_context, + gss_buffer_t status_string) { int rc; @@ -460,7 +457,7 @@ Curl_gss_init_sec_context_a(OM_uint32 *minor_status, } QadrtConvertA2E(in.value, input_token->value, i, i); - ((char *) in.value)[i] = '\0'; + ((char *)in.value)[i] = '\0'; in.length = i; inp = ∈ } @@ -486,11 +483,9 @@ Curl_gss_init_sec_context_a(OM_uint32 *minor_status, return rc; } - -OM_uint32 -Curl_gss_delete_sec_context_a(OM_uint32 *minor_status, - gss_ctx_id_t *context_handle, - gss_buffer_t output_token) +OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token) { OM_uint32 rc; @@ -516,38 +511,36 @@ Curl_gss_delete_sec_context_a(OM_uint32 *minor_status, /* ASCII wrappers for the LDAP procedures. */ -void * -Curl_ldap_init_a(char *host, int port) +void *Curl_ldap_init_a(char *host, int port) { size_t i; char *ehost; void *result; if(!host) - return (void *) ldap_init(host, port); + return (void *)ldap_init(host, port); i = strlen(host); ehost = malloc(i + 1); if(!ehost) - return (void *) NULL; + return (void *)NULL; QadrtConvertA2E(ehost, host, i, i); ehost[i] = '\0'; - result = (void *) ldap_init(ehost, port); + result = (void *)ldap_init(ehost, port); free(ehost); return result; } -int -Curl_ldap_simple_bind_s_a(void *ld, char *dn, char *passwd) +int Curl_ldap_simple_bind_s_a(void *ld, char *dn, char *passwd) { int i; char *edn; char *epasswd; - edn = (char *) NULL; - epasswd = (char *) NULL; + edn = (char *)NULL; + epasswd = (char *)NULL; if(dn) { i = strlen(dn); @@ -579,9 +572,8 @@ Curl_ldap_simple_bind_s_a(void *ld, char *dn, char *passwd) return i; } -int -Curl_ldap_search_s_a(void *ld, char *base, int scope, char *filter, - char **attrs, int attrsonly, LDAPMessage **res) +int Curl_ldap_search_s_a(void *ld, char *base, int scope, char *filter, + char **attrs, int attrsonly, LDAPMessage **res) { int i; int j; @@ -590,9 +582,9 @@ Curl_ldap_search_s_a(void *ld, char *base, int scope, char *filter, char **eattrs; int status; - ebase = (char *) NULL; - efilter = (char *) NULL; - eattrs = (char **) NULL; + ebase = (char *)NULL; + efilter = (char *)NULL; + eattrs = (char **)NULL; status = LDAP_SUCCESS; if(base) { @@ -659,14 +651,13 @@ Curl_ldap_search_s_a(void *ld, char *base, int scope, char *filter, return status; } - -struct berval ** -Curl_ldap_get_values_len_a(void *ld, LDAPMessage *entry, const char *attr) +struct berval **Curl_ldap_get_values_len_a(void *ld, LDAPMessage *entry, + const char *attr) { char *cp; struct berval **result; - cp = (char *) NULL; + cp = (char *)NULL; if(attr) { int i = strlen(attr); @@ -675,7 +666,7 @@ Curl_ldap_get_values_len_a(void *ld, LDAPMessage *entry, const char *attr) if(!cp) { ldap_set_lderrno(ld, LDAP_NO_MEMORY, NULL, ldap_err2string(LDAP_NO_MEMORY)); - return (struct berval **) NULL; + return (struct berval **)NULL; } QadrtConvertA2E(cp, attr, i, i); @@ -685,20 +676,18 @@ Curl_ldap_get_values_len_a(void *ld, LDAPMessage *entry, const char *attr) result = ldap_get_values_len(ld, entry, cp); free(cp); - /* Result data are binary in nature, so they haven't been + /* Result data are binary in nature, so they have not been converted to EBCDIC. Therefore do not convert. */ return result; } -char * -Curl_ldap_err2string_a(int error) +char *Curl_ldap_err2string_a(int error) { return set_thread_string(LK_LDAP_ERROR, ldap_err2string(error)); } -char * -Curl_ldap_get_dn_a(void *ld, LDAPMessage *entry) +char *Curl_ldap_get_dn_a(void *ld, LDAPMessage *entry) { int i; char *cp; @@ -716,20 +705,20 @@ Curl_ldap_get_dn_a(void *ld, LDAPMessage *entry) return cp2; QadrtConvertE2A(cp2, cp, i, i); - cp2[i] = '\0'; /* No way to allocate a buffer here, because it will be released by ldap_memfree() and ldap_memalloc() does not exist. The solution is to - overwrite the EBCDIC buffer with ASCII to return it. */ + overwrite the EBCDIC buffer with ASCII to return it. - strcpy(cp, cp2); + The destination buffer already has a null-terminator at the correct + position. Keep it outouched and copy the buffer without a terminator. */ + memcpy(cp, cp2, i); free(cp2); return cp; } -char * -Curl_ldap_first_attribute_a(void *ld, - LDAPMessage *entry, BerElement **berptr) +char *Curl_ldap_first_attribute_a(void *ld, LDAPMessage *entry, + BerElement **berptr) { int i; char *cp; @@ -747,20 +736,20 @@ Curl_ldap_first_attribute_a(void *ld, return cp2; QadrtConvertE2A(cp2, cp, i, i); - cp2[i] = '\0'; /* No way to allocate a buffer here, because it will be released by ldap_memfree() and ldap_memalloc() does not exist. The solution is to - overwrite the EBCDIC buffer with ASCII to return it. */ + overwrite the EBCDIC buffer with ASCII to return it. - strcpy(cp, cp2); + The destination buffer already has a null-terminator at the correct + position. Keep it outouched and copy the buffer without a terminator. */ + memcpy(cp, cp2, i); free(cp2); return cp; } -char * -Curl_ldap_next_attribute_a(void *ld, - LDAPMessage *entry, BerElement *berptr) +char *Curl_ldap_next_attribute_a(void *ld, LDAPMessage *entry, + BerElement *berptr) { int i; char *cp; @@ -778,22 +767,22 @@ Curl_ldap_next_attribute_a(void *ld, return cp2; QadrtConvertE2A(cp2, cp, i, i); - cp2[i] = '\0'; /* No way to allocate a buffer here, because it will be released by ldap_memfree() and ldap_memalloc() does not exist. The solution is to - overwrite the EBCDIC buffer with ASCII to return it. */ + overwrite the EBCDIC buffer with ASCII to return it. - strcpy(cp, cp2); + The destination buffer already has a null-terminator at the correct + position. Keep it outouched and copy the buffer without a terminator. */ + memcpy(cp, cp2, i); free(cp2); return cp; } #endif /* CURL_DISABLE_LDAP */ -static int -sockaddr2ebcdic(struct sockaddr_storage *dstaddr, - const struct sockaddr *srcaddr, int srclen) +static int sockaddr2ebcdic(struct sockaddr_storage *dstaddr, + const struct sockaddr *srcaddr, int srclen) { const struct sockaddr_un *srcu; struct sockaddr_un *dstu; @@ -809,13 +798,13 @@ sockaddr2ebcdic(struct sockaddr_storage *dstaddr, return -1; } - memcpy((char *) dstaddr, (char *) srcaddr, srclen); + memcpy((char *)dstaddr, (char *)srcaddr, srclen); switch(srcaddr->sa_family) { case AF_UNIX: - srcu = (const struct sockaddr_un *) srcaddr; - dstu = (struct sockaddr_un *) dstaddr; + srcu = (const struct sockaddr_un *)srcaddr; + dstu = (struct sockaddr_un *)dstaddr; dstsize = sizeof(*dstaddr) - offsetof(struct sockaddr_un, sun_path); srclen -= offsetof(struct sockaddr_un, sun_path); i = QadrtConvertA2E(dstu->sun_path, srcu->sun_path, dstsize - 1, srclen); @@ -826,10 +815,8 @@ sockaddr2ebcdic(struct sockaddr_storage *dstaddr, return srclen; } - -static int -sockaddr2ascii(struct sockaddr *dstaddr, int dstlen, - const struct sockaddr_storage *srcaddr, int srclen) +static int sockaddr2ascii(struct sockaddr *dstaddr, int dstlen, + const struct sockaddr_storage *srcaddr, int srclen) { const struct sockaddr_un *srcu; struct sockaddr_un *dstu; @@ -847,15 +834,15 @@ sockaddr2ascii(struct sockaddr *dstaddr, int dstlen, return -1; } - memcpy((char *) dstaddr, (char *) srcaddr, srclen); + memcpy((char *)dstaddr, (char *)srcaddr, srclen); if(srclen >= offsetof(struct sockaddr_storage, ss_family) + sizeof(srcaddr->ss_family)) { switch(srcaddr->ss_family) { case AF_UNIX: - srcu = (const struct sockaddr_un *) srcaddr; - dstu = (struct sockaddr_un *) dstaddr; + srcu = (const struct sockaddr_un *)srcaddr; + dstu = (struct sockaddr_un *)dstaddr; dstsize = dstlen - offsetof(struct sockaddr_un, sun_path); srclen -= offsetof(struct sockaddr_un, sun_path); if(dstsize > 0 && srclen > 0) { @@ -870,8 +857,7 @@ sockaddr2ascii(struct sockaddr *dstaddr, int dstlen, return srclen; } -int -Curl_os400_connect(int sd, struct sockaddr *destaddr, int addrlen) +int Curl_os400_connect(int sd, struct sockaddr *destaddr, int addrlen) { int i; struct sockaddr_storage laddr; @@ -881,11 +867,10 @@ Curl_os400_connect(int sd, struct sockaddr *destaddr, int addrlen) if(i < 0) return -1; - return connect(sd, (struct sockaddr *) &laddr, i); + return connect(sd, (struct sockaddr *)&laddr, i); } -int -Curl_os400_bind(int sd, struct sockaddr *localaddr, int addrlen) +int Curl_os400_bind(int sd, struct sockaddr *localaddr, int addrlen) { int i; struct sockaddr_storage laddr; @@ -895,12 +880,11 @@ Curl_os400_bind(int sd, struct sockaddr *localaddr, int addrlen) if(i < 0) return -1; - return bind(sd, (struct sockaddr *) &laddr, i); + return bind(sd, (struct sockaddr *)&laddr, i); } -int -Curl_os400_sendto(int sd, char *buffer, int buflen, int flags, - const struct sockaddr *dstaddr, int addrlen) +int Curl_os400_sendto(int sd, char *buffer, int buflen, int flags, + const struct sockaddr *dstaddr, int addrlen) { int i; struct sockaddr_storage laddr; @@ -910,12 +894,11 @@ Curl_os400_sendto(int sd, char *buffer, int buflen, int flags, if(i < 0) return -1; - return sendto(sd, buffer, buflen, flags, (struct sockaddr *) &laddr, i); + return sendto(sd, buffer, buflen, flags, (struct sockaddr *)&laddr, i); } -int -Curl_os400_recvfrom(int sd, char *buffer, int buflen, int flags, - struct sockaddr *fromaddr, int *addrlen) +int Curl_os400_recvfrom(int sd, char *buffer, int buflen, int flags, + struct sockaddr *fromaddr, int *addrlen) { int rcvlen; struct sockaddr_storage laddr; @@ -926,7 +909,7 @@ Curl_os400_recvfrom(int sd, char *buffer, int buflen, int flags, laddr.ss_family = AF_UNSPEC; /* To detect if unused. */ rcvlen = recvfrom(sd, buffer, buflen, flags, - (struct sockaddr *) &laddr, &laddrlen); + (struct sockaddr *)&laddr, &laddrlen); if(rcvlen < 0) return rcvlen; @@ -942,12 +925,11 @@ Curl_os400_recvfrom(int sd, char *buffer, int buflen, int flags, return rcvlen; } -int -Curl_os400_getpeername(int sd, struct sockaddr *addr, int *addrlen) +int Curl_os400_getpeername(int sd, struct sockaddr *addr, int *addrlen) { struct sockaddr_storage laddr; int laddrlen = sizeof(laddr); - int retcode = getpeername(sd, (struct sockaddr *) &laddr, &laddrlen); + int retcode = getpeername(sd, (struct sockaddr *)&laddr, &laddrlen); if(!retcode) { laddrlen = sockaddr2ascii(addr, *addrlen, &laddr, laddrlen); @@ -959,12 +941,11 @@ Curl_os400_getpeername(int sd, struct sockaddr *addr, int *addrlen) return retcode; } -int -Curl_os400_getsockname(int sd, struct sockaddr *addr, int *addrlen) +int Curl_os400_getsockname(int sd, struct sockaddr *addr, int *addrlen) { struct sockaddr_storage laddr; int laddrlen = sizeof(laddr); - int retcode = getsockname(sd, (struct sockaddr *) &laddr, &laddrlen); + int retcode = getsockname(sd, (struct sockaddr *)&laddr, &laddrlen); if(!retcode) { laddrlen = sockaddr2ascii(addr, *addrlen, &laddr, laddrlen); @@ -976,17 +957,14 @@ Curl_os400_getsockname(int sd, struct sockaddr *addr, int *addrlen) return retcode; } - #ifdef HAVE_LIBZ -const char * -Curl_os400_zlibVersion(void) +const char *Curl_os400_zlibVersion(void) { return set_thread_string(LK_ZLIB_VERSION, zlibVersion()); } - -int -Curl_os400_inflateInit_(z_streamp strm, const char *version, int stream_size) +int Curl_os400_inflateInit_(z_streamp strm, const char *version, + int stream_size) { z_const char *msgb4 = strm->msg; int ret; @@ -999,9 +977,8 @@ Curl_os400_inflateInit_(z_streamp strm, const char *version, int stream_size) return ret; } -int -Curl_os400_inflateInit2_(z_streamp strm, int windowBits, - const char *version, int stream_size) +int Curl_os400_inflateInit2_(z_streamp strm, int windowBits, + const char *version, int stream_size) { z_const char *msgb4 = strm->msg; int ret; @@ -1014,8 +991,7 @@ Curl_os400_inflateInit2_(z_streamp strm, int windowBits, return ret; } -int -Curl_os400_inflate(z_streamp strm, int flush) +int Curl_os400_inflate(z_streamp strm, int flush) { z_const char *msgb4 = strm->msg; int ret; @@ -1028,8 +1004,7 @@ Curl_os400_inflate(z_streamp strm, int flush) return ret; } -int -Curl_os400_inflateEnd(z_streamp strm) +int Curl_os400_inflateEnd(z_streamp strm) { z_const char *msgb4 = strm->msg; int ret; diff --git a/packages/OS400/os400sys.h b/projects/OS400/os400sys.h similarity index 73% rename from packages/OS400/os400sys.h rename to projects/OS400/os400sys.h index d5ff412f28..eb2cdb828a 100644 --- a/packages/OS400/os400sys.h +++ b/projects/OS400/os400sys.h @@ -28,30 +28,27 @@ #ifndef __OS400_SYS_ #define __OS400_SYS_ - /* Per-thread item identifiers. */ typedef enum { - LK_GSK_ERROR, - LK_LDAP_ERROR, - LK_CURL_VERSION, - LK_VERSION_INFO, - LK_VERSION_INFO_DATA, - LK_EASY_STRERROR, - LK_SHARE_STRERROR, - LK_MULTI_STRERROR, - LK_URL_STRERROR, - LK_ZLIB_VERSION, - LK_ZLIB_MSG, - LK_LAST -} localkey_t; - - -extern char * (* Curl_thread_buffer)(localkey_t key, long size); + LK_GSK_ERROR, + LK_LDAP_ERROR, + LK_CURL_VERSION, + LK_VERSION_INFO, + LK_VERSION_INFO_DATA, + LK_EASY_STRERROR, + LK_SHARE_STRERROR, + LK_MULTI_STRERROR, + LK_URL_STRERROR, + LK_ZLIB_VERSION, + LK_ZLIB_MSG, + LK_LAST +} localkey_t; +extern char *(*Curl_thread_buffer)(localkey_t key, long size); /* Maximum string expansion factor due to character code conversion. */ -#define MAX_CONV_EXPANSION 4 /* Can deal with UTF-8. */ +#define MAX_CONV_EXPANSION 4 /* Can deal with UTF-8. */ #endif diff --git a/packages/OS400/rpg-examples/HEADERAPI b/projects/OS400/rpg-examples/HEADERAPI similarity index 99% rename from packages/OS400/rpg-examples/HEADERAPI rename to projects/OS400/rpg-examples/HEADERAPI index 2046306bb8..f6ef80bbd4 100644 --- a/packages/OS400/rpg-examples/HEADERAPI +++ b/projects/OS400/rpg-examples/HEADERAPI @@ -1,4 +1,4 @@ - * Curl header API: extract headers post transfer + * curl header API: extract headers post transfer * h DFTACTGRP(*NO) ACTGRP(*NEW) h OPTION(*NOSHOWCPY) diff --git a/packages/OS400/rpg-examples/HTTPPOST b/projects/OS400/rpg-examples/HTTPPOST similarity index 99% rename from packages/OS400/rpg-examples/HTTPPOST rename to projects/OS400/rpg-examples/HTTPPOST index 9a562e9084..8e5d0d74bb 100644 --- a/packages/OS400/rpg-examples/HTTPPOST +++ b/projects/OS400/rpg-examples/HTTPPOST @@ -1,4 +1,4 @@ - * Curl MIME post data and display response + * curl MIME post data and display response * h DFTACTGRP(*NO) ACTGRP(*NEW) h OPTION(*NOSHOWCPY) diff --git a/packages/OS400/rpg-examples/INMEMORY b/projects/OS400/rpg-examples/INMEMORY similarity index 99% rename from packages/OS400/rpg-examples/INMEMORY rename to projects/OS400/rpg-examples/INMEMORY index ab425fc22f..7111d565e9 100644 --- a/packages/OS400/rpg-examples/INMEMORY +++ b/projects/OS400/rpg-examples/INMEMORY @@ -1,4 +1,4 @@ - * Curl get in memory and count HTML tags + * curl get in memory and count HTML tags * h DFTACTGRP(*NO) ACTGRP(*NEW) h OPTION(*NOSHOWCPY) diff --git a/packages/OS400/rpg-examples/SIMPLE1 b/projects/OS400/rpg-examples/SIMPLE1 similarity index 99% rename from packages/OS400/rpg-examples/SIMPLE1 rename to projects/OS400/rpg-examples/SIMPLE1 index e1286d1215..2d2296ed40 100644 --- a/packages/OS400/rpg-examples/SIMPLE1 +++ b/projects/OS400/rpg-examples/SIMPLE1 @@ -1,4 +1,4 @@ - * Curl simple URL request + * curl simple URL request * h DFTACTGRP(*NO) ACTGRP(*NEW) h OPTION(*NOSHOWCPY) diff --git a/packages/OS400/rpg-examples/SIMPLE2 b/projects/OS400/rpg-examples/SIMPLE2 similarity index 98% rename from packages/OS400/rpg-examples/SIMPLE2 rename to projects/OS400/rpg-examples/SIMPLE2 index b0c590cc29..2fead8517c 100644 --- a/packages/OS400/rpg-examples/SIMPLE2 +++ b/projects/OS400/rpg-examples/SIMPLE2 @@ -1,4 +1,4 @@ - * Curl simple URL request (free-format RPG) + * curl simple URL request (free-format RPG) * ctl-opt dftactgrp(*NO) actgrp(*NEW) option(*NOSHOWCPY) diff --git a/packages/OS400/rpg-examples/SMTPSRCMBR b/projects/OS400/rpg-examples/SMTPSRCMBR similarity index 99% rename from packages/OS400/rpg-examples/SMTPSRCMBR rename to projects/OS400/rpg-examples/SMTPSRCMBR index d11ea96307..7e3a3fce6c 100644 --- a/packages/OS400/rpg-examples/SMTPSRCMBR +++ b/projects/OS400/rpg-examples/SMTPSRCMBR @@ -1,4 +1,4 @@ - * Curl SMTP send source member as attachment + * curl SMTP send source member as attachment * h DFTACTGRP(*NO) ACTGRP(*NEW) h OPTION(*NOSHOWCPY) diff --git a/projects/README.md b/projects/README.md index e587249dbb..802a78f86a 100644 --- a/projects/README.md +++ b/projects/README.md @@ -4,149 +4,9 @@ Copyright (C) Daniel Stenberg, , et al. SPDX-License-Identifier: curl --> -Building via IDE Project Files -============================== +# Packages -This document describes how to compile, build and install curl and libcurl -from sources using legacy versions of Visual Studio 2010 - 2013. - -You need to generate the project files before using them. Please run "generate --help" for usage details. - -To generate project files for recent versions of Visual Studio instead, use -cmake. Refer to INSTALL-CMAKE.md in the docs directory. - -Another way to build curl using Visual Studio is without project files. Refer -to README in the winbuild directory. - -## Directory Structure - -The following directory structure is used for the legacy project files: - - somedirectory\ - |_curl - |_projects - |_ - |_ - |_lib - |_src - -This structure allows for side-by-side compilation of curl on the same machine -using different versions of a given compiler (for example VC10 and VC12) and -allows for your own application or product to be compiled against those -variants of libcurl for example. - -Note: Typically this side-by-side compilation is generally only required when -a library is being compiled against dynamic runtime libraries. - -## Dependencies - -The projects files also support build configurations that require third party -dependencies such as OpenSSL and libssh2. If you wish to support these, you -also need to download and compile those libraries as well. - -To support compilation of these libraries using different versions of -compilers, the following directory structure has been used for both the output -of curl and libcurl as well as these dependencies. - - somedirectory\ - |_curl - | |_ build - | |_ - | |_ - | |_ - | |_lib - | |_src - | - |_openssl - | |_ build - | |_ - | |_VC - | |_ - | - |_libssh2 - |_ build - |_ - |_VC - |_ - -As OpenSSL does not support side-by-side compilation when using different -versions of Visual Studio, a helper batch file has been provided to assist -with this. Please run `build-openssl -help` for usage details. - -## Building with Visual C++ - -To build with VC++, you have to first install VC++ which is part of Visual -Studio. - -Once you have VC++ installed you should launch the application and open one of -the solution or workspace files. The VC directory names are based on the -version of Visual C++ that you use. Each version of Visual Studio has a -default version of Visual C++. We offer these versions: - - - VC10 (Visual Studio 2010 Version 10.0) - - VC11 (Visual Studio 2012 Version 11.0) - - VC12 (Visual Studio 2013 Version 12.0) - -Separate solutions are provided for both libcurl and the curl command line -tool as well as a solution that includes both projects. libcurl.sln, curl.sln -and curl-all.sln, respectively. We recommend using curl-all.sln to build both -projects. - -For example, if you are using Visual Studio 2010 then you should be able to -use `VC10\curl-all.sln` to build curl and libcurl. - -## Running DLL based configurations - -If you are a developer and plan to run the curl tool from Visual Studio with -any third-party libraries (such as OpenSSL or libssh2) then you need to add -the search path of these DLLs to the configuration's PATH environment. To do -that: - - 1. Open the 'curl-all.sln' or 'curl.sln' solutions - 2. Right-click on the 'curl' project and select Properties - 3. Navigate to 'Configuration Properties > Debugging > Environment' - 4. Add `PATH='Path to DLL';C:\Windows\System32;C:\Windows;C:\Windows\System32\Wbem` - -... where 'Path to DLL` is the configuration specific path. For example the -following configurations in Visual Studio 2010 might be: - -DLL Debug - DLL OpenSSL (Win32): - - PATH=..\..\..\..\..\openssl\build\Win32\VC10\DLL Debug;C:\Windows\System32; - C:\Windows;C:\Windows\System32\Wbem - -DLL Debug - DLL OpenSSL (x64): - - PATH=..\..\..\..\..\openssl\build\Win64\VC10\DLL Debug;C:\Windows\System32; - C:\Windows;C:\Windows\System32\Wbem - -If you are using a configuration that uses multiple third-party library DLLs -(such as DLL Debug - DLL OpenSSL - DLL libssh2) then 'Path to DLL' need to -contain the path to both of these. - -## Notes - -The following keywords have been used in the directory hierarchy: - - - `` - The platform (For example: Windows) - - `` - The IDE (For example: VC10) - - `` - The platform architecture (For example: Win32, Win64) - - `` - The target configuration (For example: DLL Debug, LIB - Release - LIB OpenSSL) - -Should you wish to help out with some of the items on the TODO list, or find -bugs in the project files that need correcting, and would like to submit -updated files back then please note that, whilst the solution files can be -edited directly, the templates for the project files (which are stored in the -git repository) need to be modified rather than the generated project files -that Visual Studio uses. - -## Legacy Windows and SSL - -Some of the project configurations use Schannel (Windows SSPI), the native SSL -library that comes with the Windows OS. Schannel in Windows 8 and earlier is -not able to connect to servers that no longer support the legacy handshakes -and algorithms used by those versions. If you are using curl in one of those -earlier versions of Windows you should choose another SSL backend like -OpenSSL. +This directory and all its subdirectories are for special package +information, templates, scripts and docs. The files herein should be of +use for those of you who want to package curl in a binary or source +format for these platforms. diff --git a/projects/Windows/README.md b/projects/Windows/README.md new file mode 100644 index 0000000000..1ce72f2b50 --- /dev/null +++ b/projects/Windows/README.md @@ -0,0 +1,148 @@ + + +# Building via IDE Project Files + +This document describes how to compile, build and install curl and libcurl +from sources using legacy versions of Visual Studio 2010 - 2013. + +You need to generate the project files before using them. Please run "generate +-help" for usage details. + +To generate project files for recent versions of Visual Studio instead, use +cmake. Refer to INSTALL-CMAKE.md in the docs directory. + +## Directory Structure + +The following directory structure is used for the legacy project files: + + somedirectory\ + |_curl + |_projects + |_ + |_ + |_lib + |_src + +This structure allows for side-by-side compilation of curl on the same machine +using different versions of a given compiler (for example VC10 and VC12) and +allows for your own application or product to be compiled against those +variants of libcurl for example. + +Note: Typically this side-by-side compilation is generally only required when +a library is being compiled against dynamic runtime libraries. + +## Dependencies + +The projects files also support build configurations that require third party +dependencies such as OpenSSL and libssh2. If you wish to support these, you +also need to download and compile those libraries as well. + +To support compilation of these libraries using different versions of +compilers, the following directory structure has been used for both the output +of curl and libcurl as well as these dependencies. + + somedirectory\ + |_curl + | |_ build + | |_ + | |_ + | |_ + | |_lib + | |_src + | + |_openssl + | |_ build + | |_ + | |_VC + | |_ + | + |_libssh2 + |_ build + |_ + |_VC + |_ + +As OpenSSL does not support side-by-side compilation when using different +versions of Visual Studio, a helper batch file has been provided to assist +with this. Please run `build-openssl -help` for usage details. + +## Building with Visual C++ + +To build with VC++, you have to first install VC++ which is part of Visual +Studio. + +Once you have VC++ installed you should launch the application and open one of +the solution or workspace files. The VC directory names are based on the +version of Visual C++ that you use. Each version of Visual Studio has a +default version of Visual C++. We offer these versions: + +- VC10 (Visual Studio 2010 Version 10.0) +- VC11 (Visual Studio 2012 Version 11.0) +- VC12 (Visual Studio 2013 Version 12.0) + +Separate solutions are provided for both libcurl and the curl command line +tool as well as a solution that includes both projects. libcurl.sln, curl.sln +and curl-all.sln, respectively. We recommend using curl-all.sln to build both +projects. + +For example, if you are using Visual Studio 2010 then you should be able to +use `VC10\curl-all.sln` to build curl and libcurl. + +## Running DLL based configurations + +If you are a developer and plan to run the curl tool from Visual Studio with +any third-party libraries (such as OpenSSL or libssh2) then you need to add +the search path of these DLLs to the configuration's PATH environment. To do +that: + +1. Open the 'curl-all.sln' or 'curl.sln' solutions +2. Right-click on the 'curl' project and select Properties +3. Navigate to 'Configuration Properties > Debugging > Environment' +4. Add `PATH='Path to DLL';C:\Windows\System32;C:\Windows;C:\Windows\System32\Wbem` + +... where `Path to DLL` is the configuration specific path. For example the +following configurations in Visual Studio 2010 might be: + +DLL Debug - DLL OpenSSL (Win32): + + PATH=..\..\..\..\..\openssl\build\Win32\VC10\DLL Debug;C:\Windows\System32; + C:\Windows;C:\Windows\System32\Wbem + +DLL Debug - DLL OpenSSL (x64): + + PATH=..\..\..\..\..\openssl\build\Win64\VC10\DLL Debug;C:\Windows\System32; + C:\Windows;C:\Windows\System32\Wbem + +If you are using a configuration that uses multiple third-party library DLLs +(such as `DLL Debug - DLL OpenSSL - DLL libssh2`) then 'Path to DLL' need to +contain the path to both of these. + +## Notes + +The following keywords have been used in the directory hierarchy: + +- `` - The platform (For example: Windows) +- `` - The IDE (For example: VC10) +- `` - The platform architecture (For example: Win32, Win64) +- `` - The target configuration (For example: DLL Debug, LIB + Release - LIB OpenSSL) + +Should you wish to help out with some of the items on the TODO list, or find +bugs in the project files that need correcting, and would like to submit +updated files back then please note that, whilst the solution files can be +edited directly, the templates for the project files (which are stored in the +git repository) need to be modified rather than the generated project files +that Visual Studio uses. + +## Legacy Windows and SSL + +Some of the project configurations use Schannel (Windows SSPI), the native SSL +library that comes with the Windows OS. Schannel in Windows 8 and earlier is +not able to connect to servers that no longer support the legacy handshakes +and algorithms used by those versions. If you are using curl in one of those +earlier versions of Windows you should choose another SSL backend like +OpenSSL. diff --git a/projects/generate.bat b/projects/Windows/generate.bat similarity index 72% rename from projects/generate.bat rename to projects/Windows/generate.bat index 3aebff183e..8441f7f3f3 100644 --- a/projects/generate.bat +++ b/projects/Windows/generate.bat @@ -39,7 +39,7 @@ rem *************************************************************************** cd /d "%~0\.." 1>NUL 2>&1 rem Check we are running from a curl git repository - if not exist ..\GIT-INFO.md goto norepo + if not exist ..\..\GIT-INFO.md goto norepo :parseArgs if "%~1" == "" goto start @@ -109,15 +109,15 @@ rem :generate_proj echo. echo Generating %1 project files - if not exist Windows\%1\lib md Windows\%1\lib - if not exist Windows\%1\src md Windows\%1\src - call :generate %1 Windows\tmpl\curl-all.sln Windows\%1\curl-all.sln - call :generate %1 Windows\tmpl\curl.sln Windows\%1\src\curl.sln - call :generate %1 Windows\tmpl\curl.vcxproj Windows\%1\src\curl.vcxproj - call :generate %1 Windows\tmpl\curl.vcxproj.filters Windows\%1\src\curl.vcxproj.filters - call :generate %1 Windows\tmpl\libcurl.sln Windows\%1\lib\libcurl.sln - call :generate %1 Windows\tmpl\libcurl.vcxproj Windows\%1\lib\libcurl.vcxproj - call :generate %1 Windows\tmpl\libcurl.vcxproj.filters Windows\%1\lib\libcurl.vcxproj.filters + if not exist %1\lib md %1\lib + if not exist %1\src md %1\src + call :generate %1 tmpl\curl-all.sln %1\curl-all.sln + call :generate %1 tmpl\curl.sln %1\src\curl.sln + call :generate %1 tmpl\curl.vcxproj %1\src\curl.vcxproj + call :generate %1 tmpl\curl.vcxproj.filters %1\src\curl.vcxproj.filters + call :generate %1 tmpl\libcurl.sln %1\lib\libcurl.sln + call :generate %1 tmpl\libcurl.vcxproj %1\lib\libcurl.vcxproj + call :generate %1 tmpl\libcurl.vcxproj.filters %1\lib\libcurl.vcxproj.filters exit /B @@ -128,13 +128,13 @@ rem :clean_proj echo. echo Removing %1 project files - call :clean Windows\%1\curl-all.sln - call :clean Windows\%1\src\curl.sln - call :clean Windows\%1\src\curl.vcxproj - call :clean Windows\%1\src\curl.vcxproj.filters - call :clean Windows\%1\lib\libcurl.sln - call :clean Windows\%1\lib\libcurl.vcxproj - call :clean Windows\%1\lib\libcurl.vcxproj.filters + call :clean %1\curl-all.sln + call :clean %1\src\curl.sln + call :clean %1\src\curl.vcxproj + call :clean %1\src\curl.vcxproj.filters + call :clean %1\lib\libcurl.sln + call :clean %1\lib\libcurl.vcxproj + call :clean %1\lib\libcurl.vcxproj.filters exit /B @@ -165,12 +165,12 @@ rem set "R02=v100" set "R03=VC10" set "R04=4.0" - ) else if "%1%" == "VC11" ( + ) else if "%1" == "VC11" ( set "R01=12.00" set "R02=v110" set "R03=VC11" set "R04=4.0" - ) else if "%1%" == "VC12" ( + ) else if "%1" == "VC12" ( set "R01=12.00" set "R02=v120" set "R03=VC12" @@ -189,43 +189,47 @@ rem set "var=!var:*:=!" if "!var!" == "CURL_SRC_C_FILES" ( - for /f "delims=" %%c in ('dir /b ..\src\*.c') do ( + for /f "delims=" %%c in ('dir /b ..\..\src\*.c') do ( if /i "%%c" NEQ "curlinfo.c" call :element src "%%c" %3 ) ) else if "!var!" == "CURL_SRC_H_FILES" ( - for /f "delims=" %%h in ('dir /b ..\src\*.h') do call :element src "%%h" %3 + for /f "delims=" %%h in ('dir /b ..\..\src\*.h') do call :element src "%%h" %3 ) else if "!var!" == "CURL_SRC_RC_FILES" ( - for /f "delims=" %%r in ('dir /b ..\src\*.rc') do call :element src "%%r" %3 + for /f "delims=" %%r in ('dir /b ..\..\src\*.rc') do call :element src "%%r" %3 ) else if "!var!" == "CURL_SRC_X_H_FILES" ( call :element lib "config-win32.h" %3 call :element lib "curl_setup.h" %3 ) else if "!var!" == "CURL_LIB_C_FILES" ( - for /f "delims=" %%c in ('dir /b ..\lib\*.c') do call :element lib "%%c" %3 + for /f "delims=" %%c in ('dir /b ..\..\lib\*.c') do call :element lib "%%c" %3 ) else if "!var!" == "CURL_LIB_H_FILES" ( - for /f "delims=" %%h in ('dir /b ..\include\curl\*.h') do call :element include\curl "%%h" %3 - for /f "delims=" %%h in ('dir /b ..\lib\*.h') do call :element lib "%%h" %3 + for /f "delims=" %%h in ('dir /b ..\..\include\curl\*.h') do call :element include\curl "%%h" %3 + for /f "delims=" %%h in ('dir /b ..\..\lib\*.h') do call :element lib "%%h" %3 ) else if "!var!" == "CURL_LIB_RC_FILES" ( - for /f "delims=" %%r in ('dir /b ..\lib\*.rc') do call :element lib "%%r" %3 + for /f "delims=" %%r in ('dir /b ..\..\lib\*.rc') do call :element lib "%%r" %3 ) else if "!var!" == "CURL_LIB_CURLX_C_FILES" ( - for /f "delims=" %%c in ('dir /b ..\lib\curlx\*.c') do call :element lib\curlx "%%c" %3 + for /f "delims=" %%c in ('dir /b ..\..\lib\curlx\*.c') do call :element lib\curlx "%%c" %3 ) else if "!var!" == "CURL_LIB_CURLX_H_FILES" ( - for /f "delims=" %%h in ('dir /b ..\lib\curlx\*.h') do call :element lib\curlx "%%h" %3 + for /f "delims=" %%h in ('dir /b ..\..\lib\curlx\*.h') do call :element lib\curlx "%%h" %3 ) else if "!var!" == "CURL_LIB_VAUTH_C_FILES" ( - for /f "delims=" %%c in ('dir /b ..\lib\vauth\*.c') do call :element lib\vauth "%%c" %3 + for /f "delims=" %%c in ('dir /b ..\..\lib\vauth\*.c') do call :element lib\vauth "%%c" %3 ) else if "!var!" == "CURL_LIB_VAUTH_H_FILES" ( - for /f "delims=" %%h in ('dir /b ..\lib\vauth\*.h') do call :element lib\vauth "%%h" %3 + for /f "delims=" %%h in ('dir /b ..\..\lib\vauth\*.h') do call :element lib\vauth "%%h" %3 ) else if "!var!" == "CURL_LIB_VQUIC_C_FILES" ( - for /f "delims=" %%c in ('dir /b ..\lib\vquic\*.c') do call :element lib\vquic "%%c" %3 + for /f "delims=" %%c in ('dir /b ..\..\lib\vquic\*.c') do call :element lib\vquic "%%c" %3 ) else if "!var!" == "CURL_LIB_VQUIC_H_FILES" ( - for /f "delims=" %%h in ('dir /b ..\lib\vquic\*.h') do call :element lib\vquic "%%h" %3 + for /f "delims=" %%h in ('dir /b ..\..\lib\vquic\*.h') do call :element lib\vquic "%%h" %3 ) else if "!var!" == "CURL_LIB_VSSH_C_FILES" ( - for /f "delims=" %%c in ('dir /b ..\lib\vssh\*.c') do call :element lib\vssh "%%c" %3 + for /f "delims=" %%c in ('dir /b ..\..\lib\vssh\*.c') do call :element lib\vssh "%%c" %3 ) else if "!var!" == "CURL_LIB_VSSH_H_FILES" ( - for /f "delims=" %%h in ('dir /b ..\lib\vssh\*.h') do call :element lib\vssh "%%h" %3 + for /f "delims=" %%h in ('dir /b ..\..\lib\vssh\*.h') do call :element lib\vssh "%%h" %3 ) else if "!var!" == "CURL_LIB_VTLS_C_FILES" ( - for /f "delims=" %%c in ('dir /b ..\lib\vtls\*.c') do call :element lib\vtls "%%c" %3 + for /f "delims=" %%c in ('dir /b ..\..\lib\vtls\*.c') do call :element lib\vtls "%%c" %3 ) else if "!var!" == "CURL_LIB_VTLS_H_FILES" ( - for /f "delims=" %%h in ('dir /b ..\lib\vtls\*.h') do call :element lib\vtls "%%h" %3 + for /f "delims=" %%h in ('dir /b ..\..\lib\vtls\*.h') do call :element lib\vtls "%%h" %3 + ) else if "!var!" == "CURL_SRC_TOOLX_C_FILES" ( + for /f "delims=" %%c in ('dir /b ..\..\src\toolx\*.c') do call :element src\toolx "%%c" %3 + ) else if "!var!" == "CURL_SRC_TOOLX_H_FILES" ( + for /f "delims=" %%h in ('dir /b ..\..\src\toolx\*.h') do call :element src\toolx "%%h" %3 ) else ( echo.!var!>> %3 ) diff --git a/projects/Windows/tmpl/curl.vcxproj b/projects/Windows/tmpl/curl.vcxproj index 940b106b74..c480fe8b33 100644 --- a/projects/Windows/tmpl/curl.vcxproj +++ b/projects/Windows/tmpl/curl.vcxproj @@ -839,7 +839,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -852,7 +852,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -869,7 +869,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -882,7 +882,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -898,7 +898,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -911,7 +911,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -928,7 +928,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -941,7 +941,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -956,7 +956,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -970,7 +970,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -988,7 +988,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -1002,7 +1002,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1019,7 +1019,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;%(PreprocessorDefinitions) true EnableFastChecks @@ -1033,7 +1033,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1051,7 +1051,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;%(PreprocessorDefinitions) true EnableFastChecks @@ -1065,7 +1065,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1082,7 +1082,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;%(PreprocessorDefinitions) true EnableFastChecks @@ -1096,7 +1096,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1114,7 +1114,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;%(PreprocessorDefinitions) true EnableFastChecks @@ -1128,7 +1128,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1145,7 +1145,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;%(PreprocessorDefinitions) true EnableFastChecks @@ -1159,7 +1159,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1176,7 +1176,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;%(PreprocessorDefinitions) true EnableFastChecks @@ -1190,7 +1190,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1208,7 +1208,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;%(PreprocessorDefinitions) true EnableFastChecks @@ -1222,7 +1222,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1240,7 +1240,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;%(PreprocessorDefinitions) true EnableFastChecks @@ -1254,7 +1254,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1272,7 +1272,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1285,7 +1285,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1302,7 +1302,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1315,7 +1315,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1331,7 +1331,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1344,7 +1344,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1360,7 +1360,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1373,7 +1373,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1390,7 +1390,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1403,7 +1403,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1420,7 +1420,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1433,7 +1433,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1448,7 +1448,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;%(PreprocessorDefinitions) true EnableFastChecks @@ -1462,7 +1462,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1480,7 +1480,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;%(PreprocessorDefinitions) true EnableFastChecks @@ -1494,7 +1494,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -1512,7 +1512,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1525,7 +1525,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1542,7 +1542,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1555,7 +1555,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -1570,7 +1570,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -1584,7 +1584,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win32\$SUBDIR\LIB Debug;%(AdditionalLibraryDirectories) true @@ -1602,7 +1602,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -1616,7 +1616,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win64\$SUBDIR\LIB Debug;%(AdditionalLibraryDirectories) true @@ -1634,7 +1634,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1647,7 +1647,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win32\$SUBDIR\LIB Release;%(AdditionalLibraryDirectories) Console @@ -1664,7 +1664,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1677,7 +1677,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win64\$SUBDIR\LIB Release;%(AdditionalLibraryDirectories) Console @@ -1692,7 +1692,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -1706,7 +1706,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;libssh2d.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;libssh2d.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win32\$SUBDIR\LIB Debug;..\..\..\..\..\libssh2\build\Win32\$SUBDIR\LIB Debug;%(AdditionalLibraryDirectories) true @@ -1724,7 +1724,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -1738,7 +1738,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;libssh2d.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;libssh2d.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win64\$SUBDIR\LIB Debug;..\..\..\..\..\libssh2\build\Win64\$SUBDIR\LIB Debug;%(AdditionalLibraryDirectories) true @@ -1756,7 +1756,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1769,7 +1769,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;libssh2.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;libssh2.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win32\$SUBDIR\LIB Release;..\..\..\..\..\libssh2\build\Win32\$SUBDIR\LIB Release;%(AdditionalLibraryDirectories) Console @@ -1786,7 +1786,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1799,7 +1799,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;libssh2.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;libssh2.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win64\$SUBDIR\LIB Release;..\..\..\..\..\libssh2\build\Win64\$SUBDIR\LIB Release;%(AdditionalLibraryDirectories) Console @@ -1814,7 +1814,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -1828,7 +1828,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win32\$SUBDIR\DLL Debug;%(AdditionalLibraryDirectories) true @@ -1846,7 +1846,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -1860,7 +1860,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win64\$SUBDIR\DLL Debug;%(AdditionalLibraryDirectories) true @@ -1878,7 +1878,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1891,7 +1891,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win32\$SUBDIR\DLL Release;%(AdditionalLibraryDirectories) Console @@ -1908,7 +1908,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -1921,7 +1921,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win64\$SUBDIR\DLL Release;%(AdditionalLibraryDirectories) Console @@ -1936,7 +1936,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -1950,7 +1950,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;libssh2d.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;libssh2d.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win32\$SUBDIR\DLL Debug;..\..\..\..\..\libssh2\build\Win32\$SUBDIR\DLL Debug;%(AdditionalLibraryDirectories) true @@ -1968,7 +1968,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -1982,7 +1982,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;libssh2d.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurld.lib;libcrypto.lib;libssl.lib;libssh2d.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win64\$SUBDIR\DLL Debug;..\..\..\..\..\libssh2\build\Win64\$SUBDIR\DLL Debug;%(AdditionalLibraryDirectories) true @@ -2000,7 +2000,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -2013,7 +2013,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;libssh2.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;libcrypto.lib;libssl.lib;libssh2.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win32\$SUBDIR\DLL Release;..\..\..\..\..\libssh2\build\Win32\$SUBDIR\DLL Release;%(AdditionalLibraryDirectories) Console @@ -2030,7 +2030,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -2043,7 +2043,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcurl.lib;libssh2.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcurl.lib;libssh2.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);..\..\..\..\..\openssl\build\Win64\$SUBDIR\DLL Release;..\..\..\..\..\libssh2\build\Win64\$SUBDIR\DLL Release;%(AdditionalLibraryDirectories) Console @@ -2058,7 +2058,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -2072,7 +2072,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -2089,7 +2089,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -2103,7 +2103,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -2121,7 +2121,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -2135,7 +2135,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -2153,7 +2153,7 @@ Disabled - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) _DEBUG;_CONSOLE;DEBUGBUILD;CURL_STATICLIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -2167,7 +2167,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;libcurld.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;libcurld.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) true @@ -2185,7 +2185,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -2198,7 +2198,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -2214,7 +2214,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -2227,7 +2227,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win32\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -2244,7 +2244,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -2257,7 +2257,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -2274,7 +2274,7 @@ MaxSpeed OnlyExplicitInline - ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\lib\curlx;%(AdditionalIncludeDirectories) + ..\..\..\..\include;..\..\..\..\lib;..\..\..\..\src;%(AdditionalIncludeDirectories) NDEBUG;_CONSOLE;CURL_STATICLIB;%(PreprocessorDefinitions) true MultiThreadedDLL @@ -2287,7 +2287,7 @@ ..\..\..\..\include;%(AdditionalIncludeDirectories) - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;libcurl.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;libcurl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\build\Win64\$SUBDIR\$(Configuration);%(AdditionalLibraryDirectories) Console @@ -2296,10 +2296,12 @@ CURL_LIB_CURLX_C_FILES +CURL_SRC_TOOLX_C_FILES CURL_SRC_C_FILES CURL_LIB_CURLX_H_FILES +CURL_SRC_TOOLX_H_FILES CURL_SRC_X_H_FILES CURL_SRC_H_FILES diff --git a/projects/Windows/tmpl/libcurl.vcxproj b/projects/Windows/tmpl/libcurl.vcxproj index f66765ca9a..dba1af6035 100644 --- a/projects/Windows/tmpl/libcurl.vcxproj +++ b/projects/Windows/tmpl/libcurl.vcxproj @@ -802,7 +802,7 @@ 0x0409 - ws2_32.lib;wldap32.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) $(IntDir)$(TargetFileName).intermediate.manifest @@ -836,7 +836,7 @@ 0x0409 - ws2_32.lib;wldap32.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) true @@ -869,7 +869,7 @@ 0x0409 - ws2_32.lib;wldap32.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) $(IntDir)$(TargetFileName).intermediate.manifest @@ -901,7 +901,7 @@ 0x0409 - ws2_32.lib;wldap32.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) $(TargetDir)$(TargetName).lib @@ -1018,7 +1018,7 @@ 0x0409 - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\openssl\build\Win32\$SUBDIR\DLL Debug;%(AdditionalLibraryDirectories) $(IntDir)$(TargetFileName).intermediate.manifest @@ -1052,7 +1052,7 @@ 0x0409 - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\openssl\build\Win64\$SUBDIR\DLL Debug;%(AdditionalLibraryDirectories) true @@ -1085,7 +1085,7 @@ 0x0409 - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\openssl\build\Win32\$SUBDIR\DLL Release;%(AdditionalLibraryDirectories) $(IntDir)$(TargetFileName).intermediate.manifest @@ -1117,7 +1117,7 @@ 0x0409 - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcrypto.lib;libssl.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\openssl\build\Win64\$SUBDIR\DLL Release;%(AdditionalLibraryDirectories) $(TargetDir)$(TargetName).lib @@ -1148,7 +1148,7 @@ 0x0409 - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) $(IntDir)$(TargetFileName).intermediate.manifest @@ -1182,7 +1182,7 @@ 0x0409 - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) $(IntDir)$(TargetFileName).intermediate.manifest @@ -1216,7 +1216,7 @@ 0x0409 - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) true @@ -1249,7 +1249,7 @@ 0x0409 - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) true @@ -1282,7 +1282,7 @@ 0x0409 - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) $(IntDir)$(TargetFileName).intermediate.manifest @@ -1314,7 +1314,7 @@ 0x0409 - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) $(IntDir)$(TargetFileName).intermediate.manifest @@ -1346,7 +1346,7 @@ 0x0409 - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) $(TargetDir)$(TargetName).lib @@ -1377,7 +1377,7 @@ 0x0409 - ws2_32.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;%(AdditionalDependencies) + ws2_32.lib;iphlpapi.lib;wldap32.lib;secur32.lib;crypt32.lib;normaliz.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) $(TargetDir)$(TargetName).lib @@ -1408,7 +1408,7 @@ 0x0409 - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcrypto.lib;libssl.lib;libssh2d.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcrypto.lib;libssl.lib;libssh2d.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\openssl\build\Win32\$SUBDIR\DLL Debug;..\..\..\..\..\libssh2\build\Win32\$SUBDIR\DLL Debug;%(AdditionalLibraryDirectories) $(IntDir)$(TargetFileName).intermediate.manifest @@ -1442,7 +1442,7 @@ 0x0409 - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcrypto.lib;libssl.lib;libssh2d.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcrypto.lib;libssl.lib;libssh2d.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\openssl\build\Win64\$SUBDIR\DLL Debug;..\..\..\..\..\libssh2\build\Win64\$SUBDIR\DLL Debug;%(AdditionalLibraryDirectories) true @@ -1475,7 +1475,7 @@ 0x0409 - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcrypto.lib;libssl.lib;libssh2.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcrypto.lib;libssl.lib;libssh2.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\openssl\build\Win32\$SUBDIR\DLL Release;..\..\..\..\..\libssh2\build\Win32\$SUBDIR\DLL Release;%(AdditionalLibraryDirectories) $(IntDir)$(TargetFileName).intermediate.manifest @@ -1507,7 +1507,7 @@ 0x0409 - secur32.lib;crypt32.lib;ws2_32.lib;wldap32.lib;libcrypto.lib;libssl.lib;libssh2.lib;%(AdditionalDependencies) + secur32.lib;crypt32.lib;ws2_32.lib;iphlpapi.lib;wldap32.lib;libcrypto.lib;libssl.lib;libssh2.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\openssl\build\Win64\$SUBDIR\DLL Release;..\..\..\..\..\libssh2\build\Win64\$SUBDIR\DLL Release;%(AdditionalLibraryDirectories) $(TargetDir)$(TargetName).lib diff --git a/packages/vms/Makefile.am b/projects/vms/Makefile.am similarity index 53% rename from packages/vms/Makefile.am rename to projects/vms/Makefile.am index e869a8978c..0a061bf20b 100644 --- a/packages/vms/Makefile.am +++ b/projects/vms/Makefile.am @@ -22,38 +22,38 @@ # ########################################################################### EXTRA_DIST = \ - backup_gnv_curl_src.com \ - build_curl-config_script.com \ - build_gnv_curl.com \ - build_gnv_curl_pcsi_desc.com \ - build_gnv_curl_pcsi_text.com \ - build_gnv_curl_release_notes.com \ - build_libcurl_pc.com \ - build_vms.com \ - clean_gnv_curl.com \ - compare_curl_source.com \ - config_h.com \ - curl_crtl_init.c \ - curl_gnv_build_steps.txt \ - curl_release_note_start.txt \ - curl_startup.com \ - curlmsg.h \ - curlmsg.msg \ - curlmsg.sdl \ - curlmsg_vms.h \ - generate_config_vms_h_curl.com \ - generate_vax_transfer.com \ - gnv_conftest.c_first \ - gnv_curl_configure.sh \ - gnv_libcurl_symbols.opt \ - gnv_link_curl.com \ - macro32_exactcase.patch \ - make_gnv_curl_install.sh \ - make_pcsi_curl_kit_name.com \ - pcsi_gnv_curl_file_list.txt \ - pcsi_product_gnv_curl.com \ - readme \ - report_openssl_version.c \ - setup_gnv_curl_build.com \ - stage_curl_install.com \ - vms_eco_level.h + backup_gnv_curl_src.com \ + build_curl-config_script.com \ + build_gnv_curl.com \ + build_gnv_curl_pcsi_desc.com \ + build_gnv_curl_pcsi_text.com \ + build_gnv_curl_release_notes.com \ + build_libcurl_pc.com \ + build_vms.com \ + clean_gnv_curl.com \ + compare_curl_source.com \ + config_h.com \ + curl_crtl_init.c \ + curl_gnv_build_steps.txt \ + curl_release_note_start.txt \ + curl_startup.com \ + curlmsg.h \ + curlmsg.msg \ + curlmsg.sdl \ + curlmsg_vms.h \ + generate_config_vms_h_curl.com \ + generate_vax_transfer.com \ + gnv_conftest.c_first \ + gnv_curl_configure.sh \ + gnv_libcurl_symbols.opt \ + gnv_link_curl.com \ + macro32_exactcase.patch \ + make_gnv_curl_install.sh \ + make_pcsi_curl_kit_name.com \ + pcsi_gnv_curl_file_list.txt \ + pcsi_product_gnv_curl.com \ + readme \ + report_openssl_version.c \ + setup_gnv_curl_build.com \ + stage_curl_install.com \ + vms_eco_level.h diff --git a/packages/vms/backup_gnv_curl_src.com b/projects/vms/backup_gnv_curl_src.com similarity index 100% rename from packages/vms/backup_gnv_curl_src.com rename to projects/vms/backup_gnv_curl_src.com diff --git a/packages/vms/build_curl-config_script.com b/projects/vms/build_curl-config_script.com similarity index 100% rename from packages/vms/build_curl-config_script.com rename to projects/vms/build_curl-config_script.com diff --git a/packages/vms/build_gnv_curl.com b/projects/vms/build_gnv_curl.com similarity index 100% rename from packages/vms/build_gnv_curl.com rename to projects/vms/build_gnv_curl.com diff --git a/packages/vms/build_gnv_curl_pcsi_desc.com b/projects/vms/build_gnv_curl_pcsi_desc.com similarity index 99% rename from packages/vms/build_gnv_curl_pcsi_desc.com rename to projects/vms/build_gnv_curl_pcsi_desc.com index 85674261ce..566384de02 100644 --- a/packages/vms/build_gnv_curl_pcsi_desc.com +++ b/projects/vms/build_gnv_curl_pcsi_desc.com @@ -22,7 +22,7 @@ $! $! The PCSI system can really only handle ODS-2 format filenames and $! assumes that there is only one source directory. It also assumes that $! all destination files with the same name come from the same source file. -$! Fortunately CURL does not trip most of these issues, so those steps +$! Fortunately curl does not trip most of these issues, so those steps $! above are marked N/A. $! $! A rename action section is needed to make sure that the files are diff --git a/packages/vms/build_gnv_curl_pcsi_text.com b/projects/vms/build_gnv_curl_pcsi_text.com similarity index 97% rename from packages/vms/build_gnv_curl_pcsi_text.com rename to projects/vms/build_gnv_curl_pcsi_text.com index 8f109c1e7e..b67ad8f187 100644 --- a/packages/vms/build_gnv_curl_pcsi_text.com +++ b/projects/vms/build_gnv_curl_pcsi_text.com @@ -2,9 +2,9 @@ $! File: Build_GNV_curl_pcsi_text.com $! $! Build the *.pcsi$text file from the four components: $! 1. Generated =product header section -$! 2. [--]readme. file from the Curl distribution, modified to fit +$! 2. [--]readme. file from the curl distribution, modified to fit $! a pcsi$text file format. -$! 3. [--]copying file from the Curl distribution, modified to fit +$! 3. [--]copying file from the curl distribution, modified to fit $! a pcsi$text file format. $! 4. Generated Producer section. $! diff --git a/packages/vms/build_gnv_curl_release_notes.com b/projects/vms/build_gnv_curl_release_notes.com similarity index 90% rename from packages/vms/build_gnv_curl_release_notes.com rename to projects/vms/build_gnv_curl_release_notes.com index 0da94454d5..89b30ecbe9 100644 --- a/packages/vms/build_gnv_curl_release_notes.com +++ b/projects/vms/build_gnv_curl_release_notes.com @@ -3,7 +3,7 @@ $! $! Build the release note file from the four components: $! 1. The curl_release_note_start.txt $! 2. The hp_ssl_release_info.txt -$! 3. [--]readme. file from the Curl distribution. +$! 3. [--]readme. file from the curl distribution. $! 4. The Curl_gnv-build_steps.txt. $! $! Set the name of the release notes from the GNV_PCSI_FILENAME_BASE @@ -42,7 +42,7 @@ $ curl_readme = f$search("sys$disk:[--]$README.") $ endif $ if curl_readme .eqs. "" $ then -$ write sys$output "Can not find Curl readme file." +$ write sys$output "Can not find curl readme file." $ goto all_exit $ endif $! @@ -53,7 +53,7 @@ $ curl_copying = f$search("sys$disk:[--]$COPYING.") $ endif $ if curl_copying .eqs. "" $ then -$ write sys$output "Can not find Curl copying file." +$ write sys$output "Can not find curl copying file." $ goto all_exit $ endif $! @@ -64,7 +64,7 @@ $ vms_readme = f$search("sys$disk:[]$README.") $ endif $ if vms_readme .eqs. "" $ then -$ write sys$output "Can not find VMS specific Curl readme file." +$ write sys$output "Can not find VMS specific curl readme file." $ goto all_exit $ endif $! @@ -75,7 +75,7 @@ $ curl_release_notes = f$search("sys$disk:[--]$RELEASE-NOTES.") $ endif $ if curl_release_notes .eqs. "" $ then -$ write sys$output "Can not find Curl release-notes file." +$ write sys$output "Can not find curl release-notes file." $ goto all_exit $ endif $! diff --git a/packages/vms/build_libcurl_pc.com b/projects/vms/build_libcurl_pc.com similarity index 100% rename from packages/vms/build_libcurl_pc.com rename to projects/vms/build_libcurl_pc.com diff --git a/packages/vms/build_vms.com b/projects/vms/build_vms.com similarity index 97% rename from packages/vms/build_vms.com rename to projects/vms/build_vms.com index d8f89f6ef4..253fccbc80 100644 --- a/packages/vms/build_vms.com +++ b/projects/vms/build_vms.com @@ -1,6 +1,6 @@ $! BUILD_VMS.COM $! -$! I've taken the original build_vms.com, supplied by Nico Baggus, if +$! I have taken the original build_vms.com, supplied by Nico Baggus, if $! memory serves me correctly, and made some modifications. $! $! SSL support is controlled by logical names. If SSL$INCLUDE is @@ -45,14 +45,14 @@ $! LIST Create C compiler listings and linker maps. $! /list/show=(expan,includ)/machine $! FULLLIST Full detailed listing. $! /list/show=(all, nomessages)/machine -$! NOHPSSL Don't use HP SSL, even if available. +$! NOHPSSL Do not use HP SSL, even if available. $! Note, you must match the pointer size that the OpenSSL $! shared image expects. This procedure will select the $! correct HP OpenSSL image. -$! NOSSL Don't use any SSL, even if available. +$! NOSSL Do not use any SSL, even if available. $! OSSLOLB Use OpenSSL object libraries (.OLB), even if shared $! images (.EXE) are available. -$! NOZLIB Don't use GNV$ZLIB shared image even if available. +$! NOZLIB Do not use GNV$ZLIB shared image even if available. $! REALCLEAN Delete product files for all host architectures. (No $! build done.) Alias for CLEAN_ALL $! @@ -65,7 +65,7 @@ $! Revisions: $! $! 2-DEC-2003, MSK, the "original" version. $! It works for me. Your mileage may vary. -$! 13-JAN-2004, MSK, moved this procedure to the [.packages.vms] directory +$! 13-JAN-2004, MSK, moved this procedure to the [.projects.vms] directory $! and updated it to do hardware dependent builds. $! 29-JAN-2004, MSK, moved logical defines into defines.com $! 6-FEB-2004, MSK, put in various SSL support bits @@ -516,7 +516,7 @@ $ endif $! $! $! CC /LIST, LINK /MAP, and MESSAGE /LIST are defaults in batch mode, -$! so be explicit when they're not desired. +$! so be explicit when they are not desired. $! $ $ if list .eq. 0 @@ -744,7 +744,7 @@ $ endif $ if ((f$search(ossl_lib1) .eqs. "") .or. - (f$search(ossl_lib2) .eqs. "")) $ then -$ write sys$output "Can't find OpenSSL ''msg':" +$ write sys$output "Cannot find OpenSSL ''msg':" $ write sys$output " ''ossl_lib1'" $ write sys$output " ''ossl_lib2'" $ goto Common_Exit @@ -820,17 +820,17 @@ $ then $ sys_inc = sys_inc + ",''curl_sys_zlibinc'" $ endif $! Build LIB -$ cc_include = "/include=([-.lib],[-.lib.vtls],[-.packages.vms]" -$ cc_include = cc_include + ",[-.packages.vms.''arch_name'])" +$ cc_include = "/include=([-.lib],[-.lib.vtls],[-.projects.vms]" +$ cc_include = cc_include + ",[-.projects.vms.''arch_name'])" $ call build "[--.lib]" "*.c" "''objdir'CURLLIB.OLB" "amigaos, nwlib, nwos" $ if ($status .eq. ctrl_y) then goto Common_Exit $! Build VTLS $ cc_include = "/include=([--.lib.vtls],[--.lib],[--.src]" -$ cc_include = cc_include + ",[--.packages.vms],[--.packages.vms.''arch_name'])" +$ cc_include = cc_include + ",[--.projects.vms],[--.projects.vms.''arch_name'])" $ call build "[--.lib.vtls]" "*.c" "''objdir'CURLLIB.OLB" "amigaos, nwlib, nwos" $! Build SRC $ cc_include = "/include=([-.src],[-.lib],[-.lib.vtls]" -$ cc_include = cc_include + ",[-.packages.vms],[-.packages.vms.''arch_name'])" +$ cc_include = cc_include + ",[-.projects.vms],[-.projects.vms.''arch_name'])" $ call build "[--.src]" "*.c" "''objdir'CURLSRC.OLB" $ if ($status .eq. ctrl_y) then goto Common_Exit $! Build MSG @@ -943,7 +943,7 @@ $ reset = f$search( "reset", 1) $Loop: $ file = f$search( search, 1) $ if file .eqs. "" then goto EndLoop -$! Skip a name if it's in the P4 exclusion list. +$! Skip a name if it is in the P4 exclusion list. $ if (p4 .nes. "") $ then $ name__ = "," + - @@ -1016,7 +1016,7 @@ $ endif $ ENDSUBROUTINE ! Compile $! $! Do a diff of the file specified in P1 with that in P2. If different -$! copy P1 to P2. This also covers if P2 doesn't exist, but not if P2 +$! copy P1 to P2. This also covers if P2 does not exist, but not if P2 $! is an invalid filespec. $! $MoveIfDiff: subroutine diff --git a/packages/vms/clean_gnv_curl.com b/projects/vms/clean_gnv_curl.com similarity index 90% rename from packages/vms/clean_gnv_curl.com rename to projects/vms/clean_gnv_curl.com index 1a9a0eb71a..ab515ede6a 100644 --- a/packages/vms/clean_gnv_curl.com +++ b/projects/vms/clean_gnv_curl.com @@ -103,34 +103,34 @@ $! $ file = "lcl_root:[...]*.lai" $ if f$search(file) .nes. "" then delete 'file';* $! -$ file = "lcl_root:[.packages.vms]curl-*_original_src.bck" +$ file = "lcl_root:[.projects.vms]curl-*_original_src.bck" $ if f$search(file) .nes. "" then delete 'file';* $! -$ file = "lcl_root:[.packages.vms]curl_d-*_original_src.bck" +$ file = "lcl_root:[.projects.vms]curl_d-*_original_src.bck" $ if f$search(file) .nes. "" then delete 'file';* $! -$ file = "lcl_root:[.packages.vms]curl-*_vms_src.bck" +$ file = "lcl_root:[.projects.vms]curl-*_vms_src.bck" $ if f$search(file) .nes. "" then delete 'file';* $! -$ file = "lcl_root:[.packages.vms]curl_d-*_vms_src.bck" +$ file = "lcl_root:[.projects.vms]curl_d-*_vms_src.bck" $ if f$search(file) .nes. "" then delete 'file';* $! -$ file = "lcl_root:[.packages.vms]curl-*.release_notes" +$ file = "lcl_root:[.projects.vms]curl-*.release_notes" $ if f$search(file) .nes. "" then delete 'file';* $! -$ file = "lcl_root:[.packages.vms]curl_d-*.release_notes" +$ file = "lcl_root:[.projects.vms]curl_d-*.release_notes" $ if f$search(file) .nes. "" then delete 'file';* $! -$ file = "lcl_root:[.packages.vms]*-curl-*.pcsi$desc" +$ file = "lcl_root:[.projects.vms]*-curl-*.pcsi$desc" $ if f$search(file) .nes. "" then delete 'file';* $! -$ file = "lcl_root:[.packages.vms]*-curl_d-*.pcsi$desc" +$ file = "lcl_root:[.projects.vms]*-curl_d-*.pcsi$desc" $ if f$search(file) .nes. "" then delete 'file';* $! -$ file = "lcl_root:[.packages.vms]*-curl-*.pcsi$text" +$ file = "lcl_root:[.projects.vms]*-curl-*.pcsi$text" $ if f$search(file) .nes. "" then delete 'file';* $! -$ file = "lcl_root:[.packages.vms]*-curl_d-*.pcsi$text" +$ file = "lcl_root:[.projects.vms]*-curl_d-*.pcsi$text" $ if f$search(file) .nes. "" then delete 'file';* $! $!====================================================================== @@ -218,13 +218,13 @@ $! $ file = "lcl_root:[...]gnv*.opt" $ if f$search(file) .nes. "" then delete 'file';* $! -$ file = "lcl_root:[.packages.vms]macro32_exactcase.exe" +$ file = "lcl_root:[.projects.vms]macro32_exactcase.exe" $ if f$search(file) .nes. "" then delete 'file';* $! -$ file = "lcl_root:[.packages.vms]report_openssl_version.exe" +$ file = "lcl_root:[.projects.vms]report_openssl_version.exe" $ if f$search(file) .nes. "" then delete 'file';* $! -$ file = "lcl_root:[.packages.vms]hp_ssl_release_info.txt" +$ file = "lcl_root:[.projects.vms]hp_ssl_release_info.txt" $ if f$search(file) .nes. "" then delete 'file';* $! $ file = "lcl_root:[.src]curl.exe" diff --git a/packages/vms/compare_curl_source.com b/projects/vms/compare_curl_source.com similarity index 99% rename from packages/vms/compare_curl_source.com rename to projects/vms/compare_curl_source.com index 13ff4667f9..b63080620e 100644 --- a/packages/vms/compare_curl_source.com +++ b/projects/vms/compare_curl_source.com @@ -255,7 +255,7 @@ $ then $ ref_fname = f$edit(ref_fname, "LOWERCASE") $ endif $! -$! These files are in the wrong format for VMS diff, and we don't change them. +$! These files are in the wrong format for VMS diff, and we do not change them. $ ref_skip = 0 $ if ref_type .eqs. ".PDF" then ref_skip = 1 $ if ref_type .eqs. ".HTML" then ref_skip = 1 diff --git a/packages/vms/config_h.com b/projects/vms/config_h.com similarity index 98% rename from packages/vms/config_h.com rename to projects/vms/config_h.com index c6f3f69721..e42ecd9d78 100644 --- a/packages/vms/config_h.com +++ b/projects/vms/config_h.com @@ -1172,17 +1172,6 @@ $ write tf "#endif" $ goto cfgh_in_loop1 $ endif $! -$ if keysym .eqs. "LONGLONG" -$ then -$ write tf "#ifndef __VAX" -$ write tf "#pragma message disable longlongtype" -$ write tf "#ifndef HAVE_LONGLONG" -$ write tf "#define HAVE_LONGLONG 1" -$ write tf "#endif" -$ write tf "#endif" -$ goto cfgh_in_loop1 -$ endif -$! $! May need to test compiler version $!----------------------------------------------- $ if keysym .eqs. "LONG_LONG" @@ -1471,12 +1460,6 @@ $ then $ write tf "#ifndef SIZEOF_LONG" $ write tf "#define SIZEOF_LONG 4" $ write tf "#endif" -$ else -$ write tf "#ifndef SIZEOF_LONG_LONG" -$ write tf "#ifndef __VAX" -$ write tf "#define SIZEOF_LONG_LONG 8" -$ write tf "#endif" -$ write tf "#endif" $ endif $ goto cfgh_in_loop1 $ endif @@ -1599,13 +1582,6 @@ $! Process SEND directives $!------------------------------------- $ if key2a .eqs. "SEND" $ then -$ if key2 .eqs. "SEND_QUAL_ARG2" -$ then -$ write tf "#ifndef ''key2'" -$ write tf "#define ''key2' const" -$ write tf "#endif" -$ goto cfgh_in_loop1 -$ endif $ if key2 .eqs. "SEND_TYPE_ARG1" $ then $ write tf "#ifndef ''key2'" diff --git a/packages/vms/curl_crtl_init.c b/projects/vms/curl_crtl_init.c similarity index 82% rename from packages/vms/curl_crtl_init.c rename to projects/vms/curl_crtl_init.c index 90bcb4c359..a044ee72e0 100644 --- a/packages/vms/curl_crtl_init.c +++ b/projects/vms/curl_crtl_init.c @@ -33,7 +33,7 @@ * will turn on some CRTL features that are not enabled by default. * * The CRTL features can also be turned on via logical names, but that - * impacts all programs and some aren't ready, willing, or able to handle + * impacts all programs and some are not ready, willing, or able to handle * those settings. * * On VMS versions that are too old to use the feature setting API, this @@ -78,34 +78,29 @@ struct itmlst_3 { #pragma member_alignment restore #ifdef __VAX -#define ENABLE "ENABLE" +#define ENABLE "ENABLE" #define DISABLE "DISABLE" #else -#define ENABLE TRUE +#define ENABLE TRUE #define DISABLE 0 -int decc$feature_get_index (const char *name); -int decc$feature_set_value (int index, int mode, int value); +int decc$feature_get_index(const char *name); +int decc$feature_set_value(int index, int mode, int value); #endif -int SYS$TRNLNM( - const unsigned long *attr, - const struct dsc$descriptor_s *table_dsc, - struct dsc$descriptor_s *name_dsc, - const unsigned char *acmode, - const struct itmlst_3 *item_list); -int SYS$CRELNM( - const unsigned long *attr, - const struct dsc$descriptor_s *table_dsc, - const struct dsc$descriptor_s *name_dsc, - const unsigned char *acmode, - const struct itmlst_3 *item_list); +int SYS$TRNLNM(const unsigned long *attr, + const struct dsc$descriptor_s *table_dsc, + struct dsc$descriptor_s *name_dsc, + const unsigned char *acmode, + const struct itmlst_3 *item_list); +int SYS$CRELNM(const unsigned long *attr, + const struct dsc$descriptor_s *table_dsc, + const struct dsc$descriptor_s *name_dsc, + const unsigned char *acmode, + const struct itmlst_3 *item_list); - -/* Take all the fun out of simply looking up a logical name */ -static int sys_trnlnm(const char *logname, - char *value, - int value_len) +/* Take all the fun out of looking up a logical name */ +static int sys_trnlnm(const char *logname, char *value, int value_len) { const $DESCRIPTOR(table_dsc, "LNM$FILE_DEV"); const unsigned long attr = LNM$M_CASE_BLIND; @@ -139,9 +134,8 @@ static int sys_trnlnm(const char *logname, return status; } -/* How to simply create a logical name */ -static int sys_crelnm(const char *logname, - const char *value) +/* How to create a logical name */ +static int sys_crelnm(const char *logname, const char *value) { int ret_val; const char *proc_table = "LNM$PROCESS_TABLE"; @@ -149,12 +143,12 @@ static int sys_crelnm(const char *logname, struct dsc$descriptor_s logname_dsc; struct itmlst_3 item_list[2]; - proc_table_dsc.dsc$a_pointer = (char *) proc_table; + proc_table_dsc.dsc$a_pointer = (char *)proc_table; proc_table_dsc.dsc$w_length = strlen(proc_table); proc_table_dsc.dsc$b_dtype = DSC$K_DTYPE_T; proc_table_dsc.dsc$b_class = DSC$K_CLASS_S; - logname_dsc.dsc$a_pointer = (char *) logname; + logname_dsc.dsc$a_pointer = (char *)logname; logname_dsc.dsc$w_length = strlen(logname); logname_dsc.dsc$b_dtype = DSC$K_DTYPE_T; logname_dsc.dsc$b_class = DSC$K_CLASS_S; @@ -172,8 +166,7 @@ static int sys_crelnm(const char *logname, return ret_val; } - - /* Start of DECC RTL Feature handling */ +/* Start of DECC RTL Feature handling */ /* ** Sets default value for a feature @@ -191,7 +184,7 @@ static void set_feature_default(const char *name, int value) index = decc$feature_get_index(name); if(index > 0) - decc$feature_set_value (index, 0, value); + decc$feature_set_value(index, 0, value); } #endif @@ -202,7 +195,7 @@ static void set_features(void) int use_unix_settings = 1; status = sys_trnlnm("GNV$UNIX_SHELL", - unix_shell_name, sizeof(unix_shell_name) -1); + unix_shell_name, sizeof(unix_shell_name) - 1); if(!$VMS_STATUS_SUCCESS(status)) { use_unix_settings = 0; } @@ -213,7 +206,6 @@ static void set_features(void) /* We always want the new parse style */ set_feature_default("DECC$ARGV_PARSE_STYLE", ENABLE); - /* Unless we are in POSIX compliant mode, we want the old POSIX root * enabled. */ @@ -237,14 +229,14 @@ static void set_features(void) set_feature_default("DECC$EXEC_FILEATTR_INHERITANCE", 2); #endif - /* Don't display trailing dot after files without type */ + /* Do not display trailing dot after files without type */ set_feature_default("DECC$READDIR_DROPDOTNOTYPE", ENABLE); /* For standard output channels buffer output until terminator */ /* Gets rid of output logs with single character lines in them. */ set_feature_default("DECC$STDIO_CTX_EOL", ENABLE); - /* Fix mv aa.bb aa */ + /* Fix mv aa.bb aa */ set_feature_default("DECC$RENAME_NO_INHERIT", ENABLE); if(use_unix_settings) { @@ -263,7 +255,6 @@ static void set_features(void) set_feature_default("DECC$FILE_OWNER_UNIX", ENABLE); set_feature_default("DECC$POSIX_SEEK_STREAM_FILE", ENABLE); - } else { set_feature_default("DECC$FILENAME_UNIX_REPORT", ENABLE); @@ -283,13 +274,14 @@ static void set_features(void) /* Set strtol to proper behavior */ set_feature_default("DECC$STRTOL_ERANGE", ENABLE); - /* Commented here to prevent future bugs: A program or user should */ + /* Commented here to prevent future bugs: A program or user should */ /* never ever enable DECC$POSIX_STYLE_UID. */ /* It will probably break all code that accesses UIDs */ - /* do_not_set_default ("DECC$POSIX_STYLE_UID", TRUE); */ +#if 0 + do_not_set_default("DECC$POSIX_STYLE_UID", TRUE); +#endif } - /* Some boilerplate to force this to be a proper LIB$INITIALIZE section */ #pragma nostandard @@ -298,25 +290,24 @@ static void set_features(void) #pragma extern_model strict_refdef "LIB$INITIALIZE" nowrt, long, nopic #else #pragma extern_model strict_refdef "LIB$INITIALIZE" nowrt, long -# if __INITIAL_POINTER_SIZE -# pragma __pointer_size __save -# pragma __pointer_size 32 -# else -# pragma __required_pointer_size __save -# pragma __required_pointer_size 32 -# endif +# if __INITIAL_POINTER_SIZE +# pragma __pointer_size __save +# pragma __pointer_size 32 +# else +# pragma __required_pointer_size __save +# pragma __required_pointer_size 32 +# endif #endif /* Set our contribution to the LIB$INITIALIZE array */ -void (* const iniarray[])(void) = {set_features }; +void (* const iniarray[])(void) = { set_features }; #ifndef __VAX -# if __INITIAL_POINTER_SIZE -# pragma __pointer_size __restore -# else -# pragma __required_pointer_size __restore -# endif +# if __INITIAL_POINTER_SIZE +# pragma __pointer_size __restore +# else +# pragma __required_pointer_size __restore +# endif #endif - /* ** Force a reference to LIB$INITIALIZE to ensure it ** exists in the image. @@ -325,7 +316,7 @@ int LIB$INITIALIZE(void); #ifdef __DECC #pragma extern_model strict_refdef #endif - int lib_init_ref = (int) LIB$INITIALIZE; +int lib_init_ref = (int)LIB$INITIALIZE; #ifdef __DECC #pragma extern_model restore #pragma standard diff --git a/packages/vms/curl_gnv_build_steps.txt b/projects/vms/curl_gnv_build_steps.txt similarity index 97% rename from packages/vms/curl_gnv_build_steps.txt rename to projects/vms/curl_gnv_build_steps.txt index b7ea95219b..c02c898dad 100644 --- a/packages/vms/curl_gnv_build_steps.txt +++ b/projects/vms/curl_gnv_build_steps.txt @@ -16,11 +16,11 @@ From File: curl_gnv_build_steps.txt SPDX-License-Identifier: ISC -Currently building Curl using GNV takes longer than building Curl via DCL. +Currently building curl using GNV takes longer than building curl via DCL. The GNV procedure actually uses the same configure and makefiles that Unix builds use. -Building CURL on OpenVMS using GNV requires GNV V2.1-2 or the updated +Building curl on OpenVMS using GNV requires GNV V2.1-2 or the updated images that are available via anonymous FTP at encompasserve.org in the gnv directory. It also requires the GNV Bash 4.2.45 kit as an update from the same location or from the sourceforge.net GNV project. @@ -38,7 +38,7 @@ time that this document was written. [gnv.common_src]curl_*_original_src.bck is the original source of the curl kit as provided by the curl project. [gnv.vms_src]curl-*_vms_src.bck, if present, has the OpenVMS specific files that are used for building that are not yet in -the curl source kits for that release distributed https://curl.se +the curl source kits for that release distributed https://curl.se/ These backup savesets should be restored to different directory trees on an ODS-5 volume(s) which are referenced by concealed rooted logical names. @@ -48,7 +48,7 @@ VMS_ROOT: is for the source files that are specific to OpenVMS. Note, you should create the VMS_ROOT: directory tree even if it is initially empty. This is where you should put edits if you are making changes. -LCL_ROOT: is manually created to have the same base and sub-directories as +LCL_ROOT: is manually created to have the same base and subdirectories as SRC_ROOT: and VMS_ROOT: The logical name REF_ROOT: may be defined to be a search list for @@ -100,7 +100,7 @@ Note to builders: GNV currently has a bug where configure scripts take a long time to run. Some of the configure steps take a while to complete, and on a 600 Mhz -DS10 with IDE disks, taking an hour to run the CURL configure is normal. +DS10 with IDE disks, taking an hour to run the curl configure is normal. The following messages can be ignored and may get fixed in a future version of GNV. The GNV$*.OPT files are used to find the libraries as many have @@ -117,7 +117,7 @@ u unimplemented switch - ignored With these search lists set up and the properly, curl can be built by -setting your default to PRJ_ROOT:[curl.packages.vms] and then issuing +setting your default to PRJ_ROOT:[curl.projects.vms] and then issuing either the command: $ @pcsi_product_gnv_curl.com diff --git a/packages/vms/curl_release_note_start.txt b/projects/vms/curl_release_note_start.txt similarity index 93% rename from packages/vms/curl_release_note_start.txt rename to projects/vms/curl_release_note_start.txt index 62b2836359..1a67b36020 100644 --- a/packages/vms/curl_release_note_start.txt +++ b/projects/vms/curl_release_note_start.txt @@ -16,16 +16,16 @@ OpenVMS specific building and kitting instructions are after the standard curl readme file. This product may be available for your platform in a PCSI kit. The source kit -contains files for building CURL using GNV or with a DCL procedure. +contains files for building curl using GNV or with a DCL procedure. The GNV based build creates a libcurl share imaged which is supplied in the PCSI kit. -This version of CURL will return VMS compatible status codes when run from +This version of curl will return VMS compatible status codes when run from DCL and Unix compatible exit codes and messages when run with the SHELL environment variable set. -This port of Curl uses the OpenSSL, Ldap, and Kerberos V5 that are bundled +This port of curl uses the OpenSSL, Ldap, and Kerberos V5 that are bundled with OpenVMS or supplied as updates by HP. Ldap and Kerberos are not available on the VAX platform. See section below for a special note about HP OpenSSL on Alpha and IA64. @@ -61,7 +61,7 @@ For the HP SSL work around to work for GNV do the following: Similar workarounds will be needed for any program linked with GNV$LIBCURL until the HP OpenSSL is upgraded to the current 1.4 version or later. -If you are installing a "daily" build instead of a release build of Curl, some +If you are installing a "daily" build instead of a release build of curl, some things have been changed so that it can be installed at the same time as a production build without conflicts. diff --git a/packages/vms/curl_startup.com b/projects/vms/curl_startup.com similarity index 97% rename from packages/vms/curl_startup.com rename to projects/vms/curl_startup.com index e90bbecbb9..7e5b2729d8 100644 --- a/packages/vms/curl_startup.com +++ b/projects/vms/curl_startup.com @@ -1,6 +1,6 @@ $! File: curl_Startup.com $! -$! Procedure to setup the CURL libraries for use by programs from the +$! Procedure to setup the curl libraries for use by programs from the $! VMS SYSTARTUP*.COM procedure. $! $! Copyright (C) John Malmberg @@ -75,7 +75,7 @@ $ define/system/exec gnv$curl_ssl_libcryptoshr32 'curl_ssl_libcrypto32' $ define/system/exec gnv$curl_ssl_libsslshr32 'curl_ssl_libssl32' $! $! -$! CURL setup +$! curl setup $ define/system/exec gnv$libcurl gnv$gnu:[usr.lib]GNV$LIBCURL.EXE $ define/system/exec gnv$curl_include gnv$gnu:[usr.include.curl] $ if .not. f$file_attributes("gnv$libcurl", "known") diff --git a/projects/vms/curlmsg.h b/projects/vms/curlmsg.h new file mode 100644 index 0000000000..f05c3a8f5a --- /dev/null +++ b/projects/vms/curlmsg.h @@ -0,0 +1,143 @@ +#ifndef HEADER_CURLMSG_H +#define HEADER_CURLMSG_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 + * + ***************************************************************************/ + +#pragma __member_alignment __save +#pragma __nomember_alignment + +/* */ +/* CURLMSG.H */ +/* */ +/* SDL File Generated by VAX-11 Message V04-00 on 3-SEP-2008 13:33:54.09 */ +/* */ +/* THESE VMS ERROR CODES ARE GENERATED BY TAKING APART THE CURL.H */ +/* FILE AND PUTTING ALL THE CURLE_* ENUM STUFF INTO THIS FILE, */ +/* CURLMSG.MSG. AN .SDL FILE IS CREATED FROM THIS FILE WITH */ +/* MESSAGE/SDL. THE .H FILE IS CREATED USING THE FREEWARE SDL TOOL */ +/* AGAINST THE .SDL FILE WITH SDL/ALPHA/LANG=CC COMMAND. */ +/* */ +/* WITH THE EXCEPTION OF CURLE_OK, ALL OF THE MESSAGES ARE AT */ +/* THE ERROR SEVERITY LEVEL. WITH THE EXCEPTION OF */ +/* PEER_FAILED_VERIF, WHICH IS A SHORTENED FORM OF */ +/* PEER_FAILED_VERIFICATION, THESE ARE THE SAME NAMES AS THE */ +/* CURLE_ ONES IN INCLUDE/CURL.H. THE MESSAGE UTILITY MANUAL STATES */ +/* "THE COMBINED LENGTH OF THE PREFIX AND THE MESSAGE SYMBOL NAME CANNOT */ +/* EXCEED 31 CHARACTERS." WITH A PREFIX OF FIVE THAT LEAVES US WITH 26 */ +/* FOR THE MESSAGE NAME. */ +/* */ +/* IF YOU UPDATE THIS FILE, UPDATE CURLMSG_VMS.H SO THAT THEY ARE IN SYNC */ +/* */ + +#define CURL_FACILITY 3841 +#define CURL_OK 251756553 +#define CURL_UNSUPPORTED_PROTOCOL 251756562 +#define CURL_FAILED_INIT 251756570 +#define CURL_URL_MALFORMAT 251756578 +#define CURL_OBSOLETE4 251756586 +#define CURL_COULDNT_RESOLVE_PROXY 251756594 +#define CURL_COULDNT_RESOLVE_HOST 251756602 +#define CURL_COULDNT_CONNECT 251756610 +#define CURL_WEIRD_SERVER_REPLY 251756618 +#define CURL_FTP_WEIRD_SERVER_REPLY CURL_WEIRD_SERVER_REPLY +#define CURL_FTP_ACCESS_DENIED 251756626 +#define CURL_OBSOLETE10 251756634 +#define CURL_FTP_WEIRD_PASS_REPLY 251756642 +#define CURL_OBSOLETE12 251756650 +#define CURL_FTP_WEIRD_PASV_REPLY 251756658 +#define CURL_FTP_WEIRD_227_FORMAT 251756666 +#define CURL_FTP_CANT_GET_HOST 251756674 +#define CURL_OBSOLETE16 251756682 +#define CURL_FTP_COULDNT_SET_TYPE 251756690 +#define CURL_PARTIAL_FILE 251756698 +#define CURL_FTP_COULDNT_RETR_FILE 251756706 +#define CURL_OBSOLETE20 251756714 +#define CURL_QUOTE_ERROR 251756722 +#define CURL_HTTP_RETURNED_ERROR 251756730 +#define CURL_WRITE_ERROR 251756738 +#define CURL_OBSOLETE24 251756746 +#define CURL_UPLOAD_FAILED 251756754 +#define CURL_READ_ERROR 251756762 +#define CURL_OUT_OF_MEMORY 251756770 +#define CURL_OPERATION_TIMEOUTED 251756778 +#define CURL_OBSOLETE29 251756786 +#define CURL_FTP_PORT_FAILED 251756794 +#define CURL_FTP_COULDNT_USE_REST 251756802 +#define CURL_OBSOLETE32 251756810 +#define CURL_RANGE_ERROR 251756818 +#define CURL_HTTP_POST_ERROR 251756826 +#define CURL_SSL_CONNECT_ERROR 251756834 +#define CURL_BAD_DOWNLOAD_RESUME 251756842 +#define CURL_FILE_COULDNT_READ_FILE 251756850 +#define CURL_LDAP_CANNOT_BIND 251756858 +#define CURL_LDAP_SEARCH_FAILED 251756866 +#define CURL_OBSOLETE40 251756874 +#define CURL_FUNCTION_NOT_FOUND 251756882 +#define CURL_ABORTED_BY_CALLBACK 251756890 +#define CURL_BAD_FUNCTION_ARGUMENT 251756898 +#define CURL_OBSOLETE44 251756906 +#define CURL_INTERFACE_FAILED 251756914 +#define CURL_OBSOLETE46 251756922 +#define CURL_TOO_MANY_REDIRECTS 251756930 +#define CURL_UNKNOWN_TELNET_OPTION 251756938 +#define CURL_TELNET_OPTION_SYNTAX 251756946 +#define CURL_OBSOLETE50 251756954 +#define CURL_PEER_FAILED_VERIF 251756962 +#define CURL_GOT_NOTHING 251756970 +#define CURL_SSL_ENGINE_NOTFOUND 251756978 +#define CURL_SSL_ENGINE_SETFAILED 251756986 +#define CURL_SEND_ERROR 251756994 +#define CURL_RECV_ERROR 251757002 +#define CURL_OBSOLETE57 251757010 +#define CURL_SSL_CERTPROBLEM 251757018 +#define CURL_SSL_CIPHER 251757026 +#define CURL_SSL_CACERT 251757034 +#define CURL_BAD_CONTENT_ENCODING 251757042 +#define CURL_LDAP_INVALID_URL 251757050 +#define CURL_FILESIZE_EXCEEDED 251757058 +#define CURL_USE_SSL_FAILED 251757066 +#define CURL_SEND_FAIL_REWIND 251757074 +#define CURL_SSL_ENGINE_INITFAILED 251757082 +#define CURL_LOGIN_DENIED 251757090 +#define CURL_TFTP_NOTFOUND 251757098 +#define CURL_TFTP_PERM 251757106 +#define CURL_REMOTE_DISK_FULL 251757114 +#define CURL_TFTP_ILLEGAL 251757122 +#define CURL_TFTP_UNKNOWNID 251757130 +#define CURL_REMOTE_FILE_EXISTS 251757138 +#define CURL_TFTP_NOSUCHUSER 251757146 +#define CURL_CONV_FAILED 251757154 +#define CURL_CONV_REQD 251757162 +#define CURL_SSL_CACERT_BADFILE 251757170 +#define CURL_REMOTE_FILE_NOT_FOUND 251757178 +#define CURL_SSH 251757186 +#define CURL_SSL_SHUTDOWN_FAILED 251757194 +#define CURL_AGAIN 251757202 +#define CURL_SSL_CRL_BADFILE 251757210 +#define CURL_SSL_ISSUER_ERROR 251757218 +#define CURL_CURL_LAST 251757226 + +#pragma __member_alignment __restore + +#endif /* HEADER_CURLMSG_H */ diff --git a/packages/vms/curlmsg.msg b/projects/vms/curlmsg.msg similarity index 100% rename from packages/vms/curlmsg.msg rename to projects/vms/curlmsg.msg diff --git a/packages/vms/curlmsg.sdl b/projects/vms/curlmsg.sdl similarity index 100% rename from packages/vms/curlmsg.sdl rename to projects/vms/curlmsg.sdl diff --git a/packages/vms/curlmsg_vms.h b/projects/vms/curlmsg_vms.h similarity index 94% rename from packages/vms/curlmsg_vms.h rename to projects/vms/curlmsg_vms.h index 29a38db493..83af6400f0 100644 --- a/packages/vms/curlmsg_vms.h +++ b/projects/vms/curlmsg_vms.h @@ -37,20 +37,6 @@ #include "curlmsg.h" -/* -#define FAC_CURL 0xC01 -#define FAC_SYSTEM 0 -#define MSG_NORMAL 0 -*/ - -/* -#define SEV_WARNING 0 -#define SEV_SUCCESS 1 -#define SEV_ERROR 2 -#define SEV_INFO 3 -#define SEV_FATAL 4 -*/ - static const long vms_cond[] = { CURL_OK, diff --git a/packages/vms/generate_config_vms_h_curl.com b/projects/vms/generate_config_vms_h_curl.com similarity index 94% rename from packages/vms/generate_config_vms_h_curl.com rename to projects/vms/generate_config_vms_h_curl.com index 271ac60869..e4d97fd566 100644 --- a/packages/vms/generate_config_vms_h_curl.com +++ b/projects/vms/generate_config_vms_h_curl.com @@ -1,6 +1,6 @@ $! File: GENERATE_CONFIG_H_CURL.COM $! -$! Curl like most open source products uses a variant of a config.h file. +$! curl like most open source products uses a variant of a config.h file. $! Depending on the curl version, this could be config.h or curl_config.h. $! $! For GNV based builds, the configure script is run and that produces @@ -197,7 +197,7 @@ $write cvh "#define __CONFIG_VMS_H__" $write cvh "" $write cvh "/* Define cpu-machine-OS */" $! -$! Curl uses an OS macro to set the build environment. +$! curl uses an OS macro to set the build environment. $!---------------------------------------------------- $! Now the DCL builds usually say xxx-HP-VMS and configure scripts $! may put DEC or COMPAQ or HP for the middle part. @@ -262,15 +262,9 @@ $write cvh "#ifdef CURL_DISABLE_LIBCURL_OPTION" $write cvh "#undef CURL_DISABLE_LIBCURL_OPTION" $write cvh "#endif" $write cvh "#ifndef __VAX" -$write cvh "#ifdef CURL_DISABLE_NTLM" -$write cvh "#undef CURL_DISABLE_NTLM" -$write cvh "#endif" $write cvh "#else" $! NTLM needs long long or int64 support, missing from DECC C. $write cvh "#ifdef __DECC -$write cvh "#ifndef CURL_DISABLE_NTLM" -$write cvh "#define CURL_DISABLE_NTLM 1" -$write cvh "#endif" $write cvh "#endif" $write cvh "#endif" $write cvh "#ifdef CURL_DISABLE_POP3" @@ -315,9 +309,6 @@ $write cvh "#endif" $write cvh "#ifdef USE_GNUTLS" $write cvh "#undef USE_GNUTLS" $write cvh "#endif" -$write cvh "#ifdef USE_LIBRTMP" -$write cvh "#undef USE_LIBRTMP" -$write cvh "#endif" $write cvh "#ifdef USE_MANUAL" $write cvh "#undef USE_MANUAL" $write cvh "#endif" @@ -327,8 +318,8 @@ $write cvh "#endif" $write cvh "#ifdef USE_OPENLDAP" $write cvh "#undef USE_OPENLDAP" $write cvh "#endif" -$write cvh "#ifdef USE_THREADS_POSIX" -$write cvh "#undef USE_THREADS_POSIX" +$write cvh "#ifdef USE_RESOLV_THREADED" +$write cvh "#undef USE_RESOLV_THREADED" $write cvh "#endif" $write cvh "#ifdef USE_TLS_SRP" $write cvh "#undef USE_TLS_SRP" @@ -337,11 +328,6 @@ $write cvh "#ifdef USE_UNIX_SOCKETS" $write cvh "#undef USE_UNIX_SOCKETS" $write cvh "#endif" $! -$write cvh "#ifndef HAVE_OLD_GSSMIT" -$write cvh "#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE" -$write cvh "#endif" -$! -$! $! Note: $! The CURL_EXTERN_SYMBOL is used for platforms that need the compiler $! to know about universal symbols. VMS does not need this support so @@ -350,7 +336,7 @@ $! $! $! I can not figure out where the C compiler is finding the ALLOCA.H file $! in the text libraries, so CONFIG_H.COM can not find it either. -$! Usually the header file name is the module name in the text library. +$! Usually the header filename is the module name in the text library. $! It does not appear to hurt anything to not find header file, so we $! are not overriding it here. $! diff --git a/packages/vms/generate_vax_transfer.com b/projects/vms/generate_vax_transfer.com similarity index 100% rename from packages/vms/generate_vax_transfer.com rename to projects/vms/generate_vax_transfer.com diff --git a/packages/vms/gnv_conftest.c_first b/projects/vms/gnv_conftest.c_first similarity index 100% rename from packages/vms/gnv_conftest.c_first rename to projects/vms/gnv_conftest.c_first diff --git a/packages/vms/gnv_curl_configure.sh b/projects/vms/gnv_curl_configure.sh similarity index 85% rename from packages/vms/gnv_curl_configure.sh rename to projects/vms/gnv_curl_configure.sh index 21558001ed..5f2038f87c 100755 --- a/packages/vms/gnv_curl_configure.sh +++ b/projects/vms/gnv_curl_configure.sh @@ -1,6 +1,6 @@ # File: gnv_curl_configure.sh # -# Set up and run the configure script for Curl so that it can find the +# Set up and run the configure script for curl so that it can find the # proper options for VMS. # # Copyright (C) John Malmberg @@ -38,7 +38,7 @@ export GNV_CC_QUALIFIERS=/STANDARD=RELAXED cd ../.. # # -./configure --prefix=/usr --exec-prefix=/usr --disable-dependency-tracking \ - --disable-libtool-lock --with-gssapi --disable-ntlm-wb \ - --with-ca-path=gnv\$curl_ca_path +./configure --prefix=/usr --exec-prefix=/usr --disable-dependency-tracking \ + --disable-libtool-lock --with-gssapi --disable-ntlm-wb \ + --with-ca-path=gnv\$curl_ca_path # diff --git a/packages/vms/gnv_libcurl_symbols.opt b/projects/vms/gnv_libcurl_symbols.opt similarity index 100% rename from packages/vms/gnv_libcurl_symbols.opt rename to projects/vms/gnv_libcurl_symbols.opt diff --git a/packages/vms/gnv_link_curl.com b/projects/vms/gnv_link_curl.com similarity index 96% rename from packages/vms/gnv_link_curl.com rename to projects/vms/gnv_link_curl.com index 83a8e021e8..330613b76d 100644 --- a/packages/vms/gnv_link_curl.com +++ b/projects/vms/gnv_link_curl.com @@ -70,20 +70,20 @@ $! $! $! Build the Message file. $!-------------------------- -$ if f$search("[.packages.vms]curlmsg.obj") .eqs. "" +$ if f$search("[.projects.vms]curlmsg.obj") .eqs. "" $ then -$ message [.packages.vms]curlmsg.msg/object=[.packages.vms] +$ message [.projects.vms]curlmsg.msg/object=[.projects.vms] $ endif $ if f$search("gnv$curlmsg.exe") .eqs. "" $ then -$ link/share=gnv$curlmsg.exe [.packages.vms]curlmsg.obj +$ link/share=gnv$curlmsg.exe [.projects.vms]curlmsg.obj $ endif $! $! $! Need to build the common init module. $!------------------------------------------- $ cflags = "/list/show=(expan,includ)" -$ init_obj = "[.packages.vms]curl_crtl_init.obj" +$ init_obj = "[.projects.vms]curl_crtl_init.obj" $ if f$search(init_obj) .eqs. "" $ then $ cc'cflags' 'default_dir'curl_crtl_init.c/obj='init_obj' @@ -96,7 +96,7 @@ $! Need to build the module to test the HP OpenSSL version $!-------------------------------------------------------- $ if arch_name .nes. "VAX" $ then -$ rpt_obj = "[.packages.vms]report_openssl_version.obj +$ rpt_obj = "[.projects.vms]report_openssl_version.obj $ if f$search(rpt_obj) .eqs. "" $ then $ cc'cflags' 'default_dir'report_openssl_version.c/obj='rpt_obj' @@ -109,12 +109,12 @@ $ report_openssl_version := $'default_dir'report_openssl_version.exe $ endif $! $! -$ base_link_opt_file = "[.packages.vms.''arch_name']gnv_libcurl_linker.opt" -$ share_link_opt_file = "[.packages.vms.''arch_name']gnv_ssl_libcurl_linker.opt" +$ base_link_opt_file = "[.projects.vms.''arch_name']gnv_libcurl_linker.opt" +$ share_link_opt_file = "[.projects.vms.''arch_name']gnv_ssl_libcurl_linker.opt" $ if f$search(base_link_opt_file) .eqs. "" $ then -$ base_link_opt_file = "[.packages.vms]gnv_libcurl_linker.opt" -$ share_link_opt_file = "[.packages.vms]gnv_ssl_libcurl_linker.opt" +$ base_link_opt_file = "[.projects.vms]gnv_libcurl_linker.opt" +$ share_link_opt_file = "[.projects.vms]gnv_ssl_libcurl_linker.opt" $ if f$search(base_link_opt_file) .eqs. "" $ then $ write sys$output "Can not find base library option file!" @@ -259,7 +259,7 @@ This package is built on with the OpenSSL version listed below and requires the shared images from the HP OpenSSL product that is kitted with that version or a compatible later version. -For Alpha and IA64 platforms, see the url below to register to get the +For Alpha and IA64 platforms, see the URL below to register to get the download URL. The kit will be HP 1.4-467 or later. https://h41379.www4.hpe.com/openvms/products/ssl/ssl.html @@ -269,10 +269,10 @@ download URLs provided and put in CPQ-VAXVMS-SSL-V0101-B-1.PCSI-DCX_VAXEXE If your system can not be upgraded to a compatible version of OpenSSL, then you can extract the two shared images from the kit and place them in the [vms$common.gnv.lib]directory of the volume that you are installing GNV and -or GNV compatible components like Curl. +or GNV compatible components like curl. If GNV is installed, you must run the GNV startup procedure before these steps -and before installing Curl. +and before installing curl. 1. make sure that [vms$common.gnv.lib] exists by using the following @@ -343,7 +343,7 @@ $ endif $! $! DCL build puts curllib in architecture directory $! GNV build uses the makefile. -$ libfile = "[.packages.vms.''arch_name']curllib.olb" +$ libfile = "[.projects.vms.''arch_name']curllib.olb" $ if f$search(libfile) .nes. "" $ then $ olb_file = libfile @@ -387,7 +387,7 @@ $! $ if f$search("[.src]curl-tool_main.o") .nes. "" $ then $! From src/makefile.inc: -$! # libcurl has sources that provide functions named curlx_* that aren't +$! # libcurl has sources that provide functions named curlx_* that are not $! # part of the official API, but we reuse the code here to avoid $! # duplication. $! @@ -424,9 +424,9 @@ $ endif $ else $ curl_exe = "[.src]curl.exe" $ curl_dsf = "[.src]curl.dsf" -$ curl_main = "[.packages.vms.''arch_name']tool_main.obj" -$ curl_src = "[.packages.vms.''arch_name']curlsrc.olb" -$ curl_lib = "[.packages.vms.''arch_name']curllib.olb" +$ curl_main = "[.projects.vms.''arch_name']tool_main.obj" +$ curl_src = "[.projects.vms.''arch_name']curlsrc.olb" +$ curl_lib = "[.projects.vms.''arch_name']curllib.olb" $ strcase = "strcase" $ nonblock = "nonblock" $ warnless = "warnless" diff --git a/packages/vms/macro32_exactcase.patch b/projects/vms/macro32_exactcase.patch similarity index 100% rename from packages/vms/macro32_exactcase.patch rename to projects/vms/macro32_exactcase.patch diff --git a/packages/vms/make_gnv_curl_install.sh b/projects/vms/make_gnv_curl_install.sh similarity index 97% rename from packages/vms/make_gnv_curl_install.sh rename to projects/vms/make_gnv_curl_install.sh index b85ef0ced2..4723070387 100755 --- a/packages/vms/make_gnv_curl_install.sh +++ b/projects/vms/make_gnv_curl_install.sh @@ -1,6 +1,6 @@ # File: make_gnv_curl_install.sh # -# Set up and run the make script for Curl. +# Set up and run the make script for curl. # # This makes the library, the curl binary and attempts an install. # A search list should be set up for GNU (GNV$GNU). diff --git a/packages/vms/make_pcsi_curl_kit_name.com b/projects/vms/make_pcsi_curl_kit_name.com similarity index 100% rename from packages/vms/make_pcsi_curl_kit_name.com rename to projects/vms/make_pcsi_curl_kit_name.com diff --git a/packages/vms/pcsi_gnv_curl_file_list.txt b/projects/vms/pcsi_gnv_curl_file_list.txt similarity index 98% rename from packages/vms/pcsi_gnv_curl_file_list.txt rename to projects/vms/pcsi_gnv_curl_file_list.txt index 586f7e7675..9ae49d5977 100644 --- a/packages/vms/pcsi_gnv_curl_file_list.txt +++ b/projects/vms/pcsi_gnv_curl_file_list.txt @@ -1,7 +1,7 @@ ! File: PCSI_GNV_CURL_FILE_LIST.TXT ! ! File list for building a PCSI kit. -! Very simple format so that the parsing logic can be simple. +! Simple format so that the parsing logic can be simple. ! links first, directory second, and files third. ! ! link -> file tells procedure to create/remove a link on install/uninstall diff --git a/packages/vms/pcsi_product_gnv_curl.com b/projects/vms/pcsi_product_gnv_curl.com similarity index 95% rename from packages/vms/pcsi_product_gnv_curl.com rename to projects/vms/pcsi_product_gnv_curl.com index 83d8fa3b66..15ab32f317 100644 --- a/packages/vms/pcsi_product_gnv_curl.com +++ b/projects/vms/pcsi_product_gnv_curl.com @@ -1,6 +1,6 @@ $! File: PCSI_PRODUCT_GNV_CURL.COM $! -$! This command file packages up the product CURL into a sequential +$! This command file packages up the product curl into a sequential $! format kit $! $! Copyright (C) John Malmberg @@ -66,7 +66,7 @@ $ arch_name = f$edit(f$getsyi("arch_name"),"UPCASE") $ if f$search("[--.src]curl.exe") .eqs. "" $ then $ build_it = 1 -$ libfile = "[.packages.vms.''arch_name']curllib.olb" +$ libfile = "[.projects.vms.''arch_name']curllib.olb" $ if f$search(libfile) .nes. "" $ then $ build_it = 0 @@ -85,8 +85,8 @@ $ endif $ @gnv_link_curl.com $ endif $! -$! Make sure that the release note file name is up to date -$!--------------------------------------------------------- +$! Make sure that the release note filename is up to date +$!-------------------------------------------------------- $ @BUILD_GNV_CURL_RELEASE_NOTES.COM $! $! diff --git a/packages/vms/readme b/projects/vms/readme similarity index 93% rename from packages/vms/readme rename to projects/vms/readme index 042a22b807..661dc9b471 100644 --- a/packages/vms/readme +++ b/projects/vms/readme @@ -7,7 +7,7 @@ History: - 9-MAR-2004, Created this readme. file. Marty Kuhrt (MSK). +09-MAR-2004, Created this readme. file. Marty Kuhrt (MSK). 15-MAR-2004, MSK, Updated to reflect the new files in this directory. 14-FEB-2005, MSK, removed config-vms.h_with* file comments 10-FEB-2010, SMS. General update. @@ -161,7 +161,7 @@ and you must make sure that none of the build files are present before running a different type of build. Use the "REALCLEAN" option for BUILD_VMS.COM and the "REALCLEAN" option for clean_gnv_curl.com. -The (brute-force) DCL based builder is [.packages.vms]build_vms.com. +The (brute-force) DCL based builder is [.projects.vms]build_vms.com. Comments in this procedure describe various optional parameters which enable or disable optional program features, or which control the build in other ways. Product files (.EXE, .H, .LIS, .MAP, .OBJ, .OLB, ...) @@ -183,9 +183,9 @@ is installed from a PCSI kit and default to using it. Example build commands: - @ [.packages.vms]build_vms.com CLEAN - @ [.packages.vms]build_vms.com LARGE LDAP - submit /noprint [.packages.vms]build_vms.com /param = (LARGE, LDAP) + @ [.projects.vms]build_vms.com CLEAN + @ [.projects.vms]build_vms.com LARGE LDAP + submit /noprint [.projects.vms]build_vms.com /param = (LARGE, LDAP) The build_vms.com procedure does not build the shared image file or the PCSI kit. If you have built a curl with ZLIB and HPSSL support as well as if @@ -202,17 +202,17 @@ Other Notes: This release fixes known bugs #22, and #57 in the [curl.docs]known_bugs. file. -The libcurl formdata.c module and Curl tools post form now have some +The libcurl formdata.c module and curl tools post form now have some understanding of VMS file types. Files will be posted in STREAM_LF format. -The Curl tool now has some understanding of VMS file types and will upload the +The curl tool now has some understanding of VMS file types and will upload the files in STREAM_LF format. -When CURL is uploading a VARIABLE format VMS file, it is less efficient as in +When curl is uploading a VARIABLE format VMS file, it is less efficient as in order to get the file size, it will first read the entire file once, and then read the file again for the actual upload. -The Curl tool will now always download files into STREAM_LF format. Even if a +The curl tool will now always download files into STREAM_LF format. Even if a file by that name with a different format already exists. This is needed to allow interrupted downloads to be continued. @@ -225,4 +225,4 @@ The test suites are not supported as of 7.11.0. The curlmsg.sdl and curlmsg.h files are generated from curlmsg.msg. This is not done automatically, since the .MSG file is a hand edit of the relevant stuff from the curl.h file. If you want to do this -yourself you'll need the SDL package from the freeware collection. +yourself you need the SDL package from the freeware collection. diff --git a/packages/vms/report_openssl_version.c b/projects/vms/report_openssl_version.c similarity index 84% rename from packages/vms/report_openssl_version.c rename to projects/vms/report_openssl_version.c index dd1713b244..d2b0d367bc 100644 --- a/packages/vms/report_openssl_version.c +++ b/projects/vms/report_openssl_version.c @@ -35,15 +35,14 @@ #include #include -unsigned long LIB$SET_SYMBOL( - const struct dsc$descriptor_s * symbol, - const struct dsc$descriptor_s * value, - const unsigned long *table_type); +unsigned long LIB$SET_SYMBOL(const struct dsc$descriptor_s *symbol, + const struct dsc$descriptor_s *value, + const unsigned long *table_type); int main(int argc, char **argv) { void *libptr; - const char * (*ssl_version)(int t); + const char *(*ssl_version)(int t); const char *version; if(argc < 1) { @@ -53,11 +52,11 @@ int main(int argc, char **argv) libptr = dlopen(argv[1], 0); - ssl_version = (const char * (*)(int))dlsym(libptr, "SSLeay_version"); + ssl_version = (const char *(*)(int))dlsym(libptr, "SSLeay_version"); if(!ssl_version) { - ssl_version = (const char * (*)(int))dlsym(libptr, "ssleay_version"); + ssl_version = (const char *(*)(int))dlsym(libptr, "ssleay_version"); if(!ssl_version) { - ssl_version = (const char * (*)(int))dlsym(libptr, "SSLEAY_VERSION"); + ssl_version = (const char *(*)(int))dlsym(libptr, "SSLEAY_VERSION"); } } diff --git a/packages/vms/setup_gnv_curl_build.com b/projects/vms/setup_gnv_curl_build.com similarity index 96% rename from packages/vms/setup_gnv_curl_build.com rename to projects/vms/setup_gnv_curl_build.com index 49882463f3..b7aa5ec379 100644 --- a/packages/vms/setup_gnv_curl_build.com +++ b/projects/vms/setup_gnv_curl_build.com @@ -1,6 +1,6 @@ $! File: setup_gnv_curl_build.com $! -$! Set up build environment for building Curl under GNV on VMS. +$! Set up build environment for building curl under GNV on VMS. $! $! GNV needs some files moved into the other directories to help with $! the configure script and the build. @@ -73,7 +73,7 @@ $! $! A logical name to make it easier to find some of the hacks. $ define/job gnv_hacks 'base_dir' $! -$! A logical name to find the [.packages.vms] directory where we started. +$! A logical name to find the [.projects.vms] directory where we started. $ define/job gnv_packages_vms 'default_dir' $! $! Kerberos headers: @@ -96,7 +96,7 @@ $ endif $! $! C compiler include path. $ define/job decc$system_include prj_root:[.include.curl],- - [-.packages.vms],- + [-.projects.vms],- ssl$include:,gnv$gnu:[usr.include],- gnv$gnu:[usr.include.libz],gnv$gnu:[include],- gnv$zlib_include:,- @@ -256,20 +256,20 @@ $! $! $! Build the Message file. $!-------------------------- -$ if f$search("[.packages.vms]curlmsg.obj") .eqs. "" +$ if f$search("[.projects.vms]curlmsg.obj") .eqs. "" $ then -$ message [.packages.vms]curlmsg.msg/object=[.packages.vms] +$ message [.projects.vms]curlmsg.msg/object=[.projects.vms] $ endif $ if f$search("gnv$curlmsg.exe") .eqs. "" $ then -$ link/share=gnv$curlmsg.exe [.packages.vms]curlmsg.obj +$ link/share=gnv$curlmsg.exe [.projects.vms]curlmsg.obj $ endif $! $! $! $! Need to build the common init module. $!------------------------------------------- -$ init_obj = "[.packages.vms]curl_crtl_init.obj" +$ init_obj = "[.projects.vms]curl_crtl_init.obj" $ if f$search(init_obj) .eqs. "" $ then $ cc'cflags' 'default_dir'curl_crtl_init.c/obj='init_obj' diff --git a/packages/vms/stage_curl_install.com b/projects/vms/stage_curl_install.com similarity index 100% rename from packages/vms/stage_curl_install.com rename to projects/vms/stage_curl_install.com diff --git a/packages/vms/vms_eco_level.h b/projects/vms/vms_eco_level.h similarity index 100% rename from packages/vms/vms_eco_level.h rename to projects/vms/vms_eco_level.h diff --git a/renovate.json b/renovate.json index efb9064dab..99164d5022 100644 --- a/renovate.json +++ b/renovate.json @@ -10,10 +10,11 @@ "matchManagers": [ "github-actions" ], - "commitMessagePrefix": "gha: ", + "commitMessagePrefix": "GHA: ", "labels": [ "CI" - ] + ], + "enabled": false }, { "matchUpdateTypes": [ @@ -21,7 +22,7 @@ "pinDigest", "digest" ], - "commitMessagePrefix": "ci: ", + "commitMessagePrefix": "GHA: ", "labels": [ "CI" ] @@ -30,7 +31,7 @@ "matchManagers": [ "custom.regex" ], - "commitMessagePrefix": "ci: ", + "commitMessagePrefix": "GHA: ", "labels": [ "CI" ] @@ -43,24 +44,35 @@ ".github/workflows/linux-old.yml" ], "enabled": false + }, + { + "description": "Schedule package updates on the 10th of each month", + "matchPackageNames": [ + "/codeql/i", + "/ruff/i" + ], + "groupName": "monthly updates by name", + "schedule": [ + "* * 10 * *" + ] + }, + { + "description": "Schedule package updates on the 10th of each month", + "matchSourceUrls": [ + "**/awslabs/**" + ], + "groupName": "monthly updates by URL", + "schedule": [ + "* * 10 * *" + ] } ], "customManagers": [ { "customType": "regex", "managerFilePatterns": [ - "/.circleci/config.yml/" - ], - "matchStrings": [ - "# renovate: datasource=(?.*?) depName=(?.*?)( versioning=(?.*?))?( extractVersion=(?.+?))?( registryUrl=(?.*?))?\\s.*?_VER=(?.*)\\s" - ], - "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}", - "extractVersionTemplate": "{{#if extractVersion}}{{{extractVersion}}}{{else}}^v?(?.+)${{/if}}" - }, - { - "customType": "regex", - "managerFilePatterns": [ - "/^.github/workflows/.*\\.yml$/" + "/^\\.circleci/config\\.yml/", + "/^\\.github/workflows/.*\\.yml$/" ], "matchStrings": [ "# renovate: datasource=(?.*?) depName=(?.*?)( versioning=(?.*?))?( extractVersion=(?.+?))?( registryUrl=(?.*?))?\\s.*?_VERSION:\\s*(?.*)\\s" @@ -71,8 +83,8 @@ { "customType": "regex", "managerFilePatterns": [ - "/^.github/workflows/linux.yml$/", - "/^.github/workflows/http3-linux.yml$/" + "/^\\.github/workflows/http3-linux\\.yml$/", + "/^\\.github/workflows/linux\\.yml$/" ], "matchStrings": [ "OPENSSL_VERSION: (?.*)\\s" @@ -85,21 +97,7 @@ { "customType": "regex", "managerFilePatterns": [ - "/^.github/workflows/linux.yml$/", - "/^.github/workflows/http3-linux.yml$/" - ], - "matchStrings": [ - "QUICTLS_VERSION: (?.*)\\s" - ], - "datasourceTemplate": "github-tags", - "depNameTemplate": "quictls/openssl", - "versioningTemplate": "semver", - "extractVersionTemplate": "^openssl-(?.*)-quic1$" - }, - { - "customType": "regex", - "managerFilePatterns": [ - "/^.github/workflows/linux.yml$/" + "/^\\.github/workflows/linux\\.yml$/" ], "matchStrings": [ "OPENLDAP_VERSION: (?.*)\\s" diff --git a/tests/data/.gitattributes b/scripts/.checksrc similarity index 84% rename from tests/data/.gitattributes rename to scripts/.checksrc index bb1b928306..03e98fee89 100644 --- a/tests/data/.gitattributes +++ b/scripts/.checksrc @@ -2,4 +2,4 @@ # # SPDX-License-Identifier: curl -test* -crlf +allowfunc printf diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index dd590c910f..54bb814267 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -27,7 +27,7 @@ option(CURL_COMPLETION_ZSH "Install zsh completions" OFF) if(CURL_COMPLETION_FISH OR CURL_COMPLETION_ZSH) - if(PERL_FOUND) + if(Perl_FOUND) if(CURL_COMPLETION_FISH) set(_completion_fish "${CMAKE_CURRENT_BINARY_DIR}/curl.fish") add_custom_command(OUTPUT "${_completion_fish}" diff --git a/scripts/Makefile.am b/scripts/Makefile.am index cfa3d3e740..e0f433422a 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -22,10 +22,12 @@ # ########################################################################### -EXTRA_DIST = coverage.sh completion.pl firefox-db2pem.sh checksrc.pl checksrc-all.pl \ - mk-ca-bundle.pl mk-unity.pl schemetable.c cd2nroff nroff2cd cdall cd2cd managen \ - dmaketgz maketgz release-tools.sh verify-release cmakelint.sh mdlinkcheck \ - CMakeLists.txt pythonlint.sh randdisable wcurl top-complexity extract-unit-protos +EXTRA_DIST = coverage.sh completion.pl firefox-db2pem.sh checksrc.pl \ + checksrc-all.pl mk-ca-bundle.pl mk-unity.pl schemetable.c cd2nroff nroff2cd \ + cdall cd2cd managen dmaketgz maketgz release-tools.sh verify-release \ + cmakelint.sh mdlinkcheck CMakeLists.txt perlcheck.sh pythonlint.sh \ + spacecheck.pl randdisable wcurl top-complexity extract-unit-protos \ + .checksrc badwords badwords-all badwords.txt dist_bin_SCRIPTS = wcurl diff --git a/scripts/badwords b/scripts/badwords new file mode 100755 index 0000000000..5ed7a3ece1 --- /dev/null +++ b/scripts/badwords @@ -0,0 +1,336 @@ +#!/usr/bin/env perl +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl +# +# bad[:=]correct +# +# If separator is '=', the string will be compared case sensitively. +# If separator is ':', the check is done case insensitively. +# +# To add white listed uses of bad words that are removed before checking for +# the bad ones: +# +# ---(accepted word) +# ---:[path]:(accepted word) +# + +use strict; +use warnings; + +use File::Basename; + +# +## States +# +# 0 - default, initial state +# 1 - there was a slash +# 2 - quoted string +# 3 - // comment +# 4 - /* comment +# 5 - asterisk found within a /* comment +# 6 - #include line +# 7 - backslash in a string +# +## Flags +# +# 1 - include preprocessor line, ignore strings + +sub srcline { + my ($state, $flags, $l) = @_; + my $line = ""; + + if(($state == 0) && ($l =~ /^ *\# *include/)) { + # preprocessor include line + $flags |= 1; + } + else { + # not preprocessor + $flags &= ~1; + } + + if($state == 3) { + # // ended on the prev line, go back to init + $state = 0; + } + + my @c = split(//, $l); + + # state machine this line + for my $c (@c) { + if($state == 1) { + # we had a slash + if($c eq "/") { + # // confirmed, the rest of the line is a comment + $line .= "//"; + $state = 3; + } + elsif($c eq "*") { + # /* confirmed + $state = 4; + $line .= "/*"; + } + else { + # back to normal + $line .= " "; + $state = 0; + } + } + elsif($state == 2) { + # a string + if($c eq "\\") { + $line .= "\\"; + $state = 7; + } + elsif($c eq "\"") { + # end of the string + $line .= "\""; + $state = 0; + } + else { + $line .= $c; + } + } + elsif($state == 3) { + # a // comment + $line .= $c; + } + elsif($state == 4) { + # a /* comment + if($c eq "*") { + # could be a comment close + $state = 5; + } + else { + $line .= $c; + } + } + elsif($state == 5) { + if($c eq "/") { + # a /* */ comment ended here */ + $line .= "*/"; + $state = 0; + } + else { + # the /* comment continues + $line .= "*$c"; + $state = 4; + } + } + elsif($state == 7) { + # the prev was a backslash in a string + $line .= $c; + # switch back to normal string + $state = 2; + } + else { + if($c eq "/") { + $state = 1; # got a slash + } + elsif(($c eq "\"") && !($flags & 1)) { + # start of a string, not within a preprocessor line + $line .= "\""; + $state = 2; + } + elsif($c eq "\n") { + $line .= "\n"; + } + else { + $line .= " "; + } + } + } + return $state, $flags, $line; +} + +sub sourcecode { + my ($f) = @_; + my $state = 0; + my $flags = 0; + my @lines; + my $line; + open(F, "<$f"); + while() { + my $l = $_; + ($state, $flags, $line) = srcline($state, $flags, $l); + push @lines, $line; + } + close(F); + return @lines; +} + +my @whitelist = ( + # ignore what looks like URLs + '(^|\W)((https|http|ftp):\/\/[a-z0-9\-._~%:\/?\#\[\]\@!\$&\'\(\)*+,;=]+)', + # remove bolded sections + '\*\*.*?\*\*', + # remove backticked texts + '\`.*?\`' + ); +my %alt; +my %exactcase; + +my %wl; + +my @w; +my @exact; +my $file = shift @ARGV; +open(CONFIG, "<$file") or die "Cannot open '$file': $!"; +while() { + chomp; + if($_ =~ /^#/) { + next; + } + if(/^---:([^:]*):(.*)/) { + # whitelist file + word + my $word = lc($2); + $wl{"$1:$word"}=1; + } + elsif($_ =~ /^---(.+)/) { + # whitelist word + push @whitelist, $1; + } + elsif($_ =~ /^(.*)([:=])(.*)/) { + my ($bad, $sep, $better)=($1, $2, $3); + if($sep eq "=") { + $alt{$bad} = $better; + push @exact, $bad; + } + else { + $alt{lc($bad)} = $better; + push @w, $bad; + } + } +} +close(CONFIG); + +# Build a single combined regex for case-insensitive words +my $re_ci; +if(@w) { + my $pat = join('|', map { quotemeta($_) } @w); + $re_ci = qr/\b($pat)\b/i; +} + +# Build a single combined regex for case-sensitive (exact) words +my $re_cs; +if(@exact) { + my $pat = join('|', map { quotemeta($_) } @exact); + $re_cs = qr/\b($pat)\b/; +} + +# Build a single combined regex for removing whitelisted content +my $pat = join('|', map { $_ } @whitelist); +my $re_wl = qr/($pat)/; + +my $errors = 0; + +sub highlight { + my ($p, $w, $in, $f, $l, $lookup) = @_; + + my $c = length($p)+1; + my $ch; + + my $dir = dirname($f); + $ch = $dir . "/" . ":" . lc($w); + if($wl{$ch}) { + # whitelisted dirname + word + return; + } + my $updir = dirname($dir); + if($dir ne $updir) { + $ch = $updir . "/" . ":" . lc($w); + if($wl{$ch}) { + # whitelisted upper dirname + word + return; + } + } + $ch = $f . ":" . lc($w); + if($wl{$ch}) { + # whitelisted filename + word + return; + } + + print STDERR "$f:$l:$c: error: found bad word \"$w\"\n"; + printf STDERR " %4d | %s\n", $l, $in; + printf STDERR " | %*s^%s\n", length($p), " ", + "~" x (length($w)-1); + printf STDERR " maybe use \"%s\" instead?\n", $alt{$lookup}; + $errors++; +} + +sub document { + my ($f) = @_; + my @lines; + open(F, "<$f"); + while() { + push @lines, $_; + } + close(F); + return @lines; +} + +sub file { + my ($f) = @_; + my $l = 0; + + my $skip_indented = 0; + my $source_code = 0; + if($f =~ /\.[ch]$/) { + $source_code = 1; + } + else { + # markdown + $skip_indented = 1; + } + + my @lines; + if($source_code) { + @lines = sourcecode($f); + } + else { + @lines = document($f); + } + for my $in (@lines) { + $l++; + chomp $in; + if($skip_indented && $in =~ /^ /) { + next; + } + # remove the link part + $in =~ s/(\[.*\])\(.*\)/$1/g; + # remove whitelisted patterns (pre-compiled) + if($re_wl) { + $in =~ s/${re_wl}//ig; + } + # case-insensitive bad words + if($re_ci) { + if($in =~ /^(.*)$re_ci/i) { + highlight($1, $2, $in, $f, $l, lc($2)); + } + } + # case-sensitive (exact) bad words + if($re_cs) { + if($in =~ /^(.*)$re_cs/) { + highlight($1, $2, $in, $f, $l, $2); + } + } + } +} + +my @filemasks = @ARGV; +open(my $git_ls_files, '-|', 'git', 'ls-files', '--', @filemasks) or die "Failed running git ls-files: $!"; +my @files; +while(my $each = <$git_ls_files>) { + chomp $each; + push @files, $each; +} +close $git_ls_files; + +my $onum = scalar(@files); +my $num; +for my $e (@files) { + #printf STDERR "Complete: %d%%\r", $num++ * 100 / $onum; + file($e); +} + +exit $errors; diff --git a/scripts/badwords-all b/scripts/badwords-all new file mode 100755 index 0000000000..99e1b52ff8 --- /dev/null +++ b/scripts/badwords-all @@ -0,0 +1,14 @@ +#!/usr/bin/env perl +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + +use strict; +use warnings; + +use File::Basename; + +chdir dirname(__FILE__) . '/..'; + +exit system('scripts/badwords', ('scripts/badwords.txt', + '**.md', 'projects/OS400/README.OS400', 'include', 'docs/examples')) >> 8; diff --git a/scripts/badwords.txt b/scripts/badwords.txt new file mode 100644 index 0000000000..2de6f5bbe8 --- /dev/null +++ b/scripts/badwords.txt @@ -0,0 +1,117 @@ +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl +# +# whitelisted uses of bad words (case insensitive) can be done in two ways, +# globally and per-file. +# +# ---[word] +# ---:[file]:[word] +# +back-end:backend +e-mail:email +run-time:runtime +set-up:setup +tool chain:toolchain +tool-chain:toolchain +wild-card:wildcard +wild card:wildcard +thread safe:thread-safe +thread unsafe:thread-unsafe +multi thread:multi-thread +null terminated:null-terminated +zero terminated:null-terminated +it's:it is +aren't:are not +can't:cannot +could've:could have +couldn't:could not +didn't:did not +doesn't:does not +don't:do not +haven't:have not +i'd:I would +i'll:I will +i'm:I am +i've:I have +isn't:is not +it'd:it would +it'll:it will +might've:might have +needn't:need not +should've:should have +shouldn't:should not +that's:that is +there's:there is +they'd:They would +they'll:They will +they're:They are +they've:They have +this'll:this will +wasn't:was not +we'd:we would +we'll:we will +we're:we are +we've:we have +weren't:were not +won't:will not +would've:would have +wouldn't:would not +you'd:you would +you'll:you will +you're:you are +you've:you have +a html:an html +a http:an http +a ftp:an ftp +a IPv4:an IPv4 +a IPv6:an IPv6 + url= URL +internet=Internet +isation:ization +So=Rewrite it somehow? +And=Rewrite it somehow? +But=Rewrite it somehow? +sub-directory:subdirectory +web page:webpage +host name:hostname +host names:hostnames +file name:filename +file names:filenames +fist:first +user name:username +user names:usernames +pass phrase:passphrase +will:rewrite to present tense +7 bit:7-bit +8 bit:8-bit +16 bit:16-bit +24 bit:24-bit +32 bit:32-bit +56 bit:56-bit +63 bit:63-bit +64 bit:64-bit +128 bit:128-bit +8-bits:8 bits +16-bits:16 bits +32-bits:32 bits +64-bits:64 bits +very:rephrase using an alternative word +just:rephrase using an alternative word +simply:rephrase using an alternative word +Curl=curl +cURL=curl +Libcurl=libcurl +LibCurl=libcurl +manpages:man pages +manpage:man page +favour:favor +basically:rephrase? +However,:rephrase? +the the:the +with with:with +---WWW::Curl +---NET::Curl +---Curl Corporation +---:lib/:will +---:src/:will diff --git a/scripts/cd2cd b/scripts/cd2cd index f4748f5d9b..182cb62c8a 100755 --- a/scripts/cd2cd +++ b/scripts/cd2cd @@ -54,7 +54,6 @@ while(1) { } } - use POSIX qw(strftime); my @ts; if(defined($ENV{SOURCE_DATE_EPOCH})) { @@ -70,7 +69,7 @@ sub outseealso { my @o; push @o, ".SH SEE ALSO\n"; for my $s (sort @sa) { - push @o, sprintf "%s.BR $s", $comma ? ",\n": ""; + push @o, sprintf "%s.BR $s", $comma ? ",\n" : ""; $comma = 1; } push @o, "\n"; @@ -90,7 +89,7 @@ sub single { my $salist = 0; my $copyright; my $spdx; - open(F, "<:crlf", "$f") || + open(F, "<:crlf", $f) || return 1; while() { $line++; @@ -156,7 +155,9 @@ sub single { return 2; } if(!$spdx) { + # REUSE-IgnoreStart print STDERR "$f:$line:1:ERROR: no 'SPDX-License-Identifier:' field present\n"; + # REUSE-IgnoreEnd return 2; } last; diff --git a/scripts/cd2nroff b/scripts/cd2nroff index d7a5dfe7b0..62c9df025a 100755 --- a/scripts/cd2nroff +++ b/scripts/cd2nroff @@ -25,7 +25,7 @@ =begin comment -Converts a curldown file to nroff (manpage). +Converts a curldown file to nroff (man page). =end comment =cut @@ -55,10 +55,10 @@ while(@ARGV) { print < Write the output to the file name from the meta-data in the +-d Write the output to the filename from the meta-data in the specified directory, instead of writing to stdout -e If -d is used, this option can provide an added "extension", arbitrary - text really, to append to the file name. + text really, to append to the filename. -h This help text, -v Show version then exit HELP @@ -89,7 +89,7 @@ sub outseealso { my @o; push @o, ".SH SEE ALSO\n"; for my $s (sort @sa) { - push @o, sprintf "%s.BR $s", $comma ? ",\n": ""; + push @o, sprintf "%s.BR $s", $comma ? ",\n" : ""; $comma = 1; } push @o, "\n"; @@ -166,8 +166,6 @@ my %knownprotos = ( 'MQTT' => 1, 'POP3' => 1, 'POP3S' => 1, - 'RTMP' => 1, - 'RTMPS' => 1, 'RTSP' => 1, 'SCP' => 1, 'SFTP' => 1, @@ -189,7 +187,7 @@ my %knowntls = ( 'GnuTLS' => 1, 'mbedTLS' => 1, 'OpenSSL' => 1, - 'rustls' => 1, + 'Rustls' => 1, 'Schannel' => 1, 'wolfSSL' => 1, 'All' => 1, @@ -216,7 +214,7 @@ sub single { my $title; if(defined($f)) { - if(!open($fh, "<:crlf", "$f")) { + if(!open($fh, "<:crlf", $f)) { print STDERR "cd2nroff failed to open '$f' for reading: $!\n"; return 1; } @@ -320,7 +318,9 @@ sub single { return 2; } if(!$spdx) { + # REUSE-IgnoreStart print STDERR "$f:$line:1:ERROR: no 'SPDX-License-Identifier:' field present\n"; + # REUSE-IgnoreEnd return 2; } if($section == 3) { @@ -371,7 +371,7 @@ sub single { my $blankline = 0; my $header = 0; - # cut off the leading path from the file name, if any + # cut off the leading path from the filename, if any $f =~ s/^(.*[\\\/])//; push @desc, ".\\\" generated by cd2nroff $cd2nroff from $f\n"; @@ -429,7 +429,7 @@ sub single { # convert backslash-'<' or '> to just the second character $d =~ s/\\([<>])/$1/g; - # mentions of curl symbols with manpages use italics by default + # mentions of curl symbols with man pages use italics by default $d =~ s/((lib|)curl([^ ]*\(3\)))/\\fI$1\\fP/gi; # backticked becomes italics @@ -513,7 +513,7 @@ sub single { $blankline++; } else { - # don't output newlines if this is the first content after a + # do not output newlines if this is the first content after a # header push @desc, "\n" if($blankline && !$header); $blankline = 0; diff --git a/scripts/cdall b/scripts/cdall index aab41b6505..1ea0f42f25 100755 --- a/scripts/cdall +++ b/scripts/cdall @@ -23,7 +23,7 @@ # ########################################################################### -# provide all dir names to scan on the cmdline +# provide all directory names to scan on the command-line use strict; use warnings; @@ -35,10 +35,10 @@ sub convert { closedir $dh; for my $cd (@cd) { - my $nroff = "$cd"; + my $nroff = $cd; $nroff =~ s/\.md\z/.3/; print "$dir/$cd = $dir/$nroff\n"; - system("./scripts/cd2nroff -d $dir $dir/$cd"); + system('./scripts/cd2nroff', ('-d', $dir, "$dir/$cd")); } } diff --git a/scripts/checksrc-all.pl b/scripts/checksrc-all.pl index 08ff6cfbf5..5b1cba7af6 100755 --- a/scripts/checksrc-all.pl +++ b/scripts/checksrc-all.pl @@ -11,17 +11,20 @@ use File::Find; use Cwd 'abs_path'; my @files; +my $is_git = 0; if(system('git rev-parse --is-inside-work-tree >/dev/null 2>&1') == 0) { - @files = `git ls-files '*.[ch]'`; + open(O, '-|', 'git', 'ls-files', '*.[ch]') || die; push @files, ; close(O); + $is_git = 1; } else { - find(sub { if(/\.[ch]$/) { push(@files, $File::Find::name) } }, ('.')); + find(sub { if(/\.[ch]$/) { push(@files, $File::Find::name) } }, ('.')); } if(@ARGV) { - find(sub { if(/\.[ch]$/) { push(@files, $File::Find::name) } }, @ARGV); + find(sub { if(/\.[ch]$/) { push(@files, $File::Find::name) } }, @ARGV); } @files = grep !/\/CMakeFiles\//, @files; +@files = grep !/tests\/data\/data.+\.c/, @files; @files = map { dirname($_) } @files; my @dirs = sort { $a cmp $b } keys %{{ map { $_ => 1 } @files }}; @@ -29,7 +32,14 @@ my $scripts_dir = dirname(abs_path($0)); my $anyfailed = 0; for my $dir (@dirs) { - @files = glob("$dir/*.[ch]"); + if($is_git) { + @files = (); + open(O, '-|', 'git', 'ls-files', "$dir/*.[ch]") || die; push @files, ; close(O); + chomp(@files); + } + else { + @files = glob("$dir/*.[ch]"); + } if(@files && system("$scripts_dir/checksrc.pl", @files) != 0) { $anyfailed = 1; } diff --git a/scripts/checksrc.pl b/scripts/checksrc.pl index 43b8195537..04b4793b41 100755 --- a/scripts/checksrc.pl +++ b/scripts/checksrc.pl @@ -48,34 +48,95 @@ my %ignore_used; my @ignore_line; my %banfunc = ( - "gmtime" => 1, - "localtime" => 1, - "gets" => 1, - "strtok" => 1, - "sprintf" => 1, - "vsprintf" => 1, - "strcat" => 1, - "strncat" => 1, - "strncpy" => 1, - "strtok_r" => 1, - "strtoul" => 1, + "_access" => 1, + "_fstati64" => 1, + "_lseeki64" => 1, "_mbscat" => 1, "_mbsncat" => 1, + "_open" => 1, "_tcscat" => 1, + "_tcsdup" => 1, "_tcsncat" => 1, + "_tcsncpy" => 1, + "_waccess" => 1, "_wcscat" => 1, - "_wcsncat" => 1, "_wcsdup" => 1, - "wcsdup" => 1, + "_wcsncat" => 1, + "_wfopen" => 1, + "_wfreopen" => 1, + "_wopen" => 1, + "accept" => 1, + "accept4" => 1, + "access" => 1, + "aprintf" => 1, + "atoi" => 1, + "atol" => 1, + "calloc" => 1, + "close" => 1, + "CreateFile" => 1, + "CreateFileA" => 1, + "CreateFileW" => 1, + "fclose" => 1, + "fdopen" => 1, + "fopen" => 1, + "fprintf" => 1, + "free" => 1, + "freeaddrinfo" => 1, + "freopen" => 1, + "fstat" => 1, + "getaddrinfo" => 1, + "gets" => 1, + "gmtime" => 1, + "llseek" => 1, "LoadLibrary" => 1, "LoadLibraryA" => 1, - "LoadLibraryW" => 1, "LoadLibraryEx" => 1, "LoadLibraryExA" => 1, "LoadLibraryExW" => 1, - "_waccess" => 1, - "_access" => 1, - "access" => 1, + "LoadLibraryW" => 1, + "localtime" => 1, + "lseek" => 1, + "malloc" => 1, + "mbstowcs" => 1, + "MoveFileEx" => 1, + "MoveFileExA" => 1, + "MoveFileExW" => 1, + "msnprintf" => 1, + "mvsnprintf" => 1, + "open" => 1, + "printf" => 1, + "realloc" => 1, + "recv" => 1, + "rename" => 1, + "send" => 1, + "snprintf" => 1, + "socket" => 1, + "socketpair" => 1, + "sprintf" => 1, + "sscanf" => 1, + "stat" => 1, + "strcat" => 1, + "strcpy" => 1, + "strdup" => 1, + "strerror" => 1, + "strncat" => 1, + "strncpy" => 1, + "strtok_r" => 1, + "strtok" => 1, + "strtol" => 1, + "strtoul" => 1, + "vaprintf" => 1, + "vfprintf" => 1, + "vprintf" => 1, + "vsnprintf" => 1, + "vsprintf" => 1, + "wcscpy" => 1, + "wcsdup" => 1, + "wcsncpy" => 1, + "wcstombs" => 1, + "WSASocket" => 1, + "WSASocketA" => 1, + "WSASocketW" => 1, ); my %warnings_extended = ( @@ -94,17 +155,22 @@ my %warnings = ( 'BRACEPOS' => 'wrong position for an open brace', 'BRACEWHILE' => 'A single space between open brace and while', 'COMMANOSPACE' => 'comma without following space', + 'CLOSEBRACE' => 'close brace indent level vs line above is off', 'COMMENTNOSPACEEND' => 'no space before */', 'COMMENTNOSPACESTART' => 'no space following /*', 'COPYRIGHT' => 'file missing a copyright statement', 'CPPCOMMENTS' => '// comment detected', + 'CPPSPACE' => 'space before preprocessor hash', 'DOBRACE' => 'A single space between do and open brace', 'EMPTYLINEBRACE' => 'Empty line before the open brace', 'EQUALSNOSPACE' => 'equals sign without following space', + 'EQUALSPACE' => 'equals sign with too many spaces following', 'EQUALSNULL' => 'if/while comparison with == NULL', 'ERRNOVAR' => 'use of bare errno define', 'EXCLAMATIONSPACE' => 'Whitespace after exclamation mark in expression', + 'FIXME' => 'FIXME or TODO comment', 'FOPENMODE' => 'fopen needs a macro for the mode string', + 'IFDEFSINGLE', => 'use ifdef/ifndef for single macro checks', 'INCLUDEDUP', => 'same file is included again', 'INDENTATION' => 'wrong start column for code', 'LONGLINE' => "Line longer than $max_column", @@ -146,7 +212,7 @@ sub readskiplist { # Reads the .checksrc in $dir for any extended warnings to enable locally. # Currently there is no support for disabling warnings from the standard set, -# and since that's already handled via !checksrc! commands there is probably +# and since that is already handled via !checksrc! commands there is probably # little use to add it. sub readlocalfile { my ($file) = @_; @@ -172,6 +238,10 @@ sub readlocalfile { if(/^\s*(#.*)/) { next; } + # Skip empty lines + elsif($_ eq '') { + next; + } elsif(/^enable ([A-Z]+)$/) { if(!defined($warnings_extended{$1})) { print STDERR "invalid warning specified in .checksrc: \"$1\"\n"; @@ -203,11 +273,11 @@ sub readlocalfile { sub checkwarn { my ($name, $num, $col, $file, $line, $msg, $error) = @_; - my $w=$error?"error":"warning"; + my $w=$error ? "error" : "warning"; my $nowarn=0; #if(!$warnings{$name}) { - # print STDERR "Dev! there's no description for $name!\n"; + # print STDERR "Dev! there is no description for $name!\n"; #} # checksrc.skip @@ -312,7 +382,7 @@ if(!$file) { print " -A[rule] Accept this violation, can be used multiple times\n"; print " -a[func] Allow use of this function\n"; print " -b[func] Ban use of this function\n"; - print " -D[DIR] Directory to prepend file names\n"; + print " -D[DIR] Directory to prepend filenames\n"; print " -h Show help output\n"; print " -W[file] Skip the given file - ignore all its flaws\n"; print " -i Indent spaces. Default: 2\n"; @@ -342,7 +412,7 @@ readskiplist(); readlocalfile($file); do { - if("$wlist" !~ / $file /) { + if($wlist !~ / $file /) { my $fullname = $file; $fullname = "$dir/$file" if($fullname !~ '^\.?\.?/'); scanfile($fullname); @@ -515,7 +585,7 @@ sub scanfile { } # detect long lines - if(length($l) > $max_column) { + if(length($l) > $max_column && $l !~ / https:\/\//) { checkwarn("LONGLINE", $line, length($l), $file, $l, "Longer than $max_column columns"); } @@ -597,6 +667,11 @@ sub scanfile { $line, length($1), $file, $l, "\/\/ comment"); } + if($l =~ /^\s*#\s*if\s+!?\s*defined\([a-zA-Z0-9_]+\)$/) { + checkwarn("IFDEFSINGLE", + $line, length($1), $file, $l, "use ifdef/ifndef for single macro checks"); + } + if($l =~ /^(\#\s*include\s+)([\">].*[>}"])/) { my ($pre, $path) = ($1, $2); if($includes{$path}) { @@ -606,6 +681,11 @@ sub scanfile { $includes{$path} = $l; } + # detect leading space before the hash + if($l =~ /^([ \t]+)\#/) { + checkwarn("CPPSPACE", + $line, 0, $file, $l, "space before preprocessor hash"); + } # detect and strip preprocessor directives if($l =~ /^[ \t]*\#/) { # preprocessor line @@ -842,6 +922,21 @@ sub scanfile { } } + # when the line starts with a brace + if($l =~ /^( *)\}/) { + my $tlen = length($1); + if($prevl =~ /^( *)(.)/) { + my $plen = length($1); + my $firstc = $2; + # skips the check if the previous line starts with a close + # brace since we see the occasional legit use of that oddity + if(($tlen + $indent) > $plen && ($firstc ne "}")) { + checkwarn("CLOSEBRACE", + $line, $plen, $file, $prevl, + "Suspicious close brace indentation"); + } + } + } # check for "} else" if($l =~ /^(.*)\} *else/) { checkwarn("BRACEELSE", @@ -878,17 +973,19 @@ sub scanfile { # scan for use of banned functions my $bl = $l; again: - if(($l =~ /^(.*?\W)(\w+)(\s*\()/x) && $banfunc{$2}) { + if((($l =~ /^(.*?\W)(\w+)(\s*\()/x) && $banfunc{$2}) || + (($l =~ /^(.*?\()(\w+)(\s*\()/x) && $banfunc{$2})) { my $bad = $2; my $prefix = $1; my $suff = $3; - checkwarn("BANNEDFUNC", - $line, length($prefix), $file, $ol, - "use of $bad is banned"); - my $replace = 'x' x (length($bad) + 1); - $prefix =~ s/\*/\\*/; - $suff =~ s/\(/\\(/; - $l =~ s/$prefix$bad$suff/$prefix$replace/; + if($prefix !~ /(->|\.)$/) { + checkwarn("BANNEDFUNC", + $line, length($prefix), $file, $ol, + "use of $bad is banned"); + } + my $search = quotemeta($prefix . $bad . $suff); + my $replace = $prefix . 'x' x (length($bad) + 1); + $l =~ s/$search/$replace/; goto again; } $l = $bl; # restore to pre-bannedfunc content @@ -907,8 +1004,8 @@ sub scanfile { } # scan for use of non-binary fopen without the macro - if($l =~ /^(.*\W)fopen\s*\([^,]*, *\"([^"]*)/) { - my $mode = $2; + if($l =~ /^(.*\W)(curlx_fopen|CURLX_FOPEN_LOW|curlx_freopen|CURLX_FREOPEN_LOW)\s*\([^,]*, *\"([^"]*)/) { + my $mode = $3; if($mode !~ /b/) { checkwarn("FOPENMODE", $line, length($1), $file, $ol, @@ -917,7 +1014,7 @@ sub scanfile { } # check for open brace first on line but not first column only alert - # if previous line ended with a close paren and it wasn't a cpp line + # if previous line ended with a close paren and it was not a cpp line if(($prevl =~ /\)\z/) && ($l =~ /^( +)\{/) && !$prevp) { checkwarn("BRACEPOS", $line, length($1), $file, $ol, "badly placed open brace"); @@ -936,13 +1033,12 @@ sub scanfile { my $diff = $second - $first; checkwarn("INDENTATION", $line, length($1), $file, $ol, "not indented $indent steps (uses $diff)"); - } } } # if the previous line starts with if/while/for AND ends with a closed - # parenthesis and there's an equal number of open and closed + # parenthesis and there is an equal number of open and closed # parentheses, check that this line is indented $indent more steps, if # not a cpp line elsif(!$prevp && ($prevl =~ /^( *)(if|while|for)(\(.*\))\z/)) { @@ -1005,6 +1101,12 @@ sub scanfile { $line, length($1)+1, $file, $ol, "no space before equals sign"); } + # check for equals sign with more than one space after it + elsif($l =~ /(.*)[a-z0-9] \= /i) { + checkwarn("EQUALSPACE", + $line, length($1)+3, $file, $ol, + "more than one space after equals sign"); + } # check for plus signs without spaces next to it if($nostr =~ /(.*)[^+]\+[a-z0-9]/i) { @@ -1087,7 +1189,7 @@ sub scanfile { # which are tracked in the Git repo and edited in the workdir, or # committed locally on the branch without being in upstream master. # - # The simple and naive test is to simply check for the current year, + # The simple and naive test is to check for the current year, # but updating the year even without an edit is against project policy # (and it would fail every file on January 1st). # @@ -1128,10 +1230,8 @@ sub scanfile { checksrc_endoffile($file); close($R); - } - if($errors || $warnings || $verbose) { printf "checksrc: %d errors and %d warnings\n", $errors, $warnings; if($suppressed) { diff --git a/scripts/ciconfig.pl b/scripts/ciconfig.pl deleted file mode 100755 index 1d8f235564..0000000000 --- a/scripts/ciconfig.pl +++ /dev/null @@ -1,198 +0,0 @@ -#!/usr/bin/env perl -#*************************************************************************** -# _ _ ____ _ -# 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 -# -########################################################################### - -use strict; -use warnings; - -my %with; -my %without; -my %used; -my %avail; - -# these options are enabled by default in the sense that they will attempt to -# check for and use this feature without the configure flag -my %defaulton = ( - # --enable- - 'shared' => 1, - 'static' => 1, - 'fast-install' => 1, - 'silent-rules' => 1, - 'optimize' => 1, - 'http' => 1, - 'ftp' => 1, - 'file' => 1, - 'ldap' => 1, - 'ldaps' => 1, - 'rtsp' => 1, - 'proxy' => 1, - 'dict' => 1, - 'telnet' => 1, - 'tftp' => 1, - 'pop3' => 1, - 'imap' => 1, - 'smb' => 1, - 'smtp' => 1, - 'gopher' => 1, - 'mqtt' => 1, - 'manual' => 1, - 'libcurl-option' => 1, - 'libgcc' => 1, - 'ipv6' => 1, - 'openssl-auto-load-config' => 1, - 'versioned-symbols' => 1, - 'symbol-hiding' => 1, - 'threaded-resolver' => 1, - 'pthreads' => 1, - 'verbose' => 1, - 'basic-auth' => 1, - 'bearer-auth' => 1, - 'digest-auth' => 1, - 'kerberos-auth' => 1, - 'negotiate-auth' => 1, - 'aws' => 1, - 'ntlm' => 1, - 'ntlm-wb' => 1, - 'tls-srp' => 1, - 'unix-sockets' => 1, - 'cookies' => 1, - 'socketpair' => 1, - 'http-auth' => 1, - 'doh' => 1, - 'mime' => 1, - 'dateparse' => 1, - 'netrc' => 1, - 'progress-meter' => 1, - 'dnsshuffle' => 1, - 'get-easy-options' => 1, - 'alt-svc' => 1, - 'hsts' => 1, - - # --with- - 'aix-soname' => 1, - 'pic' => 1, - 'zlib' => 1, - 'zstd' => 1, - 'brotli' => 1, - 'random' => 1, - 'ca-bundle' => 1, - 'ca-path' => 1, - 'libssh2' => 1, - 'nghttp2' => 1, - 'librtmp' => 1, - 'libidn2' => 1, - 'sysroot' => 1, - 'lber-lib' => 1, - 'ldap-lib' => 1, - - ); - - -sub configureopts { - my ($opts)=@_; - my %thisin; - my %thisout; - - while($opts =~ s/--with-([^ =]*)//) { - $with{$1}++; - $used{$1}++; - $thisin{$1}++; - } - while($opts =~ s/--enable-([^ =]*)//) { - $with{$1}++; - $used{$1}++; - $thisin{$1}++; - } - - while($opts =~ s/--without-([^ =]*)//) { - $without{$1}++; - $used{$1}++; - $thisout{$1}++; - } - while($opts =~ s/--disable-([^ =]*)//) { - $without{$1}++; - $used{$1}++; - $thisout{$1}++; - } - return join(" ", sort(keys %thisin), "/", sort(keys %thisout)); -} - -# run configure --help and check what available WITH/ENABLE options that exist -sub configurehelp { - open(C, "./configure --help|"); - while() { - if($_ =~ /^ --(with|enable)-([a-z0-9-]+)/) { - $avail{$2}++; - } - } - close(C); -} - -sub scanjobs { - - my $jobs; - open(CI, "./scripts/cijobs.pl|"); - while() { - if($_ =~ /^\#\#\#/) { - $jobs++; - } - if($_ =~ /^configure: (.*)/) { - my $c= configureopts($1); - #print "C: $c\n"; - } - } - close(CI); -} - -configurehelp(); -scanjobs(); - -print "Used configure options (with / without)\n"; -for my $w (sort keys %used) { - printf " %s: %d %d%s\n", $w, $with{$w}, $without{$w}, - $defaulton{$w} ? " (auto)":""; -} - -print "Never used configure options\n"; -for my $w (sort keys %avail) { - if(!$used{$w}) { - printf " %s%s\n", $w, - $defaulton{$w} ? " (auto)":""; - } -} - -print "Never ENABLED configure options that aren't on by default\n"; -for my $w (sort keys %avail) { - if(!$with{$w} && !$defaulton{$w}) { - printf " %s\n", $w; - } -} - - -print "ENABLED configure options that aren't available\n"; -for my $w (sort keys %with) { - if(!$avail{$w}) { - printf " %s\n", $w; - } -} diff --git a/scripts/cijobs.pl b/scripts/cijobs.pl deleted file mode 100755 index 2be9d2565c..0000000000 --- a/scripts/cijobs.pl +++ /dev/null @@ -1,521 +0,0 @@ -#!/usr/bin/env perl -#*************************************************************************** -# _ _ ____ _ -# 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 -# -########################################################################### - -use strict; -use warnings; - -my %filelevel= ('file' => 1, - 'service' => 1); - -my $jobid = 1; - -sub submit { - my ($jref)=@_; - my %job = %$jref; - - printf "\n##### job %u \n", $jobid++; - for my $k (sort keys %job) { - printf "%s: %s\n", $k, $job{$k} if($job{$k}); - undef $$jref{$k} if(!$filelevel{$k}); - } -} - -my %job; - -sub githubactions { - my ($tag)=@_; - my @files= `git ls-tree -r --name-only $tag .github/workflows 2>/dev/null`; - my $c = 0; - foreach my $f (sort @files) { - my $j = 0; - my $m = -1; - my $done = 0; - chomp $f; - open(G, "git show $tag:$f 2>/dev/null|"); - # start counting file jobs - undef %job; - $job{'file'} = $f; - $job{'service'} = "gha"; - my @cc; - my $os; - my $topname; - my $line = 1; - while() { - $job{'line'} = $line; - if($_ =~ /^name: (.*)/) { - $topname=$1; - } - elsif($_ =~ /runs-on: (.*)/) { - my $r = $1; - #print "runs-on: $r\n"; - if($r =~ /ubuntu/) { - $os = "linux"; - } - elsif($r =~ /macos/) { - $os = "macos"; - } - elsif($r =~ /windows/) { - $os = "windows"; - } - - # commit previously counted jobs - $c += $j; - # non-matrix job - $j = 1; - } - elsif($_ =~ /^\s*matrix:/) { - # switch to matrix mode - $m = 0; - $j = 0; - } - elsif($_ =~ /^ - run: .* apt-get .* install (.*)/) { - $job{'install'} = $1; - } - elsif($m >= 0) { - if($_ =~ /^ - name: (.*)/) { - # matrix job - #print "name: $1\n"; - $job{'name'} = $1; - $j += ($m?$m:1); - } - elsif($_ =~ /install: (.*)/) { - $job{'install'} = $1; - } - elsif($_ =~ /( |curl-)configure: (.*)/) { - $job{'configure'} = $2; - $job{'os'}=$os; - submit(\%job); - $done++; - } - elsif($_ =~ /generate: (.*)/) { - $job{'cmake'} = $1; - if($m) { - # matrix mode, multiple copies - my %dupe = %job; - for my $cc (@cc) { - %job = %dupe; - $job{'cc'} = $cc; - $job{'os'}=$os; - submit(\%job); - $done++; - } - } - else { - $job{'os'}=$os; - submit(\%job); - $done++; - } - } - elsif($_ =~ /- CC: (.*)/) { - # matrix multiplier - push @cc, $1; - $m++; - } - elsif($_ =~ /^\s*steps:/) { - # disable matrix mode - $m = -1; - } - } - $line++; - } - close(G); - # commit final counted jobs - $c += $j; - - if(!$done) { - $job{'name'} = $topname? $topname : '[unnamed]'; - $job{'os'}=$os; - submit(\%job); - $done++; - } - # reset internal job counter - $j = 0; - } - #print "Jobs: $c\n"; - return $c; -} - -sub azurepipelines { - my ($tag)=@_; - open(G, "git show $tag:.azure-pipelines.yml 2>/dev/null|"); - my $c = 0; - my $j = 0; - my $m = -1; - my $image; - my %job; - my $line = 1; - my $os; - $job{'file'} = ".azure-pipelines.yml"; - $job{'service'} = "azure"; - while() { - if($_ =~ /^ vmImage: (.*)/) { - my $i = $1; - if($i =~ /ubuntu/) { - $os = "linux"; - } - elsif($i =~ /windows/) { - $os = "windows"; - } - } - elsif($_ =~ /^ - stage: (.*)/) { - my $topname = $1; - if($topname !~ /(windows|linux)/) { - $job{'name'} = $topname; - $job{'line'}=$line; - submit(\%job); - } - } - elsif($_ =~ /job:/) { - # commit previously counted jobs - $c += $j; - # initial value for non-matrix job - $j = 1; - } - elsif($_ =~ /matrix:/) { - # start of new matrix list(!) - $m = 0; - $j = 0; - } - elsif($m >= 0) { - if($_ =~ /^ name: (.*)/) { - # single matrix list entry job - $j++; - $job{'name'} = $1; - } - # azure matrix is a simple list, - # therefore no multiplier needed - elsif($_ =~ /steps:/) { - # disable matrix mode - $m = -1; - } - elsif($_ =~ /^ configure: (.*)/) { - $job{'configure'} = $1; - $job{'line'}=$line; - $job{'os'}=$os; - submit(\%job); - } - } - $line++; - } - close(G); - # commit final counted jobs - $c += $j; - - return $c; -} - -sub appveyor { - my ($tag)=@_; - open(G, "git show $tag:appveyor.yml 2>/dev/null|"); - my $c = 0; - my %job; - my $line=0; - $job{'file'} = "appveyor.yml"; - $job{'service'} = "appveyor"; - - while() { - $line++; - if($_ =~ /^( - |install)/) { - if($job{'image'}) { - $job{'os'} = "windows"; - submit(\%job); - $c++; - } - } - $job{'line'} = $line; - if($_ =~ /^ APPVEYOR_BUILD_WORKER_IMAGE: \'(.*)\'/) { - $job{'image'}= $1; - } - elsif($_ =~ /^ BUILD_SYSTEM: (.*)/) { - $job{'build'} = lc($1); - } - elsif($_ =~ /^ PRJ_GEN: \'(.*)\'/) { - $job{'compiler'} = $1; - } - elsif($_ =~ /^ PRJ_CFG: (.*)/) { - $job{'config'} = $1; - } - elsif($_ =~ /^ OPENSSL: \'(.*)\'/) { - $job{'openssl'} = $1 eq "ON" ? "true": "false"; - } - elsif($_ =~ /^ SCHANNEL: \'(.*)\'/) { - $job{'schannel'} = $1 eq "ON" ? "true": "false"; - } - elsif($_ =~ /^ ENABLE_UNICODE: \'(.*)\'/) { - $job{'unicode'} = $1 eq "ON" ? "true": "false"; - } - elsif($_ =~ /^ HTTP_ONLY: \'(.*)\'/) { - $job{'http-only'} = $1 eq "ON" ? "true": "false"; - } - elsif($_ =~ /^ TESTING: \'(.*)\'/) { - $job{'testing'} = $1 eq "ON" ? "true": "false"; - } - elsif($_ =~ /^ SHARED: \'(.*)\'/) { - $job{'shared'} = $1 eq "ON" ? "true": "false"; - } - elsif($_ =~ /^ TARGET: \'-A (.*)\'/) { - $job{'target'} = $1; - } - } - close(G); - - return $c; -} - -sub cirrus { - my ($tag)=@_; - open(G, "git show $tag:.cirrus.yml 2>/dev/null|"); - my $c = 0; - my %job; - my $line=0; - my $name = 0; - my $os; - $job{'file'} = ".cirrus.yml"; - $job{'service'} = "cirrus"; - while() { - $line++; - if($_ =~ /^ ( |-) (name|image_family|image):/) { - $c++; - } - if($_ =~ /^ - name:/) { - if($name) { - $job{'os'} = $os; - $job{'line'} = $line; - submit(\%job); - $name = 0; - } - } - if($_ =~ /^ - name: (.*)/) { - $job{'name'} = $1; - $name = 1; - } - elsif($_ =~ /^ image_family: (.*)/) { - $os = "freebsd"; - } - elsif($_ =~ /^windows_task:/) { - $os = "windows"; - } - elsif($_ =~ /^ prepare: pacman -S --needed --noconfirm --noprogressbar (.*)/) { - $job{'install'} = $1; - } - elsif($_ =~ /^ configure: (.*)/) { - $job{'configure'} = $1; - } - } - close(G); - if($name) { - $job{'os'} = $os; - $job{'line'} = $line; - submit(\%job); - } - return $c; -} - -sub circle { - my ($tag)=@_; - open(G, "git show $tag:.circleci/config.yml 2>/dev/null|"); - my $c = 0; - my $wf = 0; - my %job; - my %cmd; - my %configure; - my %target; - my $line=0; - my $cmds; - my $jobs; - my $workflow; - my $cmdname; - my $jobname; - $job{'file'} = ".circleci/config.yml"; - $job{'service'} = "circleci"; - while() { - $line++; - if($_ =~ /^commands:/) { - # we record configure lines in this state - $cmds = 1; - } - elsif($cmds) { - if($_ =~ /^ ([^ ]*):/) { - $cmdname = $1; - } - elsif($_ =~ /^ .*.\/configure (.*)/) { - $cmd{$cmdname}=$1; - } - } - if($_ =~ /^jobs:/) { - # we record which job runs with configure here - $jobs = 1; - $cmds = 0; - } - elsif($jobs) { - if($_ =~ /^ ([^ ]*):/) { - $jobname = $1; - } - elsif($_ =~ /^ - (configure.*)/) { - $configure{$jobname}=$1; - } - elsif($_ =~ /^ resource_class: arm.medium/) { - $target{$jobname}="arm"; - } - } - if($_ =~ /^workflows:/) { - $wf = 1; - $cmds = 0; - } - elsif($wf) { - if($_ =~ /^ ([^ ]+):/) { - $workflow = $1; - } - elsif($_ =~ /^ - (.*)\n/) { - my $jb = $1; - my $cnfgure = $configure{$jb}; - my $trgt = $target{$jb}; - $job{'configure'} = $cmd{$cnfgure}; - $job{'name' }=$workflow; - $job{'os'} = "linux"; - $job{'line'} = $line; - $job{'target'} = $trgt if($trgt); - submit(\%job); - } - if($_ =~ / *jobs:/) { - $c++; - } - } - } - close(G); - return $c; -} - -sub zuul { - my ($tag)=@_; - open(G, "git show $tag:zuul.d/jobs.yaml 2>/dev/null|"); - my $c = 0; - my %job; - my $line=0; - my $type; - my $jobmode; - my $apt = 0; - my $env = 0; - my $envcont; - $job{'file'} = "zuul.d/jobs.yaml"; - $job{'service'} = "zuul"; - while() { - $line++; - #print "L: ($jobmode / $env) $_"; - if($_ =~ /^- job:/) { - $jobmode = 1; # start a new - $type="configure"; - } - if($jobmode) { - if($apt) { - if($_ =~ /^ - (.*)/) { - my $value = $1; - $job{'install'} .= "$value "; - } - else { - $apt = 0; # end of curl_apt_packages - } - } - if($env) { - if($envcont) { - if($_ =~ /^ (.*)/) { - $job{$envcont} .= "$1 "; - } - else { - $envcont = ""; - } - } - if($_ =~ /^ ([^:]+): (.*)/) { - my ($var, $value) = ($1, $2); - - if($var eq "C") { - $var = $type; - } - elsif($var eq "T") { - $var = "tests"; - if($value eq "cmake") { - # otherwise it remains configure - $type = "cmake"; - } - } - elsif($var eq "CC") { - $var = "compiler"; - } - elsif($var eq "CHECKSRC") { - $job{'checksrc'} = $value ? "true": "false"; - $var = ""; - } - else { - $var = ""; - } - if($value eq ">-") { - $envcont = $var; - } - elsif($var) { - $job{$var} = $value; - } - } - elsif($_ !~ /^ /) { - # end of envs - $env = 0; - } - } - if($_ =~ /^ curl_env:/) { - $env = 1; # start of envs - } - elsif($_ =~ /^ curl_apt_packages:/) { - $apt = 1; # start of apt packages - } - elsif($_ =~ /^ name: (.*)/) { - my $n = $1; - if($n eq "curl-base") { - # not counted - $jobmode = 0; - next; - } - $job{'name'} = $n; - } - elsif($_ =~ /^\n\z/) { - # a job is complete - $job{'line'}=$line; - $job{'os'}="linux"; - submit(\%job); - $jobmode = 0; - $c++; - } - } - } - close(G); - return $c; -} - -my $tag = `git rev-parse --abbrev-ref HEAD 2>/dev/null` || "master"; -chomp $tag; -githubactions($tag); -azurepipelines($tag); -appveyor($tag); -zuul($tag); -cirrus($tag); -circle($tag); diff --git a/scripts/cmakelint.sh b/scripts/cmakelint.sh index 788f087305..30a735f87e 100755 --- a/scripts/cmakelint.sh +++ b/scripts/cmakelint.sh @@ -34,7 +34,7 @@ # cmake-lint can be installed from PyPi with the command "python3 -m pip # install cmakelang". # -# The xargs invocation is portable, but does not preserve spaces in file names. +# The xargs invocation is portable, but does not preserve spaces in filenames. # If such a file is ever added, then this can be portably fixed by switching to # "xargs -I{}" and appending {} to the end of the xargs arguments (which will # call cmakelint once per file) or by using the GNU extension "xargs -d'\n'". @@ -45,15 +45,13 @@ cd "$(dirname "$0")"/.. { if [ -n "${1:-}" ]; then - for A in "$@"; do printf "%s\n" "$A"; done + for A in "$@"; do printf '%s\n' "$A"; done elif git rev-parse --is-inside-work-tree >/dev/null 2>&1; then - git ls-files + git ls-files '**CMakeLists.txt' '*.cmake' else - # strip off the leading ./ to make the grep regexes work properly - find . -type f | sed 's@^\./@@' + find . -type f \( -name 'CMakeLists.txt' -o -name '*.cmake' \) fi -} | grep -E '(^CMake|/CMake|\.cmake$)' | grep -v -E '(\.h\.cmake|\.in|\.c)$' \ - | xargs \ +} | sort | xargs \ cmake-lint \ --suppress-decorations \ --disable \ @@ -74,7 +72,8 @@ cd "$(dirname "$0")"/.. --min-statement-spacing 1 \ --max-statement-spacing 2 \ --max-returns 6 \ - --max-branches 12 \ + --max-branches 20 \ --max-arguments 5 \ --max-localvars 15 \ - --max-statements 50 + --max-statements 95 \ + -- diff --git a/scripts/completion.pl b/scripts/completion.pl index 7b689c74fb..27e2f062e7 100755 --- a/scripts/completion.pl +++ b/scripts/completion.pl @@ -84,8 +84,8 @@ sub parse_main_opts { @files = readdir($dir_handle); closedir($dir_handle) || die "Unable to close handle on dir: $opts_dir due to error: $!"; - # We want regular files that end with .md and don't start with an underscore - # Edge case: MANPAGE.md doesn't start with an underscore but also isn't documentation for an option + # We want regular files that end with .md and do not start with an underscore + # Edge case: MANPAGE.md does not start with an underscore but also is not documentation for an option @files = grep { $_ =~ /\.md$/i && !/^_/ && -f "$opts_dir/$_" && $_ ne "MANPAGE.md" } @files; for my $file (@files) { @@ -145,8 +145,8 @@ sub parse_main_opts { push(@list, $option); } - # Sort longest first, because zsh won't complete an option listed - # after one that's a prefix of it. When length is equal, fall back + # Sort longest first, because zsh does not complete an option listed + # after one that is a prefix of it. When length is equal, fall back # to stringwise cmp. @list = sort { $a =~ /([^=]*)/; my $ma = $1; diff --git a/scripts/contributors.sh b/scripts/contributors.sh index ddc9bdfba0..37f1e5835b 100755 --- a/scripts/contributors.sh +++ b/scripts/contributors.sh @@ -62,7 +62,7 @@ CURLWWW="${CURLWWW:-../curl-www}" git -C "$CURLWWW" log --pretty=full --use-mailmap "$start..HEAD" fi } | \ - grep -Eai '(^Author|^Commit|by):' | \ + grep -Eai '(^Author|^Commit|^ +[a-z-]+-by):' | \ cut -d: -f2- | \ cut '-d(' -f1 | \ cut '-d<' -f1 | \ @@ -80,16 +80,16 @@ sed -f ./docs/THANKS-filter | \ sort -fu | \ awk ' { - if(length($0)) { - num++; - n = sprintf("%s%s%s,", n, length(n)?" ":"", $0); - #print n; - if(length(n) > 77) { - printf(" %s\n", p); - n=sprintf("%s,", $0); - } - p=n; - } + if(length($0)) { + num++; + n = sprintf("%s%s%s,", n, length(n)?" ":"", $0); + #print n; + if(length(n) > 77) { + printf(" %s\n", p); + n=sprintf("%s,", $0); + } + p=n; + } } END { diff --git a/scripts/contrithanks.sh b/scripts/contrithanks.sh index 59db5b876a..47438701cd 100755 --- a/scripts/contrithanks.sh +++ b/scripts/contrithanks.sh @@ -62,7 +62,7 @@ tail -n +7 ./docs/THANKS | sed 's/ github/ github/i' > $rand git -C "$CURLWWW" log --use-mailmap "$start..HEAD" fi } | \ - grep -Eai '(^Author|^Commit|by):' | \ + grep -Eai '(^Author|^Commit|^ +[a-z-]+-by):' | \ cut -d: -f2- | \ cut '-d(' -f1 | \ cut '-d<' -f1 | \ diff --git a/scripts/delta b/scripts/delta index b6a1df8a17..02b0232c47 100755 --- a/scripts/delta +++ b/scripts/delta @@ -67,7 +67,7 @@ my $contribs = $acontribs - $bcontribs; # number of setops: sub setopts { my ($f)=@_; - open(H, "$f"); + open(H, $f); my $opts; while() { if(/^ CURLOPT(|DEPRECATED)\(/ && ($_ !~ /OBSOLETE/)) { @@ -82,8 +82,8 @@ my $bsetopts = setopts("git show $start:include/curl/curl.h|"); my $nsetopts = $asetopts - $bsetopts; # Number of command line options: -my $aoptions=`grep -c '{"....--' src/tool_listhelp.c`; -my $boptions=`git show $start:src/tool_listhelp.c 2>/dev/null | grep -c '{"....--'`; +my $aoptions=`grep -c '{ *"....--' src/tool_listhelp.c`; +my $boptions=`git show $start:src/tool_listhelp.c 2>/dev/null | grep -c '{ *"....--'`; my $noptions=$aoptions - $boptions; # current local branch @@ -95,7 +95,7 @@ my $deletes=`git diff-tree --diff-filter=A -r --summary origin/$branch $start 2> my $creates=`git diff-tree --diff-filter=D -r --summary origin/$branch $start 2>/dev/null | wc -l`; # Time since that tag -my $tagged=`git for-each-ref --format="%(refname:short) | %(taggerdate:unix)" refs/tags/* | grep ^$start | cut "-d|" -f2`; # Unix timestamp +my $tagged=`git for-each-ref --format="%(refname:short) | %(taggerdate:unix)" refs/tags/* | grep ^$start | cut '-d|' -f2`; # Unix timestamp my $taggednice=`git for-each-ref --format="%(refname:short) | %(creatordate)" refs/tags/* | grep ^$start | cut '-d|' -f2`; # human readable time chomp $taggednice; my $now=POSIX::strftime("%s", localtime()); @@ -120,7 +120,7 @@ my $numchanges = 0; my $numbugfixes = 0; my $numcontributors = 0; open(F, ") { if($_ =~ /following changes:/) { $mode=1; @@ -169,13 +169,13 @@ printf "New command line options: %d (total %d)\n", $noptions, $aoptions; printf "Changes logged: %d\n", $numchanges; printf "Bugfixes logged: %d (%.2f per day)\n", - $numbugfixes, $numbugfixes / $days; + $numbugfixes, $days ? $numbugfixes / $days : $numbugfixes; printf "Added files: %d (total %d)\n", $creates, $afiles; printf "Deleted files: %d (delta: %d)\n", $deletes, $creates - $deletes; print "Diffstat:$diffstat" if(!$fileschanged); -printf "Files changed: %d (%.2f%%)\n", $fileschanged, $fileschanged*100/$afiles; +printf "Files changed: %d (%.2f%%)\n", $fileschanged, $fileschanged * 100 / $afiles; printf "Lines inserted: %d\n", $insertions; printf "Lines deleted: %d (delta: %d)\n", $deletions, $insertions - $deletions; diff --git a/scripts/extract-unit-protos b/scripts/extract-unit-protos index 80c2831ede..4fdcf99fad 100755 --- a/scripts/extract-unit-protos +++ b/scripts/extract-unit-protos @@ -27,15 +27,38 @@ use strict; use warnings; my @proto; +my %func; my %inc; sub scanfile { my ($file) = @_; open(F, "<$file") || die "$file failed"; + my $unit = 0; while() { - if($_ =~ /^UNITTEST .*\);/) { - push @proto, $_; - $inc{$file} = 1; + if($_ =~ /^UNITTEST .*[* ]([a-z0-9_]+)\(/i) { + my $n = $1; + if($func{$n}) { + # already prototyped, this is now the function itself + } + else { + $func{$n} = 1; + push @proto, $_; + $inc{$file} = 1; + $unit = 1; + } + } + if($unit) { + if($unit != 1) { + push @proto, $_; + } + if($_ =~ /\);/) { + # end of proto + $unit = 0; + } + else { + # proto continues + $unit++; + } } } close(F); @@ -81,7 +104,6 @@ for my $f (sort keys %inc) { $f =~ s/\.c/.h/; # .h extension if(-f $f) { - $f =~ s/.*\///; # cut the path off print "#include \"$f\"\n"; } } diff --git a/scripts/firefox-db2pem.sh b/scripts/firefox-db2pem.sh index 2a4b9ceace..7d31b12886 100755 --- a/scripts/firefox-db2pem.sh +++ b/scripts/firefox-db2pem.sh @@ -32,7 +32,11 @@ set -eu -db=$(ls -1d "$HOME"/.mozilla/firefox/*default*) +if [ -d "$HOME/Library/Application Support"/Firefox/Profiles ]; then + db=$(ls -1d "$HOME/Library/Application Support"/Firefox/Profiles/*default*) +else + db=$(ls -1d "$HOME"/.mozilla/firefox/*default*) +fi out="${1:-}" if test -z "$out"; then @@ -50,12 +54,11 @@ cat > "$out" <> "$out" diff --git a/scripts/maketgz b/scripts/maketgz index d7059554d3..e6bc53dcdc 100755 --- a/scripts/maketgz +++ b/scripts/maketgz @@ -70,7 +70,7 @@ fi # As a precaution, remove all *.dist files that may be lying around, to reduce # the risk of old leftovers getting shipped. echo "removing all old *.dist files" -find . -name "*.dist" -exec rm {} \; +find . -name "*.dist" -exec rm -- {} \; numeric="$(printf "%02x%02x%02x\n" "$major" "$minor" "$patch")" @@ -184,7 +184,7 @@ retar() { mkdir "$tempdir" cd "$tempdir" gzip -dc "../$targz" | tar -xf - - find curl-* -depth -exec touch -c -t "$filestamp" '{}' + + find curl-* -depth -exec touch -c -t "$filestamp" -- '{}' + tar --create --format=ustar --owner=0 --group=0 --numeric-owner --sort=name curl-* | gzip --best --no-name > out.tar.gz mv out.tar.gz ../ cd .. diff --git a/scripts/managen b/scripts/managen index 459b651240..d70df2fbfd 100755 --- a/scripts/managen +++ b/scripts/managen @@ -25,7 +25,7 @@ =begin comment -This script generates the manpage. +This script generates the man page. Example: managen [files] > curl.1 @@ -64,10 +64,9 @@ my $globals; my $error = 0; my $indent = 4; -# get the long name version, return the manpage string +# get the long name version, return the man page string sub manpageify { - my ($k)=@_; - my $l; + my ($k, $manpage)=@_; my $trail = ''; # the matching pattern might include a trailing dot that cannot be part of # the option name @@ -75,20 +74,16 @@ sub manpageify { # cut off trailing dot $trail = "."; } - my $klong = $k; - # quote "bare" minuses in the long name - $klong =~ s/-/\\-/g; - if($optlong{$k}) { - # both short + long - $l = "\\fI-".$optlong{$k}.", \\-\\-$klong\\fP$trail"; - } - else { - # only long - $l = "\\fI\\-\\-$klong\\fP$trail"; - } - return $l; -} + if($manpage) { + my $klong = $k; + # quote "bare" minuses in the long name + $klong =~ s/-/\\-/g; + # only long + return "\\fI\\-\\-$klong\\fP$trail"; + } + return "--$k$trail"; +} my $colwidth=79; # max number of columns @@ -121,7 +116,7 @@ sub justline { print " "; $spare--; } - printf "%s%s", $prev?" ":"", $_; + printf "%s%s", $prev ? " " : "", $_; $prev = 1; $spare += $ratio; } @@ -136,7 +131,7 @@ sub lastline { prefixline($lvl * $indent + $l); my $prev = 0; for(@line) { - printf "%s%s", $prev?" ":"", $_; + printf "%s%s", $prev ? " " : "", $_; $prev = 1; } print "\n"; @@ -245,8 +240,38 @@ sub overrides { } } +my %protexists = ( + 'DNS' => 1, + 'FILE' => 1, + 'FTP' => 1, + 'FTPS' => 1, + 'GSS/kerberos' => 1, + 'HTTP' => 1, + 'HTTPS' => 1, + 'IMAP' => 1, + 'IPFS' => 1, + 'LDAP' => 1, + 'MQTT' => 1, + 'POP3' => 1, + 'SCP' => 1, + 'SFTP' => 1, + 'SMTP' => 1, + 'SSL' => 2, # deprecated + 'TELNET' => 1, + 'TFTP' => 1, + 'TLS' => 1, + ); + sub protocols { - my ($manpage, $standalone, $data)=@_; + my ($f, $line, $manpage, $standalone, $data)=@_; + my @e = split(/ +/, $data); + for my $pr (@e) { + if(!$protexists{$pr}) { + + print STDERR "$f:$line:1:ERROR: unrecognized protocol: $pr\n"; + exit 2; + } + } if($standalone) { return ".SH \"PROTOCOLS\"\n$data\n"; } @@ -265,8 +290,8 @@ sub too_old { elsif($version =~ /^(\d+)\.(\d+)/) { $a = $1 * 1000 + $2 * 10; } - if($a < 7600) { - # we consider everything before 7.60.0 to be too old to mention + if($a < 7660) { + # we consider everything before 7.66.0 to be too old to mention # specific changes for return 1; } @@ -386,7 +411,7 @@ sub render { print STDERR "$f:$line:1:WARN: un-escaped < or > used: $nontick\n"; } - # if there is a space, it needs quotes for manpage + # if there is a space, it needs quotes for man page if(($word =~ / /) && $manpage) { $word = "\"$word\""; } @@ -474,10 +499,9 @@ sub render { # convert single backslash to double-backslash $d =~ s/\\/\\\\/g if($manpage); - if($manpage) { if(!$quote && $d =~ /--/) { - $d =~ s/--([a-z0-9.-]+)/manpageify($1)/ge; + $d =~ s/--([a-z0-9.-]+)/manpageify($1, 1)/ge; } # quote minuses in the output @@ -502,17 +526,16 @@ sub render { push @desc, "\n" if($blankline && !$header); $blankline = 0; push @desc, $d if($manpage); - my $qstr = $quote ? "q": ""; + my $qstr = $quote ? "q" : ""; push @desc, "[".(1 + $level)."$qstr]$d" if(!$manpage); $header = 0; - } if($finalblank) { print STDERR "$f:$line:1:ERROR: trailing blank line\n"; exit 3; } if($quote) { - # don't leave the quote "hanging" + # do not leave the quote "hanging" push @desc, ".fi\n" if($manpage); } if($tablemode) { @@ -557,10 +580,10 @@ sub single { my $magic; # cmdline special option my $line; my $dline; - my $multi; + my $multi = ""; my $scope; my $experimental; - my $start; + my $start = 0; my $list; # identifies the list, 1 example, 2 see-also while(<$fh>) { $line++; @@ -635,12 +658,14 @@ sub single { elsif(/^Experimental: yes/i) { $experimental=1; } + # REUSE-IgnoreStart elsif(/^C: (.*)/i) { $copyright=$1; } elsif(/^SPDX-License-Identifier: (.*)/i) { $spdx=$1; } + # REUSE-IgnoreEnd elsif(/^Help: *(.*)/i) { ; } @@ -671,7 +696,9 @@ sub single { return 2; } if(!$spdx) { + # REUSE-IgnoreStart print STDERR "$f:$line:1:ERROR: no 'SPDX-License-Identifier:' field present\n"; + # REUSE-IgnoreEnd return 2; } last; @@ -720,7 +747,7 @@ sub single { } my @leading; if($protocols) { - push @leading, protocols($manpage, $standalone, $protocols); + push @leading, protocols($f, $line, $manpage, $standalone, $protocols); } if($standalone) { @@ -728,10 +755,11 @@ sub single { } if($experimental) { - push @leading, "**WARNING**: this option is experimental. Do not use in production.\n\n"; + my $pref = $manpage ? "" : "[1]"; + push @leading, "$pref**WARNING**: this option is experimental. Do not use in production.\n\n"; } - my $pre = $manpage ? "\n": "[1]"; + my $pre = $manpage ? "\n" : "[1]"; if($scope) { if($category !~ /global/) { @@ -740,7 +768,9 @@ sub single { } if($scope eq "global") { push @desc, "\n" if(!$manpage); - push @desc, "${pre}This option is global and does not need to be specified for each use of --next.\n"; + push @desc, + sprintf("${pre}This option is global and does not need to be specified for each use of %s.\n", + manpageify("next", $manpage)); } else { print STDERR "$f:$line:1:ERROR: unrecognized scope: '$scope'\n"; @@ -750,11 +780,15 @@ sub single { my @extra; if($multi eq "single") { - push @extra, "${pre}If --$long is provided several times, the last set ". - "value is used.\n"; + push @extra, + sprintf("${pre}If %s is provided several times, the last set ". + "value is used.\n", + manpageify($long, $manpage)); } elsif($multi eq "append") { - push @extra, "${pre}--$long can be used several times in a command line\n"; + push @extra, + sprintf("${pre}%s can be used several times in a command line\n", + manpageify($long, $manpage)); } elsif($multi eq "boolean") { my $rev = "no-$long"; @@ -766,22 +800,25 @@ sub single { } my $dashes = $manpage ? "\\-\\-" : "--"; push @extra, - "${pre}Providing --$long multiple times has no extra effect.\n". - "Disable it again with $dashes$rev.\n"; + sprintf("${pre}Providing %s multiple times has no extra effect.\n". + "Disable it again with $dashes$rev.\n", + manpageify($long, $manpage)); } elsif($multi eq "mutex") { push @extra, - "${pre}Providing --$long multiple times has no extra effect.\n"; + sprintf("${pre}Providing %s multiple times has no extra effect.\n", + manpageify($long, $manpage)); } elsif($multi eq "custom") { ; # left for the text to describe } elsif($multi eq "per-URL") { push @extra, - "${pre}--$long is associated with a single URL. Use it once per URL ". - "when you use several URLs in a command line.\n"; + sprintf("${pre}%s is associated with a single URL. Use it once per URL ". + "when you use several URLs in a command line.\n", + manpageify($long, $manpage)); } - else { + elsif($multi) { print STDERR "$f:$line:1:ERROR: unrecognized Multi: '$multi'\n"; return 2; } @@ -803,18 +840,18 @@ sub single { if(!$helplong{$k}) { print STDERR "$f:$line:1:WARN: see-also a non-existing option: $k\n"; } - my $l = $manpage ? manpageify($k) : "--$k"; + my $l = manpageify($k, $manpage); my $sep = " and"; if($and && ($i < $and)) { $sep = ","; } - $mstr .= sprintf "%s$l", $mstr?"$sep ":""; + $mstr .= sprintf "%s$l", $mstr ? "$sep " : ""; $i++; } if($requires) { - my $l = $manpage ? manpageify($long) : "--$long"; - push @foot, "$l requires that libcurl". + my $l = manpageify($long, $manpage); + push @foot, "For $l to work, it requires that the underlying libcurl". " is built to support $requires.\n"; } if($mutexed) { @@ -826,7 +863,7 @@ sub single { if(!$helplong{$k}) { print STDERR "WARN: $f mutexes a non-existing option: $k\n"; } - my $l = $manpage ? manpageify($k) : "--$k"; + my $l = manpageify($k, $manpage); my $sep = ", "; if($count == ($num -1)) { $sep = " and "; @@ -840,6 +877,29 @@ sub single { if($examples[0]) { my $s =""; $s="s" if($examples[1]); + foreach my $e (@examples) { + my $check = $e; + # verify the used options + for my $e (split(/ /, $check)) { + if($e =~ /^-([^- ])/) { + my $opt = $1; + if(!$optshort{$opt}) { + print STDERR "$f:$line:1:ERROR: unknown option in ". + "example: -$opt\n"; + return 2; + } + } + elsif($e =~ /^--([^ =]*)/) { + my $opt = $1; + $opt =~ s/^expand-//g; + if(!$helplong{$opt}) { + print STDERR "$f:$line:1:ERROR: unknown option in ". + "example: '--$opt'\n"; + return 2; + } + } + } + } if($manpage) { print "\nExample$s:\n"; print ".nf\n"; @@ -984,12 +1044,11 @@ sub header { printdesc($manpage, 0, @d); } - sub sourcecategories { my ($dir) = @_; my %cats; open(H, "<$dir/../../src/tool_help.h") || - die "can't find the header file"; + die "cannot find the header file"; while() { if(/^\#define CURLHELP_([A-Z0-9]*)/) { $cats{lc($1)}++; @@ -1028,6 +1087,7 @@ sub listhelp { * ***************************************************************************/ #include "tool_setup.h" + #include "tool_help.h" /* @@ -1067,7 +1127,7 @@ HEAD $bitmask .= ' | '; } } - $bitmask =~ s/(?=.{76}).{1,76}\|/$&\n /g; + $bitmask =~ s/(?=.{76}).{1,76}\|/$&\n /g; my $arg = $arglong{$long}; if($arg) { $opt .= " $arg"; @@ -1075,7 +1135,7 @@ HEAD my $desc = $helplong{$f}; $desc =~ s/\"/\\\"/g; # escape double quotes - my $line = sprintf " {\"%s\",\n \"%s\",\n %s},\n", $opt, $desc, $bitmask; + my $line = sprintf " { \"%s\",\n \"%s\",\n %s },\n", $opt, $desc, $bitmask; if(length($opt) > 78) { print STDERR "WARN: the --$long name is too long\n"; @@ -1107,7 +1167,7 @@ sub listcats { @categories = sort @categories; for my $i (0..$#categories) { printf("#define CURLHELP_%-10s (%s)\n", - uc($categories[$i]), "1u << ${i}u"); + uc($categories[$i]), "1 << ${i}"); } } @@ -1190,7 +1250,7 @@ sub mainpage { .\\" * .\\" ************************************************************************** .\\" -.\\" DO NOT EDIT. Generated by the curl project managen manpage generator. +.\\" DO NOT EDIT. Generated by the curl project managen man page generator. .\\" .TH curl 1 "$date" "curl $version" "curl Manual" HEADER @@ -1277,7 +1337,8 @@ my $dir = "."; my $include = "../../include"; my $cmd = shift @ARGV || ''; - check: +check: + if($cmd eq "-d") { # specifies source directory $dir = shift @ARGV; diff --git a/scripts/mdlinkcheck b/scripts/mdlinkcheck index 925edc5294..4569b764d7 100755 --- a/scripts/mdlinkcheck +++ b/scripts/mdlinkcheck @@ -27,7 +27,10 @@ use strict; use warnings; my %whitelist = ( + 'https://curl.se' => 1, 'https://curl.se/' => 1, + 'https://curl.se/bug/' => 1, + 'https://curl.se/bug/view.cgi' => 1, 'https://curl.se/changes.html' => 1, 'https://curl.se/dev/advisory.html' => 1, 'https://curl.se/dev/builds.html' => 1, @@ -37,22 +40,27 @@ my %whitelist = ( 'https://curl.se/dev/secprocess.html' => 1, 'https://curl.se/dev/sourceactivity.html' => 1, 'https://curl.se/docs/' => 1, - 'https://curl.se/docs/bugbounty.html' => 1, 'https://curl.se/docs/caextract.html' => 1, 'https://curl.se/docs/copyright.html' => 1, + 'https://curl.se/docs/http-cookies.html' => 1, 'https://curl.se/docs/install.html' => 1, 'https://curl.se/docs/knownbugs.html' => 1, 'https://curl.se/docs/manpage.html' => 1, + 'https://curl.se/docs/releases.html' => 1, 'https://curl.se/docs/security.html' => 1, + 'https://curl.se/docs/ssl-ciphers.html' => 1, + 'https://curl.se/docs/ssl-compared.html' => 1, 'https://curl.se/docs/sslcerts.html' => 1, 'https://curl.se/docs/thanks.html' => 1, 'https://curl.se/docs/todo.html' => 1, 'https://curl.se/docs/vulnerabilities.html' => 1, + 'https://curl.se/download.html' => 1, 'https://curl.se/libcurl/' => 1, - 'https://curl.se/libcurl/c/CURLOPT_SSLVERSION.html' => 1, 'https://curl.se/libcurl/c/CURLOPT_SSL_CIPHER_LIST.html' => 1, + 'https://curl.se/libcurl/c/CURLOPT_SSLVERSION.html' => 1, 'https://curl.se/libcurl/c/CURLOPT_TLS13_CIPHERS.html' => 1, 'https://curl.se/libcurl/c/libcurl.html' => 1, + 'https://curl.se/libcurl/c/threadsafe.html' => 1, 'https://curl.se/logo/curl-logo.svg' => 1, 'https://curl.se/mail/' => 1, 'https://curl.se/mail/etiquette.html' => 1, @@ -62,22 +70,27 @@ my %whitelist = ( 'https://curl.se/rfc/rfc2255.txt' => 1, 'https://curl.se/sponsors.html' => 1, 'https://curl.se/support.html' => 1, + 'https://curl.se/windows/' => 1, + + 'https://testclutch.curl.se/' => 1, - 'https://github.com/curl/curl' => 1, 'https://github.com/curl/curl-fuzzer' => 1, 'https://github.com/curl/curl-www' => 1, - 'https://github.com/curl/curl/discussions' => 1, - 'https://github.com/curl/curl/issues' => 1, - 'https://github.com/curl/curl/labels/help%20wanted' => 1, - 'https://github.com/curl/curl/pulls' => 1, + 'https://github.com/curl/curl/wcurl' => 1, ); my %url; my %flink; -# list all .md files in the repo -my @files=`git ls-files '**.md'`; +my $dry; +if(defined $ARGV[0] && $ARGV[0] eq "--dry-run") { + $dry = 1; + shift @ARGV; +} + +# list all files to scan for links +my @files=`git ls-files docs include lib scripts src`; sub storelink { my ($f, $line, $link) = @_; @@ -91,7 +104,33 @@ sub storelink { $link =~ s:\#.*\z::; if($link =~ /^(https|http):/) { - $url{$link} .= "$f:$line "; + if($whitelist{$link}) { + #print "-- whitelisted: $link\n"; + $whitelist{$link}++; + } + # example.com is just example + elsif($link =~ /^https:\/\/(.*)example.(com|org|net)/) { + #print "-- example: $link\n"; + } + # so is using the .example TLD + elsif($link =~ /^https:\/\/(.*)\.example(\/|$|:)/) { + #print "-- .example: $link\n"; + } + # so is using anything on localhost + elsif($link =~ /^http(s|):\/\/localhost/) { + #print "-- localhost: $link\n"; + } + # ignore all links to curl's github repo + elsif($link =~ /^https:\/\/github.com\/curl\/curl(\/|$)/) { + #print "-- curl github repo: $link\n"; + } + elsif($link =~ /^(https|http):\/\/[0-9.]+(\/|$)/) { + #print "-- IPv4 number: $link\n"; + } + else { + #print "ADD '$link'\n"; + $url{$link} .= "$f:$line "; + } return; } @@ -115,15 +154,26 @@ sub storelink { sub findlinks { my ($f) = @_; my $line = 1; - open(F, "<:crlf", "$f") || + open(F, "<:crlf", $f) || return; + # is it a markdown extension? + my $md = ($f =~ /\.md$/i); + while() { - if(/\]\(([^)]*)/) { + chomp; + if($md && /\]\(([^)]*)/) { my $link = $1; #print "$f:$line $link\n"; storelink($f, $line, $link); } + # ignore trailing: dot, double quote, single quote, asterisk, hash, + # comma, question mark, colon, closing parenthesis, backslash, + # closing angle bracket, whitespace, pipe, backtick, semicolon + elsif(/(https:\/\/[a-z0-9.\/:%_+@-]+[^."'*\#,?:\)> \t|`;\\])/i) { + #print "RAW '$_'\n"; + storelink($f, $line, $1); + } $line++; } close(F); @@ -133,34 +183,53 @@ sub checkurl { my ($url) = @_; if($whitelist{$url}) { - #print "$url is whitelisted\n"; + #print STDERR "$url is whitelisted\n"; return 0; } - print "check $url\n"; - my $curlcmd="curl -ILfsm10 --retry 2 --retry-delay 5 -A \"Mozilla/curl.se link-probe\""; $url =~ s/\+/%2B/g; - my @content = `$curlcmd \"$url\"`; + my @content; + if(open(my $fh, '-|', 'curl', '-ILfsm10', '--retry', '2', '--retry-delay', '5', + '-A', 'Mozilla/curl.se link-probe', $url)) { + @content = <$fh>; + close $fh; + } if(!$content[0]) { - print STDERR "FAIL\n"; + print "FAIL: $url\n"; return 1; # fail } + print "OK: $url\n"; return 0; # ok } for my $f (@files) { chomp $f; - findlinks($f); + if($f !~ /\/mdlinkcheck$/) { + findlinks($f); + } +} + +for my $u (sort keys %whitelist) { + if($whitelist{$u} == 1) { + printf STDERR "warning: unused whitelist entry: '$u'\n"; + } +} + +if($dry) { + for my $u (sort keys %url) { + print "$u\n"; + } + exit; } my $error; - +my @errlist; for my $u (sort keys %url) { my $r = checkurl($u); if($r) { for my $f (split(/ /, $url{$u})) { - printf "%s ERROR links to missing URL %s\n", $f, $u; + push @errlist, sprintf "%s ERROR links to missing URL %s\n", $f, $u; $error++; } } @@ -169,10 +238,17 @@ for my $u (sort keys %url) { for my $l (sort keys %flink) { if(! -r $l) { for my $f (split(/ /, $flink{$l})) { - printf "%s ERROR links to missing file %s\n", $f, $l; + push @errlist, sprintf "%s ERROR links to missing file %s\n", $f, $l; $error++; } } } +printf "Checked %d URLs\n", scalar(keys %url); +if($error) { + print "$error URLs had problems:\n"; + for(@errlist) { + print $_; + } +} exit 1 if($error); diff --git a/scripts/mk-ca-bundle.pl b/scripts/mk-ca-bundle.pl index cb0b586093..4eb7598634 100755 --- a/scripts/mk-ca-bundle.pl +++ b/scripts/mk-ca-bundle.pl @@ -51,8 +51,8 @@ eval "require LWP::UserAgent"; my %urls = ( 'autoland' => 'https://raw.githubusercontent.com/mozilla-firefox/firefox/refs/heads/autoland/security/nss/lib/ckfw/builtins/certdata.txt', - 'beta' => 'https://raw.githubusercontent.com/mozilla-firefox/firefox/refs/heads/beta/security/nss/lib/ckfw/builtins/certdata.txt', - 'release' => 'https://raw.githubusercontent.com/mozilla-firefox/firefox/refs/heads/release/security/nss/lib/ckfw/builtins/certdata.txt', + 'beta' => 'https://raw.githubusercontent.com/mozilla-firefox/firefox/refs/heads/beta/security/nss/lib/ckfw/builtins/certdata.txt', + 'release' => 'https://raw.githubusercontent.com/mozilla-firefox/firefox/refs/heads/release/security/nss/lib/ckfw/builtins/certdata.txt', ); $opt_d = 'release'; @@ -60,7 +60,7 @@ $opt_d = 'release'; # If the OpenSSL commandline is not in search path you can configure it here! my $openssl = 'openssl'; -my $version = '1.29'; +my $version = '1.33'; $opt_w = 76; # default base64 encoded lines length @@ -91,8 +91,8 @@ my @valid_mozilla_trust_purposes = ( my @valid_mozilla_trust_levels = ( "TRUSTED_DELEGATOR", # CAs - "NOT_TRUSTED", # Don't trust these certs. - "MUST_VERIFY_TRUST", # This explicitly tells us that it ISN'T a CA but is + "NOT_TRUSTED", # Do not trust these certs. + "MUST_VERIFY_TRUST", # This explicitly tells us that it IS NOT a CA but is # otherwise ok. In other words, this should tell the # app to ignore any other sources that claim this is # a CA. @@ -100,11 +100,9 @@ my @valid_mozilla_trust_levels = ( # for delegates (i.e. it is not a CA). ); -my $default_signature_algorithms = $opt_s = "MD5"; +my $default_signature_algorithms = $opt_s = "SHA256"; my @valid_signature_algorithms = ( - "MD5", - "SHA1", "SHA256", "SHA384", "SHA512" @@ -150,12 +148,12 @@ sub warning_message() { if($opt_d =~ m/^risk$/i) { # Long Form Warning and Exit print "Warning: Use of this script may pose some risk:\n"; print "\n"; - print " 1) If you use HTTP URLs they are subject to a man in the middle attack\n"; - print " 2) Default to 'release', but more recent updates may be found in other trees\n"; - print " 3) certdata.txt file format may change, lag time to update this script\n"; - print " 4) Generally unwise to blindly trust CAs without manual review & verification\n"; - print " 5) Mozilla apps use additional security checks aren't represented in certdata\n"; - print " 6) Use of this script will make a security engineer grind his teeth and\n"; + print " 1. If you use HTTP URLs they are subject to a man in the middle attack\n"; + print " 2. Default to 'release', but more recent updates may be found in other trees\n"; + print " 3. certdata.txt file format may change, lag time to update this script\n"; + print " 4. Generally unwise to blindly trust CAs without manual review & verification\n"; + print " 5. Mozilla apps use additional security checks are not represented in certdata\n"; + print " 6. Use of this script will make a security engineer grind his teeth and\n"; print " swear at you. ;)\n"; exit; } else { # Short Form Warning @@ -168,22 +166,24 @@ sub HELP_MESSAGE() { print "\t-b\tbackup an existing version of ca-bundle.crt\n"; print "\t-d\tspecify Mozilla tree to pull certdata.txt or custom URL\n"; print "\t\t Valid names are:\n"; - print "\t\t ", join( ", ", map { ( $_ =~ m/$opt_d/ ) ? "$_ (default)" : "$_" } sort keys %urls ), "\n"; + print "\t\t ", join(", ", map { ($_ =~ m/$opt_d/) ? "$_ (default)" : $_ } sort keys %urls), "\n"; print "\t-f\tforce rebuild even if certdata.txt is current\n"; print "\t-i\tprint version info about used modules\n"; print "\t-k\tallow URLs other than HTTPS, enable HTTP fallback (insecure)\n"; print "\t-l\tprint license info about certdata.txt\n"; print "\t-m\tinclude meta data in output\n"; print "\t-n\tno download of certdata.txt (to use existing)\n"; - print wrap("\t","\t\t", "-p\tlist of Mozilla trust purposes and levels for certificates to include in output. Takes the form of a comma separated list of purposes, a colon, and a comma separated list of levels. (default: $default_mozilla_trust_purposes:$default_mozilla_trust_levels)"), "\n"; + print wrap("\t","\t\t", "-p\tlist of Mozilla trust purposes and levels for certificates to include in output. " . + "Takes the form of a comma separated list of purposes, a colon, and a comma separated list of levels. " . + "(default: $default_mozilla_trust_purposes:$default_mozilla_trust_levels)"), "\n"; print "\t\t Valid purposes are:\n"; - print wrap("\t\t ","\t\t ", join( ", ", "ALL", @valid_mozilla_trust_purposes ) ), "\n"; + print wrap("\t\t ","\t\t ", join(", ", "ALL", @valid_mozilla_trust_purposes)), "\n"; print "\t\t Valid levels are:\n"; - print wrap("\t\t ","\t\t ", join( ", ", "ALL", @valid_mozilla_trust_levels ) ), "\n"; + print wrap("\t\t ","\t\t ", join(", ", "ALL", @valid_mozilla_trust_levels)), "\n"; print "\t-q\tbe really quiet (no progress output at all)\n"; print wrap("\t","\t\t", "-s\tcomma separated list of certificate signatures/hashes to output in plain text mode. (default: $default_signature_algorithms)\n"); print "\t\t Valid signature algorithms are:\n"; - print wrap("\t\t ","\t\t ", join( ", ", "ALL", @valid_signature_algorithms ) ), "\n"; + print wrap("\t\t ","\t\t ", join(", ", "ALL", @valid_signature_algorithms)), "\n"; print "\t-t\tinclude plain text listing of certificates\n"; print "\t-u\tunlink (remove) certdata.txt after processing\n"; print "\t-v\tbe verbose and print out processed CAs\n"; @@ -195,7 +195,7 @@ sub VERSION_MESSAGE() { print "${0} version ${version} running Perl ${]} on ${^O}\n"; } -warning_message() unless ($opt_q || $url =~ m/^(ht|f)tps:/i ); +warning_message() unless ($opt_q || $url =~ m/^(ht|f)tps:/i); HELP_MESSAGE() if($opt_h); sub report($@) { @@ -221,7 +221,7 @@ sub parse_csv_param($$@) { s/^\s+//; # strip leading spaces s/\s+$//; # strip trailing spaces uc $_ # return the modified string as upper case - } split( ',', $param_string ); + } split(',', $param_string); # Find all values which are not in the list of valid values or "ALL" my @invalid = grep { !is_in_list($_,"ALL",@valid_values) } @values; @@ -229,7 +229,7 @@ sub parse_csv_param($$@) { if(scalar(@invalid) > 0) { # Tell the user which parameters were invalid and print the standard help # message which will exit - print "Error: Invalid ", $description, scalar(@invalid) == 1 ? ": " : "s: ", join( ", ", map { "\"$_\"" } @invalid ), "\n"; + print "Error: Invalid ", $description, scalar(@invalid) == 1 ? ": " : "s: ", join(", ", map { "\"$_\"" } @invalid), "\n"; HELP_MESSAGE(); } @@ -241,19 +241,20 @@ sub parse_csv_param($$@) { sub sha256 { my $result; if($Digest::SHA::VERSION || $Digest::SHA::PurePerl::VERSION) { - open(FILE, $_[0]) or die "Can't open '$_[0]': $!"; + open(FILE, $_[0]) or die "Could not open '$_[0]': $!"; binmode(FILE); $result = $MOD_SHA->new(256)->addfile(*FILE)->hexdigest; close(FILE); } else { # Use OpenSSL command if Perl Digest::SHA modules not available - $result = `"$openssl" dgst -r -sha256 "$_[0]"`; + open(my $fh, '-|', $openssl, 'dgst', '-r', '-sha256', $_[0]) or die "Failed running openssl on '$_[0]': $!"; + $result = <$fh>; # read first line + close $fh; $result =~ s/^([0-9a-f]{64}) .+/$1/is; } return $result; } - sub oldhash { my $hash = ""; open(C, "<$_[0]") || return 0; @@ -273,11 +274,11 @@ if($opt_p !~ m/:/) { HELP_MESSAGE(); } -(my $included_mozilla_trust_purposes_string, my $included_mozilla_trust_levels_string) = split( ':', $opt_p ); -my @included_mozilla_trust_purposes = parse_csv_param( "trust purpose", $included_mozilla_trust_purposes_string, @valid_mozilla_trust_purposes ); -my @included_mozilla_trust_levels = parse_csv_param( "trust level", $included_mozilla_trust_levels_string, @valid_mozilla_trust_levels ); +(my $included_mozilla_trust_purposes_string, my $included_mozilla_trust_levels_string) = split(':', $opt_p); +my @included_mozilla_trust_purposes = parse_csv_param("trust purpose", $included_mozilla_trust_purposes_string, @valid_mozilla_trust_purposes); +my @included_mozilla_trust_levels = parse_csv_param("trust level", $included_mozilla_trust_levels_string, @valid_mozilla_trust_levels); -my @included_signature_algorithms = parse_csv_param( "signature algorithm", $opt_s, @valid_signature_algorithms ); +my @included_signature_algorithms = parse_csv_param("signature algorithm", $opt_s, @valid_signature_algorithms); sub should_output_cert(%) { my %trust_purposes_by_level = @_; @@ -285,7 +286,7 @@ sub should_output_cert(%) { foreach my $level (@included_mozilla_trust_levels) { # for each level we want to output, see if any of our desired purposes are # included - return 1 if(defined( List::Util::first { is_in_list( $_, @included_mozilla_trust_purposes ) } @{$trust_purposes_by_level{$level}} )); + return 1 if(defined(List::Util::first { is_in_list($_, @included_mozilla_trust_purposes) } @{$trust_purposes_by_level{$level}})); } return 0; @@ -303,6 +304,7 @@ my $oldhash = oldhash($crt); report "SHA256 of old file: $oldhash"; if(!$opt_n) { + report "Using URL: $url"; report "Downloading $txt ..."; # If we have an HTTPS URL then use curl @@ -311,10 +313,16 @@ if(!$opt_n) { if($curl) { if($curl =~ /^Protocols:.* https( |$)/m) { report "Get certdata with curl!"; - my $proto = !$opt_k ? "--proto =https" : ""; - my $quiet = $opt_q ? "-s" : ""; - my @out = `curl -Lw %{response_code} $proto $quiet -o "$txt" "$url"`; - if(!$? && @out && $out[0] == 200) { + my @opts = (); + push @opts, '--proto', '=https' if !$opt_k; + push @opts, '-s' if $opt_q; + my $out = ''; + if(open(my $fh, '-|', 'curl', '-Lw', '%{response_code}', (@opts), '-o', $txt, $url)) { + $out = <$fh>; # read first line + chomp $out; + close $fh; + } + if($out && $out == 200) { $fetched = 1; report "Downloaded $txt"; } @@ -349,7 +357,7 @@ if(!$opt_n) { report "LWP is not available (LWP::UserAgent not found)"; exit 1; } - my $ua = new LWP::UserAgent(agent => "$0/$version"); + my $ua = new LWP::UserAgent(agent => "$0/$version"); $ua->env_proxy(); $resp = $ua->mirror($url, $txt); if($resp && $resp->code eq '304') { @@ -373,11 +381,11 @@ my $datesrc = "as of"; if(!$filedate) { # mxr.mozilla.org gave us a time, hg.mozilla.org does not! $filedate = time(); - $datesrc="downloaded on"; + $datesrc = "downloaded on"; } # get the hash from the download file -my $newhash= sha256($txt); +my $newhash = sha256($txt); if(!$opt_f && $oldhash eq $newhash) { report "Downloaded file identical to previous run\'s source file. Exiting"; @@ -393,9 +401,9 @@ my $currentdate = scalar gmtime($filedate); my $format = $opt_t ? "plain text and " : ""; if($stdout) { - open(CRT, '> -') or die "Couldn't open STDOUT: $!\n"; + open(CRT, '> -') or die "Could not open STDOUT: $!\n"; } else { - open(CRT,">$crt.~") or die "Couldn't open $crt.~: $!\n"; + open(CRT,">$crt.~") or die "Could not open $crt.~: $!\n"; } print CRT <) { if(/\*\*\*\*\* BEGIN LICENSE BLOCK \*\*\*\*\*/) { print CRT; @@ -621,25 +629,25 @@ while() { $pipe = "|$openssl x509 -" . $hash . " -fingerprint -noout -inform PEM"; if(!$stdout) { $pipe .= " >> $crt.~"; - close(CRT) or die "Couldn't close $crt.~: $!"; + close(CRT) or die "Could not close $crt.~: $!"; } - open(TMP, $pipe) or die "Couldn't open openssl pipe: $!"; + open(TMP, $pipe) or die "Could not open openssl pipe: $!"; print TMP $pem; - close(TMP) or die "Couldn't close openssl pipe: $!"; + close(TMP) or die "Could not close openssl pipe: $!"; if(!$stdout) { - open(CRT, ">>$crt.~") or die "Couldn't open $crt.~: $!"; + open(CRT, ">>$crt.~") or die "Could not open $crt.~: $!"; } } $pipe = "|$openssl x509 -text -inform PEM"; if(!$stdout) { $pipe .= " >> $crt.~"; - close(CRT) or die "Couldn't close $crt.~: $!"; + close(CRT) or die "Could not close $crt.~: $!"; } - open(TMP, $pipe) or die "Couldn't open openssl pipe: $!"; + open(TMP, $pipe) or die "Could not open openssl pipe: $!"; print TMP $pem; - close(TMP) or die "Couldn't close openssl pipe: $!"; + close(TMP) or die "Could not close openssl pipe: $!"; if(!$stdout) { - open(CRT, ">>$crt.~") or die "Couldn't open $crt.~: $!"; + open(CRT, ">>$crt.~") or die "Could not open $crt.~: $!"; } } report "Processed: $caname" if($opt_v); @@ -647,8 +655,9 @@ while() { } } } -close(TXT) or die "Couldn't close $txt: $!\n"; -close(CRT) or die "Couldn't close $crt.~: $!\n"; +close(TXT) or die "Could not close $txt: $!\n"; +close(CRT) or die "Could not close $crt.~: $!\n"; +utime($filedate, $filedate, "$crt.~"); unless($stdout) { if($opt_b && -e $crt) { my $bk = 1; diff --git a/scripts/mk-unity.pl b/scripts/mk-unity.pl index e1bd39de6d..014cfe8ece 100755 --- a/scripts/mk-unity.pl +++ b/scripts/mk-unity.pl @@ -31,15 +31,23 @@ use strict; use warnings; if(!@ARGV) { - die "Usage: $0 [--test ] [--include ]\n"; + die "Usage: $0 [--concat [-I]] [--test ] [--include ]\n"; } my @src; my %include; my $in_include = 0; my $any_test = 0; +my $concat = 0; +my @incpath; foreach my $src (@ARGV) { - if($src eq "--test") { + if($src eq "--concat") { + $concat = 1; + } + elsif($src =~ "^-I") { + push @incpath, substr($src, 2); + } + elsif($src eq "--test") { $in_include = 0; } elsif($src eq "--include") { @@ -55,9 +63,35 @@ foreach my $src (@ARGV) { } } +sub include($@) { + my $filename = shift; + if($concat) { + if(! -f $filename) { + foreach my $path (@incpath) { + my $fullfn = $path . "/" . $filename; + if(-f $fullfn) { + $filename = $fullfn; + last; + } + } + } + open(my $fh, '<', $filename) or die "Cannot open '$filename': $!"; + my $content = do { local $/; <$fh> }; + close $fh; + print "#line 1 \"$filename\"\n$content\n"; + } + else { + print "#include \"$filename\"\n"; + } +} + print "/* !checksrc! disable COPYRIGHT all */\n\n"; if($any_test) { - print "#include \"first.h\"\n\n"; + if($concat) { + print "/* NOLINTBEGIN(readability-duplicate-include) */\n\n"; + } + include("first.h"); + print "\n"; } my $tlist = ""; @@ -65,7 +99,7 @@ my $tlist = ""; foreach my $src (@src) { if($src =~ /([a-z0-9_]+)\.c$/) { my $name = $1; - print "#include \"$src\"\n"; + include($src); if(not exists $include{$src}) { # register test entry function $tlist .= " {\"$name\", test_$name},\n"; } @@ -73,6 +107,9 @@ foreach my $src (@src) { } if($any_test) { - print "\nconst struct entry_s s_entries[] = {\n$tlist {NULL, NULL}\n};\n"; - print "\n#include \"first.c\"\n"; + print "\nconst struct entry_s s_entries[] = {\n$tlist {NULL, NULL}\n};\n\n"; + include("first.c"); + if($concat) { + print "/* NOLINTEND(readability-duplicate-include) */\n"; + } } diff --git a/scripts/nroff2cd b/scripts/nroff2cd index b0308b6ef0..51b6974b8d 100755 --- a/scripts/nroff2cd +++ b/scripts/nroff2cd @@ -43,7 +43,7 @@ my $nroff2cd = "0.1"; # to keep check sub single { my ($f)=@_; - open(F, "<:crlf", "$f") || + open(F, "<:crlf", $f) || return 1; my $line; my $title; @@ -67,7 +67,7 @@ sub single { # remove leading directory $f =~ s/(.*?\/)//; close(F); - open(F, "<:crlf", "$f") || return 1; + open(F, "<:crlf", $f) || return 1; } if($d =~ /^\.TH ([^ ]*) (\d) \"(.*?)\" ([^ \n]*)/) { # header, this needs to be the first thing after leading comments diff --git a/plan9/src/mkfile b/scripts/perlcheck.sh old mode 100644 new mode 100755 similarity index 53% rename from plan9/src/mkfile rename to scripts/perlcheck.sh index beb98cbd60..c243f50271 --- a/plan9/src/mkfile +++ b/scripts/perlcheck.sh @@ -1,3 +1,4 @@ +#!/bin/sh #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | @@ -5,7 +6,7 @@ # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # -# Copyright (C) Daniel Stenberg, , et al. +# Copyright (C) Dan Fandrich, , Viktor Szakats, et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms @@ -22,26 +23,26 @@ # ########################################################################### -<../mkfile.proto -<|mkfile.inc +# The xargs invocation is portable, but does not preserve spaces in filenames. +# If such a file is ever added, then this can be portably fixed by switching to +# "xargs -I{}" and appending {} to the end of the xargs arguments (which will +# call cmakelint once per file) or by using the GNU extension "xargs -d'\n'". -CFLAGS=$CFLAGS -I../include -I../lib -c +set -eu -OFILES=${CURL_CFILES:%.c=%.$O} -HFILES=$CURL_HFILES +cd "$(dirname "$0")"/.. -LIB=\ - /$objtype/lib/ape/libcurl.a\ - /$objtype/lib/ape/libssl.a\ - /$objtype/lib/ape/libcrypto.a\ - /$objtype/lib/ape/libz.a\ +procs=6 +command -v nproc >/dev/null && procs="$(nproc)" +echo "parallel: ${procs}" -BIN=/$objtype/bin -TARG=curl - -CLEANFILES=tool_hugehelp.c - -$target +{ + if [ -n "${1:-}" ]; then + for A in "$@"; do printf '%s\n' "$A"; done + elif git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + git ls-files '*.pl' '*.pm' + git grep -l '^#!/usr/bin/env perl' + else + find . -type f \( -name '*.pl' -o -name '*.pm' \) + fi +} | sort -u | xargs -n 1 -P "${procs}" perl -c -Itests -- diff --git a/scripts/pythonlint.sh b/scripts/pythonlint.sh index b7dc95633b..5c1a3cdcc3 100755 --- a/scripts/pythonlint.sh +++ b/scripts/pythonlint.sh @@ -27,4 +27,9 @@ # locations, or all Python files found in the current directory tree by # default. -ruff check --extend-select=B007,B016,C405,C416,COM818,D200,D213,D204,D401,D415,FURB129,N818,PERF401,PERF403,PIE790,PIE808,PLW0127,Q004,RUF010,SIM101,SIM117,SIM118,TRY400,TRY401 "$@" +ruff check --extend-select=B007,B016,C405,C416,COM818,D200,D213,D204,D401,\ +D415,FURB129,N818,PERF401,PERF403,PIE790,PIE808,PLW0127,Q004,RUF010,SIM101,\ +SIM117,SIM118,TRY400,TRY401,RET503,RET504,UP004,B018,B904,RSE102,RET505,I001 \ +"$@" +# Checks that are in preview only, since --preview otherwise turns them all on +ruff check --preview --select=E301,E302,E303,E304,E305,E306,E502 "$@" diff --git a/scripts/randdisable b/scripts/randdisable index 89778c146c..a59c6c8125 100755 --- a/scripts/randdisable +++ b/scripts/randdisable @@ -53,31 +53,31 @@ do { my $num = rand(scalar(@disable) - 2) + 2; my $c = 0; - my $arg; + my @arg; for my $d (shuffle @disable) { - $arg .= " $d"; + push @arg, $d; if(++$c >= $num) { last; } } my @stls = shuffle @tls; - $arg.= " ".$stls[0]; + push @arg, @stls; - system("make clean"); - if(system("./configure $arg")) { + system('make', ('clean')); + if(system('./configure', @arg)) { print STDERR "configure problem\n"; - print STDERR "./configure $arg\n"; + print STDERR "./configure " . join(' ', @arg) . "\n"; exit 1; } - if(system("make -sj10")) { + if(system('make', ('-sj10'))) { print STDERR "Build problem\n"; - print STDERR "./configure $arg\n"; + print STDERR "./configure " . join(' ', @arg) . "\n"; exit 1; } if(system("./src/curl -V 2>/dev/null")) { print STDERR "Running problem\n"; - print STDERR "./configure $arg\n"; + print STDERR "./configure " . join(' ', @arg) . "\n"; exit 1; } } while(1); diff --git a/scripts/release-notes.pl b/scripts/release-notes.pl index 593d7965a3..a4b4e2550f 100755 --- a/scripts/release-notes.pl +++ b/scripts/release-notes.pl @@ -31,10 +31,10 @@ # # $ ./scripts/release-notes.pl # -# 2. Edit RELEASE-NOTES and remove all entries that don't belong. Unused +# 2. Edit RELEASE-NOTES and remove all entries that do not belong. Unused # references below will be cleaned up in the next step. Make sure to move # "changes" up to the changes section. All entries will by default be listed -# under bug-fixes as this script can't know where to put them. +# under bug-fixes as this script cannot know where to put them. # # 3. Run the cleanup script and let it sort the entries and remove unused # references from lines you removed in step (2): diff --git a/scripts/release-tools.sh b/scripts/release-tools.sh index 4c4ed7d809..fc5d87fb90 100755 --- a/scripts/release-tools.sh +++ b/scripts/release-tools.sh @@ -31,7 +31,7 @@ version=${2:-unknown} tag=$(echo "curl-$version" | tr '.' '_') commit=${3} if [ -n "$commit" ] && [ -r "docs/tarball-commit.txt.dist" ]; then - # If commit is given, then the tag likely doesn't actually exist + # If commit is given, then the tag likely does not actually exist tag="$(cat docs/tarball-commit.txt.dist)" fi diff --git a/scripts/schemetable.c b/scripts/schemetable.c index 7da4613212..afa54c39af 100644 --- a/scripts/schemetable.c +++ b/scripts/schemetable.c @@ -29,52 +29,35 @@ * function in url.c. */ -struct detail { - const char *n; - const char *ifdef; -}; - -static const struct detail scheme[] = { - {"dict", "#ifndef CURL_DISABLE_DICT" }, - {"file", "#ifndef CURL_DISABLE_FILE" }, - {"ftp", "#ifndef CURL_DISABLE_FTP" }, - {"ftps", "#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)" }, - {"gopher", "#ifndef CURL_DISABLE_GOPHER" }, - {"gophers", "#if defined(USE_SSL) && !defined(CURL_DISABLE_GOPHER)" }, - {"http", "#ifndef CURL_DISABLE_HTTP" }, - {"https", "#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)" }, - {"imap", "#ifndef CURL_DISABLE_IMAP" }, - {"imaps", "#if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP)" }, - {"ldap", "#ifndef CURL_DISABLE_LDAP" }, - {"ldaps", "#if !defined(CURL_DISABLE_LDAP) && \\\n" - " !defined(CURL_DISABLE_LDAPS) && \\\n" - " ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \\\n" - " (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL)))" }, - {"mqtt", "#ifndef CURL_DISABLE_MQTT" }, - {"pop3", "#ifndef CURL_DISABLE_POP3" }, - {"pop3s", "#if defined(USE_SSL) && !defined(CURL_DISABLE_POP3)" }, - {"rtmp", "#ifdef USE_LIBRTMP" }, - {"rtmpt", "#ifdef USE_LIBRTMP" }, - {"rtmpe", "#ifdef USE_LIBRTMP" }, - {"rtmpte", "#ifdef USE_LIBRTMP" }, - {"rtmps", "#ifdef USE_LIBRTMP" }, - {"rtmpts", "#ifdef USE_LIBRTMP" }, - {"rtsp", "#ifndef CURL_DISABLE_RTSP" }, - {"scp", "#if defined(USE_SSH) && !defined(USE_WOLFSSH)" }, - {"sftp", "#ifdef USE_SSH" }, - {"smb", "#if !defined(CURL_DISABLE_SMB) && \\\n" - " defined(USE_CURL_NTLM_CORE) && (SIZEOF_CURL_OFF_T > 4)" }, - {"smbs", "#if defined(USE_SSL) && !defined(CURL_DISABLE_SMB) && \\\n" - " defined(USE_CURL_NTLM_CORE) && (SIZEOF_CURL_OFF_T > 4)" }, - {"smtp", "#ifndef CURL_DISABLE_SMTP" }, - {"smtps", "#if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP)" }, - {"telnet", "#ifndef CURL_DISABLE_TELNET" }, - {"tftp", "#ifndef CURL_DISABLE_TFTP" }, - {"ws", - "#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)" }, - {"wss", "#if !defined(CURL_DISABLE_WEBSOCKETS) && \\\n" - " defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)" }, - { NULL, NULL } +static const char *scheme[] = { + "dict", + "file", + "ftp", + "ftps", + "gopher", + "gophers", + "http", + "https", + "imap", + "imaps", + "ldap", + "ldaps", + "mqtt", + "mqtts", + "pop3", + "pop3s", + "rtsp", + "scp", + "sftp", + "smb", + "smbs", + "smtp", + "smtps", + "telnet", + "tftp", + "ws", + "wss", + NULL, }; unsigned int calc(const char *s, int add, int shift) @@ -96,9 +79,9 @@ static void showtable(int try, int init, int shift) { int nulls = 0; int i; - for(i = 0; scheme[i].n; ++i) - num[i] = calc(scheme[i].n, init, shift); - for(i = 0; scheme[i].n; ++i) + for(i = 0; scheme[i]; ++i) + num[i] = calc(scheme[i], init, shift); + for(i = 0; scheme[i]; ++i) ix[i] = num[i] % try; printf("/*\n" " unsigned int c = %d\n" @@ -108,34 +91,25 @@ static void showtable(int try, int init, int shift) " s++;\n" " l--;\n" " }\n" - "*/\n", init, shift); + "*/\n", + init, shift); - printf(" static const struct Curl_handler * const protocols[%d] = {", try); + printf(" static const struct Curl_scheme * const all_schemes[%d] = {", try); /* generate table */ for(i = 0; i < try; i++) { int match = 0; int j; - for(j = 0; scheme[j].n; j++) { + for(j = 0; scheme[j]; j++) { if(ix[j] == i) { - printf("\n"); - printf("%s\n", scheme[j].ifdef); - printf(" &Curl_handler_%s,\n", scheme[j].n); - printf("#else\n NULL,\n"); - printf("#endif"); + printf("\n &Curl_scheme_%s,", scheme[j]); match = 1; nulls = 0; break; } } - if(!match) { - if(!nulls || (nulls > 10)) { - printf("\n "); - nulls = 0; - } - printf(" NULL,", nulls); - nulls++; - } + if(!match) + printf(" NULL,"); } printf("\n };\n"); } @@ -151,17 +125,17 @@ int main(void) int shift; for(shift = 0; shift < 8; shift++) { for(add = 0; add < 999; add++) { - for(i = 0; scheme[i].n; ++i) { - unsigned int v = calc(scheme[i].n, add, shift); + for(i = 0; scheme[i]; ++i) { + unsigned int v = calc(scheme[i], add, shift); int j; int badcombo = 0; for(j = 0; j < i; j++) { if(num[j] == v) { - /* +#if 0 printf("NOPE: %u is a dupe (%s and %s)\n", v, scheme[i], scheme[j]); - */ +#endif badcombo = 1; break; } @@ -178,11 +152,11 @@ int main(void) /* try different remainders to find smallest possible table */ for(try = 28; try < 199; try++) { int good = 1; - for(i = 0; scheme[i].n; ++i) { + for(i = 0; scheme[i]; ++i) { ix[i] = num[i] % try; } /* check for dupes */ - for(i = 0; scheme[i].n && good; ++i) { + for(i = 0; scheme[i] && good; ++i) { int j; for(j = 0; j < i; j++) { if(ix[j] == ix[i]) { diff --git a/scripts/singleuse.pl b/scripts/singleuse.pl index 2e8a8e0afd..9573342150 100755 --- a/scripts/singleuse.pl +++ b/scripts/singleuse.pl @@ -36,9 +36,9 @@ use strict; use warnings; -my $unittests; +my @unittests; if(@ARGV && $ARGV[0] eq "--unit") { - $unittests = "tests/unit "; + push @unittests, 'tests/unit'; shift @ARGV; } @@ -116,6 +116,8 @@ my %api = ( 'curl_multi_get_offt' => 'API', 'curl_multi_info_read' => 'API', 'curl_multi_init' => 'API', + 'curl_multi_notify_disable' => 'API', + 'curl_multi_notify_enable' => 'API', 'curl_multi_perform' => 'API', 'curl_multi_remove_handle' => 'API', 'curl_multi_setopt' => 'API', @@ -165,7 +167,7 @@ my %api = ( sub doublecheck { my ($f, $used) = @_; - open(F, "git grep -Fwle '$f' -- lib ${unittests}packages|"); + open(F, '-|', 'git', 'grep', '-Fwle', $f, '--', 'lib', @unittests, 'projects'); my @also; while() { my $e = $_; @@ -180,8 +182,7 @@ sub doublecheck { return @also; } -open(N, "nm $file|") || - die; +open(N, '-|', 'nm', $file) || die; my %exist; my %uses; diff --git a/scripts/spacecheck.pl b/scripts/spacecheck.pl new file mode 100755 index 0000000000..416408e56e --- /dev/null +++ b/scripts/spacecheck.pl @@ -0,0 +1,306 @@ +#!/usr/bin/env perl +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) Viktor Szakats +# +# 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 +# +########################################################################### + +use strict; +use warnings; + +use File::Basename; + +my @tabs = ( + '^m4/zz40-xc-ovr\.m4$', + 'Makefile\.(am|example)$', + '\.sln$', + '^tests/data/data1706-stdout\.txt', + '^tests/data/test', +); + +my @need_crlf = ( + '\.(bat|sln)$', +); + +my @double_empty_lines = ( + '^RELEASE-NOTES$', + '^lib/.+\.(c|h)$', + '^projects/OS400/', + '^projects/vms/', + '^tests/data/test', + '\.(m4|py)$', +); + +my @longline = ( + '\.github/workflows/windows\.yml$', + '^renovate\.json$', + '^docs/DISTROS\.md$', + '^projects/Windows/tmpl/.+\.vcxproj$', + '^tests/certs/srp-verifier-', + '^tests/data/test', +); + +my @non_ascii_allowed = ( + '\xC3\xB6', # UTF-8 for https://codepoints.net/U+00F6 LATIN SMALL LETTER O WITH DIAERESIS +); + +my $non_ascii_allowed = join(', ', @non_ascii_allowed); + +my @non_ascii = ( + '^\.github/scripts/pyspelling\.words$', + '^\.mailmap$', + '^RELEASE-NOTES$', + '^docs/BINDINGS\.md$', + '^docs/THANKS$', + '^docs/THANKS-filter$', +); + +sub fn_match { + my ($filename, @masklist) = @_; + + foreach my $mask (@masklist) { + if($filename =~ $mask) { + return 1; + } + } + return 0; +} + +sub eol_detect { + my ($content) = @_; + + my $cr = () = $content =~ /\r/g; + my $lf = () = $content =~ /\n/g; + + if($cr > 0 && $lf == 0) { + return 'cr'; + } + elsif($cr == 0 && $lf > 0) { + return 'lf'; + } + elsif($cr == 0 && $lf == 0) { + return 'bin'; + } + elsif($cr == $lf) { + return 'crlf'; + } + + return ''; +} + +my $max_repeat_space = 79; +my $max_line_len = 192; +my $max_path_len = 64; +my $max_filename_len = 48; + +my $issues = 0; + +open(my $git_ls_files, '-|', 'git', 'ls-files') or die "Failed running git ls-files: $!"; +while(my $filename = <$git_ls_files>) { + chomp $filename; + + my @err = (); + + if(length($filename) > $max_path_len) { + push @err, sprintf('long (%d > %d) path', length($filename), $max_path_len); + } + + my $bn = basename($filename); + if(length($bn) > $max_filename_len) { + push @err, sprintf('long (%d > %d) filename', length($bn), $max_filename_len); + } + + if($filename !~ /^[A-Za-z0-9\/._-]+$/) { + push @err, sprintf("filename contains character(s) outside [A-Za-z0-9/._-]"); + } + + open(my $fh, '<', $filename) or die "Cannot open '$filename': $!"; + my $content = do { local $/; <$fh> }; + close $fh; + + if(!fn_match($filename, @tabs) && + $content =~ /\t/) { + push @err, 'content: has tab'; + } + + my $eol = eol_detect($content); + + if($eol eq '') { + push @err, 'content: has mixed EOL types'; + } + + if($eol ne 'crlf' && + fn_match($filename, @need_crlf)) { + push @err, 'content: must use CRLF EOL for this file type'; + } + + if($eol ne 'lf' && $content ne '' && + !fn_match($filename, @need_crlf)) { + push @err, 'content: must use LF EOL for this file type'; + } + + if($content =~ /[ \t]\n/) { + my $line; + for my $l (split(/\n/, $content)) { + $line++; + if($l =~ /[ \t]$/) { + push @err, "line $line: trailing whitespace"; + } + } + } + + if($content ne '' && + $content !~ /\n\z/) { + push @err, 'content: has no EOL at EOF'; + } + + if($content =~ /\n\n\z/ || + $content =~ /\r\n\r\n\z/) { + push @err, 'content: has multiple EOL at EOF'; + } + + if((!fn_match($filename, @double_empty_lines) && + ($content =~ /\n\n\n/ || + $content =~ /\r\n\r\n\r\n/)) || + $content =~ />\n\n\n+[<#]/) { + my $line = 0; + my $blank = 0; + for my $l (split(/\n/, $content)) { + chomp $l; + $line++; + if($l =~ /^$/) { + if($blank) { + my $lineno = sprintf('duplicate empty line @ line %d', $line); + push @err, $lineno; + } + $blank = 1; + } + else { + $blank = 0; + } + } + } + + if($content =~ /\n\n\n\n/ || + $content =~ /\r\n\r\n\r\n\r\n/) { + my $line = 0; + my $blank = 0; + for my $l (split(/\n/, $content)) { + chomp $l; + $line++; + if($l =~ /^$/) { + if($blank > 1) { + my $lineno = sprintf('3 or more consecutive empty lines @ line %d', $line); + push @err, $lineno; + } + $blank++; + } + else { + $blank = 0; + } + } + } + + if(!fn_match($filename, @longline)) { + my $line = 0; + for my $l (split(/\n/, $content)) { + $line++; + if(length($l) > $max_line_len) { + push @err, sprintf('line %d: long (%d > %d) line', $line, length($l), $max_line_len); + } + } + } + + my $line = 0; + for my $l (split(/\n/, $content)) { + $line++; + if($l =~ /( {$max_repeat_space,})/) { + push @err, sprintf('line %d: repeat spaces (%d >= %d)', $line, length($1), $max_repeat_space); + } + } + + my $search = $content; + my $linepos = 0; + while($search =~ /[^ ] "\n *" [^ ]/) { + my $part = substr($search, 0, $+[0]); + $search = substr($search, $+[0]); + my $line = ($part =~ tr/\n//); + push @err, sprintf('line %d: double spaces in folded string', $linepos + $line); + $linepos += $line; + } + + $search = $content; + $linepos = 0; + while($search =~ /\n\n *}\n/) { + my $part = substr($search, 0, $+[0] - 1); + $search = substr($search, $+[0]); + my $line = ($part =~ tr/\n//); + push @err, sprintf("line %d: '}' preceded by empty line", $linepos + $line); + $linepos += $line + 1; + } + + $search = $content; + $linepos = 0; + while($search =~ /\n\{\n\n/) { + my $part = substr($search, 0, $+[0]); + $search = substr($search, $+[0]); + my $line = ($part =~ tr/\n//); + push @err, sprintf("line %d: top-level '{' followed by empty line", $linepos + $line); + $linepos += $line; + } + + if($content =~ /([\x00-\x08\x0b\x0c\x0e-\x1f\x7f])/) { + push @err, 'content: has binary contents'; + } + + if($filename !~ /tests\/data/) { + # the tests have no allowed UTF bytes + $content =~ s/[$non_ascii_allowed]//g; + } + + if(!fn_match($filename, @non_ascii) && + ($content =~ /([\x80-\xff]+)/)) { + my $non = $1; + my $hex; + for my $e (split(//, $non)) { + $hex .= sprintf('%s%02x', $hex ? ' ': '', ord($e)); + } + my $line; + for my $l (split(/\n/, $content)) { + $line++; + if($l =~ /([\x80-\xff]+)/) { + push @err, "line $line: has non-ASCII: '$non' ($hex)"; + } + } + } + + if(@err) { + $issues++; + foreach my $err (@err) { + print "$filename: $err\n"; + } + } +} +close $git_ls_files; + +if($issues) { + exit 1; +} diff --git a/scripts/top-complexity b/scripts/top-complexity index 414adaf56c..f2d869f607 100755 --- a/scripts/top-complexity +++ b/scripts/top-complexity @@ -60,29 +60,32 @@ if(! -r "lib/url.c" || ! -r "lib/urldata.h") { } my @files; -open(F, "git ls-files '*.c'|"); -while() { +open(my $git, "-|", "git", "ls-files", "*.c") or die "git ls-files failed: $!"; +while(<$git>) { chomp $_; my $file = $_; - # we can't filter these with git so do it here + # we cannot filter these with git so do it here if($file =~ /^(lib|src)/) { push @files, $file; } } +close($git); -my $cmd = "$pmccabe ".join(" ", @files); -my @output=`$cmd`; +open(my $pmc, "-|", $pmccabe, @files) or die "pmccabe failed: $!"; +my @output = <$pmc>; +close($pmc); # these functions can have these scores, but not higher my %whitelist = ( ); -# functions with complexity above this level causes the function to return error -my $cutoff = 70; +# complexity above this level is treated as an error and contributes to the +# script's exit code +my $cutoff = 60; -# functions above this complexity level are shown -my $show = 57; +# show this many from the top +my $top = $ARGV[0] ? $ARGV[0] : 25; my $error = 0; my %where; @@ -96,22 +99,20 @@ for my $l (@output) { if($l =~/^(\d+)\t\d+\t\d+\t\d+\t(\d+)\t([^\(]+).*: ([^ ]*)/) { my ($score, $len, $path, $func)=($1, $2, $3, $4); - if($score > $show) { - my $allow = 0; - if($whitelist{$func} && - ($score <= $whitelist{$func})) { - $allow = 1; - } - $where{"$path:$func"}=$score; - $perm{"$path:$func"}=$allow; - if(($score > $cutoff) && !$allow) { - $error++; - } + my $allow = 0; + if($whitelist{$func} && + ($score <= $whitelist{$func})) { + $allow = 1; } + $where{"$path:$func"}=$score; + $perm{"$path:$func"}=$allow; + if(($score > $cutoff) && !$allow) { + $error++; + } + $alllines += $len; $allscore += ($len * $score); } - } my $showncutoff; @@ -122,7 +123,10 @@ for my $e (sort {$where{$b} <=> $where{$a}} keys %where) { $showncutoff = 1; } printf "%-5d %s%s\n", $where{$e}, $e, - $perm{$e} ? " [ALLOWED]": ""; + $perm{$e} ? " [ALLOWED]" : ""; + if(!--$top) { + last; + } } printf "\nAverage complexity: %.2f\n", $allscore / $alllines; diff --git a/scripts/verify-release b/scripts/verify-release index 2f7a0bf89f..b24d9b370d 100755 --- a/scripts/verify-release +++ b/scripts/verify-release @@ -40,12 +40,7 @@ if [ -z "$tarball" ]; then exit fi -i="0" - -# shellcheck disable=SC2034 -for dl in curl-*; do - i=$((i + 1)) -done +i="$(find . -maxdepth 1 -type d -name 'curl-*' | wc -l)" if test "$i" -gt 1; then echo "multiple curl-* entries found, disambiguate please" diff --git a/scripts/wcurl b/scripts/wcurl index af66ae7fca..c39806cf94 100755 --- a/scripts/wcurl +++ b/scripts/wcurl @@ -29,7 +29,7 @@ # Stop on errors and on usage of unset variables. set -eu -VERSION="2025.05.26" +VERSION="2026.01.05" PROGRAM_NAME="$(basename "$0")" readonly PROGRAM_NAME @@ -64,10 +64,10 @@ Options: number appended to the end (curl >= 7.83.0). If this option is provided multiple times, only the last value is considered. - --no-decode-filename: Don't percent-decode the output filename, even if the percent-encoding in - the URL was done by wcurl, e.g.: The URL contained whitespaces. + --no-decode-filename: Do not percent-decode the output filename, even if the percent-encoding in + the URL was done by wcurl, e.g.: The URL contained whitespace. - --dry-run: Don't actually execute curl, just print what would be invoked. + --dry-run: Do not actually execute curl, just print what would be invoked. -V, --version: Print version information. @@ -77,7 +77,7 @@ Options: instead forwarded to the curl invocation. : URL to be downloaded. Anything that is not a parameter is considered - an URL. Whitespaces are percent-encoded and the URL is passed to curl, which + an URL. Whitespace is percent-encoded and the URL is passed to curl, which then performs the parsing. May be specified more than once. _EOF_ } @@ -85,13 +85,13 @@ _EOF_ # Display an error message and bail out. error() { - printf "%s\n" "$*" > /dev/stderr + printf "%s\n" "$*" >&2 exit 1 } # Extra curl options provided by the user. # This is set per-URL for every URL provided. -# Some options are global, but we are erroring on the side of needlesly setting +# Some options are global, but we are erroring on the side of needlessly setting # them multiple times instead of causing issues with parameters that needs to # be set per-URL. CURL_OPTIONS="" @@ -113,6 +113,14 @@ readonly PER_URL_PARAMETERS="\ --remote-time \ --retry 5 " +# Valid percent-encode codes that are considered unsafe to be decoded. +# This is a list of space-separated percent-encoded uppercase +# characters. +# 2F = / +# 5C = \ +# 3A = : +readonly UNSAFE_PERCENT_ENCODE="%2F %5C %3A" + # Whether to invoke curl or not. DRY_RUN="false" @@ -133,10 +141,24 @@ sanitize() is_subset_of() { case "${1}" in - *[!${2}]*|'') return 1;; + *[!${2}]* | '') return 1 ;; esac } +# Indicate via exit code whether the HTML code given in the first +# parameter is safe to be decoded. +is_safe_percent_encode() +{ + upper_str=$(printf "%s" "${1}" | tr "[:lower:]" "[:upper:]") + for unsafe in ${UNSAFE_PERCENT_ENCODE}; do + if [ "${unsafe}" = "${upper_str}" ]; then + return 1 + fi + done + + return 0 +} + # Print the given string percent-decoded. percent_decode() { @@ -146,14 +168,15 @@ percent_decode() # If character is a "%", read the next character as decode_hex1. if [ "${decode_out}" = % ] && IFS= read -r decode_hex1; then decode_out="${decode_out}${decode_hex1}" - # If there's one more character, read it as decode_hex2. + # If there is one more character, read it as decode_hex2. if IFS= read -r decode_hex2; then decode_out="${decode_out}${decode_hex2}" # Skip decoding if this is a control character (00-1F). # Skip decoding if DECODE_FILENAME is not "true". - if is_subset_of "${decode_hex1}" "23456789abcdefABCDEF" && \ - is_subset_of "${decode_hex2}" "0123456789abcdefABCDEF" && \ - [ "${DECODE_FILENAME}" = "true" ]; then + if [ "${DECODE_FILENAME}" = "true" ] \ + && is_subset_of "${decode_hex1}" "23456789abcdefABCDEF" \ + && is_subset_of "${decode_hex2}" "0123456789abcdefABCDEF" \ + && is_safe_percent_encode "${decode_out}"; then # Use printf to decode it into octal and then decode it to the final format. decode_out="$(printf "%b" "\\$(printf %o "0x${decode_hex1}${decode_hex2}")")" fi @@ -168,10 +191,11 @@ get_url_filename() { # Remove protocol and query string if present. hostname_and_path="$(printf %s "${1}" | sed -e 's,^[^/]*//,,' -e 's,?.*$,,')" - # If what remains contains a slash, there's a path; return it percent-decoded. + # If what remains contains a slash, there is a path; return it percent-decoded. case "${hostname_and_path}" in # sed to remove everything preceding the last '/', e.g.: "example/something" becomes "something" - */*) percent_decode "$(printf %s "${hostname_and_path}" | sed -e 's,^.*/,,')";; + # sed to also replace ':' with the percent_encoded %3A + */*) percent_decode "$(printf %s "${hostname_and_path}" | sed -e 's,^.*/,,' -e 's,:,%3A,g')" ;; esac # No slash means there was just a hostname and no path; return empty string. } @@ -181,41 +205,46 @@ exec_curl() { CMD="curl " - # Store version to check if it supports --no-clobber and --parallel. + # Store version to check if it supports --no-clobber, --parallel and --parallel-max-host. curl_version=$($CMD --version | cut -f2 -d' ' | head -n1) curl_version_major=$(echo "$curl_version" | cut -f1 -d.) curl_version_minor=$(echo "$curl_version" | cut -f2 -d.) - CURL_HAS_NO_CLOBBER="" - CURL_HAS_PARALLEL="" - # --no-clobber is only supported since 7.83.0. - # --parallel is only supported since 7.66.0. + CURL_NO_CLOBBER="" + CURL_PARALLEL="" + if [ "${curl_version_major}" -ge 8 ]; then - CURL_HAS_NO_CLOBBER="--no-clobber" - CURL_HAS_PARALLEL="--parallel" - elif [ "${curl_version_major}" -eq 7 ];then - if [ "${curl_version_minor}" -ge 83 ]; then - CURL_HAS_NO_CLOBBER="--no-clobber" + CURL_NO_CLOBBER="--no-clobber" + CURL_PARALLEL="--parallel --parallel-max-host 5" + + # --parallel-max-host is only supported since 8.16.0. + if [ "${curl_version_major}" -eq 8 ] && [ "${curl_version_minor}" -lt 16 ]; then + CURL_PARALLEL="--parallel" fi + elif [ "${curl_version_major}" -eq 7 ]; then + # --no-clobber is only supported since 7.83.0. + if [ "${curl_version_minor}" -ge 83 ]; then + CURL_NO_CLOBBER="--no-clobber" + fi + # --parallel is only supported since 7.66.0. if [ "${curl_version_minor}" -ge 66 ]; then - CURL_HAS_PARALLEL="--parallel" + CURL_PARALLEL="--parallel" fi fi - # Detecting whether we need --parallel. It's easier to rely on + # Detecting whether we need --parallel. It is easier to rely on # the shell's argument parsing. # shellcheck disable=SC2086 set -- $URLS - if [ "$#" -gt 1 ]; then - CURL_PARALLEL="$CURL_HAS_PARALLEL" - else + # If there are less than two URLs, do not set the parallel flag. + if [ "$#" -lt 2 ]; then CURL_PARALLEL="" fi # Start assembling the command. # - # We use 'set --' here (again) because (a) we don't have arrays on + # We use 'set --' here (again) because (a) we do not have arrays on # POSIX shell, and (b) we need better control over the way we # split arguments. # @@ -231,7 +260,7 @@ exec_curl() [ -z "${OUTPUT_PATH}" ] && OUTPUT_PATH=index.html fi # shellcheck disable=SC2086 - set -- "$@" ${NEXT_PARAMETER} ${PER_URL_PARAMETERS} ${CURL_HAS_NO_CLOBBER} ${CURL_OPTIONS} --output "${OUTPUT_PATH}" "${url}" + set -- "$@" ${NEXT_PARAMETER} ${PER_URL_PARAMETERS} ${CURL_NO_CLOBBER} --output "${OUTPUT_PATH}" ${CURL_OPTIONS} "${url}" NEXT_PARAMETER="--next" done @@ -268,13 +297,13 @@ while [ -n "${1-}" ]; do OUTPUT_PATH="${opt}" ;; - -o|-O|--output) + -o | -O | --output) shift HAS_USER_SET_OUTPUT="true" OUTPUT_PATH="${1}" ;; - -o*|-O*) + -o* | -O*) opt=$(printf "%s\n" "${1}" | sed 's/^-[oO]//') HAS_USER_SET_OUTPUT="true" OUTPUT_PATH="${opt}" @@ -284,12 +313,12 @@ while [ -n "${1-}" ]; do DECODE_FILENAME="false" ;; - -h|--help) + -h | --help) usage exit 0 ;; - -V|--version) + -V | --version) print_version exit 0 ;; @@ -298,7 +327,7 @@ while [ -n "${1-}" ]; do # This is the start of the list of URLs. shift for url in "$@"; do - # Encode whitespaces into %20, since wget supports those URLs. + # Encode whitespace into %20, since wget supports those URLs. newurl=$(printf "%s\n" "${url}" | sed 's/ /%20/g') URLS="${URLS} ${newurl}" done @@ -311,7 +340,7 @@ while [ -n "${1-}" ]; do *) # This must be a URL. - # Encode whitespaces into %20, since wget supports those URLs. + # Encode whitespace into %20, since wget supports those URLs. newurl=$(printf "%s\n" "${1}" | sed 's/ /%20/g') URLS="${URLS} ${newurl}" ;; diff --git a/src/.checksrc b/src/.checksrc index 7b71afb236..bc97c06028 100644 --- a/src/.checksrc +++ b/src/.checksrc @@ -1,5 +1,5 @@ +# Copyright (C) Daniel Stenberg, , et al. +# +# SPDX-License-Identifier: curl + enable STDERR -banfunc snprintf -banfunc sscanf -banfunc strtol -banfunc vsnprint diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 37ca979bab..1f76839009 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,8 +28,8 @@ set(_curl_cfiles_gen "") set(_curl_hfiles_gen "") set(_curl_definitions "") -if(ENABLE_CURL_MANUAL AND (PERL_FOUND OR EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tool_hugehelp.c")) - if(PERL_FOUND) +if(ENABLE_CURL_MANUAL AND (Perl_FOUND OR EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tool_hugehelp.c")) + if(Perl_FOUND) add_custom_command(OUTPUT "tool_hugehelp.c" COMMAND ${CMAKE_COMMAND} -E echo "#include \"tool_setup.h\"" > "tool_hugehelp.c" COMMAND ${CMAKE_COMMAND} -E echo "/* !checksrc! disable COPYRIGHT all */" >> "tool_hugehelp.c" @@ -55,7 +55,7 @@ if(ENABLE_CURL_MANUAL AND (PERL_FOUND OR EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/too endif() if(CURL_CA_EMBED_SET) - if(PERL_FOUND) + if(Perl_FOUND) add_custom_command(OUTPUT "tool_ca_embed.c" COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mk-file-embed.pl" --var curl_ca_embed < "${CURL_CA_EMBED}" > "tool_ca_embed.c" @@ -66,7 +66,7 @@ if(CURL_CA_EMBED_SET) list(APPEND _curl_cfiles_gen "tool_ca_embed.c") list(APPEND _curl_definitions "CURL_CA_EMBED") else() - message(WARNING "Perl not found. Will not embed the CA bundle.") + message(WARNING "Perl not found. Cannot embed the CA bundle.") endif() endif() @@ -79,10 +79,6 @@ source_group("curl header files" FILES ${CURL_HFILES} ${_curl_hfiles_gen}) source_group("curlx source files" FILES ${CURLX_CFILES}) source_group("curlx header files" FILES ${CURLX_HFILES}) -if(WIN32) - list(APPEND CURL_CFILES ${CURL_RCFILES}) -endif() - set(_curlx_cfiles_lib ${CURLX_CFILES}) set(_curlx_hfiles_lib ${CURLX_HFILES}) if(LIB_SELECTED STREQUAL LIB_STATIC) @@ -105,34 +101,53 @@ set_property(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES add_executable(${EXE_NAME} ${CURL_CFILES} ${CURL_HFILES} ${_curl_cfiles_gen} ${_curl_hfiles_gen} ${CURLX_CFILES} ${CURLX_HFILES}) target_compile_definitions(${EXE_NAME} PRIVATE ${_curl_definitions}) target_link_libraries(${EXE_NAME} ${LIB_SELECTED_FOR_EXE} ${CURL_LIBS}) +set_property(TARGET ${EXE_NAME} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAGS}") +if(WIN32) + set_property(TARGET ${EXE_NAME} APPEND PROPERTY SOURCES ${CURL_RCFILES}) +endif() add_executable(${PROJECT_NAME}::${EXE_NAME} ALIAS ${EXE_NAME}) add_executable(curlinfo EXCLUDE_FROM_ALL "curlinfo.c") +target_link_libraries(curlinfo PRIVATE ${CURL_LIBS}) set_target_properties(curlinfo PROPERTIES UNITY_BUILD OFF) -# special libcurltool library just for unittests +# special libcurltool library for unittests add_library(curltool STATIC EXCLUDE_FROM_ALL ${CURL_CFILES} ${CURL_HFILES} ${_curlx_cfiles_lib} ${_curlx_hfiles_lib}) target_compile_definitions(curltool PUBLIC "CURL_STATICLIB" "UNITTESTS") -target_link_libraries(curltool PRIVATE ${CURL_LIBS}) -set_target_properties(curltool PROPERTIES C_CLANG_TIDY "") +target_link_libraries(curltool PUBLIC ${CURL_LIBS}) +set_target_properties(curltool PROPERTIES UNITY_BUILD ON C_CLANG_TIDY "") if(CURL_HAS_LTO) set_target_properties(${EXE_NAME} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) endif() +if(CURL_CLANG_TIDY) + set_target_properties(${EXE_NAME} PROPERTIES UNITY_BUILD OFF) +endif() +if(CURL_ANALYZER_CFLAGS) + set_target_properties(${EXE_NAME} PROPERTIES UNITY_BUILD OFF) + set_property(TARGET ${EXE_NAME} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS}) +endif() -if(ENABLE_UNICODE AND MINGW AND NOT MINGW32CE) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - target_link_options(${EXE_NAME} PRIVATE "-municode") - else() - target_link_libraries(${EXE_NAME} PRIVATE "-municode") - endif() +if(ENABLE_UNICODE AND MINGW) + set_property(TARGET ${EXE_NAME} APPEND PROPERTY LINK_OPTIONS "-municode") +endif() + +if(CURL_CODE_COVERAGE) + set_property(TARGET ${EXE_NAME} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS}) + set_property(TARGET ${EXE_NAME} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS}) + set_property(TARGET ${EXE_NAME} APPEND PROPERTY LINK_OPTIONS ${CURL_COVERAGE_LDFLAGS}) endif() ################################################################################ -install(TARGETS ${EXE_NAME} EXPORT ${TARGETS_EXPORT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) -export(TARGETS ${EXE_NAME} - FILE "${PROJECT_BINARY_DIR}/curl-target.cmake" - NAMESPACE ${PROJECT_NAME}:: -) +if(CURL_ENABLE_EXPORT_TARGET) + if(NOT CURL_DISABLE_INSTALL) + install(TARGETS ${EXE_NAME} EXPORT ${TARGETS_EXPORT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + + export(TARGETS ${EXE_NAME} + FILE "${PROJECT_BINARY_DIR}/curl-target.cmake" + NAMESPACE ${PROJECT_NAME}:: + ) +endif() diff --git a/src/Makefile.am b/src/Makefile.am index c1bcf27359..69554ee490 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -57,11 +57,7 @@ endif if DEBUGBUILD AM_CPPFLAGS += -DDEBUGBUILD endif -if CURLDEBUG -AM_CPPFLAGS += -DCURLDEBUG -endif -AM_LDFLAGS = if USE_UNICODE UNICODEFLAG = -municode endif @@ -94,7 +90,7 @@ curl_SOURCES += $(CURL_RCFILES) $(CURL_RCFILES): tool_version.h endif -curl_LDFLAGS = $(AM_LDFLAGS) $(CURL_LDFLAGS_BIN) $(UNICODEFLAG) +curl_LDFLAGS = $(CURL_LDFLAGS_BIN) $(UNICODEFLAG) # This might hold -Werror CFLAGS += @CURL_CFLAG_EXTRAS@ @@ -219,17 +215,19 @@ all-local: checksrc endif endif -# disable the tests that are mostly causing false positives -TIDYFLAGS := -checks=-clang-analyzer-security.insecureAPI.bzero,-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-security.ArrayBound,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling -quiet +_tidy_cflags = +TIDYFLAGS = if CURL_WERROR -TIDYFLAGS += --warnings-as-errors=* +TIDYFLAGS += '--warnings-as-errors=*' +endif +if CLANG +_tidy_cflags += $(CFLAGS) endif - -TIDY := clang-tidy tidy: $(HUGE) $(CA_EMBED_CSOURCE) (_curl_cfiles=`echo ' $(CURL_CFILES)' | sed -e 's/ +/ /g' -e 's| | $(srcdir)/|g'`; \ - $(TIDY) $$_curl_cfiles $(curl_cfiles_gen) $(TIDYFLAGS) $(CURL_CLANG_TIDYFLAGS) -- $(curl_CPPFLAGS) $(CPPFLAGS) $(AM_CPPFLAGS) -DHAVE_CONFIG_H) + @CLANG_TIDY@ --config-file=$(top_srcdir)/.clang-tidy.yml $(TIDYFLAGS) $(CURL_CLANG_TIDYFLAGS) $$_curl_cfiles $(curl_cfiles_gen) \ + -- $(curl_CPPFLAGS) $(CPPFLAGS) $(AM_CPPFLAGS) -DHAVE_CONFIG_H $(_tidy_cflags)) listhelp: (cd $(top_srcdir)/docs/cmdline-opts && make listhelp) diff --git a/src/Makefile.inc b/src/Makefile.inc index 1086c07feb..fe2bae5c32 100644 --- a/src/Makefile.inc +++ b/src/Makefile.inc @@ -34,34 +34,51 @@ # the official API, but we reuse the code here to avoid duplication. CURLX_CFILES = \ ../lib/curlx/base64.c \ - ../lib/curlx/multibyte.c \ + ../lib/curlx/basename.c \ ../lib/curlx/dynbuf.c \ + ../lib/curlx/fopen.c \ + ../lib/curlx/multibyte.c \ ../lib/curlx/nonblock.c \ + ../lib/curlx/strcopy.c \ + ../lib/curlx/strdup.c \ + ../lib/curlx/strerr.c \ ../lib/curlx/strparse.c \ ../lib/curlx/timediff.c \ ../lib/curlx/timeval.c \ ../lib/curlx/version_win32.c \ ../lib/curlx/wait.c \ - ../lib/curlx/warnless.c + ../lib/curlx/warnless.c \ + ../lib/curlx/winapi.c CURLX_HFILES = \ - ../lib/curlx/binmode.h \ - ../lib/curlx/multibyte.h \ ../lib/curl_setup.h \ + ../lib/curlx/base64.h \ + ../lib/curlx/basename.h \ ../lib/curlx/dynbuf.h \ + ../lib/curlx/fopen.h \ + ../lib/curlx/multibyte.h \ ../lib/curlx/nonblock.h \ + ../lib/curlx/strcopy.h \ + ../lib/curlx/strdup.h \ + ../lib/curlx/strerr.h \ ../lib/curlx/strparse.h \ ../lib/curlx/timediff.h \ ../lib/curlx/timeval.h \ ../lib/curlx/version_win32.h \ ../lib/curlx/wait.h \ - ../lib/curlx/warnless.h + ../lib/curlx/warnless.h \ + ../lib/curlx/winapi.h + +TOOLX_CFILES = \ + toolx/tool_time.c + +TOOLX_HFILES = \ + toolx/tool_time.h CURL_CFILES = \ config2setopts.c \ slist_wc.c \ terminal.c \ - tool_bname.c \ tool_cb_dbg.c \ tool_cb_hdr.c \ tool_cb_prg.c \ @@ -93,20 +110,19 @@ CURL_CFILES = \ tool_setopt.c \ tool_ssls.c \ tool_stderr.c \ - tool_strdup.c \ tool_urlglob.c \ tool_util.c \ tool_vms.c \ tool_writeout.c \ tool_writeout_json.c \ tool_xattr.c \ - var.c + var.c \ + $(TOOLX_CFILES) CURL_HFILES = \ config2setopts.h \ slist_wc.h \ terminal.h \ - tool_bname.h \ tool_cb_dbg.h \ tool_cb_hdr.h \ tool_cb_prg.h \ @@ -139,7 +155,6 @@ CURL_HFILES = \ tool_setup.h \ tool_ssls.h \ tool_stderr.h \ - tool_strdup.h \ tool_urlglob.h \ tool_util.h \ tool_version.h \ @@ -147,6 +162,7 @@ CURL_HFILES = \ tool_writeout.h \ tool_writeout_json.h \ tool_xattr.h \ - var.h + var.h \ + $(TOOLX_HFILES) CURL_RCFILES = curl.rc diff --git a/src/config2setopts.c b/src/config2setopts.c index e8f4b0a407..171ba6986a 100644 --- a/src/config2setopts.c +++ b/src/config2setopts.c @@ -26,6 +26,7 @@ #include "tool_cfgable.h" #include "tool_setopt.h" #include "tool_findfile.h" +#include "tool_formparse.h" #include "tool_msgs.h" #include "tool_libinfo.h" #include "tool_cb_soc.h" @@ -37,6 +38,11 @@ #include "tool_cb_see.h" #include "tool_cb_dbg.h" #include "tool_helpers.h" +#include "tool_version.h" + +#ifdef HAVE_NETINET_IN_H +#include /* IPPROTO_IPV6 */ +#endif #define BUFFER_SIZE 102400L @@ -46,14 +52,14 @@ static int get_address_family(curl_socket_t sockfd) struct sockaddr addr; curl_socklen_t addrlen = sizeof(addr); memset(&addr, 0, sizeof(addr)); - if(getsockname(sockfd, (struct sockaddr *)&addr, &addrlen) == 0) + if(getsockname(sockfd, &addr, &addrlen) == 0) return addr.sa_family; return AF_UNSPEC; } #endif #ifndef SOL_IP -# define SOL_IP IPPROTO_IP +#define SOL_IP IPPROTO_IP #endif #if defined(IP_TOS) || defined(IPV6_TCLASS) || defined(SO_PRIORITY) @@ -83,9 +89,10 @@ static int sockopt_callback(void *clientp, curl_socket_t curlfd, #endif } if(result < 0) { + char buffer[STRERROR_LEN]; int error = errno; - warnf("Setting type of service to %d failed with errno %d: %s", - tos, error, strerror(error)); + warnf("Setting type of service to %d failed with errno %d: %s", tos, + error, curlx_strerror(error, buffer, sizeof(buffer))); } } #endif @@ -94,9 +101,10 @@ static int sockopt_callback(void *clientp, curl_socket_t curlfd, int priority = (int)config->vlan_priority; if(setsockopt(curlfd, SOL_SOCKET, SO_PRIORITY, (void *)&priority, sizeof(priority)) != 0) { + char buffer[STRERROR_LEN]; int error = errno; - warnf("VLAN priority %d failed with errno %d: %s", - priority, error, strerror(error)); + warnf("VLAN priority %d failed with errno %d: %s", priority, + error, curlx_strerror(error, buffer, sizeof(buffer))); } } #endif @@ -112,7 +120,8 @@ static char *ssl_backend(void) if(!already) { /* if there is no existing version */ const char *v = curl_version_info(CURLVERSION_NOW)->ssl_version; if(v) - msnprintf(ssl_ver, sizeof(ssl_ver), "%.*s", (int) strcspn(v, " "), v); + curl_msnprintf(ssl_ver, sizeof(ssl_ver), + "%.*s", (int)strcspn(v, " "), v); already = TRUE; } return ssl_ver; @@ -134,30 +143,36 @@ static CURLcode url_proto_and_rewrite(char **url, DEBUGASSERT(url && *url); if(uh) { char *schemep = NULL; - if(!curl_url_set(uh, CURLUPART_URL, *url, - CURLU_GUESS_SCHEME | CURLU_NON_SUPPORT_SCHEME) && - !curl_url_get(uh, CURLUPART_SCHEME, &schemep, - CURLU_DEFAULT_SCHEME)) { + CURLUcode uc = + curl_url_set(uh, CURLUPART_URL, *url, + CURLU_GUESS_SCHEME | CURLU_NON_SUPPORT_SCHEME); + if(!uc) { + uc = curl_url_get(uh, CURLUPART_SCHEME, &schemep, CURLU_DEFAULT_SCHEME); + if(!uc) { #ifdef CURL_DISABLE_IPFS - (void)config; + (void)config; #else - if(curl_strequal(schemep, proto_ipfs) || - curl_strequal(schemep, proto_ipns)) { - result = ipfs_url_rewrite(uh, schemep, url, config); - /* short-circuit proto_token, we know it is ipfs or ipns */ - if(curl_strequal(schemep, proto_ipfs)) - proto = proto_ipfs; - else if(curl_strequal(schemep, proto_ipns)) - proto = proto_ipns; - if(result) - config->synthetic_error = TRUE; - } - else + if(curl_strequal(schemep, proto_ipfs) || + curl_strequal(schemep, proto_ipns)) { + result = ipfs_url_rewrite(uh, schemep, url, config); + /* short-circuit proto_token, we know it is ipfs or ipns */ + if(curl_strequal(schemep, proto_ipfs)) + proto = proto_ipfs; + else if(curl_strequal(schemep, proto_ipns)) + proto = proto_ipns; + if(result) + config->synthetic_error = TRUE; + } + else #endif /* !CURL_DISABLE_IPFS */ - proto = proto_token(schemep); - - curl_free(schemep); + proto = proto_token(schemep); + curl_free(schemep); + } + else if(uc == CURLUE_OUT_OF_MEMORY) + result = CURLE_OUT_OF_MEMORY; } + else if(uc == CURLUE_OUT_OF_MEMORY) + result = CURLE_OUT_OF_MEMORY; curl_url_cleanup(uh); } else @@ -167,63 +182,61 @@ static CURLcode url_proto_and_rewrite(char **url, return result; } -static CURLcode ssh_setopts(struct OperationConfig *config, CURL *curl) +static CURLcode ssh_setopts(struct OperationConfig *config, CURL *curl, + const char *use_proto) { CURLcode result; + if(use_proto != proto_scp && use_proto != proto_sftp) + return CURLE_OK; + /* SSH and SSL private key uses same command-line option */ - /* new in libcurl 7.16.1 */ - my_setopt_str(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key); - /* new in libcurl 7.16.1 */ - my_setopt_str(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey); + MY_SETOPT_STR(curl, CURLOPT_SSH_PRIVATE_KEYFILE, config->key); + MY_SETOPT_STR(curl, CURLOPT_SSH_PUBLIC_KEYFILE, config->pubkey); - /* new in libcurl 7.17.1: SSH host key md5 checking allows us - to fail if we are not talking to who we think we should */ - my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, - config->hostpubmd5); + /* SSH host key md5 checking allows us to fail if we are not talking to who + we think we should */ + MY_SETOPT_STR(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5, config->hostpubmd5); - /* new in libcurl 7.80.0: SSH host key sha256 checking allows us - to fail if we are not talking to who we think we should */ - my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, + /* SSH host key sha256 checking allows us to fail if we are not talking to + who we think we should */ + MY_SETOPT_STR(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, config->hostpubsha256); - /* new in libcurl 7.56.0 */ if(config->ssh_compression) my_setopt_long(curl, CURLOPT_SSH_COMPRESSION, 1); if(!config->insecure_ok) { - char *known = global->knownhosts; - - if(!known) - known = findfile(".ssh/known_hosts", FALSE); + char *known = config->knownhosts; + if(!known) { + char *found = findfile(".ssh/known_hosts", FALSE); + if(found) { + known = curlx_strdup(found); + curl_free(found); + if(!known) + return CURLE_OUT_OF_MEMORY; + } + } if(known) { - /* new in curl 7.19.6 */ result = my_setopt_str(curl, CURLOPT_SSH_KNOWNHOSTS, known); if(result) { - global->knownhosts = NULL; - curl_free(known); + config->knownhosts = NULL; + curlx_free(known); return result; } /* store it in global to avoid repeated checks */ - global->knownhosts = known; + config->knownhosts = known; } else if(!config->hostpubmd5 && !config->hostpubsha256) { - errorf("Couldn't find a known_hosts file"); + errorf("Could not find a known_hosts file"); return CURLE_FAILED_INIT; } else - warnf("Couldn't find a known_hosts file"); + warnf("Could not find a known_hosts file"); } return CURLE_OK; /* ignore if SHA256 did not work */ } -#ifdef CURL_CA_EMBED -#ifndef CURL_DECLARED_CURL_CA_EMBED -#define CURL_DECLARED_CURL_CA_EMBED -extern const unsigned char curl_ca_embed[]; -#endif -#endif - static long tlsversion(unsigned char mintls, unsigned char maxtls) { @@ -247,7 +260,7 @@ static long tlsversion(unsigned char mintls, tlsver = CURL_SSLVERSION_TLSv1_2; break; case 4: - default: /* just in case */ + default: /* in case */ tlsver = CURL_SSLVERSION_TLSv1_3; break; } @@ -264,7 +277,7 @@ static long tlsversion(unsigned char mintls, tlsver |= CURL_SSLVERSION_MAX_TLSv1_2; break; case 4: - default: /* just in case */ + default: /* in case */ tlsver |= CURL_SSLVERSION_MAX_TLSv1_3; break; } @@ -272,37 +285,32 @@ static long tlsversion(unsigned char mintls, } /* only called if libcurl supports TLS */ -static CURLcode ssl_setopts(struct OperationConfig *config, CURL *curl) +static CURLcode ssl_ca_setopts(struct OperationConfig *config, CURL *curl) { CURLcode result = CURLE_OK; if(config->cacert) - my_setopt_str(curl, CURLOPT_CAINFO, config->cacert); + MY_SETOPT_STR(curl, CURLOPT_CAINFO, config->cacert); if(config->proxy_cacert) - my_setopt_str(curl, CURLOPT_PROXY_CAINFO, config->proxy_cacert); + MY_SETOPT_STR(curl, CURLOPT_PROXY_CAINFO, config->proxy_cacert); + if(config->capath) + MY_SETOPT_STR(curl, CURLOPT_CAPATH, config->capath); - if(config->capath) { - result = my_setopt_str(curl, CURLOPT_CAPATH, config->capath); - if(result) - return result; - } /* For the time being if --proxy-capath is not set then we use the --capath value for it, if any. See #1257 */ if(config->proxy_capath || config->capath) { - result = my_setopt_str(curl, CURLOPT_PROXY_CAPATH, - (config->proxy_capath ? config->proxy_capath : - config->capath)); - if((result == CURLE_NOT_BUILT_IN) || - (result == CURLE_UNKNOWN_OPTION)) { - if(config->proxy_capath) { - warnf("ignoring %s, not supported by libcurl with %s", - config->proxy_capath ? "--proxy-capath" : "--capath", - ssl_backend()); - } + MY_SETOPT_STR(curl, CURLOPT_PROXY_CAPATH, + (config->proxy_capath ? config->proxy_capath : + config->capath)); + if((result == CURLE_NOT_BUILT_IN) || (result == CURLE_UNKNOWN_OPTION)) { + warnf("ignoring %s, not supported by libcurl with %s", + "setting the CA path for the proxy", + ssl_backend()); + result = CURLE_OK; } - else if(result) - return result; } + if(result) + return result; #ifdef CURL_CA_EMBED if(!config->cacert && !config->capath) { @@ -312,9 +320,10 @@ static CURLcode ssl_setopts(struct OperationConfig *config, CURL *curl) blob.flags = CURL_BLOB_NOCOPY; notef("Using embedded CA bundle (%zu bytes)", blob.len); result = curl_easy_setopt(curl, CURLOPT_CAINFO_BLOB, &blob); - if(result == CURLE_NOT_BUILT_IN) { + if((result == CURLE_NOT_BUILT_IN) || (result == CURLE_UNKNOWN_OPTION)) { warnf("ignoring %s, not supported by libcurl with %s", "embedded CA bundle", ssl_backend()); + result = CURLE_OK; } } if(!config->proxy_cacert && !config->proxy_capath) { @@ -324,55 +333,61 @@ static CURLcode ssl_setopts(struct OperationConfig *config, CURL *curl) blob.flags = CURL_BLOB_NOCOPY; notef("Using embedded CA bundle, for proxies (%zu bytes)", blob.len); result = curl_easy_setopt(curl, CURLOPT_PROXY_CAINFO_BLOB, &blob); - if(result == CURLE_NOT_BUILT_IN) { + if((result == CURLE_NOT_BUILT_IN) || (result == CURLE_UNKNOWN_OPTION)) { warnf("ignoring %s, not supported by libcurl with %s", "embedded CA bundle", ssl_backend()); + result = CURLE_OK; } } #endif + return result; +} + +/* only called if libcurl supports TLS */ +static CURLcode ssl_setopts(struct OperationConfig *config, CURL *curl) +{ + CURLcode result = CURLE_OK; if(config->crlfile) - my_setopt_str(curl, CURLOPT_CRLFILE, config->crlfile); + MY_SETOPT_STR(curl, CURLOPT_CRLFILE, config->crlfile); if(config->proxy_crlfile) - my_setopt_str(curl, CURLOPT_PROXY_CRLFILE, config->proxy_crlfile); - else if(config->crlfile) /* CURLOPT_PROXY_CRLFILE default is crlfile */ - my_setopt_str(curl, CURLOPT_PROXY_CRLFILE, config->crlfile); + MY_SETOPT_STR(curl, CURLOPT_PROXY_CRLFILE, config->proxy_crlfile); + else if(config->crlfile) + /* CURLOPT_PROXY_CRLFILE default is crlfile */ + MY_SETOPT_STR(curl, CURLOPT_PROXY_CRLFILE, config->crlfile); if(config->pinnedpubkey) { - result = my_setopt_str(curl, CURLOPT_PINNEDPUBLICKEY, - config->pinnedpubkey); - if(result == CURLE_NOT_BUILT_IN) + MY_SETOPT_STR(curl, CURLOPT_PINNEDPUBLICKEY, config->pinnedpubkey); + if(result) warnf("ignoring %s, not supported by libcurl with %s", "--pinnedpubkey", ssl_backend()); } if(config->proxy_pinnedpubkey) { - result = my_setopt_str(curl, CURLOPT_PROXY_PINNEDPUBLICKEY, - config->proxy_pinnedpubkey); - if(result == CURLE_NOT_BUILT_IN) + MY_SETOPT_STR(curl, CURLOPT_PROXY_PINNEDPUBLICKEY, + config->proxy_pinnedpubkey); + if(result) warnf("ignoring %s, not supported by libcurl with %s", "--proxy-pinnedpubkey", ssl_backend()); } if(config->ssl_ec_curves) - my_setopt_str(curl, CURLOPT_SSL_EC_CURVES, config->ssl_ec_curves); + MY_SETOPT_STR(curl, CURLOPT_SSL_EC_CURVES, config->ssl_ec_curves); if(config->ssl_signature_algorithms) - my_setopt_str(curl, CURLOPT_SSL_SIGNATURE_ALGORITHMS, + MY_SETOPT_STR(curl, CURLOPT_SSL_SIGNATURE_ALGORITHMS, config->ssl_signature_algorithms); if(config->writeout) my_setopt_long(curl, CURLOPT_CERTINFO, 1); - my_setopt_str(curl, CURLOPT_SSLCERT, config->cert); - my_setopt_str(curl, CURLOPT_PROXY_SSLCERT, config->proxy_cert); - my_setopt_str(curl, CURLOPT_SSLCERTTYPE, config->cert_type); - my_setopt_str(curl, CURLOPT_PROXY_SSLCERTTYPE, - config->proxy_cert_type); - my_setopt_str(curl, CURLOPT_SSLKEY, config->key); - my_setopt_str(curl, CURLOPT_PROXY_SSLKEY, config->proxy_key); - my_setopt_str(curl, CURLOPT_SSLKEYTYPE, config->key_type); - my_setopt_str(curl, CURLOPT_PROXY_SSLKEYTYPE, - config->proxy_key_type); + MY_SETOPT_STR(curl, CURLOPT_SSLCERT, config->cert); + MY_SETOPT_STR(curl, CURLOPT_PROXY_SSLCERT, config->proxy_cert); + MY_SETOPT_STR(curl, CURLOPT_SSLCERTTYPE, config->cert_type); + MY_SETOPT_STR(curl, CURLOPT_PROXY_SSLCERTTYPE, config->proxy_cert_type); + MY_SETOPT_STR(curl, CURLOPT_SSLKEY, config->key); + MY_SETOPT_STR(curl, CURLOPT_PROXY_SSLKEY, config->proxy_key); + MY_SETOPT_STR(curl, CURLOPT_SSLKEYTYPE, config->key_type); + MY_SETOPT_STR(curl, CURLOPT_PROXY_SSLKEYTYPE, config->proxy_key_type); /* libcurl default is strict verifyhost -> 1L, verifypeer -> 1L */ if(config->insecure_ok) { @@ -419,8 +434,7 @@ static CURLcode ssl_setopts(struct OperationConfig *config, CURL *curl) { long mask = (config->proxy_ssl_allow_beast ? CURLSSLOPT_ALLOW_BEAST : 0) | - (config->proxy_ssl_auto_client_cert ? - CURLSSLOPT_AUTO_CLIENT_CERT : 0) | + (config->proxy_ssl_auto_client_cert ? CURLSSLOPT_AUTO_CLIENT_CERT : 0) | (config->proxy_native_ca_store ? CURLSSLOPT_NATIVE_CA : 0); if(mask) @@ -428,30 +442,28 @@ static CURLcode ssl_setopts(struct OperationConfig *config, CURL *curl) } if(config->cipher_list) { - result = my_setopt_str(curl, CURLOPT_SSL_CIPHER_LIST, - config->cipher_list); - if(result == CURLE_NOT_BUILT_IN) + MY_SETOPT_STR(curl, CURLOPT_SSL_CIPHER_LIST, config->cipher_list); + if(result) warnf("ignoring %s, not supported by libcurl with %s", "--ciphers", ssl_backend()); } if(config->proxy_cipher_list) { - result = my_setopt_str(curl, CURLOPT_PROXY_SSL_CIPHER_LIST, - config->proxy_cipher_list); - if(result == CURLE_NOT_BUILT_IN) + MY_SETOPT_STR(curl, CURLOPT_PROXY_SSL_CIPHER_LIST, + config->proxy_cipher_list); + if(result) warnf("ignoring %s, not supported by libcurl with %s", "--proxy-ciphers", ssl_backend()); } if(config->cipher13_list) { - result = my_setopt_str(curl, CURLOPT_TLS13_CIPHERS, - config->cipher13_list); - if(result == CURLE_NOT_BUILT_IN) + MY_SETOPT_STR(curl, CURLOPT_TLS13_CIPHERS, config->cipher13_list); + if(result) warnf("ignoring %s, not supported by libcurl with %s", "--tls13-ciphers", ssl_backend()); } if(config->proxy_cipher13_list) { - result = my_setopt_str(curl, CURLOPT_PROXY_TLS13_CIPHERS, - config->proxy_cipher13_list); - if(result == CURLE_NOT_BUILT_IN) + MY_SETOPT_STR(curl, CURLOPT_PROXY_TLS13_CIPHERS, + config->proxy_cipher13_list); + if(result) warnf("ignoring %s, not supported by libcurl with %s", "--proxy-tls13-ciphers", ssl_backend()); } @@ -464,29 +476,22 @@ static CURLcode ssl_setopts(struct OperationConfig *config, CURL *curl) if(feature_ech) { /* only if enabled in libcurl */ if(config->ech) /* only if set (optional) */ - my_setopt_str(curl, CURLOPT_ECH, config->ech); + MY_SETOPT_STR(curl, CURLOPT_ECH, config->ech); if(config->ech_public) /* only if set (optional) */ - my_setopt_str(curl, CURLOPT_ECH, config->ech_public); + MY_SETOPT_STR(curl, CURLOPT_ECH, config->ech_public); if(config->ech_config) /* only if set (optional) */ - my_setopt_str(curl, CURLOPT_ECH, config->ech_config); + MY_SETOPT_STR(curl, CURLOPT_ECH, config->ech_config); } - /* new in curl 7.9.3 */ - if(config->engine) { - result = my_setopt_str(curl, CURLOPT_SSLENGINE, config->engine); - if(result) - return result; - } + if(config->engine) + MY_SETOPT_STR(curl, CURLOPT_SSLENGINE, config->engine); - /* new in curl 7.15.5 */ if(config->ftp_ssl_reqd) my_setopt_enum(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); - /* new in curl 7.11.0 */ else if(config->ftp_ssl) my_setopt_enum(curl, CURLOPT_USE_SSL, CURLUSESSL_TRY); - /* new in curl 7.16.0 */ else if(config->ftp_ssl_control) my_setopt_enum(curl, CURLOPT_USE_SSL, CURLUSESSL_CONTROL); @@ -496,64 +501,6 @@ static CURLcode ssl_setopts(struct OperationConfig *config, CURL *curl) return CURLE_OK; } -/* only called for HTTP transfers */ -static CURLcode http_setopts(struct OperationConfig *config, - CURL *curl) -{ - long postRedir = 0; - - my_setopt_long(curl, CURLOPT_FOLLOWLOCATION, config->followlocation); - my_setopt_long(curl, CURLOPT_UNRESTRICTED_AUTH, - config->unrestricted_auth); - my_setopt_str(curl, CURLOPT_AWS_SIGV4, config->aws_sigv4); - my_setopt_long(curl, CURLOPT_AUTOREFERER, config->autoreferer); - - /* new in libcurl 7.36.0 */ - if(config->proxyheaders) { - my_setopt_slist(curl, CURLOPT_PROXYHEADER, config->proxyheaders); - my_setopt_long(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE); - } - - /* new in libcurl 7.5 */ - my_setopt_long(curl, CURLOPT_MAXREDIRS, config->maxredirs); - - if(config->httpversion) - my_setopt_enum(curl, CURLOPT_HTTP_VERSION, config->httpversion); - - /* curl 7.19.1 (the 301 version existed in 7.18.2), - 303 was added in 7.26.0 */ - if(config->post301) - postRedir |= CURL_REDIR_POST_301; - if(config->post302) - postRedir |= CURL_REDIR_POST_302; - if(config->post303) - postRedir |= CURL_REDIR_POST_303; - my_setopt_long(curl, CURLOPT_POSTREDIR, postRedir); - - /* new in libcurl 7.21.6 */ - if(config->encoding) - my_setopt_str(curl, CURLOPT_ACCEPT_ENCODING, ""); - - /* new in libcurl 7.21.6 */ - if(config->tr_encoding) - my_setopt_long(curl, CURLOPT_TRANSFER_ENCODING, 1); - /* new in libcurl 7.64.0 */ - my_setopt_long(curl, CURLOPT_HTTP09_ALLOWED, config->http09_allowed); - - if(config->altsvc) - my_setopt_str(curl, CURLOPT_ALTSVC, config->altsvc); - - if(config->hsts) - my_setopt_str(curl, CURLOPT_HSTS, config->hsts); - - /* new in 7.47.0 */ - if(config->expect100timeout_ms > 0) - my_setopt_long(curl, CURLOPT_EXPECT_100_TIMEOUT_MS, - config->expect100timeout_ms); - - return CURLE_OK; -} - static CURLcode cookie_setopts(struct OperationConfig *config, CURL *curl) { CURLcode result = CURLE_OK; @@ -568,8 +515,8 @@ static CURLcode cookie_setopts(struct OperationConfig *config, CURL *curl) if(cl == config->cookies) result = curlx_dyn_add(&cookies, cl->data); else - result = curlx_dyn_addf(&cookies, ";%s", cl->data); - + result = curlx_dyn_addf(&cookies, ";%s%s", + ISBLANK(cl->data[0]) ? "" : " ", cl->data); if(result) { warnf("skipped provided cookie, the cookie header " "would go over %u bytes", MAX_COOKIE_LINE); @@ -577,29 +524,92 @@ static CURLcode cookie_setopts(struct OperationConfig *config, CURL *curl) } } - my_setopt_str(curl, CURLOPT_COOKIE, curlx_dyn_ptr(&cookies)); + result = my_setopt_str(curl, CURLOPT_COOKIE, curlx_dyn_ptr(&cookies)); curlx_dyn_free(&cookies); + if(result) + return result; } if(config->cookiefiles) { struct curl_slist *cfl; for(cfl = config->cookiefiles; cfl; cfl = cfl->next) - my_setopt_str(curl, CURLOPT_COOKIEFILE, cfl->data); + MY_SETOPT_STR(curl, CURLOPT_COOKIEFILE, cfl->data); } - /* new in libcurl 7.9 */ if(config->cookiejar) - my_setopt_str(curl, CURLOPT_COOKIEJAR, config->cookiejar); + MY_SETOPT_STR(curl, CURLOPT_COOKIEJAR, config->cookiejar); - /* new in libcurl 7.9.7 */ my_setopt_long(curl, CURLOPT_COOKIESESSION, config->cookiesession); return result; } -static CURLcode tcp_setopts(struct OperationConfig *config, - CURL *curl) +/* only for HTTP transfers */ +static CURLcode http_setopts(struct OperationConfig *config, CURL *curl, + const char *use_proto) +{ + CURLcode result = CURLE_OK; + long postRedir = 0; + + if(use_proto != proto_http && use_proto != proto_https) + return CURLE_OK; + + my_setopt_long(curl, CURLOPT_FOLLOWLOCATION, config->followlocation); + my_setopt_long(curl, CURLOPT_UNRESTRICTED_AUTH, config->unrestricted_auth); +#ifndef CURL_DISABLE_AWS + MY_SETOPT_STR(curl, CURLOPT_AWS_SIGV4, config->aws_sigv4); +#endif + my_setopt_long(curl, CURLOPT_AUTOREFERER, config->autoreferer); + + if(config->proxyheaders) { + my_setopt_slist(curl, CURLOPT_PROXYHEADER, config->proxyheaders); + } + + my_setopt_long(curl, CURLOPT_MAXREDIRS, config->maxredirs); + + if(config->httpversion) + my_setopt_enum(curl, CURLOPT_HTTP_VERSION, config->httpversion); + + if(config->post301) + postRedir |= CURL_REDIR_POST_301; + if(config->post302) + postRedir |= CURL_REDIR_POST_302; + if(config->post303) + postRedir |= CURL_REDIR_POST_303; + my_setopt_long(curl, CURLOPT_POSTREDIR, postRedir); + + if(config->encoding) + MY_SETOPT_STR(curl, CURLOPT_ACCEPT_ENCODING, ""); + + if(config->tr_encoding) + my_setopt_long(curl, CURLOPT_TRANSFER_ENCODING, 1); + + my_setopt_long(curl, CURLOPT_HTTP09_ALLOWED, config->http09_allowed); + + if(config->altsvc) + MY_SETOPT_STR(curl, CURLOPT_ALTSVC, config->altsvc); + + if(config->hsts) + MY_SETOPT_STR(curl, CURLOPT_HSTS, config->hsts); + + if(config->expect100timeout_ms > 0) + my_setopt_long(curl, CURLOPT_EXPECT_100_TIMEOUT_MS, + config->expect100timeout_ms); + + if(!result) + result = cookie_setopts(config, curl); + if(result) + return result; + /* Enable header separation when using a proxy with HTTPS or proxytunnel + * to prevent --header content from leaking into CONNECT requests */ + if((config->proxy || config->proxyheaders) && + (use_proto == proto_https || config->proxytunnel)) + my_setopt_long(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE); + return result; +} + +static void tcp_setopts(struct OperationConfig *config, CURL *curl) { if(!config->tcp_nodelay) my_setopt_long(curl, CURLOPT_TCP_NODELAY, 0); @@ -608,7 +618,7 @@ static CURLcode tcp_setopts(struct OperationConfig *config, my_setopt_long(curl, CURLOPT_TCP_FASTOPEN, 1); if(config->mptcp) - my_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, tool_socket_open_mptcp_cb); + my_setopt_ptr(curl, CURLOPT_OPENSOCKETFUNCTION, tool_socket_open_mptcp_cb); /* curl 7.17.1 */ if(!config->nokeepalive) { @@ -622,51 +632,46 @@ static CURLcode tcp_setopts(struct OperationConfig *config, } else my_setopt_long(curl, CURLOPT_TCP_KEEPALIVE, 0); - return CURLE_OK; } -static CURLcode ftp_setopts(struct OperationConfig *config, CURL *curl) +static CURLcode ftp_setopts(struct OperationConfig *config, CURL *curl, + const char *use_proto) { - my_setopt_str(curl, CURLOPT_FTPPORT, config->ftpport); + CURLcode result; + + if(use_proto != proto_ftp && use_proto != proto_ftps) + return CURLE_OK; + + MY_SETOPT_STR(curl, CURLOPT_FTPPORT, config->ftpport); - /* new in libcurl 7.9.2: */ if(config->disable_epsv) /* disable it */ my_setopt_long(curl, CURLOPT_FTP_USE_EPSV, 0); - /* new in libcurl 7.10.5 */ if(config->disable_eprt) /* disable it */ my_setopt_long(curl, CURLOPT_FTP_USE_EPRT, 0); - /* new in curl 7.16.1 */ if(config->ftp_ssl_ccc) my_setopt_enum(curl, CURLOPT_FTP_SSL_CCC, config->ftp_ssl_ccc_mode); - my_setopt_str(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account); - - /* curl 7.14.2 */ + MY_SETOPT_STR(curl, CURLOPT_FTP_ACCOUNT, config->ftp_account); my_setopt_long(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip); - - /* curl 7.15.1 */ my_setopt_long(curl, CURLOPT_FTP_FILEMETHOD, config->ftp_filemethod); - - /* curl 7.15.5 */ - my_setopt_str(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER, + MY_SETOPT_STR(curl, CURLOPT_FTP_ALTERNATIVE_TO_USER, config->ftp_alternative_to_user); - /* curl 7.20.x */ if(config->ftp_pret) my_setopt_long(curl, CURLOPT_FTP_USE_PRET, 1); - return CURLE_OK; + return result; } static void gen_trace_setopts(struct OperationConfig *config, CURL *curl) { if(global->tracetype != TRACE_NONE) { - my_setopt(curl, CURLOPT_DEBUGFUNCTION, tool_debug_cb); - my_setopt(curl, CURLOPT_DEBUGDATA, config); + my_setopt_ptr(curl, CURLOPT_DEBUGFUNCTION, tool_debug_cb); + my_setopt_ptr(curl, CURLOPT_DEBUGDATA, config); my_setopt_long(curl, CURLOPT_VERBOSE, 1L); } } @@ -678,45 +683,45 @@ static void gen_cb_setopts(struct OperationConfig *config, (void)config; /* when --libcurl is disabled */ /* where to store */ - my_setopt(curl, CURLOPT_WRITEDATA, per); - my_setopt(curl, CURLOPT_INTERLEAVEDATA, per); + my_setopt_ptr(curl, CURLOPT_WRITEDATA, per); + my_setopt_ptr(curl, CURLOPT_INTERLEAVEDATA, per); /* what call to write */ - my_setopt(curl, CURLOPT_WRITEFUNCTION, tool_write_cb); + my_setopt_ptr(curl, CURLOPT_WRITEFUNCTION, tool_write_cb); /* what to read */ - my_setopt(curl, CURLOPT_READDATA, per); - my_setopt(curl, CURLOPT_READFUNCTION, tool_read_cb); + my_setopt_ptr(curl, CURLOPT_READDATA, per); + my_setopt_ptr(curl, CURLOPT_READFUNCTION, tool_read_cb); /* in 7.18.0, the CURLOPT_SEEKFUNCTION/DATA pair is taking over what CURLOPT_IOCTLFUNCTION/DATA pair previously provided for seeking */ - my_setopt(curl, CURLOPT_SEEKDATA, per); - my_setopt(curl, CURLOPT_SEEKFUNCTION, tool_seek_cb); + my_setopt_ptr(curl, CURLOPT_SEEKDATA, per); + my_setopt_ptr(curl, CURLOPT_SEEKFUNCTION, tool_seek_cb); if((global->progressmode == CURL_PROGRESS_BAR) && !global->noprogress && !global->silent) { /* we want the alternative style, then we have to implement it ourselves! */ - my_setopt(curl, CURLOPT_XFERINFOFUNCTION, tool_progress_cb); - my_setopt(curl, CURLOPT_XFERINFODATA, per); + my_setopt_ptr(curl, CURLOPT_XFERINFOFUNCTION, tool_progress_cb); + my_setopt_ptr(curl, CURLOPT_XFERINFODATA, per); } else if(per->uploadfile && !strcmp(per->uploadfile, ".")) { /* when reading from stdin in non-blocking mode, we use the progress function to unpause a busy read */ my_setopt_long(curl, CURLOPT_NOPROGRESS, 0); - my_setopt(curl, CURLOPT_XFERINFOFUNCTION, tool_readbusy_cb); - my_setopt(curl, CURLOPT_XFERINFODATA, per); + my_setopt_ptr(curl, CURLOPT_XFERINFOFUNCTION, tool_readbusy_cb); + my_setopt_ptr(curl, CURLOPT_XFERINFODATA, per); } - my_setopt(curl, CURLOPT_HEADERFUNCTION, tool_header_cb); - my_setopt(curl, CURLOPT_HEADERDATA, per); + my_setopt_ptr(curl, CURLOPT_HEADERFUNCTION, tool_header_cb); + my_setopt_ptr(curl, CURLOPT_HEADERDATA, per); } static CURLcode proxy_setopts(struct OperationConfig *config, CURL *curl) { + CURLcode result; if(config->proxy) { - CURLcode result = my_setopt_str(curl, CURLOPT_PROXY, config->proxy); - + result = my_setopt_str(curl, CURLOPT_PROXY, config->proxy); if(result) { errorf("proxy support is disabled in this libcurl"); config->synthetic_error = TRUE; @@ -724,20 +729,14 @@ static CURLcode proxy_setopts(struct OperationConfig *config, CURL *curl) } } - /* new in libcurl 7.5 */ if(config->proxy) my_setopt_enum(curl, CURLOPT_PROXYTYPE, config->proxyver); - my_setopt_str(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd); - - /* new in libcurl 7.3 */ + MY_SETOPT_STR(curl, CURLOPT_PROXYUSERPWD, config->proxyuserpwd); my_setopt_long(curl, CURLOPT_HTTPPROXYTUNNEL, config->proxytunnel); - - /* new in libcurl 7.52.0 */ if(config->preproxy) - my_setopt_str(curl, CURLOPT_PRE_PROXY, config->preproxy); + MY_SETOPT_STR(curl, CURLOPT_PRE_PROXY, config->preproxy); - /* new in libcurl 7.10.6 */ if(config->proxyanyauth) my_setopt_bitmask(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY); else if(config->proxynegotiate) @@ -749,126 +748,47 @@ static CURLcode proxy_setopts(struct OperationConfig *config, CURL *curl) else if(config->proxybasic) my_setopt_bitmask(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); - /* new in libcurl 7.19.4 */ - my_setopt_str(curl, CURLOPT_NOPROXY, config->noproxy); - + MY_SETOPT_STR(curl, CURLOPT_NOPROXY, config->noproxy); my_setopt_long(curl, CURLOPT_SUPPRESS_CONNECT_HEADERS, config->suppress_connect_headers); - /* new in curl 7.43.0 */ if(config->proxy_service_name) - my_setopt_str(curl, CURLOPT_PROXY_SERVICE_NAME, + MY_SETOPT_STR(curl, CURLOPT_PROXY_SERVICE_NAME, config->proxy_service_name); - /* new in 7.60.0 */ if(config->haproxy_protocol) my_setopt_long(curl, CURLOPT_HAPROXYPROTOCOL, 1); - /* new in 8.2.0 */ if(config->haproxy_clientip) - my_setopt_str(curl, CURLOPT_HAPROXY_CLIENT_IP, config->haproxy_clientip); + MY_SETOPT_STR(curl, CURLOPT_HAPROXY_CLIENT_IP, config->haproxy_clientip); - return CURLE_OK; + return result; } -static void tls_srp_setopts(struct OperationConfig *config, CURL *curl) +static CURLcode tls_srp_setopts(struct OperationConfig *config, CURL *curl) { + CURLcode result = CURLE_OK; if(config->tls_username) - my_setopt_str(curl, CURLOPT_TLSAUTH_USERNAME, config->tls_username); + MY_SETOPT_STR(curl, CURLOPT_TLSAUTH_USERNAME, config->tls_username); if(config->tls_password) - my_setopt_str(curl, CURLOPT_TLSAUTH_PASSWORD, config->tls_password); + MY_SETOPT_STR(curl, CURLOPT_TLSAUTH_PASSWORD, config->tls_password); if(config->tls_authtype) - my_setopt_str(curl, CURLOPT_TLSAUTH_TYPE, config->tls_authtype); + MY_SETOPT_STR(curl, CURLOPT_TLSAUTH_TYPE, config->tls_authtype); if(config->proxy_tls_username) - my_setopt_str(curl, CURLOPT_PROXY_TLSAUTH_USERNAME, + MY_SETOPT_STR(curl, CURLOPT_PROXY_TLSAUTH_USERNAME, config->proxy_tls_username); if(config->proxy_tls_password) - my_setopt_str(curl, CURLOPT_PROXY_TLSAUTH_PASSWORD, + MY_SETOPT_STR(curl, CURLOPT_PROXY_TLSAUTH_PASSWORD, config->proxy_tls_password); if(config->proxy_tls_authtype) - my_setopt_str(curl, CURLOPT_PROXY_TLSAUTH_TYPE, + MY_SETOPT_STR(curl, CURLOPT_PROXY_TLSAUTH_TYPE, config->proxy_tls_authtype); + return result; } -CURLcode config2setopts(struct OperationConfig *config, - struct per_transfer *per, - CURL *curl, - CURLSH *share) +static CURLcode setopt_post(struct OperationConfig *config, CURL *curl) { - const char *use_proto; - CURLcode result = url_proto_and_rewrite(&per->url, config, &use_proto); - - /* Avoid having this setopt added to the --libcurl source output. */ - if(!result) - result = curl_easy_setopt(curl, CURLOPT_SHARE, share); - if(result) - return result; - -#ifndef DEBUGBUILD - /* On most modern OSes, exiting works thoroughly, - we will clean everything up via exit(), so do not bother with - slow cleanups. Crappy ones might need to skip this. - Note: avoid having this setopt added to the --libcurl source - output. */ - result = curl_easy_setopt(curl, CURLOPT_QUICK_EXIT, 1L); - if(result) - return result; -#endif - - gen_trace_setopts(config, curl); - - { -#ifdef DEBUGBUILD - char *env = getenv("CURL_BUFFERSIZE"); - if(env) { - curl_off_t num; - const char *p = env; - if(!curlx_str_number(&p, &num, LONG_MAX)) - my_setopt_long(curl, CURLOPT_BUFFERSIZE, (long)num); - } - else -#endif - if(config->recvpersecond && (config->recvpersecond < BUFFER_SIZE)) - /* use a smaller sized buffer for better sleeps */ - my_setopt_long(curl, CURLOPT_BUFFERSIZE, (long)config->recvpersecond); - else - my_setopt_long(curl, CURLOPT_BUFFERSIZE, BUFFER_SIZE); - } - - my_setopt_str(curl, CURLOPT_URL, per->url); - my_setopt_long(curl, CURLOPT_NOPROGRESS, - global->noprogress || global->silent); - /* call after the line above. It may override CURLOPT_NOPROGRESS */ - gen_cb_setopts(config, per, curl); - - my_setopt_long(curl, CURLOPT_NOBODY, config->no_body); - my_setopt_str(curl, CURLOPT_XOAUTH2_BEARER, config->oauth_bearer); - - result = proxy_setopts(config, curl); - if(result) - return result; - - my_setopt_long(curl, CURLOPT_FAILONERROR, config->failonerror); - my_setopt_str(curl, CURLOPT_REQUEST_TARGET, config->request_target); - my_setopt_long(curl, CURLOPT_UPLOAD, !!per->uploadfile); - my_setopt_long(curl, CURLOPT_DIRLISTONLY, config->dirlistonly); - my_setopt_long(curl, CURLOPT_APPEND, config->ftp_append); - - if(config->netrc_opt) - my_setopt_enum(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); - else if(config->netrc || config->netrc_file) - my_setopt_enum(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED); - else - my_setopt_enum(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED); - - my_setopt_str(curl, CURLOPT_NETRC_FILE, config->netrc_file); - my_setopt_long(curl, CURLOPT_TRANSFERTEXT, config->use_ascii); - my_setopt_str(curl, CURLOPT_LOGIN_OPTIONS, config->login_options); - my_setopt_str(curl, CURLOPT_USERPWD, config->userpwd); - my_setopt_str(curl, CURLOPT_RANGE, config->range); - my_setopt(curl, CURLOPT_ERRORBUFFER, per->errorbuffer); - my_setopt_long(curl, CURLOPT_TIMEOUT_MS, config->timeout_ms); - + CURLcode result = CURLE_OK; switch(config->httpreq) { case TOOL_HTTPREQ_SIMPLEPOST: if(config->resume_from) { @@ -876,7 +796,7 @@ CURLcode config2setopts(struct OperationConfig *config, result = CURLE_FAILED_INIT; } else { - my_setopt_str(curl, CURLOPT_POSTFIELDS, + MY_SETOPT_STR(curl, CURLOPT_POSTFIELDS, curlx_dyn_ptr(&config->postdata)); my_setopt_offt(curl, CURLOPT_POSTFIELDSIZE_LARGE, curlx_dyn_len(&config->postdata)); @@ -893,12 +813,98 @@ CURLcode config2setopts(struct OperationConfig *config, else { result = tool2curlmime(curl, config->mimeroot, &config->mimepost); if(!result) - my_setopt_mimepost(curl, CURLOPT_MIMEPOST, config->mimepost); + result = my_setopt_mimepost(curl, CURLOPT_MIMEPOST, config->mimepost); } break; default: break; } + return result; +} + +static void buffersize(struct OperationConfig *config, CURL *curl) +{ +#ifdef DEBUGBUILD + char *env = getenv("CURL_BUFFERSIZE"); + if(env) { + curl_off_t num; + const char *p = env; + if(!curlx_str_number(&p, &num, LONG_MAX)) + my_setopt_long(curl, CURLOPT_BUFFERSIZE, (long)num); + return; + } +#endif + if(config->recvpersecond && (config->recvpersecond < BUFFER_SIZE)) + /* use a smaller sized buffer for better sleeps */ + my_setopt_long(curl, CURLOPT_BUFFERSIZE, (long)config->recvpersecond); + else + my_setopt_long(curl, CURLOPT_BUFFERSIZE, BUFFER_SIZE); +} + +CURLcode config2setopts(struct OperationConfig *config, + struct per_transfer *per, + CURL *curl, + CURLSH *share) +{ + const char *use_proto; + CURLcode result = url_proto_and_rewrite(&per->url, config, &use_proto); + + /* Avoid having this setopt added to the --libcurl source output. */ + if(!result) + result = curl_easy_setopt(curl, CURLOPT_SHARE, share); + if(result) + return result; + + if(TRUE +#ifdef DEBUGBUILD + && getenv("CURL_QUICK_EXIT") +#endif + ) { + /* QUICK_EXIT allows for running threads to be detached and not + * joined. Preferably in non-debug runs. */ + result = curl_easy_setopt(curl, CURLOPT_QUICK_EXIT, 1L); + if(result) + return result; + } + + gen_trace_setopts(config, curl); + + buffersize(config, curl); + + MY_SETOPT_STR(curl, CURLOPT_URL, per->url); + my_setopt_long(curl, CURLOPT_NOPROGRESS, + global->noprogress || global->silent); + /* call after the line above. It may override CURLOPT_NOPROGRESS */ + gen_cb_setopts(config, per, curl); + + my_setopt_long(curl, CURLOPT_NOBODY, config->no_body); + MY_SETOPT_STR(curl, CURLOPT_XOAUTH2_BEARER, config->oauth_bearer); + result = proxy_setopts(config, curl); + if(setopt_bad(result) || config->synthetic_error) + return result; + + my_setopt_long(curl, CURLOPT_FAILONERROR, config->fail == FAIL_WO_BODY); + MY_SETOPT_STR(curl, CURLOPT_REQUEST_TARGET, config->request_target); + my_setopt_long(curl, CURLOPT_UPLOAD, !!per->uploadfile); + my_setopt_long(curl, CURLOPT_DIRLISTONLY, config->dirlistonly); + my_setopt_long(curl, CURLOPT_APPEND, config->ftp_append); + + if(config->netrc_opt) + my_setopt_enum(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); + else if(config->netrc || config->netrc_file) + my_setopt_enum(curl, CURLOPT_NETRC, CURL_NETRC_REQUIRED); + else + my_setopt_enum(curl, CURLOPT_NETRC, CURL_NETRC_IGNORED); + + MY_SETOPT_STR(curl, CURLOPT_NETRC_FILE, config->netrc_file); + my_setopt_long(curl, CURLOPT_TRANSFERTEXT, config->use_ascii); + MY_SETOPT_STR(curl, CURLOPT_LOGIN_OPTIONS, config->login_options); + MY_SETOPT_STR(curl, CURLOPT_USERPWD, config->userpwd); + MY_SETOPT_STR(curl, CURLOPT_RANGE, config->range); + my_setopt_ptr(curl, CURLOPT_ERRORBUFFER, per->errorbuffer); + my_setopt_long(curl, CURLOPT_TIMEOUT_MS, config->timeout_ms); + + result = setopt_post(config, curl); if(result) return result; @@ -911,23 +917,16 @@ CURLcode config2setopts(struct OperationConfig *config, my_setopt_slist(curl, CURLOPT_HTTPHEADER, config->headers); if(proto_http || proto_rtsp) { - my_setopt_str(curl, CURLOPT_REFERER, config->referer); - my_setopt_str(curl, CURLOPT_USERAGENT, config->useragent); + MY_SETOPT_STR(curl, CURLOPT_REFERER, config->referer); + MY_SETOPT_STR(curl, CURLOPT_USERAGENT, config->useragent ? + config->useragent : CURL_NAME "/" CURL_VERSION); } - if(use_proto == proto_http || use_proto == proto_https) { - result = http_setopts(config, curl); - if(!result) - result = cookie_setopts(config, curl); - if(result) - return result; - } - - if(use_proto == proto_ftp || use_proto == proto_ftps) { - result = ftp_setopts(config, curl); - if(result) - return result; - } + result = http_setopts(config, curl, use_proto); + if(!result) + result = ftp_setopts(config, curl, use_proto); + if(result) + return result; my_setopt_long(curl, CURLOPT_LOW_SPEED_LIMIT, config->low_speed_limit); my_setopt_long(curl, CURLOPT_LOW_SPEED_TIME, config->low_speed_time); @@ -939,28 +938,27 @@ CURLcode config2setopts(struct OperationConfig *config, else my_setopt_offt(curl, CURLOPT_RESUME_FROM_LARGE, 0); - my_setopt_str(curl, CURLOPT_KEYPASSWD, config->key_passwd); - my_setopt_str(curl, CURLOPT_PROXY_KEYPASSWD, config->proxy_key_passwd); + MY_SETOPT_STR(curl, CURLOPT_KEYPASSWD, config->key_passwd); + MY_SETOPT_STR(curl, CURLOPT_PROXY_KEYPASSWD, config->proxy_key_passwd); - if(use_proto == proto_scp || use_proto == proto_sftp) { - result = ssh_setopts(config, curl); - if(result) - return result; - } + result = ssh_setopts(config, curl, use_proto); + if(setopt_bad(result)) + return result; if(feature_ssl) { - result = ssl_setopts(config, curl); - if(result) + result = ssl_ca_setopts(config, curl); + if(!result) + result = ssl_setopts(config, curl); + if(setopt_bad(result)) return result; } if(config->path_as_is) my_setopt_long(curl, CURLOPT_PATH_AS_IS, 1); - if(config->no_body || config->remote_time) { + if(config->no_body || config->remote_time) /* no body or use remote time */ my_setopt_long(curl, CURLOPT_FILETIME, 1); - } my_setopt_long(curl, CURLOPT_CRLF, config->crlf); my_setopt_slist(curl, CURLOPT_QUOTE, config->quote); @@ -969,30 +967,28 @@ CURLcode config2setopts(struct OperationConfig *config, my_setopt_enum(curl, CURLOPT_TIMECONDITION, config->timecond); my_setopt_offt(curl, CURLOPT_TIMEVALUE_LARGE, config->condtime); - my_setopt_str(curl, CURLOPT_CUSTOMREQUEST, config->customrequest); + MY_SETOPT_STR(curl, CURLOPT_CUSTOMREQUEST, config->customrequest); customrequest_helper(config->httpreq, config->customrequest); - my_setopt(curl, CURLOPT_STDERR, tool_stderr); - my_setopt_str(curl, CURLOPT_INTERFACE, config->iface); - my_setopt_str(curl, CURLOPT_KRBLEVEL, config->krblevel); + my_setopt_ptr(curl, CURLOPT_STDERR, tool_stderr); + MY_SETOPT_STR(curl, CURLOPT_INTERFACE, config->iface); progressbarinit(&per->progressbar, config); - my_setopt_str(curl, CURLOPT_DNS_SERVERS, config->dns_servers); - my_setopt_str(curl, CURLOPT_DNS_INTERFACE, config->dns_interface); - my_setopt_str(curl, CURLOPT_DNS_LOCAL_IP4, config->dns_ipv4_addr); - my_setopt_str(curl, CURLOPT_DNS_LOCAL_IP6, config->dns_ipv6_addr); + MY_SETOPT_STR(curl, CURLOPT_DNS_SERVERS, config->dns_servers); + MY_SETOPT_STR(curl, CURLOPT_DNS_INTERFACE, config->dns_interface); + MY_SETOPT_STR(curl, CURLOPT_DNS_LOCAL_IP4, config->dns_ipv4_addr); + MY_SETOPT_STR(curl, CURLOPT_DNS_LOCAL_IP6, config->dns_ipv6_addr); my_setopt_slist(curl, CURLOPT_TELNETOPTIONS, config->telnet_options); my_setopt_long(curl, CURLOPT_CONNECTTIMEOUT_MS, config->connecttimeout_ms); - my_setopt_str(curl, CURLOPT_DOH_URL, config->doh_url); + MY_SETOPT_STR(curl, CURLOPT_DOH_URL, config->doh_url); my_setopt_long(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, (config->ftp_create_dirs ? CURLFTP_CREATE_DIR_RETRY : CURLFTP_CREATE_DIR_NONE)); - my_setopt_offt(curl, CURLOPT_MAXFILESIZE_LARGE, - config->max_filesize); + my_setopt_offt(curl, CURLOPT_MAXFILESIZE_LARGE, config->max_filesize); my_setopt_long(curl, CURLOPT_IPRESOLVE, config->ip_version); if(config->socks5_gssapi_nec) my_setopt_long(curl, CURLOPT_SOCKS5_GSSAPI_NEC, 1); if(config->socks5_auth) my_setopt_bitmask(curl, CURLOPT_SOCKS5_AUTH, config->socks5_auth); - my_setopt_str(curl, CURLOPT_SERVICE_NAME, config->service_name); + MY_SETOPT_STR(curl, CURLOPT_SERVICE_NAME, config->service_name); my_setopt_long(curl, CURLOPT_IGNORE_CONTENT_LENGTH, config->ignorecl); if(config->localport) { @@ -1005,14 +1001,12 @@ CURLcode config2setopts(struct OperationConfig *config, my_setopt_long(curl, CURLOPT_HTTP_TRANSFER_DECODING, 0); } - result = tcp_setopts(config, curl); - if(result) - return result; + tcp_setopts(config, curl); if(config->tftp_blksize && proto_tftp) my_setopt_long(curl, CURLOPT_TFTP_BLKSIZE, config->tftp_blksize); - my_setopt_str(curl, CURLOPT_MAIL_FROM, config->mail_from); + MY_SETOPT_STR(curl, CURLOPT_MAIL_FROM, config->mail_from); my_setopt_slist(curl, CURLOPT_MAIL_RCPT, config->mail_rcpt); my_setopt_long(curl, CURLOPT_MAIL_RCPT_ALLOWFAILS, config->mail_rcpt_allowfails); @@ -1020,35 +1014,36 @@ CURLcode config2setopts(struct OperationConfig *config, my_setopt_long(curl, CURLOPT_NEW_FILE_PERMS, config->create_file_mode); if(config->proto_present) - my_setopt_str(curl, CURLOPT_PROTOCOLS_STR, config->proto_str); + MY_SETOPT_STR(curl, CURLOPT_PROTOCOLS_STR, config->proto_str); if(config->proto_redir_present) - my_setopt_str(curl, CURLOPT_REDIR_PROTOCOLS_STR, config->proto_redir_str); + MY_SETOPT_STR(curl, CURLOPT_REDIR_PROTOCOLS_STR, config->proto_redir_str); my_setopt_slist(curl, CURLOPT_RESOLVE, config->resolve); my_setopt_slist(curl, CURLOPT_CONNECT_TO, config->connect_to); - if(feature_tls_srp) - tls_srp_setopts(config, curl); + if(feature_tls_srp) { + result = tls_srp_setopts(config, curl); + if(setopt_bad(result)) + return result; + } if(config->gssapi_delegation) my_setopt_long(curl, CURLOPT_GSSAPI_DELEGATION, config->gssapi_delegation); - my_setopt_str(curl, CURLOPT_MAIL_AUTH, config->mail_auth); - my_setopt_str(curl, CURLOPT_SASL_AUTHZID, config->sasl_authzid); + MY_SETOPT_STR(curl, CURLOPT_MAIL_AUTH, config->mail_auth); + MY_SETOPT_STR(curl, CURLOPT_SASL_AUTHZID, config->sasl_authzid); my_setopt_long(curl, CURLOPT_SASL_IR, config->sasl_ir); if(config->unix_socket_path) { - if(config->abstract_unix_socket) { - my_setopt_str(curl, CURLOPT_ABSTRACT_UNIX_SOCKET, + if(config->abstract_unix_socket) + MY_SETOPT_STR(curl, CURLOPT_ABSTRACT_UNIX_SOCKET, config->unix_socket_path); - } - else { - my_setopt_str(curl, CURLOPT_UNIX_SOCKET_PATH, + else + MY_SETOPT_STR(curl, CURLOPT_UNIX_SOCKET_PATH, config->unix_socket_path); - } } - my_setopt_str(curl, CURLOPT_DEFAULT_PROTOCOL, config->proto_default); + MY_SETOPT_STR(curl, CURLOPT_DEFAULT_PROTOCOL, config->proto_default); my_setopt_long(curl, CURLOPT_TFTP_NO_OPTIONS, config->tftp_no_options && proto_tftp); @@ -1061,8 +1056,8 @@ CURLcode config2setopts(struct OperationConfig *config, if(config->ip_tos > 0 || config->vlan_priority > 0) { #if defined(IP_TOS) || defined(IPV6_TCLASS) || defined(SO_PRIORITY) - my_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_callback); - my_setopt(curl, CURLOPT_SOCKOPTDATA, config); + my_setopt_ptr(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_callback); + my_setopt_ptr(curl, CURLOPT_SOCKOPTDATA, config); #else if(config->ip_tos > 0) { errorf("Type of service is not supported in this build."); diff --git a/src/curl.rc b/src/curl.rc index 389b483b4b..3c5c8709d5 100644 --- a/src/curl.rc +++ b/src/curl.rc @@ -32,7 +32,7 @@ VS_VERSION_INFO VERSIONINFO FILEVERSION RC_VERSION PRODUCTVERSION RC_VERSION FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#if defined(DEBUGBUILD) || defined(UNITTESTS) || defined(CURLDEBUG) || defined(_DEBUG) +#if defined(DEBUGBUILD) || defined(UNITTESTS) || defined(_DEBUG) FILEFLAGS VS_FF_DEBUG #else FILEFLAGS 0L diff --git a/src/curlinfo.c b/src/curlinfo.c index d601904f3e..13b9d62ce0 100644 --- a/src/curlinfo.c +++ b/src/curlinfo.c @@ -21,7 +21,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - /* * The purpose of this tool is to figure out which, if any, features that are * disabled which should otherwise exist and work. These are not visible in @@ -30,16 +29,21 @@ * Disabled protocols are visible in curl_version_info() and are not included * in this table. */ - #include "curl_setup.h" + #include "multihandle.h" /* for ENABLE_WAKEUP */ #include "tool_xattr.h" /* for USE_XATTR */ #include "curl_sha512_256.h" /* for CURL_HAVE_SHA512_256 */ -#include "asyn.h" /* for CURLRES_ARES */ +#include "asyn.h" /* for USE_RESOLV_ARES, USE_RESOLV_THREADED */ #include "fake_addrinfo.h" /* for USE_FAKE_GETADDRINFO */ + #include -static const char *disabled[]={ +#ifdef USE_OPENSSL +#include /* for OPENSSL_NO_OCSP */ +#endif + +static const char *disabled[] = { "bindlocal: " #ifdef CURL_DISABLE_BINDLOCAL "OFF" @@ -139,6 +143,13 @@ static const char *disabled[]={ "OFF" #else "ON" +#endif + , + "resolv-threaded: " +#ifndef USE_RESOLV_THREADED + "OFF" +#else + "ON" #endif , "typecheck: " @@ -230,25 +241,37 @@ static const char *disabled[]={ #endif , "override-dns: " -#if defined(CURLDEBUG) && \ - (defined(CURLRES_ARES) || defined(USE_FAKE_GETADDRINFO)) +#if defined(CURL_MEMDEBUG) && \ + (defined(USE_RESOLV_ARES) || defined(USE_FAKE_GETADDRINFO)) "ON" #else "OFF" #endif , - NULL + "ssl-sessions: " +#ifdef USE_SSLS_EXPORT + "ON" +#else + "OFF" +#endif + , + "cert-status: " +#if defined(USE_GNUTLS) || (defined(USE_OPENSSL) && !defined(OPENSSL_NO_OCSP)) + "ON" +#else + "OFF" +#endif }; -int main(int argc, char **argv) +int main(int argc, const char **argv) { - int i; + size_t i; (void)argc; (void)argv; - for(i = 0; disabled[i]; i++) - printf("%s\n", disabled[i]); + for(i = 0; i < CURL_ARRAYSIZE(disabled); i++) + puts(disabled[i]); return 0; } diff --git a/src/mk-file-embed.pl b/src/mk-file-embed.pl index fcd8e04136..9f3a1b3976 100755 --- a/src/mk-file-embed.pl +++ b/src/mk-file-embed.pl @@ -39,10 +39,8 @@ print < -#include /* keep this as LAST include */ static const unsigned char hugehelpgz[] = { /* This mumbo-jumbo is the huge help text compressed with gzip. Thanks to this operation, the size of this data shrank from $gzip @@ -105,13 +104,13 @@ HEAD static voidpf zalloc_func(voidpf opaque, unsigned int items, unsigned int size) { (void)opaque; - /* not a typo, keep it calloc() */ - return (voidpf) calloc(items, size); + /* not a typo, keep it curlx_calloc() */ + return (voidpf)curlx_calloc(items, size); } static void zfree_func(voidpf opaque, voidpf ptr) { (void)opaque; - free(ptr); + curlx_free(ptr); } #define HEADERLEN 10 @@ -136,7 +135,7 @@ void hugehelp(void) if(inflateInit2(&z, -MAX_WBITS) != Z_OK) return; - buf = malloc(BUF_SIZE); + buf = curlx_malloc(BUF_SIZE); if(buf) { while(1) { z.avail_out = BUF_SIZE; @@ -150,10 +149,11 @@ void hugehelp(void) else break; /* error */ } - free(buf); + curlx_free(buf); } inflateEnd(&z); } + /* Show the help text for the 'arg' curl argument on stdout */ void showhelp(const char *trigger, const char *arg, const char *endarg) { @@ -176,7 +176,7 @@ void showhelp(const char *trigger, const char *arg, const char *endarg) if(inflateInit2(&z, -MAX_WBITS) != Z_OK) return; - buf = malloc(BUF_SIZE); + buf = curlx_malloc(BUF_SIZE); if(buf) { while(1) { z.avail_out = BUF_SIZE; @@ -192,7 +192,7 @@ void showhelp(const char *trigger, const char *arg, const char *endarg) else break; /* error */ } - free(buf); + curlx_free(buf); } inflateEnd(&z); } @@ -220,7 +220,7 @@ for my $n (@out) { } else { $n =~ s/ /\\t/g; - printf(" \"%s%s\",\n", $blank?"\\n":"", $n); + printf(" \"%s%s\",\n", $blank ? "\\n" : "", $n); $blank = 0; } } diff --git a/src/slist_wc.c b/src/slist_wc.c index 7f1e8f19be..f34cbd4861 100644 --- a/src/slist_wc.c +++ b/src/slist_wc.c @@ -21,22 +21,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "tool_setup.h" #ifndef CURL_DISABLE_LIBCURL_OPTION #include "slist_wc.h" -/* The last #include files should be: */ -#include "memdebug.h" - /* * slist_wc_append() appends a string to the linked list. This function can be * used as an initialization function as well as an append function. */ -struct slist_wc *slist_wc_append(struct slist_wc *list, - const char *data) +struct slist_wc *slist_wc_append(struct slist_wc *list, const char *data) { struct curl_slist *new_item = curl_slist_append(NULL, data); @@ -44,7 +39,7 @@ struct slist_wc *slist_wc_append(struct slist_wc *list, return NULL; if(!list) { - list = malloc(sizeof(struct slist_wc)); + list = curlx_malloc(sizeof(struct slist_wc)); if(!list) { curl_slist_free_all(new_item); @@ -68,7 +63,7 @@ void slist_wc_free_all(struct slist_wc *list) return; curl_slist_free_all(list->first); - free(list); + curlx_free(list); } #endif /* CURL_DISABLE_LIBCURL_OPTION */ diff --git a/src/slist_wc.h b/src/slist_wc.h index dd7b8c10a7..694d80c4a8 100644 --- a/src/slist_wc.h +++ b/src/slist_wc.h @@ -23,8 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "tool_setup.h" + #ifndef CURL_DISABLE_LIBCURL_OPTION /* linked-list structure with last node cache for easysrc */ @@ -38,10 +38,10 @@ struct slist_wc { * * DESCRIPTION * - * Appends a string to a linked list. If no list exists, it will be created + * Appends a string to a linked list. If no list exists, it is created * first. Returns the new list, after appending. */ -struct slist_wc *slist_wc_append(struct slist_wc *, const char *); +struct slist_wc *slist_wc_append(struct slist_wc *list, const char *data); /* * NAME curl_slist_free_all() @@ -50,7 +50,7 @@ struct slist_wc *slist_wc_append(struct slist_wc *, const char *); * * free a previously built curl_slist_wc. */ -void slist_wc_free_all(struct slist_wc *); +void slist_wc_free_all(struct slist_wc *list); #endif /* CURL_DISABLE_LIBCURL_OPTION */ diff --git a/src/terminal.c b/src/terminal.c index 0150cd4a56..b25c0e6e80 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -28,7 +28,6 @@ #endif #include "terminal.h" -#include "memdebug.h" /* keep this as LAST include */ #ifdef HAVE_TERMIOS_H # include @@ -38,9 +37,8 @@ /* * get_terminal_columns() returns the number of columns in the current - * terminal. It will return 79 on failure. Also, the number can be big. + * terminal. It returns 79 on failure. Also, the number can be big. */ - unsigned int get_terminal_columns(void) { unsigned int width = 0; @@ -64,19 +62,18 @@ unsigned int get_terminal_columns(void) struct winsize ts; if(!ioctl(STDIN_FILENO, TIOCGWINSZ, &ts)) cols = (int)ts.ws_col; -#elif defined(_WIN32) && !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) +#elif defined(_WIN32) && !defined(CURL_WINDOWS_UWP) { - HANDLE stderr_hnd = GetStdHandle(STD_ERROR_HANDLE); + HANDLE stderr_hnd = GetStdHandle(STD_ERROR_HANDLE); CONSOLE_SCREEN_BUFFER_INFO console_info; if((stderr_hnd != INVALID_HANDLE_VALUE) && GetConsoleScreenBufferInfo(stderr_hnd, &console_info)) { /* * Do not use +1 to get the true screen-width since writing a - * character at the right edge will cause a line wrap. + * character at the right edge causes a line wrap. */ - cols = (int) - (console_info.srWindow.Right - console_info.srWindow.Left); + cols = (int)(console_info.srWindow.Right - console_info.srWindow.Left); } } #endif /* TIOCGSIZE */ diff --git a/src/tool_cb_dbg.c b/src/tool_cb_dbg.c index 6a587cc1cc..c9c14e6d13 100644 --- a/src/tool_cb_dbg.c +++ b/src/tool_cb_dbg.c @@ -27,16 +27,10 @@ #include "tool_msgs.h" #include "tool_cb_dbg.h" #include "tool_util.h" - -#include "memdebug.h" /* keep this as LAST include */ - -static void dump(const char *timebuf, const char *idsbuf, const char *text, - FILE *stream, const unsigned char *ptr, size_t size, - trace tracetype, curl_infotype infotype); +#include "toolx/tool_time.h" /* * Return the formatted HH:MM:SS for the tv_sec given. - * NOT thread safe. */ static const char *hms_for_sec(time_t tv_sec) { @@ -44,10 +38,12 @@ static const char *hms_for_sec(time_t tv_sec) static char hms_buf[12]; if(tv_sec != cached_tv_sec) { - /* !checksrc! disable BANNEDFUNC 1 */ - struct tm *now = localtime(&tv_sec); /* not thread safe either */ - msnprintf(hms_buf, sizeof(hms_buf), "%02d:%02d:%02d", - now->tm_hour, now->tm_min, now->tm_sec); + struct tm now; + CURLcode result = toolx_localtime(tv_sec, &now); + if(result) + memset(&now, 0, sizeof(now)); + curl_msnprintf(hms_buf, sizeof(hms_buf), "%02d:%02d:%02d", + now.tm_hour, now.tm_min, now.tm_sec); cached_tv_sec = tv_sec; } return hms_buf; @@ -64,11 +60,65 @@ static void log_line_start(FILE *log, const char *timebuf, "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; if((timebuf && *timebuf) || (idsbuf && *idsbuf)) - fprintf(log, "%s%s%s", timebuf, idsbuf, s_infotype[type]); + curl_mfprintf(log, "%s%s%s", timebuf, idsbuf, s_infotype[type]); else fputs(s_infotype[type], log); } +static void dump(const char *timebuf, const char *idsbuf, const char *text, + FILE *stream, const unsigned char *ptr, size_t size, + trace tracetype, curl_infotype infotype) +{ + size_t i; + size_t c; + + unsigned int width = 0x10; + + if(tracetype == TRACE_ASCII) + /* without the hex output, we can fit more on screen */ + width = 0x40; + + curl_mfprintf(stream, "%s%s%s, %zu bytes (0x%zx)\n", timebuf, idsbuf, + text, size, size); + + for(i = 0; i < size; i += width) { + + curl_mfprintf(stream, "%04zx: ", i); + + if(tracetype == TRACE_BIN) { + /* hex not disabled, show it */ + for(c = 0; c < width; c++) + if(i + c < size) + curl_mfprintf(stream, "%02x ", ptr[i + c]); + else + fputs(" ", stream); + } + + for(c = 0; (c < width) && (i + c < size); c++) { + /* check for 0D0A; if found, skip past and start a new line of output */ + if((tracetype == TRACE_ASCII) && + (i + c + 1 < size) && (ptr[i + c] == 0x0D) && + (ptr[i + c + 1] == 0x0A)) { + i += (c + 2 - width); + break; + } + (void)infotype; + curl_mfprintf(stream, "%c", + ((ptr[i + c] >= 0x20) && (ptr[i + c] < 0x7F)) ? + ptr[i + c] : UNPRINTABLE_CHAR); + /* check again for 0D0A, to avoid an extra \n if it is at width */ + if((tracetype == TRACE_ASCII) && + (i + c + 2 < size) && (ptr[i + c + 1] == 0x0D) && + (ptr[i + c + 2] == 0x0A)) { + i += (c + 3 - width); + break; + } + } + fputc('\n', stream); /* newline */ + } + fflush(stream); +} + #define TRC_IDS_FORMAT_IDS_1 "[%" CURL_FORMAT_CURL_OFF_T "-x] " #define TRC_IDS_FORMAT_IDS_2 "[%" CURL_FORMAT_CURL_OFF_T "-%" \ CURL_FORMAT_CURL_OFF_T "] " @@ -96,8 +146,8 @@ int tool_debug_cb(CURL *handle, curl_infotype type, if(global->tracetime) { tv = tvrealnow(); - msnprintf(timebuf, sizeof(timebuf), "%s.%06ld ", - hms_for_sec(tv.tv_sec), (long)tv.tv_usec); + curl_msnprintf(timebuf, sizeof(timebuf), "%s.%06ld ", + hms_for_sec(tv.tv_sec), (long)tv.tv_usec); } else timebuf[0] = 0; @@ -105,12 +155,12 @@ int tool_debug_cb(CURL *handle, curl_infotype type, if(handle && global->traceids && !curl_easy_getinfo(handle, CURLINFO_XFER_ID, &xfer_id) && xfer_id >= 0) { if(!curl_easy_getinfo(handle, CURLINFO_CONN_ID, &conn_id) && - conn_id >= 0) { - msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, - xfer_id, conn_id); + conn_id >= 0) { + curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_2, + xfer_id, conn_id); } else { - msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id); + curl_msnprintf(idsbuf, sizeof(idsbuf), TRC_IDS_FORMAT_IDS_1, xfer_id); } } else @@ -124,7 +174,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type, /* Ok, this is somewhat hackish but we do it undocumented for now */ global->trace_stream = tool_stderr; else { - global->trace_stream = fopen(global->trace_dump, FOPEN_WRITETEXT); + global->trace_stream = curlx_fopen(global->trace_dump, FOPEN_WRITETEXT); global->trace_fopened = TRUE; } } @@ -178,13 +228,12 @@ int tool_debug_cb(CURL *handle, curl_infotype type, if(!traced_data) { /* if the data is output to a tty and we are sending this debug trace to stderr or stdout, we do not display the alert about the data not - being shown as the data _is_ shown then just not via this - function */ + being shown as the data _is_ shown then not via this function */ if(!global->isatty || ((output != tool_stderr) && (output != stdout))) { if(!newl) log_line_start(output, timebuf, idsbuf, type); - fprintf(output, "[%zu bytes data]\n", size); + curl_mfprintf(output, "[%zu bytes data]\n", size); newl = FALSE; traced_data = TRUE; } @@ -201,7 +250,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type, switch(type) { case CURLINFO_TEXT: - fprintf(output, "%s%s== Info: %.*s", timebuf, idsbuf, (int)size, data); + curl_mfprintf(output, "%s%s* %.*s", timebuf, idsbuf, (int)size, data); FALLTHROUGH(); default: /* in case a new one is introduced to shock us */ return 0; @@ -226,60 +275,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type, break; } - dump(timebuf, idsbuf, text, output, (unsigned char *) data, size, + dump(timebuf, idsbuf, text, output, (unsigned char *)data, size, global->tracetype, type); return 0; } - -static void dump(const char *timebuf, const char *idsbuf, const char *text, - FILE *stream, const unsigned char *ptr, size_t size, - trace tracetype, curl_infotype infotype) -{ - size_t i; - size_t c; - - unsigned int width = 0x10; - - if(tracetype == TRACE_ASCII) - /* without the hex output, we can fit more on screen */ - width = 0x40; - - fprintf(stream, "%s%s%s, %zu bytes (0x%zx)\n", timebuf, idsbuf, - text, size, size); - - for(i = 0; i < size; i += width) { - - fprintf(stream, "%04zx: ", i); - - if(tracetype == TRACE_BIN) { - /* hex not disabled, show it */ - for(c = 0; c < width; c++) - if(i + c < size) - fprintf(stream, "%02x ", ptr[i + c]); - else - fputs(" ", stream); - } - - for(c = 0; (c < width) && (i + c < size); c++) { - /* check for 0D0A; if found, skip past and start a new line of output */ - if((tracetype == TRACE_ASCII) && - (i + c + 1 < size) && (ptr[i + c] == 0x0D) && - (ptr[i + c + 1] == 0x0A)) { - i += (c + 2 - width); - break; - } - (void)infotype; - fprintf(stream, "%c", ((ptr[i + c] >= 0x20) && (ptr[i + c] < 0x7F)) ? - ptr[i + c] : UNPRINTABLE_CHAR); - /* check again for 0D0A, to avoid an extra \n if it is at width */ - if((tracetype == TRACE_ASCII) && - (i + c + 2 < size) && (ptr[i + c + 1] == 0x0D) && - (ptr[i + c + 2] == 0x0A)) { - i += (c + 3 - width); - break; - } - } - fputc('\n', stream); /* newline */ - } - fflush(stream); -} diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c index 6af9d1947e..9b3a33aefb 100644 --- a/src/tool_cb_hdr.c +++ b/src/tool_cb_hdr.c @@ -34,38 +34,208 @@ #include "tool_cb_wrt.h" #include "tool_operate.h" #include "tool_libinfo.h" -#include "tool_strdup.h" - -#include "memdebug.h" /* keep this as LAST include */ - -static char *parse_filename(const char *ptr, size_t len); #ifdef _WIN32 -#define BOLD "\x1b[1m" +#define BOLD "\x1b[1m" #define BOLDOFF "\x1b[22m" #else -#define BOLD "\x1b[1m" +#define BOLD "\x1b[1m" /* Switch off bold by setting "all attributes off" since the explicit bold-off code (21) is not supported everywhere - like in the mac Terminal. */ #define BOLDOFF "\x1b[0m" /* OSC 8 hyperlink escape sequence */ -#define LINK "\x1b]8;;" -#define LINKST "\x1b\\" +#define LINK "\x1b]8;;" +#define LINKST "\x1b\\" #define LINKOFF LINK LINKST #endif #ifdef LINK +/* + * Treat the Location: header specially, by writing a special escape + * sequence that adds a hyperlink to the displayed text. This makes + * the absolute URL of the redirect clickable in supported terminals, + * which could not happen otherwise for relative URLs. The Location: + * header is supposed to always be absolute so this theoretically + * should not be needed but the real world returns plenty of relative + * URLs here. + */ static void write_linked_location(CURL *curl, const char *location, - size_t loclen, FILE *stream); -#endif + size_t loclen, FILE *stream) +{ + /* This would so simple if CURLINFO_REDIRECT_URL were available here */ + CURLU *u = NULL; + char *copyloc = NULL, *scheme = NULL, *finalurl = NULL; + const char *loc = location, *locurl = NULL; + size_t llen = loclen; + int space_skipped = 0; + const char *vver = getenv("VTE_VERSION"); + + if(vver) { + curl_off_t num; + if(curlx_str_number(&vver, &num, CURL_OFF_T_MAX) || + /* Skip formatting for old versions of VTE <= 0.48.1 (Mar 2017) since + some of those versions have formatting bugs. (#10428) */ + (num <= 4801)) + goto locout; + } + + /* Strip leading whitespace of the redirect URL */ + while(llen && (*loc == ' ' || *loc == '\t')) { + ++loc; + --llen; + ++space_skipped; + } + + /* Strip the trailing end-of-line characters, normally "\r\n" */ + while(llen && (loc[llen - 1] == '\n' || loc[llen - 1] == '\r')) + --llen; + + /* CURLU makes it easy to handle the relative URL case */ + u = curl_url(); + if(!u) + goto locout; + + /* Create a null-terminated and whitespace-stripped copy of Location: */ + copyloc = curlx_memdup0(loc, llen); + if(!copyloc) + goto locout; + + /* The original URL to use as a base for a relative redirect URL */ + if(curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &locurl)) + goto locout; + if(curl_url_set(u, CURLUPART_URL, locurl, 0)) + goto locout; + + /* Redirected location. This can be either absolute or relative. */ + if(curl_url_set(u, CURLUPART_URL, copyloc, 0)) + goto locout; + + if(curl_url_get(u, CURLUPART_URL, &finalurl, CURLU_NO_DEFAULT_PORT)) + goto locout; + + if(curl_url_get(u, CURLUPART_SCHEME, &scheme, 0)) + goto locout; + + if(!strcmp("http", scheme) || + !strcmp("https", scheme) || + !strcmp("ftp", scheme) || + !strcmp("ftps", scheme)) { + curl_mfprintf(stream, "%.*s" LINK "%s" LINKST "%.*s" LINKOFF, + space_skipped, location, + finalurl, + (int)loclen - space_skipped, loc); + goto locdone; + } + + /* Not a "safe" URL: do not linkify it */ + +locout: + /* Write the normal output in case of error or unsafe */ + fwrite(location, loclen, 1, stream); + +locdone: + if(u) { + curl_free(finalurl); + curl_free(scheme); + curl_url_cleanup(u); + curlx_free(copyloc); + } +} +#endif /* LINK */ + +/* + * Copies a filename part and returns an ALLOCATED data buffer. + */ +static char *parse_filename(const char *ptr, size_t len, char stop) +{ + char *copy; + char *p; + char *q; + + copy = curlx_memdup0(ptr, len); + if(!copy) + return NULL; + + p = copy; + if(stop) { + /* a Content-Disposition: header */ + if(*p == '\'' || *p == '"') { + /* store the starting quote */ + stop = *p; + p++; + } + + /* scan for the end letter and stop there */ + q = strchr(p, stop); + if(q) + *q = '\0'; + } + else { + /* this is a Location: header, so we need to trim off any queries and + fragments present */ + q = strchr(p, '?'); /* trim off query, if present */ + if(q) + *q = '\0'; + q = strchr(p, '#'); /* trim off fragment, if present */ + if(q) + *q = '\0'; + } + + /* if the filename contains a path, only use filename portion */ + q = strrchr(p, '/'); + if(q) { + p = q + 1; + if(!*p) { + curlx_safefree(copy); + return NULL; + } + } + + /* If the filename contains a backslash, only use filename portion. The idea + is that even systems that do not handle backslashes as path separators + probably want the path removed for convenience. */ + q = strrchr(p, '\\'); + if(q) { + p = q + 1; + if(!*p) { + curlx_safefree(copy); + return NULL; + } + } + + /* make sure the filename does not end in \r or \n */ + q = strchr(p, '\r'); + if(q) + *q = '\0'; + + q = strchr(p, '\n'); + if(q) + *q = '\0'; + + if(copy != p) + memmove(copy, p, strlen(p) + 1); + +#if defined(_WIN32) || defined(MSDOS) + { + char *sanitized; + SANITIZEcode sc = sanitize_file_name(&sanitized, copy, 0); + curlx_safefree(copy); + if(sc) + return NULL; + copy = sanitized; + } +#endif /* _WIN32 || MSDOS */ + + return copy; +} int tool_write_headers(struct HdrCbData *hdrcbdata, FILE *stream) { struct curl_slist *h = hdrcbdata->headlist; int rc = 1; while(h) { - /* not "handled", just show it */ + /* not "handled", show it */ size_t len = strlen(h->data); if(len != fwrite(h->data, 1, len, stream)) goto fail; @@ -78,9 +248,185 @@ fail: return rc; } +/* + * Write etag to file when --etag-save option is given. + */ +static size_t save_etag(const char *etag_h, const char *endp, + struct OutStruct *etag_save) +{ + const char *eot = endp - 1; + if(*eot == '\n') { + while(ISBLANK(*etag_h) && (etag_h < eot)) + etag_h++; + while(ISSPACE(*eot)) + eot--; + + if(eot >= etag_h) { + size_t etag_length = eot - etag_h + 1; + curlx_struct_stat file; + int fd = fileno(etag_save->stream); + + /* Truncate regular files to avoid stale etag content */ + if((fd != -1) && + !curlx_fstat(fd, &file) && + (S_ISREG(file.st_mode) && + toolx_ftruncate(fd, 0))) + return CURL_WRITEFUNC_ERROR; + + fwrite(etag_h, 1, etag_length, etag_save->stream); + /* terminate with newline */ + fputc('\n', etag_save->stream); + (void)fflush(etag_save->stream); + } + } + return 0; /* ok */ +} + +/* + * This function sets the filename where output shall be written when curl + * options --remote-name (-O) and --remote-header-name (-J) have been + * simultaneously given and additionally server returns an HTTP + * Content-Disposition header specifying a filename property. + */ +static size_t content_disposition(const char *str, const char *end, + size_t cb, struct per_transfer *per, + long response) /* response code */ +{ + struct HdrCbData *hdrcbdata = &per->hdrcbdata; + struct OutStruct *outs = &per->outs; + + if((cb > 9) && checkprefix("Location:", str) && (response/100 == 3)) { + /* Get the name off the location header as a temporary measure in case + there is no Content-Disposition */ + const char *p = &str[9]; + curlx_str_passblanks(&p); + if(p < end) { /* as a precaution */ + char *filename = parse_filename(p, cb - (p - str), 0); + if(filename) { + if(outs->stream) { + /* indication of problem, get out! */ + curlx_free(filename); + return CURL_WRITEFUNC_ERROR; + } + if(outs->alloc_filename) + curlx_safefree(outs->filename); + + if(per->config->output_dir) { + char *f = curl_maprintf("%s/%s", per->config->output_dir, + filename); + curlx_free(filename); + if(!f) + return CURL_WRITEFUNC_ERROR; + outs->filename = curlx_strdup(f); + curl_free(f); + if(!outs->filename) + return CURL_WRITEFUNC_ERROR; + } + else + outs->filename = filename; + outs->alloc_filename = TRUE; + outs->is_cd_filename = TRUE; /* set to avoid clobbering existing files + by default */ + } + } + } + else if((cb > 20) && checkprefix("Content-disposition:", str)) { + const char *p = str + 20; + /* look for the 'filename=' parameter (encoded filenames (*=) are not + supported) */ + for(;;) { + char *filename; + size_t len; + + while((p < end) && *p && !ISALPHA(*p)) + p++; + if(p > end - 9) + break; + + if(memcmp(p, "filename=", 9)) { + /* no match, find next parameter */ + while((p < end) && *p && (*p != ';')) + p++; + if((p < end) && *p) + continue; + else + break; + } + p += 9; + + curlx_str_passblanks(&p); + len = cb - (size_t)(p - str); + filename = parse_filename(p, len, ';'); + if(filename) { + if(outs->stream) { + /* indication of problem, get out! */ + curlx_free(filename); + return CURL_WRITEFUNC_ERROR; + } + if(outs->alloc_filename) + curlx_safefree(outs->filename); + + if(per->config->output_dir) { + char *f = curl_maprintf("%s/%s", per->config->output_dir, + filename); + curlx_free(filename); + if(!f) + return CURL_WRITEFUNC_ERROR; + outs->filename = curlx_strdup(f); + curl_free(f); + if(!outs->filename) + return CURL_WRITEFUNC_ERROR; + } + else + outs->filename = filename; + + outs->is_cd_filename = TRUE; + outs->regular_file = TRUE; + outs->fopened = FALSE; + outs->alloc_filename = TRUE; + hdrcbdata->honor_cd_filename = FALSE; /* done now! */ + if(!tool_create_output_file(outs, per->config)) + return CURL_WRITEFUNC_ERROR; + if(tool_write_headers(&per->hdrcbdata, outs->stream)) + return CURL_WRITEFUNC_ERROR; + } + break; + } + if(!outs->stream && !tool_create_output_file(outs, per->config)) + return CURL_WRITEFUNC_ERROR; + if(tool_write_headers(&per->hdrcbdata, outs->stream)) + return CURL_WRITEFUNC_ERROR; + } /* content-disposition handling */ + + if(hdrcbdata->honor_cd_filename && + hdrcbdata->config->show_headers) { + /* still awaiting the Content-Disposition header, store the header in + memory. Since it is not null-terminated, we need an extra dance. */ + char *clone = curlx_memdup0(str, cb); + if(clone) { + struct curl_slist *old = hdrcbdata->headlist; + hdrcbdata->headlist = curl_slist_append(old, clone); + curlx_free(clone); + if(!hdrcbdata->headlist) { + curl_slist_free_all(old); + return CURL_WRITEFUNC_ERROR; + } + } + else { + curl_slist_free_all(hdrcbdata->headlist); + hdrcbdata->headlist = NULL; + return CURL_WRITEFUNC_ERROR; + } + return cb; /* done for now */ + } + + return 0; /* ok */ +} /* ** callback for CURLOPT_HEADERFUNCTION +* +* 'size' is always 1 */ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata) { @@ -91,7 +437,7 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata) struct OutStruct *etag_save = &per->etag_save; const char *str = ptr; const size_t cb = size * nmemb; - const char *end = (char *)ptr + cb; + const char *end = ptr + cb; const char *scheme = NULL; if(!per->config) @@ -116,7 +462,7 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata) if(per->config->headerfile && heads->stream) { size_t rc = fwrite(ptr, size, nmemb, heads->stream); - if(rc != cb) + if(rc != nmemb) return rc; /* flush the stream to send off what we got earlier */ if(fflush(heads->stream)) { @@ -131,142 +477,31 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata) long response = 0; curl_easy_getinfo(per->curl, CURLINFO_RESPONSE_CODE, &response); - if((response/100 != 2) && (response/100 != 3)) + if((response / 100 != 2) && (response / 100 != 3)) /* only care about etag and content-disposition headers in 2xx and 3xx responses */ ; - /* - * Write etag to file when --etag-save option is given. - */ else if(per->config->etag_save_file && etag_save->stream && /* match only header that start with etag (case insensitive) */ checkprefix("etag:", str)) { - const char *etag_h = &str[5]; - const char *eot = end - 1; - if(*eot == '\n') { - while(ISBLANK(*etag_h) && (etag_h < eot)) - etag_h++; - while(ISSPACE(*eot)) - eot--; - - if(eot >= etag_h) { - size_t etag_length = eot - etag_h + 1; - /* - * Truncate the etag save stream, it can have an existing etag value. - */ -#if defined(HAVE_FTRUNCATE) && !defined(__MINGW32CE__) - if(ftruncate(fileno(etag_save->stream), 0)) { - return CURL_WRITEFUNC_ERROR; - } -#else - if(fseek(etag_save->stream, 0, SEEK_SET)) { - return CURL_WRITEFUNC_ERROR; - } -#endif - - fwrite(etag_h, size, etag_length, etag_save->stream); - /* terminate with newline */ - fputc('\n', etag_save->stream); - (void)fflush(etag_save->stream); - } - } + size_t rc = save_etag(&str[5], end, etag_save); + if(rc) + return rc; } - /* - * This callback sets the filename where output shall be written when - * curl options --remote-name (-O) and --remote-header-name (-J) have - * been simultaneously given and additionally server returns an HTTP - * Content-Disposition header specifying a filename property. - */ - + /* Parse the content-disposition and location headers. When + honor_cd_filename is true, other headers may be stored until the + content-disposition header is reached, at which point the saved headers + can be written. That means the content_disposition() may return an rc + when it has saved a different header for writing later. */ else if(hdrcbdata->honor_cd_filename) { - if((cb > 20) && checkprefix("Content-disposition:", str)) { - const char *p = str + 20; - - /* look for the 'filename=' parameter - (encoded filenames (*=) are not supported) */ - for(;;) { - char *filename; - size_t len; - - while((p < end) && *p && !ISALPHA(*p)) - p++; - if(p > end - 9) - break; - - if(memcmp(p, "filename=", 9)) { - /* no match, find next parameter */ - while((p < end) && *p && (*p != ';')) - p++; - if((p < end) && *p) - continue; - else - break; - } - p += 9; - - len = cb - (size_t)(p - str); - filename = parse_filename(p, len); - if(filename) { - if(outs->stream) { - /* indication of problem, get out! */ - free(filename); - return CURL_WRITEFUNC_ERROR; - } - - if(per->config->output_dir) { - outs->filename = aprintf("%s/%s", per->config->output_dir, - filename); - free(filename); - if(!outs->filename) - return CURL_WRITEFUNC_ERROR; - } - else - outs->filename = filename; - - outs->is_cd_filename = TRUE; - outs->s_isreg = TRUE; - outs->fopened = FALSE; - outs->alloc_filename = TRUE; - hdrcbdata->honor_cd_filename = FALSE; /* done now! */ - if(!tool_create_output_file(outs, per->config)) - return CURL_WRITEFUNC_ERROR; - if(tool_write_headers(&per->hdrcbdata, outs->stream)) - return CURL_WRITEFUNC_ERROR; - } - break; - } - if(!outs->stream && !tool_create_output_file(outs, per->config)) - return CURL_WRITEFUNC_ERROR; - if(tool_write_headers(&per->hdrcbdata, outs->stream)) - return CURL_WRITEFUNC_ERROR; - } /* content-disposition handling */ - - if(hdrcbdata->honor_cd_filename && - hdrcbdata->config->show_headers) { - /* still awaiting the Content-Disposition header, store the header in - memory. Since it is not null-terminated, we need an extra dance. */ - char *clone = aprintf("%.*s", (int)cb, str); - if(clone) { - struct curl_slist *old = hdrcbdata->headlist; - hdrcbdata->headlist = curl_slist_append(old, clone); - free(clone); - if(!hdrcbdata->headlist) { - curl_slist_free_all(old); - return CURL_WRITEFUNC_ERROR; - } - } - else { - curl_slist_free_all(hdrcbdata->headlist); - hdrcbdata->headlist = NULL; - return CURL_WRITEFUNC_ERROR; - } - return cb; /* done for now */ - } + size_t rc = content_disposition(str, end, cb, per, response); + if(rc) + return rc; } } if(hdrcbdata->config->writeout) { - char *value = memchr(ptr, ':', cb); + const char *value = memchr(ptr, ':', cb); if(value) { if(per->was_last_header_empty) per->num_headers = 0; @@ -276,11 +511,11 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata) else if(ptr[0] == '\r' || ptr[0] == '\n') per->was_last_header_empty = TRUE; } - if(hdrcbdata->config->show_headers && - (scheme == proto_http || scheme == proto_https || - scheme == proto_rtsp || scheme == proto_file)) { + if(hdrcbdata->config->show_headers && !outs->out_null && + (scheme == proto_http || scheme == proto_https || + scheme == proto_rtsp || scheme == proto_file)) { /* bold headers only for selected protocols */ - char *value = NULL; + const char *value = NULL; if(!outs->stream && !tool_create_output_file(outs, per->config)) return CURL_WRITEFUNC_ERROR; @@ -293,191 +528,21 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata) value = memchr(ptr, ':', cb); if(value) { size_t namelen = value - ptr; - fprintf(outs->stream, BOLD "%.*s" BOLDOFF ":", (int)namelen, ptr); + curl_mfprintf(outs->stream, BOLD "%.*s" BOLDOFF ":", (int)namelen, ptr); #ifndef LINK fwrite(&value[1], cb - namelen - 1, 1, outs->stream); #else if(curl_strnequal("Location", ptr, namelen)) { write_linked_location(per->curl, &value[1], cb - namelen - 1, - outs->stream); + outs->stream); } else fwrite(&value[1], cb - namelen - 1, 1, outs->stream); #endif } else - /* not "handled", just show it */ + /* not "handled", show it */ fwrite(ptr, cb, 1, outs->stream); } return cb; } - -/* - * Copies a filename part and returns an ALLOCATED data buffer. - */ -static char *parse_filename(const char *ptr, size_t len) -{ - char *copy; - char *p; - char *q; - char stop = '\0'; - - copy = memdup0(ptr, len); - if(!copy) - return NULL; - - p = copy; - if(*p == '\'' || *p == '"') { - /* store the starting quote */ - stop = *p; - p++; - } - else - stop = ';'; - - /* scan for the end letter and stop there */ - q = strchr(p, stop); - if(q) - *q = '\0'; - - /* if the filename contains a path, only use filename portion */ - q = strrchr(p, '/'); - if(q) { - p = q + 1; - if(!*p) { - tool_safefree(copy); - return NULL; - } - } - - /* If the filename contains a backslash, only use filename portion. The idea - is that even systems that do not handle backslashes as path separators - probably want the path removed for convenience. */ - q = strrchr(p, '\\'); - if(q) { - p = q + 1; - if(!*p) { - tool_safefree(copy); - return NULL; - } - } - - /* make sure the filename does not end in \r or \n */ - q = strchr(p, '\r'); - if(q) - *q = '\0'; - - q = strchr(p, '\n'); - if(q) - *q = '\0'; - - if(copy != p) - memmove(copy, p, strlen(p) + 1); - -#if defined(_WIN32) || defined(MSDOS) - { - char *sanitized; - SANITIZEcode sc = sanitize_file_name(&sanitized, copy, 0); - tool_safefree(copy); - if(sc) - return NULL; - copy = sanitized; - } -#endif /* _WIN32 || MSDOS */ - - return copy; -} - -#ifdef LINK -/* - * Treat the Location: header specially, by writing a special escape - * sequence that adds a hyperlink to the displayed text. This makes - * the absolute URL of the redirect clickable in supported terminals, - * which could not happen otherwise for relative URLs. The Location: - * header is supposed to always be absolute so this theoretically - * should not be needed but the real world returns plenty of relative - * URLs here. - */ -static void write_linked_location(CURL *curl, const char *location, - size_t loclen, FILE *stream) -{ - /* This would so simple if CURLINFO_REDIRECT_URL were available here */ - CURLU *u = NULL; - char *copyloc = NULL, *locurl = NULL, *scheme = NULL, *finalurl = NULL; - const char *loc = location; - size_t llen = loclen; - int space_skipped = 0; - const char *vver = getenv("VTE_VERSION"); - - if(vver) { - curl_off_t num; - if(curlx_str_number(&vver, &num, CURL_OFF_T_MAX) || - /* Skip formatting for old versions of VTE <= 0.48.1 (Mar 2017) since - some of those versions have formatting bugs. (#10428) */ - (num <= 4801)) - goto locout; - } - - /* Strip leading whitespace of the redirect URL */ - while(llen && (*loc == ' ' || *loc == '\t')) { - ++loc; - --llen; - ++space_skipped; - } - - /* Strip the trailing end-of-line characters, normally "\r\n" */ - while(llen && (loc[llen-1] == '\n' || loc[llen-1] == '\r')) - --llen; - - /* CURLU makes it easy to handle the relative URL case */ - u = curl_url(); - if(!u) - goto locout; - - /* Create a null-terminated and whitespace-stripped copy of Location: */ - copyloc = memdup0(loc, llen); - if(!copyloc) - goto locout; - - /* The original URL to use as a base for a relative redirect URL */ - if(curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &locurl)) - goto locout; - if(curl_url_set(u, CURLUPART_URL, locurl, 0)) - goto locout; - - /* Redirected location. This can be either absolute or relative. */ - if(curl_url_set(u, CURLUPART_URL, copyloc, 0)) - goto locout; - - if(curl_url_get(u, CURLUPART_URL, &finalurl, CURLU_NO_DEFAULT_PORT)) - goto locout; - - if(curl_url_get(u, CURLUPART_SCHEME, &scheme, 0)) - goto locout; - - if(!strcmp("http", scheme) || - !strcmp("https", scheme) || - !strcmp("ftp", scheme) || - !strcmp("ftps", scheme)) { - fprintf(stream, "%.*s" LINK "%s" LINKST "%.*s" LINKOFF, - space_skipped, location, - finalurl, - (int)loclen - space_skipped, loc); - goto locdone; - } - - /* Not a "safe" URL: do not linkify it */ - -locout: - /* Write the normal output in case of error or unsafe */ - fwrite(location, loclen, 1, stream); - -locdone: - if(u) { - curl_free(finalurl); - curl_free(scheme); - curl_url_cleanup(u); - free(copyloc); - } -} -#endif diff --git a/src/tool_cb_prg.c b/src/tool_cb_prg.c index c6ee6b2822..1af621dbaa 100644 --- a/src/tool_cb_prg.c +++ b/src/tool_cb_prg.c @@ -25,12 +25,9 @@ #include "tool_cfgable.h" #include "tool_cb_prg.h" -#include "tool_util.h" #include "tool_operate.h" #include "terminal.h" -#include "memdebug.h" /* keep this as LAST include */ - #define MAX_BARLENGTH 400 #define MIN_BARLENGTH 20 @@ -38,7 +35,7 @@ my $pi = 3.1415; foreach my $i (1 .. 200) { - printf "%d, ", sin($i/200 * 2 * $pi) * 500000 + 500000; + printf "%d, ", sin($i / 200 * 2 * $pi) * 500000 + 500000; } */ static const int sinus[] = { @@ -56,14 +53,15 @@ static const int sinus[] = { 500046, 484341, 468651, 452993, 437381, 421830, 406357, 390976, 375703, 360552, 345539, 330679, 315985, 301474, 287158, 273052, 259170, 245525, 232132, 219003, 206152, 193590, 181331, 169386, 157768, 146487, 135555, - 124983, 114781, 104959, 95526, 86493, 77868, 69660, 61876, 54525, 47613, - 41147, 35135, 29581, 24491, 19871, 15724, 12056, 8868, 6166, 3951, 2225, - 990, 248, 0, 244, 982, 2212, 3933, 6144, 8842, 12025, 15690, 19832, 24448, - 29534, 35084, 41092, 47554, 54462, 61809, 69589, 77794, 86415, 95445, - 104873, 114692, 124891, 135460, 146389, 157667, 169282, 181224, 193480, - 206039, 218888, 232015, 245406, 259048, 272928, 287032, 301346, 315856, - 330548, 345407, 360419, 375568, 390841, 406221, 421693, 437243, 452854, - 468513, 484202, 499907 + 124983, 114781, 104959, 95526, 86493, 77868, 69660, 61876, 54525, + 47613, 41147, 35135, 29581, 24491, 19871, 15724, 12056, 8868, + 6166, 3951, 2225, 990, 248, 0, 244, 982, 2212, + 3933, 6144, 8842, 12025, 15690, 19832, 24448, 29534, 35084, + 41092, 47554, 54462, 61809, 69589, 77794, 86415, 95445, 104873, + 114692, 124891, 135460, 146389, 157667, 169282, 181224, 193480, 206039, + 218888, 232015, 245406, 259048, 272928, 287032, 301346, 315856, 330548, + 345407, 360419, 375568, 390841, 406221, 421693, 437243, 452854, 468513, + 484202, 499907 }; static void fly(struct ProgressData *bar, bool moved) @@ -80,13 +78,13 @@ static void fly(struct ProgressData *bar, bool moved) memcpy(&buf[bar->bar + 1], "-=O=-", 5); - pos = sinus[bar->tick%200] / (1000000 / check) + 1; + pos = (sinus[bar->tick % 200] / (1000000 / check)) + 1; buf[pos] = '#'; - pos = sinus[(bar->tick + 5)%200] / (1000000 / check) + 1; + pos = (sinus[(bar->tick + 5) % 200] / (1000000 / check)) + 1; buf[pos] = '#'; - pos = sinus[(bar->tick + 10)%200] / (1000000 / check) + 1; + pos = (sinus[(bar->tick + 10) % 200] / (1000000 / check)) + 1; buf[pos] = '#'; - pos = sinus[(bar->tick + 15)%200] / (1000000 / check) + 1; + pos = (sinus[(bar->tick + 15) % 200] / (1000000 / check)) + 1; buf[pos] = '#'; fputs(buf, bar->out); @@ -109,20 +107,13 @@ static void fly(struct ProgressData *bar, bool moved) ** callback for CURLOPT_XFERINFOFUNCTION */ -#if (SIZEOF_CURL_OFF_T < 8) -#error "too small curl_off_t" -#else - /* assume SIZEOF_CURL_OFF_T == 8 */ -# define CURL_OFF_T_MAX 0x7FFFFFFFFFFFFFFF -#endif - static void update_width(struct ProgressData *bar) { int cols = get_terminal_columns(); if(cols > MAX_BARLENGTH) bar->width = MAX_BARLENGTH; else if(cols > MIN_BARLENGTH) - bar->width = (int)cols; + bar->width = cols; else bar->width = MIN_BARLENGTH; } @@ -171,13 +162,13 @@ int tool_progress_cb(void *clientp, if(bar->prev == point) /* progress did not change since last invoke */ return 0; - else if((curlx_timediff(now, bar->prevtime) < 100L) && point < total) + else if((curlx_timediff_ms(now, bar->prevtime) < 100L) && point < total) /* limit progress-bar updating to 10 Hz except when we are at 100% */ return 0; } else { /* total is unknown */ - if(curlx_timediff(now, bar->prevtime) < 100L) + if(curlx_timediff_ms(now, bar->prevtime) < 100L) /* limit progress-bar updating to 10 Hz */ return 0; update_width(bar); @@ -185,7 +176,7 @@ int tool_progress_cb(void *clientp, } } - /* simply count invokes */ + /* count invokes */ bar->calls++; update_width(bar); @@ -203,19 +194,19 @@ int tool_progress_cb(void *clientp, frac = (double)point / (double)total; percent = frac * 100.0; barwidth = bar->width - 7; - num = (size_t) (((double)barwidth) * frac); + num = (size_t)(((double)barwidth) * frac); if(num > MAX_BARLENGTH) num = MAX_BARLENGTH; memset(line, '#', num); line[num] = '\0'; - msnprintf(format, sizeof(format), "\r%%-%ds %%5.1f%%%%", barwidth); -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wformat-nonliteral" + curl_msnprintf(format, sizeof(format), "\r%%-%ds %%5.1f%%%%", barwidth); +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif - fprintf(bar->out, format, line, percent); -#ifdef __clang__ -#pragma clang diagnostic pop + curl_mfprintf(bar->out, format, line, percent); +#ifdef CURL_HAVE_DIAG +#pragma GCC diagnostic pop #endif } fflush(bar->out); @@ -230,13 +221,12 @@ int tool_progress_cb(void *clientp, return 0; } -void progressbarinit(struct ProgressData *bar, - struct OperationConfig *config) +void progressbarinit(struct ProgressData *bar, struct OperationConfig *config) { memset(bar, 0, sizeof(struct ProgressData)); /* pass the resume from value through to the progress function so it can - * display progress towards total file not just the part that is left. */ + * display progress towards total file not the part that is left. */ if(config->use_resume) bar->initial_size = config->resume_from; diff --git a/src/tool_cb_prg.h b/src/tool_cb_prg.h index 1f005dbfc4..9c2dda2d7e 100644 --- a/src/tool_cb_prg.h +++ b/src/tool_cb_prg.h @@ -29,12 +29,12 @@ #define CURL_PROGRESS_BAR 1 struct ProgressData { - int calls; - curl_off_t prev; + int calls; + curl_off_t prev; struct curltime prevtime; - int width; - FILE *out; /* where to write everything to */ - curl_off_t initial_size; + int width; + FILE *out; /* where to write everything to */ + curl_off_t initial_size; unsigned int tick; int bar; int barmove; @@ -42,8 +42,7 @@ struct ProgressData { struct OperationConfig; -void progressbarinit(struct ProgressData *bar, - struct OperationConfig *config); +void progressbarinit(struct ProgressData *bar, struct OperationConfig *config); /* ** callback for CURLOPT_PROGRESSFUNCTION diff --git a/src/tool_cb_rea.c b/src/tool_cb_rea.c index 0a3e9ef0ee..a818d0804d 100644 --- a/src/tool_cb_rea.c +++ b/src/tool_cb_rea.c @@ -26,14 +26,46 @@ #ifdef HAVE_SYS_SELECT_H #include #endif +#ifdef HAVE_POLL_H +#include +#elif defined(HAVE_SYS_POLL_H) +#include +#endif #include "tool_cfgable.h" #include "tool_cb_rea.h" #include "tool_operate.h" -#include "tool_util.h" #include "tool_msgs.h" -#include "memdebug.h" /* keep this as LAST include */ +#ifndef _WIN32 +/* Wait up to a number of milliseconds for socket activity. This function + waits on read activity on a file descriptor that is not a socket which + makes it not work with select() or poll() on Windows. */ +static void waitfd(int waitms, int fd) +{ +#ifdef HAVE_POLL + struct pollfd set; + + set.fd = fd; + set.events = POLLIN; + set.revents = 0; + poll(&set, 1, waitms); +#else + fd_set bits; + struct timeval timeout; + + if(fd < FD_SETSIZE) { + /* wait this long at the most */ + timeout.tv_sec = waitms / 1000; + timeout.tv_usec = (int)((waitms % 1000) * 1000); + + FD_ZERO(&bits); + FD_SET(fd, &bits); + select(fd + 1, &bits, NULL, NULL, &timeout); + } +#endif +} +#endif /* ** callback for CURLOPT_READFUNCTION @@ -53,49 +85,32 @@ size_t tool_read_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) if(config->timeout_ms) { struct curltime now = curlx_now(); - long msdelta = (long)curlx_timediff(now, per->start); + long msdelta = (long)curlx_timediff_ms(now, per->start); if(msdelta > config->timeout_ms) - /* timeout */ - return 0; + return 0; /* timeout */ #ifndef _WIN32 - /* this logic waits on read activity on a file descriptor that is not a - socket which makes it not work with select() on Windows */ else { - fd_set bits; - struct timeval timeout; - long wait = config->timeout_ms - msdelta; - - /* wait this long at the most */ - timeout.tv_sec = wait/1000; - timeout.tv_usec = (int)((wait%1000)*1000); - - FD_ZERO(&bits); -#ifdef __DJGPP__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warith-conversion" -#endif - FD_SET(per->infd, &bits); -#ifdef __DJGPP__ -#pragma GCC diagnostic pop -#endif - if(!select(per->infd + 1, &bits, NULL, NULL, &timeout)) - return 0; /* timeout */ + long w = config->timeout_ms - msdelta; + if(w > INT_MAX) + w = INT_MAX; + waitfd((int)w, per->infd); } #endif } +#ifdef _WIN32 /* If we are on Windows, and using `-T .`, then per->infd points to a socket connected to stdin via a reader thread, and needs to be read with recv() Make sure we are in non-blocking mode and infd is not regular stdin On Linux per->infd should be stdin (0) and the block below should not execute */ if(per->uploadfile && !strcmp(per->uploadfile, ".") && per->infd > 0) { -#if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) - rc = recv(per->infd, buffer, curlx_uztosi(sz * nmemb), 0); +#ifndef CURL_WINDOWS_UWP + rc = sread(per->infd, buffer, curlx_uztosi(sz * nmemb)); if(rc < 0) { if(SOCKERRNO == SOCKEWOULDBLOCK) { - CURL_SETERRNO(0); + errno = 0; config->readbusy = TRUE; return CURL_READFUNC_PAUSE; } @@ -103,15 +118,17 @@ size_t tool_read_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) rc = 0; } #else - warnf("per->infd != 0: FD == %d. This behavior" - " is only supported on desktop Windows", per->infd); + warnf("per->infd != 0: FD == %d. " + "This behavior is only supported on desktop Windows", per->infd); #endif } - else { - rc = read(per->infd, buffer, sz*nmemb); + else +#endif /* _WIN32 */ + { + rc = read(per->infd, buffer, sz * nmemb); if(rc < 0) { if(errno == EAGAIN) { - CURL_SETERRNO(0); + errno = 0; config->readbusy = TRUE; return CURL_READFUNC_PAUSE; } @@ -119,6 +136,7 @@ size_t tool_read_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) rc = 0; } } + if((per->uploadfilesize != -1) && (per->uploadedsofar + rc > per->uploadfilesize)) { /* do not allow uploading more than originally set out to do */ @@ -154,25 +172,9 @@ int tool_readbusy_cb(void *clientp, if(config->readbusy) { if(ulprev == ulnow) { #ifndef _WIN32 - fd_set bits; - struct timeval timeout; - /* wait this long at the most */ - timeout.tv_sec = 0; - timeout.tv_usec = 1000; - - FD_ZERO(&bits); -#ifdef __DJGPP__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Warith-conversion" -#endif - FD_SET(per->infd, &bits); -#ifdef __DJGPP__ -#pragma GCC diagnostic pop -#endif - select(per->infd + 1, &bits, NULL, NULL, &timeout); + waitfd(1, per->infd); #else - /* sleep */ - curlx_wait_ms(1); + curlx_wait_ms(1); /* sleep */ #endif } diff --git a/src/tool_cb_see.c b/src/tool_cb_see.c index 68899c5829..bb33bd2289 100644 --- a/src/tool_cb_see.c +++ b/src/tool_cb_see.c @@ -27,8 +27,6 @@ #include "tool_operate.h" #include "tool_cb_see.h" -#include "memdebug.h" /* keep this as LAST include */ - /* ** callback for CURLOPT_SEEKFUNCTION ** @@ -40,14 +38,14 @@ int tool_seek_cb(void *userdata, curl_off_t offset, int whence) { struct per_transfer *per = userdata; -#if (SIZEOF_CURL_OFF_T > SIZEOF_OFF_T) && !defined(USE_WIN32_LARGE_FILES) +#if (SIZEOF_CURL_OFF_T > SIZEOF_OFF_T) && !defined(_WIN32) /* OUR_MAX_SEEK_L has 'long' data type, OUR_MAX_SEEK_O has 'curl_off_t, both represent the same value. Maximum offset used here when we lseek using a 'long' data type offset */ -#define OUR_MAX_SEEK_L 2147483647L - 1L -#define OUR_MAX_SEEK_O 0x7FFFFFFF - 0x1 +#define OUR_MAX_SEEK_L (2147483647L - 1L) +#define OUR_MAX_SEEK_O (0x7FFFFFFF - 0x1) /* The offset check following here is only interesting if curl_off_t is larger than off_t and we are not using the Win32 large file support @@ -55,21 +53,21 @@ int tool_seek_cb(void *userdata, curl_off_t offset, int whence) if(offset > OUR_MAX_SEEK_O) { /* Some precaution code to work around problems with different data sizes - to allow seeking >32-bit even if off_t is 32-bit. Should be very rare - and is really valid on weirdo-systems. */ + to allow seeking >32-bit even if off_t is 32-bit. Should be rare and + is really valid on weirdo-systems. */ curl_off_t left = offset; if(whence != SEEK_SET) /* this code path does not support other types */ return CURL_SEEKFUNC_FAIL; - if(LSEEK_ERROR == lseek(per->infd, 0, SEEK_SET)) + if(curl_lseek(per->infd, 0, SEEK_SET) == LSEEK_ERROR) /* could not rewind to beginning */ return CURL_SEEKFUNC_FAIL; while(left) { long step = (left > OUR_MAX_SEEK_O) ? OUR_MAX_SEEK_L : (long)left; - if(LSEEK_ERROR == lseek(per->infd, step, SEEK_CUR)) + if(curl_lseek(per->infd, step, SEEK_CUR) == LSEEK_ERROR) /* could not seek forwards the desired amount */ return CURL_SEEKFUNC_FAIL; left -= step; @@ -78,13 +76,9 @@ int tool_seek_cb(void *userdata, curl_off_t offset, int whence) } #endif -#if defined(__AMIGA__) || defined(__MINGW32CE__) - if(LSEEK_ERROR == lseek(per->infd, (off_t)offset, whence)) -#else - if(LSEEK_ERROR == lseek(per->infd, offset, whence)) -#endif - /* could not rewind, the reason is in errno but errno is just not portable - enough and we do not actually care that much why we failed. We will let + if(curl_lseek(per->infd, offset, whence) == LSEEK_ERROR) + /* could not rewind, the reason is in errno but errno is not portable + enough and we do not actually care that much why we failed. We let libcurl know that it may try other means if it wants to. */ return CURL_SEEKFUNC_CANTSEEK; diff --git a/src/tool_cb_see.h b/src/tool_cb_see.h index e7b30a765d..e473223526 100644 --- a/src/tool_cb_see.h +++ b/src/tool_cb_see.h @@ -28,7 +28,6 @@ /* ** callback for CURLOPT_SEEKFUNCTION */ - int tool_seek_cb(void *userdata, curl_off_t offset, int whence); #endif /* HEADER_CURL_TOOL_CB_SEE_H */ diff --git a/src/tool_cb_soc.c b/src/tool_cb_soc.c index 22048ee6bb..0df0a1e6fb 100644 --- a/src/tool_cb_soc.c +++ b/src/tool_cb_soc.c @@ -45,7 +45,7 @@ curl_socket_t tool_socket_open_mptcp_cb(void *clientp, (void)purpose; if(protocol == IPPROTO_TCP) -#if defined(__linux__) +#ifdef __linux__ # ifndef IPPROTO_MPTCP # define IPPROTO_MPTCP 262 # endif @@ -54,5 +54,5 @@ curl_socket_t tool_socket_open_mptcp_cb(void *clientp, return CURL_SOCKET_BAD; #endif - return socket(addr->family, addr->socktype, protocol); + return CURL_SOCKET(addr->family, addr->socktype, protocol); } diff --git a/src/tool_cb_wrt.c b/src/tool_cb_wrt.c index 6fbf80753d..d412a1ea88 100644 --- a/src/tool_cb_wrt.c +++ b/src/tool_cb_wrt.c @@ -23,22 +23,15 @@ ***************************************************************************/ #include "tool_setup.h" -#ifdef HAVE_FCNTL_H -/* for open() */ -#include -#endif - #include "tool_cfgable.h" #include "tool_msgs.h" #include "tool_cb_wrt.h" #include "tool_operate.h" -#include "memdebug.h" /* keep this as LAST include */ - #ifdef _WIN32 -#define OPENMODE S_IREAD | S_IWRITE +#define OPENMODE (_S_IREAD | _S_IWRITE) #else -#define OPENMODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH +#define OPENMODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) #endif /* create/open a local file for writing, return TRUE on success */ @@ -55,36 +48,42 @@ bool tool_create_output_file(struct OutStruct *outs, (config->file_clobber_mode == CLOBBER_DEFAULT && !outs->is_cd_filename)) { /* open file for writing */ - file = fopen(fname, "wb"); + file = curlx_fopen(fname, "wb"); } else { int fd; do { - fd = open(fname, O_CREAT | O_WRONLY | O_EXCL | CURL_O_BINARY, OPENMODE); + fd = curlx_open(fname, O_CREAT | O_WRONLY | O_EXCL | CURL_O_BINARY, + OPENMODE); /* Keep retrying in the hope that it is not interrupted sometime */ /* !checksrc! disable ERRNOVAR 1 */ } while(fd == -1 && errno == EINTR); if(config->file_clobber_mode == CLOBBER_NEVER && fd == -1) { int next_num = 1; struct dynbuf fbuffer; + char *newfile; curlx_dyn_init(&fbuffer, 1025); /* !checksrc! disable ERRNOVAR 1 */ while(fd == -1 && /* have not successfully opened a file */ (errno == EEXIST || errno == EISDIR) && /* because we keep having files that already exist */ - next_num < 100 /* and we have not reached the retry limit */ ) { + next_num < 100 /* and we have not reached the retry limit */) { curlx_dyn_reset(&fbuffer); if(curlx_dyn_addf(&fbuffer, "%s.%d", fname, next_num)) return FALSE; next_num++; do { - fd = open(curlx_dyn_ptr(&fbuffer), - O_CREAT | O_WRONLY | O_EXCL | CURL_O_BINARY, OPENMODE); + fd = curlx_open(curlx_dyn_ptr(&fbuffer), + O_CREAT | O_WRONLY | O_EXCL | CURL_O_BINARY, + OPENMODE); /* Keep retrying in the hope that it is not interrupted sometime */ } while(fd == -1 && errno == EINTR); } - outs->filename = curlx_dyn_ptr(&fbuffer); /* remember the new one */ - outs->alloc_filename = TRUE; + newfile = curlx_dyn_ptr(&fbuffer); /* remember the new one */ + if(newfile) { + outs->filename = newfile; + outs->alloc_filename = TRUE; + } } /* An else statement to not overwrite existing files and not retry with new numbered names (which would cover @@ -92,17 +91,19 @@ bool tool_create_output_file(struct OutStruct *outs, is not needed because we would have failed earlier, in the while loop and `fd` would now be -1 */ if(fd != -1) { - file = fdopen(fd, "wb"); + file = curlx_fdopen(fd, "wb"); if(!file) - close(fd); + curlx_close(fd); } } if(!file) { - warnf("Failed to open the file %s: %s", fname, strerror(errno)); + char errbuf[STRERROR_LEN]; + warnf("Failed to open the file %s: %s", fname, + curlx_strerror(errno, errbuf, sizeof(errbuf))); return FALSE; } - outs->s_isreg = TRUE; + outs->regular_file = TRUE; outs->fopened = TRUE; outs->stream = file; outs->bytes = 0; @@ -110,7 +111,7 @@ bool tool_create_output_file(struct OutStruct *outs, return TRUE; } -#if defined(_WIN32) && !defined(UNDER_CE) +#ifdef _WIN32 static size_t win_console(intptr_t fhnd, struct OutStruct *outs, char *buffer, size_t bytes, size_t *retp) @@ -161,12 +162,12 @@ static size_t win_console(intptr_t fhnd, struct OutStruct *outs, } if(complete) { - WCHAR prefix[3] = {0}; /* UTF-16 (1-2 WCHARs) + NUL */ + WCHAR prefix[3] = { 0 }; /* UTF-16 (1-2 WCHARs) + NUL */ if(MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)outs->utf8seq, -1, prefix, CURL_ARRAYSIZE(prefix))) { DEBUGASSERT(prefix[2] == L'\0'); - if(!WriteConsoleW((HANDLE) fhnd, prefix, prefix[1] ? 2 : 1, + if(!WriteConsoleW((HANDLE)fhnd, prefix, prefix[1] ? 2 : 1, &chars_written, NULL)) { return CURL_WRITEFUNC_ERROR; } @@ -212,8 +213,8 @@ static size_t win_console(intptr_t fhnd, struct OutStruct *outs, /* grow the buffer if needed */ if(len > global->term.len) { - wchar_t *buf = (wchar_t *) realloc(global->term.buf, - len * sizeof(wchar_t)); + wchar_t *buf = (wchar_t *)curlx_realloc(global->term.buf, + len * sizeof(wchar_t)); if(!buf) return CURL_WRITEFUNC_ERROR; global->term.len = len; @@ -234,7 +235,7 @@ static size_t win_console(intptr_t fhnd, struct OutStruct *outs, *retp = bytes; return 0; } -#endif +#endif /* _WIN32 */ /* ** callback for CURLOPT_WRITEFUNCTION @@ -247,8 +248,8 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) struct OutStruct *outs = &per->outs; struct OperationConfig *config = per->config; size_t bytes = sz * nmemb; - bool is_tty = global->isatty; -#if defined(_WIN32) && !defined(UNDER_CE) + bool is_tty = (bool)global->isatty; +#ifdef _WIN32 CONSOLE_SCREEN_BUFFER_INFO console_info; intptr_t fhnd; #endif @@ -285,7 +286,7 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) /* regular file */ if(!*outs->filename) check_fails = TRUE; - if(!outs->s_isreg) + if(!outs->regular_file) check_fails = TRUE; if(outs->fopened && !outs->stream) check_fails = TRUE; @@ -296,7 +297,7 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) } else { /* standard stream */ - if(!outs->stream || outs->s_isreg || outs->fopened) + if(!outs->stream || outs->regular_file || outs->fopened) check_fails = TRUE; if(outs->alloc_filename || outs->is_cd_filename || outs->init) check_fails = TRUE; @@ -306,7 +307,7 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) return CURL_WRITEFUNC_ERROR; } } -#endif +#endif /* DEBUGBUILD */ if(!outs->stream && !tool_create_output_file(outs, per->config)) return CURL_WRITEFUNC_ERROR; @@ -322,7 +323,7 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) } } -#if defined(_WIN32) && !defined(UNDER_CE) +#ifdef _WIN32 fhnd = _get_osfhandle(fileno(outs->stream)); /* if Windows console then UTF-8 must be converted to UTF-16 */ if(isatty(fileno(outs->stream)) && diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index 0321848b0d..515df19073 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -23,12 +23,14 @@ ***************************************************************************/ #include "tool_setup.h" +#include + #include "tool_cfgable.h" #include "tool_formparse.h" +#include "tool_libinfo.h" #include "tool_paramhlp.h" #include "tool_main.h" #include "tool_msgs.h" -#include "memdebug.h" /* keep this as LAST include */ static struct GlobalConfig globalconf; struct GlobalConfig *global; @@ -36,7 +38,7 @@ struct GlobalConfig *global; struct OperationConfig *config_alloc(void) { struct OperationConfig *config = - calloc(1, sizeof(struct OperationConfig)); + curlx_calloc(1, sizeof(struct OperationConfig)); if(!config) return NULL; @@ -52,7 +54,6 @@ struct OperationConfig *config_alloc(void) config->ftp_skip_ip = TRUE; config->file_clobber_mode = CLOBBER_DEFAULT; config->upload_flags = CURLULFLAG_SEEN; - config->retry_delay_ms = RETRY_SLEEP_DEFAULT; curlx_dyn_init(&config->postdata, MAX_FILE2MEMORY); return config; } @@ -61,57 +62,57 @@ static void free_config_fields(struct OperationConfig *config) { struct getout *urlnode; - tool_safefree(config->useragent); - tool_safefree(config->altsvc); - tool_safefree(config->hsts); - tool_safefree(config->haproxy_clientip); + curlx_safefree(config->useragent); + curlx_safefree(config->altsvc); + curlx_safefree(config->hsts); + curlx_safefree(config->haproxy_clientip); curl_slist_free_all(config->cookies); - tool_safefree(config->cookiejar); + curlx_safefree(config->cookiejar); curl_slist_free_all(config->cookiefiles); curlx_dyn_free(&config->postdata); - tool_safefree(config->query); - tool_safefree(config->referer); + curlx_safefree(config->query); + curlx_safefree(config->referer); - tool_safefree(config->headerfile); - tool_safefree(config->ftpport); - tool_safefree(config->iface); + curlx_safefree(config->headerfile); + curlx_safefree(config->ftpport); + curlx_safefree(config->iface); - tool_safefree(config->range); + curlx_safefree(config->range); - tool_safefree(config->userpwd); - tool_safefree(config->tls_username); - tool_safefree(config->tls_password); - tool_safefree(config->tls_authtype); - tool_safefree(config->proxy_tls_username); - tool_safefree(config->proxy_tls_password); - tool_safefree(config->proxy_tls_authtype); - tool_safefree(config->proxyuserpwd); - tool_safefree(config->proxy); + curlx_safefree(config->userpwd); + curlx_safefree(config->tls_username); + curlx_safefree(config->tls_password); + curlx_safefree(config->tls_authtype); + curlx_safefree(config->proxy_tls_username); + curlx_safefree(config->proxy_tls_password); + curlx_safefree(config->proxy_tls_authtype); + curlx_safefree(config->proxyuserpwd); + curlx_safefree(config->proxy); - tool_safefree(config->dns_ipv6_addr); - tool_safefree(config->dns_ipv4_addr); - tool_safefree(config->dns_interface); - tool_safefree(config->dns_servers); + curlx_safefree(config->dns_ipv6_addr); + curlx_safefree(config->dns_ipv4_addr); + curlx_safefree(config->dns_interface); + curlx_safefree(config->dns_servers); - tool_safefree(config->noproxy); + curlx_safefree(config->noproxy); - tool_safefree(config->mail_from); + curlx_safefree(config->mail_from); curl_slist_free_all(config->mail_rcpt); - tool_safefree(config->mail_auth); + curlx_safefree(config->mail_auth); - tool_safefree(config->netrc_file); - tool_safefree(config->output_dir); - tool_safefree(config->proto_str); - tool_safefree(config->proto_redir_str); + curlx_safefree(config->netrc_file); + curlx_safefree(config->output_dir); + curlx_safefree(config->proto_str); + curlx_safefree(config->proto_redir_str); urlnode = config->url_list; while(urlnode) { struct getout *next = urlnode->next; - tool_safefree(urlnode->url); - tool_safefree(urlnode->outfile); - tool_safefree(urlnode->infile); - tool_safefree(urlnode); + curlx_safefree(urlnode->url); + curlx_safefree(urlnode->outfile); + curlx_safefree(urlnode->infile); + curlx_safefree(urlnode); urlnode = next; } config->url_list = NULL; @@ -120,47 +121,48 @@ static void free_config_fields(struct OperationConfig *config) config->url_out = NULL; #ifndef CURL_DISABLE_IPFS - tool_safefree(config->ipfs_gateway); -#endif /* !CURL_DISABLE_IPFS */ - tool_safefree(config->doh_url); - tool_safefree(config->cipher_list); - tool_safefree(config->proxy_cipher_list); - tool_safefree(config->cipher13_list); - tool_safefree(config->proxy_cipher13_list); - tool_safefree(config->cert); - tool_safefree(config->proxy_cert); - tool_safefree(config->cert_type); - tool_safefree(config->proxy_cert_type); - tool_safefree(config->cacert); - tool_safefree(config->login_options); - tool_safefree(config->proxy_cacert); - tool_safefree(config->capath); - tool_safefree(config->proxy_capath); - tool_safefree(config->crlfile); - tool_safefree(config->pinnedpubkey); - tool_safefree(config->proxy_pinnedpubkey); - tool_safefree(config->proxy_crlfile); - tool_safefree(config->key); - tool_safefree(config->proxy_key); - tool_safefree(config->key_type); - tool_safefree(config->proxy_key_type); - tool_safefree(config->key_passwd); - tool_safefree(config->proxy_key_passwd); - tool_safefree(config->pubkey); - tool_safefree(config->hostpubmd5); - tool_safefree(config->hostpubsha256); - tool_safefree(config->engine); - tool_safefree(config->etag_save_file); - tool_safefree(config->etag_compare_file); - tool_safefree(config->ssl_ec_curves); - tool_safefree(config->request_target); - tool_safefree(config->customrequest); - tool_safefree(config->krblevel); - tool_safefree(config->oauth_bearer); - tool_safefree(config->sasl_authzid); - tool_safefree(config->unix_socket_path); - tool_safefree(config->writeout); - tool_safefree(config->proto_default); + curlx_safefree(config->ipfs_gateway); +#endif + curlx_safefree(config->doh_url); + curlx_safefree(config->cipher_list); + curlx_safefree(config->proxy_cipher_list); + curlx_safefree(config->cipher13_list); + curlx_safefree(config->proxy_cipher13_list); + curlx_safefree(config->cert); + curlx_safefree(config->proxy_cert); + curlx_safefree(config->cert_type); + curlx_safefree(config->proxy_cert_type); + curlx_safefree(config->cacert); + curlx_safefree(config->login_options); + curlx_safefree(config->proxy_cacert); + curlx_safefree(config->capath); + curlx_safefree(config->proxy_capath); + curlx_safefree(config->crlfile); + curlx_safefree(config->pinnedpubkey); + curlx_safefree(config->proxy_pinnedpubkey); + curlx_safefree(config->proxy_crlfile); + curlx_safefree(config->key); + curlx_safefree(config->proxy_key); + curlx_safefree(config->key_type); + curlx_safefree(config->proxy_key_type); + curlx_safefree(config->key_passwd); + curlx_safefree(config->proxy_key_passwd); + curlx_safefree(config->pubkey); + curlx_safefree(config->hostpubmd5); + curlx_safefree(config->hostpubsha256); + curlx_safefree(config->engine); + curlx_safefree(config->etag_save_file); + curlx_safefree(config->etag_compare_file); + curlx_safefree(config->ssl_ec_curves); + curlx_safefree(config->ssl_signature_algorithms); + curlx_safefree(config->request_target); + curlx_safefree(config->customrequest); + curlx_safefree(config->krblevel); + curlx_safefree(config->oauth_bearer); + curlx_safefree(config->sasl_authzid); + curlx_safefree(config->unix_socket_path); + curlx_safefree(config->writeout); + curlx_safefree(config->proto_default); curl_slist_free_all(config->quote); curl_slist_free_all(config->postquote); @@ -179,17 +181,16 @@ static void free_config_fields(struct OperationConfig *config) curl_slist_free_all(config->resolve); curl_slist_free_all(config->connect_to); - tool_safefree(config->preproxy); - tool_safefree(config->proxy_service_name); - tool_safefree(config->service_name); - tool_safefree(config->ftp_account); - tool_safefree(config->ftp_alternative_to_user); - tool_safefree(config->aws_sigv4); - tool_safefree(config->proto_str); - tool_safefree(config->proto_redir_str); - tool_safefree(config->ech); - tool_safefree(config->ech_config); - tool_safefree(config->ech_public); + curlx_safefree(config->preproxy); + curlx_safefree(config->proxy_service_name); + curlx_safefree(config->service_name); + curlx_safefree(config->ftp_account); + curlx_safefree(config->ftp_alternative_to_user); + curlx_safefree(config->aws_sigv4); + curlx_safefree(config->ech); + curlx_safefree(config->ech_config); + curlx_safefree(config->ech_public); + curlx_safefree(config->knownhosts); } void config_free(struct OperationConfig *config) @@ -201,12 +202,105 @@ void config_free(struct OperationConfig *config) struct OperationConfig *prev = last->prev; free_config_fields(last); - free(last); + curlx_free(last); last = prev; } } +#ifdef CURL_DEBUG_GLOBAL_MEM + +#ifdef CURL_MEMDEBUG +#error "curl_global_init_mem() testing does not work with memdebug debugging" +#endif + +/* + * This is the custom memory functions handed to curl when we run special test + * round to verify them. + * + * The main point is to make sure that what is returned is different than what + * the regular memory functions return so that mixup will trigger problems. + * + * This test setup currently only works when building with a *shared* libcurl + * and not static, as in the latter case the tool and the library share some of + * the functions in incompatible ways. + */ + +/* + * This code appends this extra chunk of memory in front of every allocation + * done by libcurl with the only purpose to cause trouble when using the wrong + * free function on memory. + */ +struct extramem { + size_t extra; + union { + curl_off_t o; + double d; + void *p; + } mem[1]; +}; + +static void *custom_calloc(size_t wanted_nmemb, size_t wanted_size) +{ + struct extramem *m; + size_t sz = wanted_size * wanted_nmemb; + sz += sizeof(struct extramem); + m = curlx_calloc(1, sz); + if(m) + return m->mem; + return NULL; +} + +static void *custom_malloc(size_t wanted_size) +{ + struct extramem *m; + size_t sz = wanted_size + sizeof(struct extramem); + m = curlx_malloc(sz); + if(m) + return m->mem; + return NULL; +} + +static char *custom_strdup(const char *ptr) +{ + struct extramem *m; + size_t len = strlen(ptr); + size_t sz = len + sizeof(struct extramem); + m = curlx_malloc(sz); + if(m) { + char *p = (char *)m->mem; + /* since strcpy is banned, we do memcpy */ + memcpy(p, ptr, len); + p[len] = 0; + return (char *)m->mem; + } + return NULL; +} + +static void *custom_realloc(void *ptr, size_t size) +{ + struct extramem *m = NULL; + size_t sz = size + sizeof(struct extramem); + if(ptr) + /* if given a pointer, figure out the original */ + ptr = (void *)((char *)ptr - offsetof(struct extramem, mem)); + m = curlx_realloc(ptr, sz); + if(m) + return m->mem; + return NULL; +} + +static void custom_free(void *ptr) +{ + struct extramem *m = NULL; + if(ptr) { + m = (void *)((char *)ptr - offsetof(struct extramem, mem)); + curlx_free(m); + } +} + +#endif + /* * This is the main global constructor for the app. Call this before * _any_ libcurl usage. If this fails, *NO* libcurl functions may be @@ -231,19 +325,25 @@ CURLcode globalconf_init(void) global->first = global->last = config_alloc(); if(global->first) { /* Perform the libcurl initialization */ +#ifdef CURL_DEBUG_GLOBAL_MEM + result = curl_global_init_mem(CURL_GLOBAL_ALL, custom_malloc, custom_free, + custom_realloc, custom_strdup, + custom_calloc); +#else result = curl_global_init(CURL_GLOBAL_DEFAULT); +#endif if(!result) { /* Get information about libcurl */ result = get_libcurl_info(); if(result) { errorf("error retrieving curl library information"); - free(global->first); + curlx_free(global->first); } } else { errorf("error initializing curl library"); - free(global->first); + curlx_free(global->first); } } else { @@ -256,15 +356,16 @@ CURLcode globalconf_init(void) static void free_globalconfig(void) { - tool_safefree(global->trace_dump); + curlx_safefree(global->trace_dump); if(global->trace_fopened && global->trace_stream) - fclose(global->trace_stream); + curlx_fclose(global->trace_stream); global->trace_stream = NULL; - tool_safefree(global->libcurl); -#if defined(_WIN32) && !defined(UNDER_CE) - free(global->term.buf); + curlx_safefree(global->ssl_sessions); + curlx_safefree(global->libcurl); +#ifdef _WIN32 + curlx_free(global->term.buf); #endif } diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 5e7222a8a8..0f8bc6fe08 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -23,44 +23,15 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - -#include #include "tool_setup.h" + #include "tool_sdecls.h" #include "tool_urlglob.h" #include "var.h" -/* the type we use for storing a single boolean bit */ -#ifndef BIT -#ifdef _MSC_VER -#define BIT(x) bool x -#else -#define BIT(x) unsigned int x:1 -#endif -#endif +#define MAX_CONFIG_LINE_LENGTH (10 * 1024 * 1024) -/* make the tool use the libcurl *printf family */ -# undef printf -# undef fprintf -# undef msnprintf -# undef vprintf -# undef vfprintf -# undef mvsnprintf -# undef aprintf -# undef vaprintf -# define printf curl_mprintf -# define fprintf curl_mfprintf -# define msnprintf curl_msnprintf -# define vprintf curl_mvprintf -# define vfprintf curl_mvfprintf -# define mvsnprintf curl_mvsnprintf -# define aprintf curl_maprintf -# define vaprintf curl_mvaprintf - -#define checkprefix(a,b) curl_strnequal(b, STRCONST(a)) - -#define tool_safefree(ptr) \ - do { free((ptr)); (ptr) = NULL;} while(0) +#define checkprefix(a, b) curl_strnequal(b, STRCONST(a)) extern struct GlobalConfig *global; @@ -68,7 +39,6 @@ struct State { struct getout *urlnode; struct URLGlob inglob; struct URLGlob urlglob; - char *httpgetfields; char *uploadfile; curl_off_t upnum; /* number of files to upload */ curl_off_t upidx; /* index for upload glob */ @@ -76,6 +46,10 @@ struct State { curl_off_t urlidx; /* index for globbed URLs */ }; +#define FAIL_NONE 0 +#define FAIL_WITH_BODY 1 +#define FAIL_WO_BODY 2 + struct OperationConfig { struct dynbuf postdata; char *useragent; @@ -112,6 +86,7 @@ struct OperationConfig { char *proxyuserpwd; char *proxy; char *noproxy; + char *knownhosts; char *mail_from; struct curl_slist *mail_rcpt; char *mail_auth; @@ -125,7 +100,7 @@ struct OperationConfig { size_t num_urls; /* number of URLs added to the list */ #ifndef CURL_DISABLE_IPFS char *ipfs_gateway; -#endif /* !CURL_DISABLE_IPFS */ +#endif char *doh_url; char *cipher_list; char *proxy_cipher_list; @@ -161,6 +136,7 @@ struct OperationConfig { char *krblevel; char *request_target; char *writeout; /* %-styled format string to output */ + char *httpgetfields; struct curl_slist *quote; struct curl_slist *postquote; struct curl_slist *prequote; @@ -209,7 +185,8 @@ struct OperationConfig { long httpversion; unsigned long socks5_auth;/* auth bitmask for socks5 proxies */ long req_retry; /* number of retries */ - long retry_delay_ms; /* delay between retries (in milliseconds) */ + uint32_t retry_delay_ms; /* delay between retries (in milliseconds), 0 means + increase exponentially */ long retry_maxtime_ms; /* maximum time to keep retrying */ unsigned long mime_options; /* Mime option flags. */ @@ -231,7 +208,7 @@ struct OperationConfig { by using the default behavior for -o, -O, and -J. If those options would have overwritten files, like -o and -O would, then overwrite them. In the case of - -J, this will not overwrite any files. */ + -J, this does not overwrite any files. */ CLOBBER_NEVER, /* If the file exists, always fail */ CLOBBER_ALWAYS /* If the file exists, always overwrite it */ } file_clobber_mode; @@ -239,6 +216,7 @@ struct OperationConfig { unsigned short porttouse; unsigned char ssl_version; /* 0 - 4, 0 being default */ unsigned char ssl_version_max; /* 0 - 4, 0 being default */ + unsigned char fail; /* NONE, with body, without body */ BIT(remote_name_all); /* --remote-name-all */ BIT(remote_time); BIT(cookiesession); /* new session? */ @@ -257,11 +235,9 @@ struct OperationConfig { BIT(ftp_append); /* APPE on ftp */ BIT(use_ascii); /* select ASCII or text transfer */ BIT(autoreferer); /* automatically set referer */ - BIT(failonerror); /* fail on (HTTP) errors */ - BIT(failwithbody); /* fail on (HTTP) errors but still store body */ BIT(show_headers); /* show headers to data output */ BIT(no_body); /* do not get the body */ - BIT(dirlistonly); /* only get the FTP dir list */ + BIT(dirlistonly); /* only get the FTP directory list */ BIT(unrestricted_auth); /* Continue to send authentication (user+password) when following redirects, even when hostname changed */ @@ -339,7 +315,7 @@ struct OperationConfig { BIT(skip_existing); }; -#if defined(_WIN32) && !defined(UNDER_CE) +#ifdef _WIN32 struct termout { wchar_t *buf; DWORD len; @@ -352,13 +328,11 @@ struct GlobalConfig { FILE *trace_stream; char *libcurl; /* Output libcurl code to this filename */ char *ssl_sessions; /* file to load/save SSL session tickets */ - char *knownhosts; /* known host path, if set. curl_free() - this */ struct tool_var *variables; struct OperationConfig *first; struct OperationConfig *current; struct OperationConfig *last; -#if defined(_WIN32) && !defined(UNDER_CE) +#ifdef _WIN32 struct termout term; #endif timediff_t ms_per_transfer; /* start next transfer after (at least) this diff --git a/src/tool_dirhie.c b/src/tool_dirhie.c index 91cc1b60cb..894c638034 100644 --- a/src/tool_dirhie.c +++ b/src/tool_dirhie.c @@ -23,20 +23,16 @@ ***************************************************************************/ #include "tool_setup.h" -#if defined(_WIN32) && !defined(UNDER_CE) -# include -#endif - #include "tool_dirhie.h" #include "tool_msgs.h" -#include "memdebug.h" /* keep this as LAST include */ - -#if defined(_WIN32) || (defined(MSDOS) && !defined(__DJGPP__)) -# define mkdir(x,y) (mkdir)((x)) -# ifndef F_OK -# define F_OK 0 -# endif +#ifdef _WIN32 +# include +# define toolx_mkdir(x, y) _mkdir(x) +#elif defined(MSDOS) && !defined(__DJGPP__) +# define toolx_mkdir(x, y) mkdir(x) +#else +# define toolx_mkdir mkdir #endif static void show_dir_errno(const char *name) @@ -60,7 +56,7 @@ static void show_dir_errno(const char *name) #endif #ifdef ENOSPC case ENOSPC: - errorf("No space left on the file system that will " + errorf("No space left on the file system that would " "contain the directory %s", name); break; #endif @@ -114,7 +110,7 @@ CURLcode create_dir_hierarchy(const char *outfile) exist, since we would be creating it erroneously. eg if outfile is X:\foo\bar\filename then do not mkdir X: This logic takes into account unsupported drives !:, 1:, etc. */ - if(len > 1 && (outfile[1]==':')) + if(len > 1 && (outfile[1] == ':')) skip = TRUE; } #endif @@ -126,7 +122,7 @@ CURLcode create_dir_hierarchy(const char *outfile) /* Create directory. Ignore access denied error to allow traversal. */ /* !checksrc! disable ERRNOVAR 1 */ - if(!skip && (mkdir(curlx_dyn_ptr(&dirbuf), (mode_t)0000750) == -1) && + if(!skip && (toolx_mkdir(curlx_dyn_ptr(&dirbuf), (mode_t)0000750) == -1) && (errno != EACCES) && (errno != EEXIST)) { show_dir_errno(curlx_dyn_ptr(&dirbuf)); result = CURLE_WRITE_ERROR; diff --git a/src/tool_dirhie.h b/src/tool_dirhie.h index cb59208d9f..d48615a0c0 100644 --- a/src/tool_dirhie.h +++ b/src/tool_dirhie.h @@ -24,8 +24,7 @@ * ***************************************************************************/ #include "tool_setup.h" -#include "tool_cfgable.h" -CURLcode create_dir_hierarchy(const char *outfileo); +CURLcode create_dir_hierarchy(const char *outfile); #endif /* HEADER_CURL_TOOL_DIRHIE_H */ diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 4ed90ba8c5..4b2a2a34b3 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -25,85 +25,132 @@ #if defined(_WIN32) || defined(MSDOS) -#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME) -# include -#endif +#include "curlx/basename.h" /* for curlx_basename() */ +#include "curlx/version_win32.h" /* for curlx_verify_windows_version() */ #ifdef _WIN32 -# include # include -# include "tool_cfgable.h" -# include "tool_libinfo.h" +#elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */ +# define CURL_USE_LFN(f) 0 /* long filenames never available */ +#elif defined(__DJGPP__) +# include /* for _use_lfn(f) prototype */ +# define CURL_USE_LFN(f) _use_lfn(f) #endif -#include "tool_bname.h" +#include "tool_cfgable.h" #include "tool_doswin.h" #include "tool_msgs.h" -#include "memdebug.h" /* keep this as LAST include */ - -#ifdef _WIN32 -# undef PATH_MAX -# define PATH_MAX MAX_PATH -#elif !defined(__DJGPP__) || (__DJGPP__ < 2) /* DJGPP 2.0 has _use_lfn() */ -# define _use_lfn(f) (0) /* long filenames never available */ -#elif defined(__DJGPP__) -# include /* _use_lfn(f) prototype */ -#endif - #ifdef MSDOS #ifndef S_ISCHR # ifdef S_IFCHR # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) # else -# define S_ISCHR(m) (0) /* cannot tell if file is a device */ +# define S_ISCHR(m) 0 /* cannot tell if file is a device */ # endif #endif -/* only used by msdosify() */ -static SANITIZEcode truncate_dryrun(const char *path, - const size_t truncate_pos); -static SANITIZEcode msdosify(char **const sanitized, const char *file_name, - int flags); -#endif -static SANITIZEcode rename_if_reserved_dos(char **const sanitized, - const char *file_name, - int flags); +/* The functions msdosify, rename_if_dos_device_name and __crt0_glob_function + * were taken with modification from the DJGPP port of tar 1.12. They use + * algorithms originally from DJTAR. + */ +#ifdef __DJGPP__ +/* + * Disable program default argument globbing. We do it on our own. + */ +char **__crt0_glob_function(char *arg) +{ + (void)arg; + return (char **)0; +} +#endif /* -Sanitize a file or path name. - -All banned characters are replaced by underscores, for example: -f?*foo => f__foo -f:foo::$DATA => f_foo__$DATA -f:\foo:bar => f__foo_bar -f:\foo:bar => f:\foo:bar (flag SANITIZE_ALLOW_PATH) - -This function was implemented according to the guidelines in 'Naming Files, -Paths, and Namespaces' section 'Naming Conventions'. -https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx - -Flags ------ -SANITIZE_ALLOW_PATH: Allow path separators and colons. -Without this flag path separators and colons are sanitized. - -SANITIZE_ALLOW_RESERVED: Allow reserved device names. -Without this flag a reserved device name is renamed (COM1 => _COM1) unless it -is in a UNC prefixed path. - -Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name. -Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL. -*/ -SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name, - int flags) + * Test if truncating a path to a file leaves at least a single character + * in the filename. Filenames suffixed by an alternate data stream cannot be + * truncated. This performs a dry run, nothing is modified. + * + * Good truncate_pos 9: C:\foo\bar => C:\foo\ba + * Good truncate_pos 6: C:\foo => C:\foo + * Good truncate_pos 5: C:\foo => C:\fo + * Bad* truncate_pos 5: C:foo => C:foo + * Bad truncate_pos 5: C:\foo:ads => C:\fo + * Bad truncate_pos 9: C:\foo:ads => C:\foo:ad + * Bad truncate_pos 5: C:\foo\bar => C:\fo + * Bad truncate_pos 5: C:\foo\ => C:\fo + * Bad truncate_pos 7: C:\foo\ => C:\foo\ + * Error truncate_pos 7: C:\foo => (pos out of range) + * Bad truncate_pos 1: C:\foo\ => C + * + * * C:foo is ambiguous, C could end up being a drive or file therefore + * something like C:superlongfilename cannot be truncated. + * + * Returns + * SANITIZE_ERR_OK: Good -- 'path' can be truncated + * SANITIZE_ERR_INVALID_PATH: Bad -- 'path' cannot be truncated + * != SANITIZE_ERR_OK && != SANITIZE_ERR_INVALID_PATH: Error + */ +static SANITIZEcode truncate_dryrun(const char *path, + const size_t truncate_pos) { - char *p, *target; size_t len; - SANITIZEcode sc; - size_t max_sanitized_len; + + if(!path) + return SANITIZE_ERR_BAD_ARGUMENT; + + len = strlen(path); + + if(truncate_pos > len) + return SANITIZE_ERR_BAD_ARGUMENT; + + if(!len || !truncate_pos) + return SANITIZE_ERR_INVALID_PATH; + + if(strpbrk(&path[truncate_pos - 1], "\\/:")) + return SANITIZE_ERR_INVALID_PATH; + + /* C:\foo can be truncated but C:\foo:ads cannot */ + if(truncate_pos > 1) { + const char *p = &path[truncate_pos - 1]; + do { + --p; + if(*p == ':') + return SANITIZE_ERR_INVALID_PATH; + } while(p != path && *p != '\\' && *p != '/'); + } + + return SANITIZE_ERR_OK; +} + +/* + * Extra sanitization MS-DOS for file_name. + * + * This is a supporting function for sanitize_file_name. + * + * Warning: This is an MS-DOS legacy function and was purposely written in + * a way that some path information may pass through. For example drive letter + * names (C:, D:, etc) are allowed to pass through. For sanitizing a filename + * use sanitize_file_name. + * + * Success: SANITIZE_ERR_OK *sanitized points to a sanitized copy of file_name. + * Failure: != SANITIZE_ERR_OK *sanitized is NULL. + */ +static SANITIZEcode msdosify(char ** const sanitized, const char *file_name, + int flags) +{ + char dos_name[PATH_MAX]; + static const char illegal_chars_dos[] = + ".+, ;=[]" /* illegal in DOS */ + "|<>/\\\":?*"; /* illegal in DOS & W95 */ + static const char *illegal_chars_w95 = &illegal_chars_dos[8]; + int idx, dot_idx; + const char *s = file_name; + char *d = dos_name; + const char * const dlimit = dos_name + sizeof(dos_name) - 1; + const char *illegal_aliens = illegal_chars_dos; + size_t len = sizeof(illegal_chars_dos) - 1; if(!sanitized) return SANITIZE_ERR_BAD_ARGUMENT; @@ -113,32 +160,303 @@ SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name, if(!file_name) return SANITIZE_ERR_BAD_ARGUMENT; - if(flags & SANITIZE_ALLOW_PATH) { -#ifndef MSDOS - if(file_name[0] == '\\' && file_name[1] == '\\') - /* UNC prefixed path \\ (eg \\?\C:\foo) */ - max_sanitized_len = 32767-1; - else -#endif - max_sanitized_len = PATH_MAX-1; - } - else - /* The maximum length of a filename. FILENAME_MAX is often the same as - PATH_MAX, in other words it is 260 and does not discount the path - information therefore we should not use it. */ - max_sanitized_len = (PATH_MAX-1 > 255) ? 255 : PATH_MAX-1; - - len = strlen(file_name); - if(len > max_sanitized_len) + if(strlen(file_name) > PATH_MAX - 1) return SANITIZE_ERR_INVALID_PATH; - target = strdup(file_name); + /* Support for Windows 9X VFAT systems, when available. */ + if(CURL_USE_LFN(file_name)) { + illegal_aliens = illegal_chars_w95; + len -= (illegal_chars_w95 - illegal_chars_dos); + } + + /* Get past the drive letter, if any. */ + if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') { + *d++ = *s++; + *d = (flags & SANITIZE_ALLOW_PATH) ? ':' : '_'; + ++d; + ++s; + } + + for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) { + if(memchr(illegal_aliens, *s, len)) { + + if((flags & SANITIZE_ALLOW_PATH) && *s == ':') + *d = ':'; + else if((flags & SANITIZE_ALLOW_PATH) && (*s == '/' || *s == '\\')) + *d = *s; + /* Dots are special: DOS does not allow them as the leading character, + and a filename cannot have more than a single dot. We leave the + first non-leading dot alone, unless it comes too close to the + beginning of the name: we want sh.lex.c to become sh_lex.c, not + sh.lex-c. */ + else if(*s == '.') { + if((flags & SANITIZE_ALLOW_PATH) && idx == 0 && + (s[1] == '/' || s[1] == '\\' || + (s[1] == '.' && (s[2] == '/' || s[2] == '\\')))) { + /* Copy "./" and "../" verbatim. */ + *d++ = *s++; + if(d == dlimit) + break; + if(*s == '.') { + *d++ = *s++; + if(d == dlimit) + break; + } + *d = *s; + } + else if(idx == 0) + *d = '_'; + else if(dot_idx >= 0) { + if(dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */ + d[dot_idx - idx] = '_'; /* replace previous dot */ + *d = '.'; + } + else + *d = '-'; + } + else + *d = '.'; + + if(*s == '.') + dot_idx = idx; + } + else if(*s == '+' && s[1] == '+') { + if(idx - 2 == dot_idx) { /* .c++, .h++ etc. */ + *d++ = 'x'; + if(d == dlimit) + break; + *d = 'x'; + } + else { + /* libg++ etc. */ + if(dlimit - d < 4) { + *d++ = 'x'; + if(d == dlimit) + break; + *d = 'x'; + } + else { + memcpy(d, "plus", 4); + d += 3; + } + } + s++; + idx++; + } + else + *d = '_'; + } + else + *d = *s; + if(*s == '/' || *s == '\\') { + idx = 0; + dot_idx = -1; + } + else + idx++; + } + *d = '\0'; + + if(*s) { + /* dos_name is truncated, check that truncation requirements are met, + specifically truncating a filename suffixed by an alternate data stream + or truncating the entire filename is not allowed. */ + if(strpbrk(s, "\\/:") || truncate_dryrun(dos_name, d - dos_name)) + return SANITIZE_ERR_INVALID_PATH; + } + + *sanitized = curlx_strdup(dos_name); + return *sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY; +} +#endif /* MSDOS */ + +/* + * Rename file_name if it is a reserved dos device name. + * + * This is a supporting function for sanitize_file_name. + * + * Warning: This is an MS-DOS legacy function and was purposely written in + * a way that some path information may pass through. For example drive letter + * names (C:, D:, etc) are allowed to pass through. For sanitizing a filename + * use sanitize_file_name. + * + * Success: SANITIZE_ERR_OK *sanitized points to a sanitized copy of file_name. + * Failure: != SANITIZE_ERR_OK *sanitized is NULL. + */ +static SANITIZEcode rename_if_reserved_dos(char ** const sanitized, + const char *file_name, + int flags) +{ + /* We could have a file whose name is a device on MS-DOS. Trying to + retrieve such a file would fail at best and wedge us at worst. We need + to rename such files. */ + char *p, *base, *buffer; +#ifdef MSDOS + curlx_struct_stat st_buf; +#endif + size_t len, bufsize; + + if(!sanitized || !file_name) + return SANITIZE_ERR_BAD_ARGUMENT; + + *sanitized = NULL; + + /* Ignore "\\" prefixed paths, they are allowed to use reserved names. */ +#ifndef MSDOS + if((flags & SANITIZE_ALLOW_PATH) && + file_name[0] == '\\' && file_name[1] == '\\') { + *sanitized = curlx_strdup(file_name); + if(!*sanitized) + return SANITIZE_ERR_OUT_OF_MEMORY; + return SANITIZE_ERR_OK; + } +#endif + + /* The buffer contains two extra bytes to allow for path expansion that + occurs if reserved name(s) need an underscore prepended. */ + len = strlen(file_name); + bufsize = len + 2 + 1; + + buffer = curlx_malloc(bufsize); + if(!buffer) + return SANITIZE_ERR_OUT_OF_MEMORY; + + memcpy(buffer, file_name, len + 1); + + base = curlx_basename(buffer); + + /* Rename reserved device names that are known to be accessible without \\.\ + Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS + https://web.archive.org/web/20160314141551/support.microsoft.com/en-us/kb/74496 + https://learn.microsoft.com/windows/win32/fileio/naming-a-file + */ + for(p = buffer; p; p = (p == buffer && buffer != base ? base : NULL)) { + size_t p_len; + int x = (curl_strnequal(p, "CON", 3) || + curl_strnequal(p, "PRN", 3) || + curl_strnequal(p, "AUX", 3) || + curl_strnequal(p, "NUL", 3)) ? 3 : + (curl_strnequal(p, "CLOCK$", 6)) ? 6 : + (curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ? + (('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0; + + if(!x) + continue; + + /* the devices may be accessible with an extension or ADS, for + example CON.AIR and 'CON . AIR' and CON:AIR access console */ + + for(; p[x] == ' '; ++x) + ; + + if(p[x] == '.') { + p[x] = '_'; + continue; + } + else if(p[x] == ':') { + if(!(flags & SANITIZE_ALLOW_PATH)) { + p[x] = '_'; + continue; + } + ++x; + } + else if(p[x]) /* no match */ + continue; + + /* p points to 'CON' or 'CON ' or 'CON:', etc */ + p_len = strlen(p); + + /* Prepend a '_' */ + memmove(p + 1, p, p_len + 1); + p[0] = '_'; + ++p_len; + ++len; + + /* the basename pointer must be updated since the path has expanded */ + if(p == buffer) + base = curlx_basename(buffer); + } + + /* This is the legacy portion from rename_if_dos_device_name that checks for + reserved device names. It only works on MS-DOS. On Windows XP the stat + check errors with EINVAL if the device name is reserved. On Windows + Vista/7/8 it sets mode S_IFREG (regular file or device). According to + MSDN stat doc the latter behavior is correct, but that does not help us + identify whether it is a reserved device name and not a regular + filename. */ +#ifdef MSDOS + if(base && (curlx_stat(base, &st_buf) == 0) && S_ISCHR(st_buf.st_mode)) { + /* Prepend a '_' */ + size_t blen = strlen(base); + if(blen) { + if(len == bufsize - 1) { + curlx_free(buffer); + return SANITIZE_ERR_INVALID_PATH; + } + memmove(base + 1, base, blen + 1); + base[0] = '_'; + ++len; + } + } +#endif + + *sanitized = buffer; + return SANITIZE_ERR_OK; +} + +/* + * Sanitize a file or path name. + * + * All banned characters are replaced by underscores, for example: + * f?*foo => f__foo + * f:foo::$DATA => f_foo__$DATA + * f:\foo:bar => f__foo_bar + * f:\foo:bar => f:\foo:bar (flag SANITIZE_ALLOW_PATH) + * + * This function was implemented according to the guidelines in 'Naming Files, + * Paths, and Namespaces' section 'Naming Conventions'. + * https://learn.microsoft.com/windows/win32/fileio/naming-a-file + * + * Flags + * ----- + * SANITIZE_ALLOW_PATH: Allow path separators and colons. + * Without this flag path separators and colons are sanitized. + * + * SANITIZE_ALLOW_RESERVED: Allow reserved device names. + * Without this flag a reserved device name is renamed (COM1 => _COM1). + * + * To fully block reserved device names requires not passing either flag. + * Some less common path styles are allowed to use reserved device names. + * For example, a "\\" prefixed path may use reserved device names if paths + * are allowed. + * + * Success: SANITIZE_ERR_OK *sanitized points to a sanitized copy of file_name. + * Failure: != SANITIZE_ERR_OK *sanitized is NULL. + */ +SANITIZEcode sanitize_file_name(char ** const sanitized, const char *file_name, + int flags) +{ + char *p, *target; + size_t len; + SANITIZEcode sc; + + if(!sanitized) + return SANITIZE_ERR_BAD_ARGUMENT; + + *sanitized = NULL; + + if(!file_name) + return SANITIZE_ERR_BAD_ARGUMENT; + + len = strlen(file_name); + + target = curlx_strdup(file_name); if(!target) return SANITIZE_ERR_OUT_OF_MEMORY; #ifndef MSDOS if((flags & SANITIZE_ALLOW_PATH) && !strncmp(target, "\\\\?\\", 4)) - /* Skip the literal path prefix \\?\ */ + /* Skip the literal-path prefix \\?\ */ p = target + 4; else #endif @@ -182,386 +500,38 @@ SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name, #ifdef MSDOS sc = msdosify(&p, target, flags); - free(target); + curlx_free(target); if(sc) return sc; target = p; - len = strlen(target); - - if(len > max_sanitized_len) { - free(target); - return SANITIZE_ERR_INVALID_PATH; - } #endif if(!(flags & SANITIZE_ALLOW_RESERVED)) { sc = rename_if_reserved_dos(&p, target, flags); - free(target); + curlx_free(target); if(sc) return sc; target = p; - len = strlen(target); - - if(len > max_sanitized_len) { - free(target); - return SANITIZE_ERR_INVALID_PATH; - } } +#ifdef DEBUGBUILD + if(getenv("CURL_FN_SANITIZE_BAD")) { + curlx_free(target); + return SANITIZE_ERR_INVALID_PATH; + } + if(getenv("CURL_FN_SANITIZE_OOM")) { + curlx_free(target); + return SANITIZE_ERR_OUT_OF_MEMORY; + } +#endif + *sanitized = target; return SANITIZE_ERR_OK; } -#ifdef MSDOS -/* -Test if truncating a path to a file will leave at least a single character in -the filename. Filenames suffixed by an alternate data stream cannot be -truncated. This performs a dry run, nothing is modified. - -Good truncate_pos 9: C:\foo\bar => C:\foo\ba -Good truncate_pos 6: C:\foo => C:\foo -Good truncate_pos 5: C:\foo => C:\fo -Bad* truncate_pos 5: C:foo => C:foo -Bad truncate_pos 5: C:\foo:ads => C:\fo -Bad truncate_pos 9: C:\foo:ads => C:\foo:ad -Bad truncate_pos 5: C:\foo\bar => C:\fo -Bad truncate_pos 5: C:\foo\ => C:\fo -Bad truncate_pos 7: C:\foo\ => C:\foo\ -Error truncate_pos 7: C:\foo => (pos out of range) -Bad truncate_pos 1: C:\foo\ => C - -* C:foo is ambiguous, C could end up being a drive or file therefore something - like C:superlongfilename cannot be truncated. - -Returns -SANITIZE_ERR_OK: Good -- 'path' can be truncated -SANITIZE_ERR_INVALID_PATH: Bad -- 'path' cannot be truncated -!= SANITIZE_ERR_OK && != SANITIZE_ERR_INVALID_PATH: Error -*/ -static SANITIZEcode truncate_dryrun(const char *path, - const size_t truncate_pos) -{ - size_t len; - - if(!path) - return SANITIZE_ERR_BAD_ARGUMENT; - - len = strlen(path); - - if(truncate_pos > len) - return SANITIZE_ERR_BAD_ARGUMENT; - - if(!len || !truncate_pos) - return SANITIZE_ERR_INVALID_PATH; - - if(strpbrk(&path[truncate_pos - 1], "\\/:")) - return SANITIZE_ERR_INVALID_PATH; - - /* C:\foo can be truncated but C:\foo:ads cannot */ - if(truncate_pos > 1) { - const char *p = &path[truncate_pos - 1]; - do { - --p; - if(*p == ':') - return SANITIZE_ERR_INVALID_PATH; - } while(p != path && *p != '\\' && *p != '/'); - } - - return SANITIZE_ERR_OK; -} - -/* The functions msdosify, rename_if_dos_device_name and __crt0_glob_function - * were taken with modification from the DJGPP port of tar 1.12. They use - * algorithms originally from DJTAR. - */ - -/* -Extra sanitization MS-DOS for file_name. - -This is a supporting function for sanitize_file_name. - -Warning: This is an MS-DOS legacy function and was purposely written in a way -that some path information may pass through. For example drive letter names -(C:, D:, etc) are allowed to pass through. For sanitizing a filename use -sanitize_file_name. - -Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name. -Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL. -*/ -static SANITIZEcode msdosify(char **const sanitized, const char *file_name, - int flags) -{ - char dos_name[PATH_MAX]; - static const char illegal_chars_dos[] = ".+, ;=[]" /* illegal in DOS */ - "|<>/\\\":?*"; /* illegal in DOS & W95 */ - static const char *illegal_chars_w95 = &illegal_chars_dos[8]; - int idx, dot_idx; - const char *s = file_name; - char *d = dos_name; - const char *const dlimit = dos_name + sizeof(dos_name) - 1; - const char *illegal_aliens = illegal_chars_dos; - size_t len = sizeof(illegal_chars_dos) - 1; - - if(!sanitized) - return SANITIZE_ERR_BAD_ARGUMENT; - - *sanitized = NULL; - - if(!file_name) - return SANITIZE_ERR_BAD_ARGUMENT; - - if(strlen(file_name) > PATH_MAX-1) - return SANITIZE_ERR_INVALID_PATH; - - /* Support for Windows 9X VFAT systems, when available. */ - if(_use_lfn(file_name)) { - illegal_aliens = illegal_chars_w95; - len -= (illegal_chars_w95 - illegal_chars_dos); - } - - /* Get past the drive letter, if any. */ - if(s[0] >= 'A' && s[0] <= 'z' && s[1] == ':') { - *d++ = *s++; - *d = ((flags & SANITIZE_ALLOW_PATH)) ? ':' : '_'; - ++d; ++s; - } - - for(idx = 0, dot_idx = -1; *s && d < dlimit; s++, d++) { - if(memchr(illegal_aliens, *s, len)) { - - if((flags & SANITIZE_ALLOW_PATH) && *s == ':') - *d = ':'; - else if((flags & SANITIZE_ALLOW_PATH) && (*s == '/' || *s == '\\')) - *d = *s; - /* Dots are special: DOS does not allow them as the leading character, - and a filename cannot have more than a single dot. We leave the - first non-leading dot alone, unless it comes too close to the - beginning of the name: we want sh.lex.c to become sh_lex.c, not - sh.lex-c. */ - else if(*s == '.') { - if((flags & SANITIZE_ALLOW_PATH) && idx == 0 && - (s[1] == '/' || s[1] == '\\' || - (s[1] == '.' && (s[2] == '/' || s[2] == '\\')))) { - /* Copy "./" and "../" verbatim. */ - *d++ = *s++; - if(d == dlimit) - break; - if(*s == '.') { - *d++ = *s++; - if(d == dlimit) - break; - } - *d = *s; - } - else if(idx == 0) - *d = '_'; - else if(dot_idx >= 0) { - if(dot_idx < 5) { /* 5 is a heuristic ad-hoc'ery */ - d[dot_idx - idx] = '_'; /* replace previous dot */ - *d = '.'; - } - else - *d = '-'; - } - else - *d = '.'; - - if(*s == '.') - dot_idx = idx; - } - else if(*s == '+' && s[1] == '+') { - if(idx - 2 == dot_idx) { /* .c++, .h++ etc. */ - *d++ = 'x'; - if(d == dlimit) - break; - *d = 'x'; - } - else { - /* libg++ etc. */ - if(dlimit - d < 4) { - *d++ = 'x'; - if(d == dlimit) - break; - *d = 'x'; - } - else { - memcpy(d, "plus", 4); - d += 3; - } - } - s++; - idx++; - } - else - *d = '_'; - } - else - *d = *s; - if(*s == '/' || *s == '\\') { - idx = 0; - dot_idx = -1; - } - else - idx++; - } - *d = '\0'; - - if(*s) { - /* dos_name is truncated, check that truncation requirements are met, - specifically truncating a filename suffixed by an alternate data stream - or truncating the entire filename is not allowed. */ - if(strpbrk(s, "\\/:") || truncate_dryrun(dos_name, d - dos_name)) - return SANITIZE_ERR_INVALID_PATH; - } - - *sanitized = strdup(dos_name); - return *sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY; -} -#endif /* MSDOS */ - -/* -Rename file_name if it is a reserved dos device name. - -This is a supporting function for sanitize_file_name. - -Warning: This is an MS-DOS legacy function and was purposely written in a way -that some path information may pass through. For example drive letter names -(C:, D:, etc) are allowed to pass through. For sanitizing a filename use -sanitize_file_name. - -Success: (SANITIZE_ERR_OK) *sanitized points to a sanitized copy of file_name. -Failure: (!= SANITIZE_ERR_OK) *sanitized is NULL. -*/ -static SANITIZEcode rename_if_reserved_dos(char **const sanitized, - const char *file_name, - int flags) -{ - /* We could have a file whose name is a device on MS-DOS. Trying to - * retrieve such a file would fail at best and wedge us at worst. We need - * to rename such files. */ - char *p, *base; - char fname[PATH_MAX]; -#ifdef MSDOS - struct_stat st_buf; -#endif - size_t len; - - if(!sanitized || !file_name) - return SANITIZE_ERR_BAD_ARGUMENT; - - *sanitized = NULL; - len = strlen(file_name); - - /* Ignore UNC prefixed paths, they are allowed to contain a reserved name. */ -#ifndef MSDOS - if((flags & SANITIZE_ALLOW_PATH) && - file_name[0] == '\\' && file_name[1] == '\\') { - *sanitized = strdup(file_name); - if(!*sanitized) - return SANITIZE_ERR_OUT_OF_MEMORY; - return SANITIZE_ERR_OK; - } -#endif - - if(len > PATH_MAX-1) - return SANITIZE_ERR_INVALID_PATH; - - memcpy(fname, file_name, len); - fname[len] = '\0'; - base = basename(fname); - - /* Rename reserved device names that are known to be accessible without \\.\ - Examples: CON => _CON, CON.EXT => CON_EXT, CON:ADS => CON_ADS - https://web.archive.org/web/20160314141551/ - support.microsoft.com/en-us/kb/74496 - https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx - */ - for(p = fname; p; p = (p == fname && fname != base ? base : NULL)) { - size_t p_len; - int x = (curl_strnequal(p, "CON", 3) || - curl_strnequal(p, "PRN", 3) || - curl_strnequal(p, "AUX", 3) || - curl_strnequal(p, "NUL", 3)) ? 3 : - (curl_strnequal(p, "CLOCK$", 6)) ? 6 : - (curl_strnequal(p, "COM", 3) || curl_strnequal(p, "LPT", 3)) ? - (('1' <= p[3] && p[3] <= '9') ? 4 : 3) : 0; - - if(!x) - continue; - - /* the devices may be accessible with an extension or ADS, for - example CON.AIR and 'CON . AIR' and CON:AIR access console */ - - for(; p[x] == ' '; ++x) - ; - - if(p[x] == '.') { - p[x] = '_'; - continue; - } - else if(p[x] == ':') { - if(!(flags & SANITIZE_ALLOW_PATH)) { - p[x] = '_'; - continue; - } - ++x; - } - else if(p[x]) /* no match */ - continue; - - /* p points to 'CON' or 'CON ' or 'CON:', etc */ - p_len = strlen(p); - - /* Prepend a '_' */ - if(strlen(fname) == PATH_MAX-1) - return SANITIZE_ERR_INVALID_PATH; - memmove(p + 1, p, p_len + 1); - p[0] = '_'; - ++p_len; - - /* if fname was just modified then the basename pointer must be updated */ - if(p == fname) - base = basename(fname); - } - - /* This is the legacy portion from rename_if_dos_device_name that checks for - reserved device names. It only works on MS-DOS. On Windows XP the stat - check errors with EINVAL if the device name is reserved. On Windows - Vista/7/8 it sets mode S_IFREG (regular file or device). According to - MSDN stat doc the latter behavior is correct, but that does not help us - identify whether it is a reserved device name and not a regular - filename. */ -#ifdef MSDOS - if(base && ((stat(base, &st_buf)) == 0) && (S_ISCHR(st_buf.st_mode))) { - /* Prepend a '_' */ - size_t blen = strlen(base); - if(blen) { - if(strlen(fname) >= PATH_MAX-1) - return SANITIZE_ERR_INVALID_PATH; - memmove(base + 1, base, blen + 1); - base[0] = '_'; - } - } -#endif - - *sanitized = strdup(fname); - return *sanitized ? SANITIZE_ERR_OK : SANITIZE_ERR_OUT_OF_MEMORY; -} - -#ifdef __DJGPP__ -/* - * Disable program default argument globbing. We do it on our own. - */ -char **__crt0_glob_function(char *arg) -{ - (void)arg; - return (char **)0; -} -#endif - #ifdef _WIN32 -#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) && \ +#if !defined(CURL_WINDOWS_UWP) && \ !defined(CURL_DISABLE_CA_SEARCH) && !defined(CURL_CA_SEARCH_SAFE) /* Search and set the CA cert file for Windows. * @@ -587,18 +557,15 @@ CURLcode FindWin32CACert(struct OperationConfig *config, { CURLcode result = CURLE_OK; DWORD res_len; - TCHAR buf[PATH_MAX]; + TCHAR buf[MAX_PATH]; TCHAR *ptr = NULL; buf[0] = TEXT('\0'); - res_len = SearchPath(NULL, bundle_file, NULL, PATH_MAX, buf, &ptr); + res_len = SearchPath(NULL, bundle_file, NULL, MAX_PATH, buf, &ptr); if(res_len > 0) { - char *mstr = curlx_convert_tchar_to_UTF8(buf); - tool_safefree(config->cacert); - if(mstr) - config->cacert = strdup(mstr); - curlx_unicodefree(mstr); + curlx_free(config->cacert); + config->cacert = curlx_convert_tchar_to_UTF8(buf); if(!config->cacert) result = CURLE_OUT_OF_MEMORY; } @@ -613,9 +580,9 @@ CURLcode FindWin32CACert(struct OperationConfig *config, struct curl_slist *GetLoadedModulePaths(void) { struct curl_slist *slist = NULL; -#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) +#ifndef CURL_WINDOWS_UWP HANDLE hnd = INVALID_HANDLE_VALUE; - MODULEENTRY32 mod = {0}; + MODULEENTRY32 mod = { 0 }; mod.dwSize = sizeof(MODULEENTRY32); @@ -635,7 +602,7 @@ struct curl_slist *GetLoadedModulePaths(void) #ifdef UNICODE /* sizeof(mod.szExePath) is the max total bytes of wchars. the max total - bytes of multibyte chars will not be more than twice that. */ + bytes of multibyte chars is not more than twice that. */ char buffer[sizeof(mod.szExePath) * 2]; if(!WideCharToMultiByte(CP_ACP, 0, mod.szExePath, -1, buffer, sizeof(buffer), NULL, NULL)) @@ -664,7 +631,7 @@ cleanup: bool tool_term_has_bold; -#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) +#ifndef CURL_WINDOWS_UWP /* The terminal settings to restore on exit */ static struct TerminalSettings { HANDLE hStdOut; @@ -672,6 +639,7 @@ static struct TerminalSettings { LONG valid; } TerminalSettings; +/* Offered by mingw-w64 v7+. MS SDK ~10.16299/~VS2017+. */ #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #endif @@ -730,19 +698,8 @@ static void init_terminal(void) } } } -#endif -CURLcode win32_init(void) -{ - curlx_now_init(); -#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) - init_terminal(); -#endif - - return CURLE_OK; -} - -#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) +#ifdef USE_WINSOCK /* The following STDIN non - blocking read techniques are heavily inspired by nmap and ncat (https://nmap.org/ncat/) */ struct win_thread_data { @@ -757,36 +714,35 @@ struct win_thread_data { static DWORD WINAPI win_stdin_thread_func(void *thread_data) { struct win_thread_data *tdata = (struct win_thread_data *)thread_data; - DWORD n; - int nwritten; - char buffer[BUFSIZ]; - BOOL r; - - SOCKADDR_IN clientAddr; + struct sockaddr_in clientAddr; int clientAddrLen = sizeof(clientAddr); - curl_socket_t socket_w = CURL_ACCEPT(tdata->socket_l, (SOCKADDR*)&clientAddr, + curl_socket_t socket_w = CURL_ACCEPT(tdata->socket_l, + (struct sockaddr *)&clientAddr, &clientAddrLen); if(socket_w == CURL_SOCKET_BAD) { - errorf("accept error: %08lx", GetLastError()); + errorf("accept error: %d", SOCKERRNO); goto ThreadCleanup; } - closesocket(tdata->socket_l); /* sclose here fails test 1498 */ + sclose(tdata->socket_l); tdata->socket_l = CURL_SOCKET_BAD; - if(shutdown(socket_w, SD_RECEIVE) == SOCKET_ERROR) { - errorf("shutdown error: %08lx", GetLastError()); + if(shutdown(socket_w, SHUT_RD)) { + errorf("shutdown error: %d", SOCKERRNO); goto ThreadCleanup; } for(;;) { - r = ReadFile(tdata->stdin_handle, buffer, sizeof(buffer), &n, NULL); - if(r == 0) + DWORD n; + ssize_t nwritten; + char buffer[BUFSIZ]; + + if(!ReadFile(tdata->stdin_handle, buffer, sizeof(buffer), &n, NULL)) break; if(n == 0) break; - nwritten = send(socket_w, buffer, n, 0); - if(nwritten == SOCKET_ERROR) + nwritten = swrite(socket_w, buffer, n); + if(nwritten == -1) break; if((DWORD)nwritten != n) break; @@ -801,24 +757,17 @@ ThreadCleanup: if(socket_w != CURL_SOCKET_BAD) sclose(socket_w); - if(tdata) { - free(tdata); - } - + curlx_free(tdata); return 0; } /* The background thread that reads and buffers the true stdin. */ -static HANDLE stdin_thread = NULL; -static curl_socket_t socket_r = CURL_SOCKET_BAD; - curl_socket_t win32_stdin_read_thread(void) { - int result; - bool r; - int rc = 0, socksize = 0; + int rc = 0; struct win_thread_data *tdata = NULL; - SOCKADDR_IN selfaddr; + static HANDLE stdin_thread = NULL; + static curl_socket_t socket_r = CURL_SOCKET_BAD; if(socket_r != CURL_SOCKET_BAD) { assert(stdin_thread != NULL); @@ -827,90 +776,88 @@ curl_socket_t win32_stdin_read_thread(void) assert(stdin_thread == NULL); do { + curl_socklen_t socksize = 0; + struct sockaddr_in selfaddr; + /* Prepare handles for thread */ - tdata = (struct win_thread_data*)calloc(1, sizeof(struct win_thread_data)); + tdata = (struct win_thread_data *) + curlx_calloc(1, sizeof(struct win_thread_data)); if(!tdata) { - errorf("calloc() error"); + errorf("curlx_calloc() error"); break; } - /* Create the listening socket for the thread. When it starts, it will - * accept our connection and begin writing STDIN data to the connection. */ - tdata->socket_l = WSASocketW(AF_INET, SOCK_STREAM, - IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); - + /* Create the listening socket for the thread. When it starts, it accepts + * our connection and begin writing STDIN data to the connection. */ + tdata->socket_l = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(tdata->socket_l == CURL_SOCKET_BAD) { - errorf("WSASocketW error: %08lx", GetLastError()); + errorf("socket() error: %d", SOCKERRNO); break; } socksize = sizeof(selfaddr); memset(&selfaddr, 0, socksize); selfaddr.sin_family = AF_INET; - selfaddr.sin_addr.S_un.S_addr = htonl(INADDR_LOOPBACK); + selfaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* Bind to any available loopback port */ - result = bind(tdata->socket_l, (SOCKADDR*)&selfaddr, socksize); - if(result == SOCKET_ERROR) { - errorf("bind error: %08lx", GetLastError()); + if(bind(tdata->socket_l, (const struct sockaddr *)&selfaddr, socksize)) { + errorf("bind error: %d", SOCKERRNO); break; } /* Bind to any available loopback port */ - result = getsockname(tdata->socket_l, (SOCKADDR*)&selfaddr, &socksize); - if(result == SOCKET_ERROR) { - errorf("getsockname error: %08lx", GetLastError()); + if(getsockname(tdata->socket_l, (struct sockaddr *)&selfaddr, &socksize)) { + errorf("getsockname error: %d", SOCKERRNO); break; } - result = listen(tdata->socket_l, 1); - if(result == SOCKET_ERROR) { - errorf("listen error: %08lx", GetLastError()); + if(listen(tdata->socket_l, 1)) { + errorf("listen error: %d", SOCKERRNO); break; } /* Make a copy of the stdin handle to be used by win_stdin_thread_func */ - r = DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), + if(!DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), GetCurrentProcess(), &tdata->stdin_handle, - 0, FALSE, DUPLICATE_SAME_ACCESS); - - if(!r) { - errorf("DuplicateHandle error: %08lx", GetLastError()); + 0, FALSE, DUPLICATE_SAME_ACCESS)) { + errorf("DuplicateHandle error: 0x%08lx", GetLastError()); break; } - /* Start up the thread. We don't bother keeping a reference to it + /* Start up the thread. We do not bother keeping a reference to it because it runs until program termination. From here on out all reads - from the stdin handle or file descriptor 0 will be reading from the + from the stdin handle or file descriptor 0 is reading from the socket that is fed by the thread. */ stdin_thread = CreateThread(NULL, 0, win_stdin_thread_func, tdata, 0, NULL); if(!stdin_thread) { - errorf("CreateThread error: %08lx", GetLastError()); + errorf("CreateThread error: 0x%08lx", GetLastError()); break; } + tdata = NULL; /* win_stdin_thread_func owns it now */ /* Connect to the thread and rearrange our own STDIN handles */ - socket_r = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + socket_r = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(socket_r == CURL_SOCKET_BAD) { - errorf("socket error: %08lx", GetLastError()); + errorf("socket error: %d", SOCKERRNO); break; } /* Hard close the socket on closesocket() */ setsockopt(socket_r, SOL_SOCKET, SO_DONTLINGER, 0, 0); - if(connect(socket_r, (SOCKADDR*)&selfaddr, socksize) == SOCKET_ERROR) { - errorf("connect error: %08lx", GetLastError()); + if(connect(socket_r, (const struct sockaddr *)&selfaddr, socksize)) { + errorf("connect error: %d", SOCKERRNO); break; } - if(shutdown(socket_r, SD_SEND) == SOCKET_ERROR) { - errorf("shutdown error: %08lx", GetLastError()); + if(shutdown(socket_r, SHUT_WR)) { + errorf("shutdown error: %d", SOCKERRNO); break; } /* Set the stdin handle to read from the socket. */ if(SetStdHandle(STD_INPUT_HANDLE, (HANDLE)socket_r) == 0) { - errorf("SetStdHandle error: %08lx", GetLastError()); + errorf("SetStdHandle error: 0x%08lx", GetLastError()); break; } @@ -932,6 +879,7 @@ curl_socket_t win32_stdin_read_thread(void) if(stdin_thread) { TerminateThread(stdin_thread, 1); + CloseHandle(stdin_thread); stdin_thread = NULL; } @@ -941,7 +889,7 @@ curl_socket_t win32_stdin_read_thread(void) if(tdata->socket_l != CURL_SOCKET_BAD) sclose(tdata->socket_l); - free(tdata); + curlx_free(tdata); } return CURL_SOCKET_BAD; @@ -950,8 +898,20 @@ curl_socket_t win32_stdin_read_thread(void) assert(socket_r != CURL_SOCKET_BAD); return socket_r; } +#endif /* USE_WINSOCK */ -#endif /* !CURL_WINDOWS_UWP && !UNDER_CE */ +#endif /* !CURL_WINDOWS_UWP */ + +CURLcode win32_init(void) +{ + curlx_verify_windows_init(); + curlx_now_init(); +#ifndef CURL_WINDOWS_UWP + init_terminal(); +#endif + + return CURLE_OK; +} #endif /* _WIN32 */ diff --git a/src/tool_doswin.h b/src/tool_doswin.h index 7fe5260476..821a2dbb67 100644 --- a/src/tool_doswin.h +++ b/src/tool_doswin.h @@ -27,18 +27,10 @@ #if defined(_WIN32) || defined(MSDOS) -#define SANITIZE_ALLOW_PATH (1<<1) /* Allow path separators and colons */ -#define SANITIZE_ALLOW_RESERVED (1<<2) /* Allow reserved device names */ +#define SANITIZE_ALLOW_PATH (1 << 1) /* Allow path separators and colons */ +#define SANITIZE_ALLOW_RESERVED (1 << 2) /* Allow reserved device names */ -typedef enum { - SANITIZE_ERR_OK = 0, /* 0 - OK */ - SANITIZE_ERR_INVALID_PATH, /* 1 - the path is invalid */ - SANITIZE_ERR_BAD_ARGUMENT, /* 2 - bad function parameter */ - SANITIZE_ERR_OUT_OF_MEMORY, /* 3 - out of memory */ - SANITIZE_ERR_LAST /* never use! */ -} SANITIZEcode; - -SANITIZEcode sanitize_file_name(char **const sanitized, const char *file_name, +SANITIZEcode sanitize_file_name(char ** const sanitized, const char *file_name, int flags); #ifdef __DJGPP__ @@ -47,7 +39,7 @@ char **__crt0_glob_function(char *arg); #ifdef _WIN32 -#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) && \ +#if !defined(CURL_WINDOWS_UWP) && \ !defined(CURL_DISABLE_CA_SEARCH) && !defined(CURL_CA_SEARCH_SAFE) CURLcode FindWin32CACert(struct OperationConfig *config, const TCHAR *bundle_file); @@ -55,9 +47,9 @@ CURLcode FindWin32CACert(struct OperationConfig *config, struct curl_slist *GetLoadedModulePaths(void); CURLcode win32_init(void); -#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) +#ifndef CURL_WINDOWS_UWP curl_socket_t win32_stdin_read_thread(void); -#endif /* !CURL_WINDOWS_UWP && !UNDER_CE */ +#endif #endif /* _WIN32 */ diff --git a/src/tool_easysrc.c b/src/tool_easysrc.c index 223c66bdf5..5b3c6cb45b 100644 --- a/src/tool_easysrc.c +++ b/src/tool_easysrc.c @@ -31,8 +31,6 @@ #include "tool_easysrc.h" #include "tool_msgs.h" -#include "memdebug.h" /* keep this as LAST include */ - /* global variable definitions, for easy-interface source code generation */ struct slist_wc *easysrc_decl; /* Variable declarations */ @@ -43,7 +41,7 @@ struct slist_wc *easysrc_clean; /* Clean up allocated data */ int easysrc_mime_count; int easysrc_slist_count; -static const char *const srchead[]={ +static const char * const srchead[] = { "/********* Sample code generated by the curl command line tool **********", " * All curl_easy_setopt() options are documented at:", " * https://curl.se/libcurl/c/curl_easy_setopt.html", @@ -52,23 +50,23 @@ static const char *const srchead[]={ "", "int main(int argc, char *argv[])", "{", - " CURLcode ret;", - " CURL *hnd;", + " CURLcode result;", + " CURL *curl;", NULL }; /* easysrc_decl declarations come here */ /* easysrc_data initialization come here */ /* easysrc_code statements come here */ -static const char *const srchard[]={ +static const char * const srchard[] = { "/* Here is a list of options the curl code used that cannot get generated", " as source easily. You may choose to either not use them or implement", " them yourself.", "", NULL }; -static const char *const srcend[]={ +static const char *const srcend[] = { "", - " return (int)ret;", + " return (int)result;", "}", "/**** End of sample code ****/", NULL @@ -92,82 +90,82 @@ static void easysrc_free(void) /* Add a source line to the main code or remarks */ CURLcode easysrc_add(struct slist_wc **plist, const char *line) { - CURLcode ret = CURLE_OK; + CURLcode result = CURLE_OK; struct slist_wc *list = slist_wc_append(*plist, line); if(!list) { easysrc_free(); - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } else *plist = list; - return ret; + return result; } CURLcode easysrc_addf(struct slist_wc **plist, const char *fmt, ...) { - CURLcode ret; + CURLcode result; char *bufp; va_list ap; va_start(ap, fmt); - bufp = vaprintf(fmt, ap); + bufp = curl_mvaprintf(fmt, ap); va_end(ap); if(!bufp) { - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } else { - ret = easysrc_add(plist, bufp); + result = easysrc_add(plist, bufp); curl_free(bufp); } - return ret; + return result; } CURLcode easysrc_init(void) { - return easysrc_add(&easysrc_code, "hnd = curl_easy_init();"); + return easysrc_add(&easysrc_code, "curl = curl_easy_init();"); } CURLcode easysrc_perform(void) { - CURLcode ret = CURLE_OK; + CURLcode result = CURLE_OK; /* Note any setopt calls which we could not convert */ if(easysrc_toohard) { int i; struct curl_slist *ptr; - ret = easysrc_add(&easysrc_code, ""); + result = easysrc_add(&easysrc_code, ""); /* Preamble comment */ - for(i = 0; srchard[i] && !ret; i++) - ret = easysrc_add(&easysrc_code, srchard[i]); + for(i = 0; srchard[i] && !result; i++) + result = easysrc_add(&easysrc_code, srchard[i]); /* Each unconverted option */ - if(easysrc_toohard && !ret) { - for(ptr = easysrc_toohard->first; ptr && !ret; ptr = ptr->next) - ret = easysrc_add(&easysrc_code, ptr->data); + if(easysrc_toohard && !result) { + for(ptr = easysrc_toohard->first; ptr && !result; ptr = ptr->next) + result = easysrc_add(&easysrc_code, ptr->data); } - if(!ret) - ret = easysrc_add(&easysrc_code, ""); - if(!ret) - ret = easysrc_add(&easysrc_code, "*/"); + if(!result) + result = easysrc_add(&easysrc_code, ""); + if(!result) + result = easysrc_add(&easysrc_code, "*/"); slist_wc_free_all(easysrc_toohard); easysrc_toohard = NULL; } - if(!ret) - ret = easysrc_add(&easysrc_code, ""); - if(!ret) - ret = easysrc_add(&easysrc_code, "ret = curl_easy_perform(hnd);"); - if(!ret) - ret = easysrc_add(&easysrc_code, ""); + if(!result) + result = easysrc_add(&easysrc_code, ""); + if(!result) + result = easysrc_add(&easysrc_code, "result = curl_easy_perform(curl);"); + if(!result) + result = easysrc_add(&easysrc_code, ""); - return ret; + return result; } CURLcode easysrc_cleanup(void) { - CURLcode ret = easysrc_add(&easysrc_code, "curl_easy_cleanup(hnd);"); - if(!ret) - ret = easysrc_add(&easysrc_code, "hnd = NULL;"); + CURLcode result = easysrc_add(&easysrc_code, "curl_easy_cleanup(curl);"); + if(!result) + result = easysrc_add(&easysrc_code, "curl = NULL;"); - return ret; + return result; } void dumpeasysrc(void) @@ -178,7 +176,7 @@ void dumpeasysrc(void) FILE *out; bool fopened = FALSE; if(strcmp(o, "-")) { - out = fopen(o, FOPEN_WRITETEXT); + out = curlx_fopen(o, FOPEN_WRITETEXT); fopened = TRUE; } else @@ -190,44 +188,44 @@ void dumpeasysrc(void) const char *c; for(i = 0; ((c = srchead[i]) != NULL); i++) - fprintf(out, "%s\n", c); + curl_mfprintf(out, "%s\n", c); /* Declare variables used for complex setopt values */ if(easysrc_decl) { for(ptr = easysrc_decl->first; ptr; ptr = ptr->next) - fprintf(out, " %s\n", ptr->data); + curl_mfprintf(out, " %s\n", ptr->data); } /* Set up complex values for setopt calls */ if(easysrc_data) { - fprintf(out, "\n"); + curl_mfprintf(out, "\n"); for(ptr = easysrc_data->first; ptr; ptr = ptr->next) - fprintf(out, " %s\n", ptr->data); + curl_mfprintf(out, " %s\n", ptr->data); } - fprintf(out, "\n"); + curl_mfprintf(out, "\n"); if(easysrc_code) { for(ptr = easysrc_code->first; ptr; ptr = ptr->next) { if(ptr->data[0]) { - fprintf(out, " %s\n", ptr->data); + curl_mfprintf(out, " %s\n", ptr->data); } else { - fprintf(out, "\n"); + curl_mfprintf(out, "\n"); } } } if(easysrc_clean) { for(ptr = easysrc_clean->first; ptr; ptr = ptr->next) - fprintf(out, " %s\n", ptr->data); + curl_mfprintf(out, " %s\n", ptr->data); } for(i = 0; ((c = srcend[i]) != NULL); i++) - fprintf(out, "%s\n", c); + curl_mfprintf(out, "%s\n", c); if(fopened) - fclose(out); + curlx_fclose(out); } easysrc_free(); diff --git a/src/tool_easysrc.h b/src/tool_easysrc.h index 44d40b727a..f37ce4ba86 100644 --- a/src/tool_easysrc.h +++ b/src/tool_easysrc.h @@ -24,6 +24,7 @@ * ***************************************************************************/ #include "tool_setup.h" + #ifndef CURL_DISABLE_LIBCURL_OPTION /* global variable declarations, for easy-interface source code generation */ @@ -38,7 +39,7 @@ extern int easysrc_mime_count; /* Number of curl_mime variables */ extern int easysrc_slist_count; /* Number of curl_slist variables */ extern CURLcode easysrc_init(void); -extern CURLcode easysrc_add(struct slist_wc **plist, const char *bupf); +extern CURLcode easysrc_add(struct slist_wc **plist, const char *line); extern CURLcode easysrc_addf(struct slist_wc **plist, const char *fmt, ...) CURL_PRINTF(2, 3); extern CURLcode easysrc_perform(void); diff --git a/src/tool_filetime.c b/src/tool_filetime.c index c818fe3ada..0b6cd7b38f 100644 --- a/src/tool_filetime.c +++ b/src/tool_filetime.c @@ -41,18 +41,14 @@ int getfiletime(const char *filename, curl_off_t *stamp) access to a 64-bit type we can bypass stat and get the times directly. */ #if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) HANDLE hfile; - TCHAR *tchar_filename = curlx_convert_UTF8_to_tchar(filename); - - hfile = CreateFile(tchar_filename, FILE_READ_ATTRIBUTES, - (FILE_SHARE_READ | FILE_SHARE_WRITE | - FILE_SHARE_DELETE), - NULL, OPEN_EXISTING, 0, NULL); - curlx_unicodefree(tchar_filename); + hfile = curlx_CreateFile(filename, FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | + FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); if(hfile != INVALID_HANDLE_VALUE) { FILETIME ft; if(GetFileTime(hfile, NULL, NULL, &ft)) { - curl_off_t converted = (curl_off_t)ft.dwLowDateTime - | ((curl_off_t)ft.dwHighDateTime) << 32; + curl_off_t converted = (curl_off_t)ft.dwLowDateTime | + ((curl_off_t)ft.dwHighDateTime) << 32; if(converted < 116444736000000000) warnf("Failed to get filetime: underflow"); @@ -62,25 +58,26 @@ int getfiletime(const char *filename, curl_off_t *stamp) } } else { - warnf("Failed to get filetime: " - "GetFileTime failed: GetLastError %u", - (unsigned int)GetLastError()); + warnf("Failed to get filetime: GetFileTime failed: GetLastError 0x%08lx", + GetLastError()); } CloseHandle(hfile); } else if(GetLastError() != ERROR_FILE_NOT_FOUND) { - warnf("Failed to get filetime: " - "CreateFile failed: GetLastError %u", - (unsigned int)GetLastError()); + warnf("Failed to get filetime: CreateFile failed: GetLastError 0x%08lx", + GetLastError()); } #else - struct_stat statbuf; - if(stat(filename, &statbuf) != -1) { + curlx_struct_stat statbuf; + if(curlx_stat(filename, &statbuf) != -1) { *stamp = (curl_off_t)statbuf.st_mtime; rc = 0; } - else - warnf("Failed to get filetime: %s", strerror(errno)); + else { + char errbuf[STRERROR_LEN]; + warnf("Failed to get filetime: %s", + curlx_strerror(errno, errbuf, sizeof(errbuf))); + } #endif return rc; } @@ -93,39 +90,39 @@ void setfiletime(curl_off_t filetime, const char *filename) access to a 64-bit type we can bypass utime and set the times directly. */ #if defined(_WIN32) && !defined(CURL_WINDOWS_UWP) HANDLE hfile; - TCHAR *tchar_filename = curlx_convert_UTF8_to_tchar(filename); - /* 910670515199 is the maximum Unix filetime that can be used as a - Windows FILETIME without overflow: 30827-12-31T23:59:59. */ + /* 910670515199 is the maximum Unix filetime that can be used as a Windows + FILETIME without overflow: 30827-12-31T23:59:59. */ if(filetime > 910670515199) { - warnf("Failed to set filetime %" CURL_FORMAT_CURL_OFF_T - " on outfile: overflow", filetime); - curlx_unicodefree(tchar_filename); - return; + filetime = 910670515199; + warnf("Capping set filetime to max to avoid overflow"); + } + else if(filetime < -6857222400) { + /* dates before 14 september 1752 (pre-Gregorian calendar) are not + accurate */ + filetime = -6857222400; + warnf("Capping set filetime to minimum to avoid overflow"); } - hfile = CreateFile(tchar_filename, FILE_WRITE_ATTRIBUTES, - (FILE_SHARE_READ | FILE_SHARE_WRITE | - FILE_SHARE_DELETE), - NULL, OPEN_EXISTING, 0, NULL); - curlx_unicodefree(tchar_filename); + hfile = curlx_CreateFile(filename, FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | + FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL); if(hfile != INVALID_HANDLE_VALUE) { - curl_off_t converted = ((curl_off_t)filetime * 10000000) + - 116444736000000000; + curl_off_t converted = (filetime * 10000000) + 116444736000000000; FILETIME ft; ft.dwLowDateTime = (DWORD)(converted & 0xFFFFFFFF); ft.dwHighDateTime = (DWORD)(converted >> 32); if(!SetFileTime(hfile, NULL, &ft, &ft)) { warnf("Failed to set filetime %" CURL_FORMAT_CURL_OFF_T - " on outfile: SetFileTime failed: GetLastError %u", - filetime, (unsigned int)GetLastError()); + " on outfile: SetFileTime failed: GetLastError 0x%08lx", + filetime, GetLastError()); } CloseHandle(hfile); } else { warnf("Failed to set filetime %" CURL_FORMAT_CURL_OFF_T - " on outfile: CreateFile failed: GetLastError %u", - filetime, (unsigned int)GetLastError()); + " on outfile: CreateFile failed: GetLastError 0x%08lx", + filetime, GetLastError()); } #elif defined(HAVE_UTIMES) @@ -133,8 +130,10 @@ void setfiletime(curl_off_t filetime, const char *filename) times[0].tv_sec = times[1].tv_sec = (time_t)filetime; times[0].tv_usec = times[1].tv_usec = 0; if(utimes(filename, times)) { + char errbuf[STRERROR_LEN]; warnf("Failed to set filetime %" CURL_FORMAT_CURL_OFF_T - " on '%s': %s", filetime, filename, strerror(errno)); + " on '%s': %s", filetime, filename, + curlx_strerror(errno, errbuf, sizeof(errbuf))); } #elif defined(HAVE_UTIME) @@ -142,8 +141,10 @@ void setfiletime(curl_off_t filetime, const char *filename) times.actime = (time_t)filetime; times.modtime = (time_t)filetime; if(utime(filename, ×)) { + char errbuf[STRERROR_LEN]; warnf("Failed to set filetime %" CURL_FORMAT_CURL_OFF_T - " on '%s': %s", filetime, filename, strerror(errno)); + " on '%s': %s", filetime, filename, + curlx_strerror(errno, errbuf, sizeof(errbuf))); } #endif } diff --git a/src/tool_filetime.h b/src/tool_filetime.h index c3663617b5..d28242027e 100644 --- a/src/tool_filetime.h +++ b/src/tool_filetime.h @@ -31,7 +31,7 @@ int getfiletime(const char *filename, curl_off_t *stamp); (defined(_WIN32) && (SIZEOF_CURL_OFF_T >= 8)) void setfiletime(curl_off_t filetime, const char *filename); #else -#define setfiletime(a,b,c) tool_nop_stmt +#define setfiletime(a, b) tool_nop_stmt #endif #endif /* HEADER_CURL_TOOL_FILETIME_H */ diff --git a/src/tool_findfile.c b/src/tool_findfile.c index 72868f4b4f..146a273773 100644 --- a/src/tool_findfile.c +++ b/src/tool_findfile.c @@ -24,20 +24,18 @@ #include "tool_setup.h" #ifdef HAVE_PWD_H +#ifdef __AMIGA__ #undef __NO_NET_API /* required for AmigaOS to declare getpwuid() */ +#endif #include +#ifdef __AMIGA__ #define __NO_NET_API #endif - -#ifdef HAVE_FCNTL_H -#include #endif #include "tool_findfile.h" #include "tool_cfgable.h" -#include "memdebug.h" /* keep this as LAST include */ - struct finder { const char *env; const char *append; @@ -53,7 +51,7 @@ static const struct finder conf_list[] = { #ifdef _WIN32 { "USERPROFILE", NULL, FALSE }, { "APPDATA", NULL, FALSE }, - { "USERPROFILE", "\\Application Data", FALSE}, + { "USERPROFILE", "\\Application Data", FALSE }, #endif /* these are for .curlrc if XDG_CONFIG_HOME is not defined */ { "CURL_HOME", "/.config", TRUE }, @@ -64,19 +62,19 @@ static const struct finder conf_list[] = { static char *checkhome(const char *home, const char *fname, bool dotscore) { - const char pref[2] = { '.', '_' }; + static const char pref[2] = { '.', '_' }; int i; for(i = 0; i < (dotscore ? 2 : 1); i++) { char *c; if(dotscore) - c = aprintf("%s" DIR_CHAR "%c%s", home, pref[i], &fname[1]); + c = curl_maprintf("%s" DIR_CHAR "%c%s", home, pref[i], &fname[1]); else - c = aprintf("%s" DIR_CHAR "%s", home, fname); + c = curl_maprintf("%s" DIR_CHAR "%s", home, fname); if(c) { - int fd = open(c, O_RDONLY); + int fd = curlx_open(c, O_RDONLY); if(fd >= 0) { - char *path = strdup(c); - close(fd); + char *path = curlx_strdup(c); + curlx_close(fd); curl_free(c); return path; } @@ -87,7 +85,8 @@ static char *checkhome(const char *home, const char *fname, bool dotscore) } /* - * findfile() - return the full path name of the file. + * findfile() - returns the full path name of the file. It must be freed with + * curl_free(). * * If 'dotscore' is TRUE, then check for the file first with a leading dot * and then with a leading underscore. @@ -115,7 +114,7 @@ char *findfile(const char *fname, int dotscore) continue; } if(conf_list[i].append) { - char *c = aprintf("%s%s", home, conf_list[i].append); + char *c = curl_maprintf("%s%s", home, conf_list[i].append); curl_free(home); if(!c) return NULL; @@ -146,6 +145,6 @@ char *findfile(const char *fname, int dotscore) return checkhome(home, fname, FALSE); } } -#endif /* PWD-stuff */ +#endif /* HAVE_GETPWUID && HAVE_GETEUID */ return NULL; } diff --git a/src/tool_formparse.c b/src/tool_formparse.c index b5ab10a00f..e129452d43 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -25,17 +25,15 @@ #include "tool_cfgable.h" #include "tool_msgs.h" -#include "tool_getparam.h" #include "tool_paramhlp.h" #include "tool_formparse.h" - -#include "memdebug.h" /* keep this as LAST include */ +#include "tool_parsecfg.h" /* tool_mime functions. */ static struct tool_mime *tool_mime_new(struct tool_mime *parent, toolmimekind kind) { - struct tool_mime *m = (struct tool_mime *) calloc(1, sizeof(*m)); + struct tool_mime *m = (struct tool_mime *)curlx_calloc(1, sizeof(*m)); if(m) { m->kind = kind; @@ -59,11 +57,11 @@ static struct tool_mime *tool_mime_new_data(struct tool_mime *parent, char *mime_data_copy; struct tool_mime *m = NULL; - mime_data_copy = strdup(mime_data); + mime_data_copy = curlx_strdup(mime_data); if(mime_data_copy) { m = tool_mime_new(parent, TOOLMIME_DATA); if(!m) - free(mime_data_copy); + curlx_free(mime_data_copy); else m->data = mime_data_copy; } @@ -87,8 +85,8 @@ static curl_off_t uztoso(size_t uznum) # pragma warning(disable:4310) /* cast truncates constant value */ #endif - DEBUGASSERT(uznum <= (size_t) CURL_MASK_SCOFFT); - return (curl_off_t)(uznum & (size_t) CURL_MASK_SCOFFT); + DEBUGASSERT(uznum <= (size_t)CURL_MASK_SCOFFT); + return (curl_off_t)(uznum & (size_t)CURL_MASK_SCOFFT); #if defined(__INTEL_COMPILER) || defined(_MSC_VER) # pragma warning(pop) @@ -106,35 +104,31 @@ static struct tool_mime *tool_mime_new_filedata(struct tool_mime *parent, *errcode = CURLE_OUT_OF_MEMORY; if(strcmp(filename, "-")) { /* This is a normal file. */ - char *filedup = strdup(filename); + char *filedup = curlx_strdup(filename); if(filedup) { m = tool_mime_new(parent, TOOLMIME_FILE); if(!m) - free(filedup); + curlx_free(filedup); else { m->data = filedup; if(!isremotefile) m->kind = TOOLMIME_FILEDATA; - *errcode = CURLE_OK; + *errcode = CURLE_OK; } } } else { /* Standard input. */ -#ifdef UNDER_CE - int fd = STDIN_FILENO; -#else int fd = fileno(stdin); -#endif char *data = NULL; curl_off_t size; curl_off_t origin; - struct_stat sbuf; + curlx_struct_stat sbuf; - CURLX_SET_BINMODE(stdin); + CURL_BINMODE(stdin); origin = ftell(stdin); /* If stdin is a regular file, do not buffer data but read it when needed. */ - if(fd >= 0 && origin >= 0 && !fstat(fd, &sbuf) && + if(fd >= 0 && origin >= 0 && !curlx_fstat(fd, &sbuf) && #ifdef __VMS sbuf.st_fab_rfm != FAB$C_VAR && sbuf.st_fab_rfm != FAB$C_VFC && #endif @@ -155,7 +149,7 @@ static struct tool_mime *tool_mime_new_filedata(struct tool_mime *parent, default: if(!stdinsize) { /* Zero-length data has been freed. Re-create it. */ - data = strdup(""); + data = curlx_strdup(""); if(!data) return m; } @@ -166,7 +160,7 @@ static struct tool_mime *tool_mime_new_filedata(struct tool_mime *parent, } m = tool_mime_new(parent, TOOLMIME_STDIN); if(!m) - tool_safefree(data); + curlx_safefree(data); else { m->data = data; m->origin = origin; @@ -187,22 +181,21 @@ void tool_mime_free(struct tool_mime *mime) tool_mime_free(mime->subparts); if(mime->prev) tool_mime_free(mime->prev); - tool_safefree(mime->name); - tool_safefree(mime->filename); - tool_safefree(mime->type); - tool_safefree(mime->encoder); - tool_safefree(mime->data); + curlx_safefree(mime->name); + curlx_safefree(mime->filename); + curlx_safefree(mime->type); + curlx_safefree(mime->encoder); + curlx_safefree(mime->data); curl_slist_free_all(mime->headers); - free(mime); + curlx_free(mime); } } - /* Mime part callbacks for stdin. */ size_t tool_mime_stdin_read(char *buffer, size_t size, size_t nitems, void *arg) { - struct tool_mime *sip = (struct tool_mime *) arg; + struct tool_mime *sip = (struct tool_mime *)arg; curl_off_t bytesleft; (void)size; /* Always 1: ignored. */ @@ -222,8 +215,9 @@ size_t tool_mime_stdin_read(char *buffer, /* Read from stdin. */ nitems = fread(buffer, 1, nitems, stdin); if(ferror(stdin)) { + char errbuf[STRERROR_LEN]; /* Show error only once. */ - warnf("stdin: %s", strerror(errno)); + warnf("stdin: %s", curlx_strerror(errno, errbuf, sizeof(errbuf))); return CURL_READFUNC_ABORT; } } @@ -234,7 +228,7 @@ size_t tool_mime_stdin_read(char *buffer, int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence) { - struct tool_mime *sip = (struct tool_mime *) instream; + struct tool_mime *sip = (struct tool_mime *)instream; switch(whence) { case SEEK_CUR: @@ -247,7 +241,7 @@ int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence) if(offset < 0) return CURL_SEEKFUNC_CANTSEEK; if(!sip->data) { - if(fseek(stdin, (long) (offset + sip->origin), SEEK_SET)) + if(curlx_fseek(stdin, offset + sip->origin, SEEK_SET)) return CURL_SEEKFUNC_CANTSEEK; } sip->curpos = offset; @@ -259,39 +253,39 @@ int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence) static CURLcode tool2curlparts(CURL *curl, struct tool_mime *m, curl_mime *mime) { - CURLcode ret = CURLE_OK; + CURLcode result = CURLE_OK; curl_mimepart *part = NULL; curl_mime *submime = NULL; const char *filename = NULL; if(m) { - ret = tool2curlparts(curl, m->prev, mime); - if(!ret) { + result = tool2curlparts(curl, m->prev, mime); + if(!result) { part = curl_mime_addpart(mime); if(!part) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } - if(!ret) { + if(!result) { filename = m->filename; switch(m->kind) { case TOOLMIME_PARTS: - ret = tool2curlmime(curl, m, &submime); - if(!ret) { - ret = curl_mime_subparts(part, submime); - if(ret) + result = tool2curlmime(curl, m, &submime); + if(!result) { + result = curl_mime_subparts(part, submime); + if(result) curl_mime_free(submime); } break; case TOOLMIME_DATA: - ret = curl_mime_data(part, m->data, CURL_ZERO_TERMINATED); + result = curl_mime_data(part, m->data, CURL_ZERO_TERMINATED); break; case TOOLMIME_FILE: case TOOLMIME_FILEDATA: - ret = curl_mime_filedata(part, m->data); - if(!ret && m->kind == TOOLMIME_FILEDATA && !filename) - ret = curl_mime_filename(part, NULL); + result = curl_mime_filedata(part, m->data); + if(!result && m->kind == TOOLMIME_FILEDATA && !filename) + result = curl_mime_filename(part, NULL); break; case TOOLMIME_STDIN: @@ -299,10 +293,10 @@ static CURLcode tool2curlparts(CURL *curl, struct tool_mime *m, filename = "-"; FALLTHROUGH(); case TOOLMIME_STDINDATA: - ret = curl_mime_data_cb(part, m->size, - (curl_read_callback) tool_mime_stdin_read, - (curl_seek_callback) tool_mime_stdin_seek, - NULL, m); + result = curl_mime_data_cb(part, m->size, + (curl_read_callback)tool_mime_stdin_read, + (curl_seek_callback)tool_mime_stdin_seek, + NULL, m); break; default: @@ -310,34 +304,34 @@ static CURLcode tool2curlparts(CURL *curl, struct tool_mime *m, break; } } - if(!ret && filename) - ret = curl_mime_filename(part, filename); - if(!ret) - ret = curl_mime_type(part, m->type); - if(!ret) - ret = curl_mime_headers(part, m->headers, 0); - if(!ret) - ret = curl_mime_encoder(part, m->encoder); - if(!ret) - ret = curl_mime_name(part, m->name); + if(!result && filename) + result = curl_mime_filename(part, filename); + if(!result) + result = curl_mime_type(part, m->type); + if(!result) + result = curl_mime_headers(part, m->headers, 0); + if(!result) + result = curl_mime_encoder(part, m->encoder); + if(!result) + result = curl_mime_name(part, m->name); } - return ret; + return result; } CURLcode tool2curlmime(CURL *curl, struct tool_mime *m, curl_mime **mime) { - CURLcode ret = CURLE_OK; + CURLcode result = CURLE_OK; *mime = curl_mime_init(curl); if(!*mime) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; else - ret = tool2curlparts(curl, m->subparts, *mime); - if(ret) { + result = tool2curlparts(curl, m->subparts, *mime); + if(result) { curl_mime_free(*mime); *mime = NULL; } - return ret; + return result; } /* @@ -376,8 +370,7 @@ static char *get_param_word(char **str, char **end_pos, char endchar) if(*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '"')) ++ptr; *ptr2++ = *ptr++; - } - while(ptr < *end_pos); + } while(ptr < *end_pos); *end_pos = ptr2; } ++ptr; @@ -416,64 +409,67 @@ static int slist_append(struct curl_slist **plist, const char *data) } /* Read headers from a file and append to list. */ -static int read_field_headers(const char *filename, FILE *fp, - struct curl_slist **pheaders) +static int read_field_headers(FILE *fp, struct curl_slist **pheaders) { - size_t hdrlen = 0; - size_t pos = 0; - bool incomment = FALSE; - int lineno = 1; - char hdrbuf[999] = ""; /* Max. header length + 1. */ + struct dynbuf line; + bool error = FALSE; + int err = 0; - for(;;) { - int c = getc(fp); - if(c == EOF || (!pos && !ISSPACE(c))) { - /* Strip and flush the current header. */ - while(hdrlen && ISSPACE(hdrbuf[hdrlen - 1])) - hdrlen--; - if(hdrlen) { - hdrbuf[hdrlen] = '\0'; - if(slist_append(pheaders, hdrbuf)) { - errorf("Out of memory for field headers"); - return -1; - } - hdrlen = 0; + curlx_dyn_init(&line, 8092); + while(my_get_line(fp, &line, &error)) { + const char *ptr = curlx_dyn_ptr(&line); + size_t len = curlx_dyn_len(&line); + bool folded = FALSE; + if(ptr[0] == '#') /* comment */ + continue; + else if(ptr[0] == ' ') /* a continuation from the line before */ + folded = TRUE; + /* trim off trailing CRLFs and whitespaces */ + while(len && (ISNEWLINE(ptr[len - 1]) || ISBLANK(ptr[len - 1]))) + len--; + + if(!len) + continue; + curlx_dyn_setlen(&line, len); /* set the new length */ + + if(folded && *pheaders) { + /* append this new line onto the previous line */ + struct dynbuf amend; + struct curl_slist *l = *pheaders; + curlx_dyn_init(&amend, 8092); + /* find the last node */ + while(l && l->next) + l = l->next; + /* add both parts */ + if(curlx_dyn_add(&amend, l->data) || curlx_dyn_addn(&amend, ptr, len)) { + err = -1; + break; + } + curl_free(l->data); + /* we use maprintf here to make it a libcurl alloc, to match the previous + curl_slist_append */ + l->data = curl_maprintf("%s", curlx_dyn_ptr(&amend)); + curlx_dyn_free(&amend); + if(!l->data) { + errorf("Out of memory for field headers"); + err = 1; } } - - switch(c) { - case EOF: - if(ferror(fp)) { - errorf("Header file %s read error: %s", filename, - strerror(errno)); - return -1; - } - return 0; /* Done. */ - case '\r': - continue; /* Ignore. */ - case '\n': - pos = 0; - incomment = FALSE; - lineno++; - continue; - case '#': - if(!pos) - incomment = TRUE; + else { + err = slist_append(pheaders, ptr); + } + if(err) { + errorf("Out of memory for field headers"); + err = -1; break; } - - pos++; - if(!incomment) { - if(hdrlen == sizeof(hdrbuf) - 1) { - warnf("File %s line %d: header too long (truncated)", - filename, lineno); - c = ' '; - } - if(hdrlen <= sizeof(hdrbuf) - 1) - hdrbuf[hdrlen++] = (char) c; - } } - /* NOTREACHED */ + if(error && !err) { + errorf("Failed to read field headers"); + err = -1; + } + curlx_dyn_free(&line); + return err; } static int get_param_part(char endchar, @@ -563,14 +559,16 @@ static int get_param_part(char endchar, endpos--; sep = *p; *endpos = '\0'; - fp = fopen(hdrfile, FOPEN_READTEXT); - if(!fp) + fp = curlx_fopen(hdrfile, FOPEN_READTEXT); + if(!fp) { + char errbuf[STRERROR_LEN]; warnf("Cannot read from %s: %s", hdrfile, - strerror(errno)); + curlx_strerror(errno, errbuf, sizeof(errbuf))); + } else { - int i = read_field_headers(hdrfile, fp, &headers); + int i = read_field_headers(fp, &headers); - fclose(fp); + curlx_fclose(fp); if(i) { curl_slist_free_all(headers); return -1; @@ -661,7 +659,6 @@ static int get_param_part(char endchar, return sep & 0xFF; } - /*************************************************************************** * * formparse() @@ -697,25 +694,25 @@ static int get_param_part(char endchar, * 'name=foo;headers=@headerfile' or why not * 'name=@filemame;headers=@headerfile' * - * To upload a file, but to fake the filename that will be included in the + * To upload a file, but to fake the filename that is included in the * formpost, do like this: * * 'name=@filename;filename=/dev/null' or quote the faked filename like: * 'name=@filename;filename="play, play, and play.txt"' * * If filename/path contains ',' or ';', it must be quoted by double-quotes, - * else curl will fail to figure out the correct filename. if the filename + * else curl fails to figure out the correct filename. if the filename * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash. * ***************************************************************************/ -#define SET_TOOL_MIME_PTR(m, field) \ - do { \ - if(field) { \ - (m)->field = strdup(field); \ - if(!(m)->field) \ - goto fail; \ - } \ +#define SET_TOOL_MIME_PTR(m, field) \ + do { \ + if(field) { \ + (m)->field = curlx_strdup(field); \ + if(!(m)->field) \ + goto fail; \ + } \ } while(0) int formparse(const char *input, @@ -723,8 +720,8 @@ int formparse(const char *input, struct tool_mime **mimecurrent, bool literal_value) { - /* input MUST be a string in the format 'name=contents' and we will - build a linked list with the info */ + /* input MUST be a string in the format 'name=contents' and we build + a linked list with the info */ char *name = NULL; char *contents = NULL; char *contp; @@ -734,7 +731,7 @@ int formparse(const char *input, char *encoder = NULL; struct curl_slist *headers = NULL; struct tool_mime *part = NULL; - CURLcode res; + CURLcode result; int err = 1; /* Allocate the main mime structure if needed. */ @@ -746,7 +743,7 @@ int formparse(const char *input, } /* Make a copy we can overwrite. */ - contents = strdup(input); + contents = curlx_strdup(input); if(!contents) goto fail; @@ -808,28 +805,27 @@ int formparse(const char *input, } /* Store that file in a part. */ - part = tool_mime_new_filedata(subparts, data, TRUE, &res); + part = tool_mime_new_filedata(subparts, data, TRUE, &result); if(!part) goto fail; part->headers = headers; headers = NULL; - if(res == CURLE_READ_ERROR) { - /* An error occurred while reading stdin: if read has started, - issue the error now. Else, delay it until processed by - libcurl. */ + if(result == CURLE_READ_ERROR) { + /* An error occurred while reading stdin: if read has started, + issue the error now. Else, delay it until processed by libcurl. */ if(part->size > 0) { warnf("error while reading standard input"); goto fail; } - tool_safefree(part->data); + curlx_safefree(part->data); part->size = -1; - res = CURLE_OK; + result = CURLE_OK; } SET_TOOL_MIME_PTR(part, filename); SET_TOOL_MIME_PTR(part, type); SET_TOOL_MIME_PTR(part, encoder); - /* *contp could be '\0', so we just check with the delimiter */ + /* *contp could be '\0', so we check with the delimiter */ } while(sep); /* loop if there is another filename */ part = (*mimecurrent)->subparts; /* Set name on group. */ } @@ -841,23 +837,22 @@ int formparse(const char *input, if(sep < 0) goto fail; - part = tool_mime_new_filedata(*mimecurrent, data, FALSE, - &res); + part = tool_mime_new_filedata(*mimecurrent, data, FALSE, &result); if(!part) goto fail; part->headers = headers; headers = NULL; - if(res == CURLE_READ_ERROR) { - /* An error occurred while reading stdin: if read has started, - issue the error now. Else, delay it until processed by - libcurl. */ + if(result == CURLE_READ_ERROR) { + /* An error occurred while reading stdin: if read has started, + issue the error now. Else, delay it until processed by + libcurl. */ if(part->size > 0) { warnf("error while reading standard input"); goto fail; } - tool_safefree(part->data); + curlx_safefree(part->data); part->size = -1; - res = CURLE_OK; + result = CURLE_OK; } } else { @@ -882,7 +877,7 @@ int formparse(const char *input, SET_TOOL_MIME_PTR(part, encoder); if(sep) { - *contp = (char) sep; + *contp = (char)sep; warnf("garbage at end of field specification: %s", contp); } } @@ -896,7 +891,7 @@ int formparse(const char *input, } err = 0; fail: - tool_safefree(contents); + curlx_safefree(contents); curl_slist_free_all(headers); return err; } diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 6be57dbd5c..30163561ea 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -38,22 +38,20 @@ #include "tool_help.h" #include "var.h" -#include "memdebug.h" /* keep this as LAST include */ - #define ALLOW_BLANK TRUE -#define DENY_BLANK FALSE +#define DENY_BLANK FALSE static ParameterError getstr(char **str, const char *val, bool allowblank) { if(*str) { - free(*str); + curlx_free(*str); *str = NULL; } DEBUGASSERT(val); if(!allowblank && !val[0]) return PARAM_BLANK_STRING; - *str = strdup(val); + *str = curlx_strdup(val); if(!*str) return PARAM_NO_MEM; @@ -64,20 +62,17 @@ static ParameterError getstrn(char **str, const char *val, size_t len, bool allowblank) { if(*str) { - free(*str); + curlx_free(*str); *str = NULL; } DEBUGASSERT(val); if(!allowblank && !val[0]) return PARAM_BLANK_STRING; - *str = malloc(len + 1); + *str = curlx_memdup0(val, len); if(!*str) return PARAM_NO_MEM; - memcpy(*str, val, len); - (*str)[len] = 0; /* null-terminate */ - return PARAM_OK; } @@ -102,7 +97,7 @@ static const struct LongShort aliases[]= { {"compressed", ARG_BOOL, ' ', C_COMPRESSED}, {"compressed-ssh", ARG_BOOL, ' ', C_COMPRESSED_SSH}, {"config", ARG_FILE, 'K', C_CONFIG}, - {"connect-timeout", ARG_STRG, ' ', C_CONNECT_TIMEOUT}, + {"connect-timeout", ARG_SECS, ' ', C_CONNECT_TIMEOUT}, {"connect-to", ARG_STRG, ' ', C_CONNECT_TO}, {"continue-at", ARG_STRG, 'C', C_CONTINUE_AT}, {"cookie", ARG_STRG, 'b', C_COOKIE}, @@ -129,7 +124,7 @@ static const struct LongShort aliases[]= { {"dns-servers", ARG_STRG, ' ', C_DNS_SERVERS}, {"doh-cert-status", ARG_BOOL|ARG_TLS, ' ', C_DOH_CERT_STATUS}, {"doh-insecure", ARG_BOOL|ARG_TLS, ' ', C_DOH_INSECURE}, - {"doh-url" , ARG_STRG, ' ', C_DOH_URL}, + {"doh-url", ARG_STRG, ' ', C_DOH_URL}, {"dump-ca-embed", ARG_NONE|ARG_TLS, ' ', C_DUMP_CA_EMBED}, {"dump-header", ARG_FILE, 'D', C_DUMP_HEADER}, {"ech", ARG_STRG|ARG_TLS, ' ', C_ECH}, @@ -139,7 +134,7 @@ static const struct LongShort aliases[]= { {"epsv", ARG_BOOL, ' ', C_EPSV}, {"etag-compare", ARG_FILE, ' ', C_ETAG_COMPARE}, {"etag-save", ARG_FILE, ' ', C_ETAG_SAVE}, - {"expect100-timeout", ARG_STRG, ' ', C_EXPECT100_TIMEOUT}, + {"expect100-timeout", ARG_SECS, ' ', C_EXPECT100_TIMEOUT}, {"fail", ARG_BOOL, 'f', C_FAIL}, {"fail-early", ARG_BOOL, ' ', C_FAIL_EARLY}, {"fail-with-body", ARG_BOOL, ' ', C_FAIL_WITH_BODY}, @@ -163,7 +158,7 @@ static const struct LongShort aliases[]= { {"ftp-ssl-reqd", ARG_BOOL|ARG_TLS, ' ', C_FTP_SSL_REQD}, {"get", ARG_BOOL, 'G', C_GET}, {"globoff", ARG_BOOL, 'g', C_GLOBOFF}, - {"happy-eyeballs-timeout-ms", ARG_STRG, ' ', C_HAPPY_EYEBALLS_TIMEOUT_MS}, + {"happy-eyeballs-timeout-ms", ARG_UNUM, ' ', C_HAPPY_EYEBALLS_TIMEOUT_MS}, {"haproxy-clientip", ARG_STRG, ' ', C_HAPROXY_CLIENTIP}, {"haproxy-protocol", ARG_BOOL, ' ', C_HAPROXY_PROTOCOL}, {"head", ARG_BOOL, 'I', C_HEAD}, @@ -186,18 +181,19 @@ static const struct LongShort aliases[]= { {"ip-tos", ARG_STRG, ' ', C_IP_TOS}, #ifndef CURL_DISABLE_IPFS {"ipfs-gateway", ARG_STRG, ' ', C_IPFS_GATEWAY}, -#endif /* !CURL_DISABLE_IPFS */ +#endif {"ipv4", ARG_NONE, '4', C_IPV4}, {"ipv6", ARG_NONE, '6', C_IPV6}, {"json", ARG_STRG, ' ', C_JSON}, {"junk-session-cookies", ARG_BOOL, 'j', C_JUNK_SESSION_COOKIES}, {"keepalive", ARG_BOOL|ARG_NO, ' ', C_KEEPALIVE}, - {"keepalive-cnt", ARG_STRG, ' ', C_KEEPALIVE_CNT}, - {"keepalive-time", ARG_STRG, ' ', C_KEEPALIVE_TIME}, + {"keepalive-cnt", ARG_UNUM, ' ', C_KEEPALIVE_CNT}, + {"keepalive-time", ARG_UNUM, ' ', C_KEEPALIVE_TIME}, {"key", ARG_FILE, ' ', C_KEY}, {"key-type", ARG_STRG|ARG_TLS, ' ', C_KEY_TYPE}, - {"krb", ARG_STRG, ' ', C_KRB}, - {"krb4", ARG_STRG, ' ', C_KRB4}, + {"knownhosts", ARG_FILE, ' ', C_KNOWNHOSTS}, + {"krb", ARG_STRG|ARG_DEPR, ' ', C_KRB}, + {"krb4", ARG_STRG|ARG_DEPR, ' ', C_KRB4}, {"libcurl", ARG_STRG, ' ', C_LIBCURL}, {"limit-rate", ARG_STRG, ' ', C_LIMIT_RATE}, {"list-only", ARG_BOOL, 'l', C_LIST_ONLY}, @@ -212,7 +208,7 @@ static const struct LongShort aliases[]= { {"manual", ARG_BOOL, 'M', C_MANUAL}, {"max-filesize", ARG_STRG, ' ', C_MAX_FILESIZE}, {"max-redirs", ARG_STRG, ' ', C_MAX_REDIRS}, - {"max-time", ARG_STRG, 'm', C_MAX_TIME}, + {"max-time", ARG_SECS, 'm', C_MAX_TIME}, {"metalink", ARG_BOOL|ARG_DEPR, ' ', C_METALINK}, {"mptcp", ARG_BOOL, ' ', C_MPTCP}, {"negotiate", ARG_BOOL, ' ', C_NEGOTIATE}, @@ -230,8 +226,8 @@ static const struct LongShort aliases[]= { {"output-dir", ARG_STRG, ' ', C_OUTPUT_DIR}, {"parallel", ARG_BOOL, 'Z', C_PARALLEL}, {"parallel-immediate", ARG_BOOL, ' ', C_PARALLEL_IMMEDIATE}, - {"parallel-max", ARG_STRG, ' ', C_PARALLEL_MAX}, - {"parallel-max-host", ARG_STRG, ' ', C_PARALLEL_HOST}, + {"parallel-max", ARG_UNUM, ' ', C_PARALLEL_MAX}, + {"parallel-max-host", ARG_UNUM, ' ', C_PARALLEL_HOST}, {"pass", ARG_STRG|ARG_CLEAR, ' ', C_PASS}, {"path-as-is", ARG_BOOL, ' ', C_PATH_AS_IS}, {"pinnedpubkey", ARG_STRG|ARG_TLS, ' ', C_PINNEDPUBKEY}, @@ -250,7 +246,8 @@ static const struct LongShort aliases[]= { {"proxy-ca-native", ARG_BOOL|ARG_TLS, ' ', C_PROXY_CA_NATIVE}, {"proxy-cacert", ARG_FILE|ARG_TLS, ' ', C_PROXY_CACERT}, {"proxy-capath", ARG_FILE|ARG_TLS, ' ', C_PROXY_CAPATH}, - {"proxy-cert", ARG_FILE|ARG_TLS|ARG_CLEAR, ' ', C_PROXY_CERT}, + {"proxy-cert", ARG_FILE|ARG_TLS|ARG_CLEAR, ' ', + C_PROXY_CERT}, {"proxy-cert-type", ARG_STRG|ARG_TLS, ' ', C_PROXY_CERT_TYPE}, {"proxy-ciphers", ARG_STRG|ARG_TLS, ' ', C_PROXY_CIPHERS}, {"proxy-crlfile", ARG_FILE|ARG_TLS, ' ', C_PROXY_CRLFILE}, @@ -292,11 +289,11 @@ static const struct LongShort aliases[]= { {"request", ARG_STRG, 'X', C_REQUEST}, {"request-target", ARG_STRG, ' ', C_REQUEST_TARGET}, {"resolve", ARG_STRG, ' ', C_RESOLVE}, - {"retry", ARG_STRG, ' ', C_RETRY}, + {"retry", ARG_UNUM, ' ', C_RETRY}, {"retry-all-errors", ARG_BOOL, ' ', C_RETRY_ALL_ERRORS}, {"retry-connrefused", ARG_BOOL, ' ', C_RETRY_CONNREFUSED}, - {"retry-delay", ARG_STRG, ' ', C_RETRY_DELAY}, - {"retry-max-time", ARG_STRG, ' ', C_RETRY_MAX_TIME}, + {"retry-delay", ARG_SECS, ' ', C_RETRY_DELAY}, + {"retry-max-time", ARG_SECS, ' ', C_RETRY_MAX_TIME}, {"sasl-authzid", ARG_STRG, ' ', C_SASL_AUTHZID}, {"sasl-ir", ARG_BOOL, ' ', C_SASL_IR}, {"service-name", ARG_STRG, ' ', C_SERVICE_NAME}, @@ -315,8 +312,8 @@ static const struct LongShort aliases[]= { {"socks5-gssapi-nec", ARG_BOOL, ' ', C_SOCKS5_GSSAPI_NEC}, {"socks5-gssapi-service", ARG_STRG, ' ', C_SOCKS5_GSSAPI_SERVICE}, {"socks5-hostname", ARG_STRG, ' ', C_SOCKS5_HOSTNAME}, - {"speed-limit", ARG_STRG, 'Y', C_SPEED_LIMIT}, - {"speed-time", ARG_STRG, 'y', C_SPEED_TIME}, + {"speed-limit", ARG_UNUM, 'Y', C_SPEED_LIMIT}, + {"speed-time", ARG_UNUM, 'y', C_SPEED_TIME}, {"ssl", ARG_BOOL|ARG_TLS, ' ', C_SSL}, {"ssl-allow-beast", ARG_BOOL|ARG_TLS, ' ', C_SSL_ALLOW_BEAST}, {"ssl-auto-client-cert", ARG_BOOL|ARG_TLS, ' ', @@ -338,7 +335,7 @@ static const struct LongShort aliases[]= { {"test-duphandle", ARG_BOOL, ' ', C_TEST_DUPHANDLE}, {"test-event", ARG_BOOL, ' ', C_TEST_EVENT}, #endif - {"tftp-blksize", ARG_STRG, ' ', C_TFTP_BLKSIZE}, + {"tftp-blksize", ARG_UNUM, ' ', C_TFTP_BLKSIZE}, {"tftp-no-options", ARG_BOOL, ' ', C_TFTP_NO_OPTIONS}, {"time-cond", ARG_STRG, 'z', C_TIME_COND}, {"tls-earlydata", ARG_BOOL|ARG_TLS, ' ', C_TLS_EARLYDATA}, @@ -369,7 +366,7 @@ static const struct LongShort aliases[]= { {"variable", ARG_STRG, ' ', C_VARIABLE}, {"verbose", ARG_BOOL, 'v', C_VERBOSE}, {"version", ARG_BOOL, 'V', C_VERSION}, - {"vlan-priority", ARG_STRG, ' ', C_VLAN_PRIORITY}, + {"vlan-priority", ARG_UNUM, ' ', C_VLAN_PRIORITY}, #ifdef USE_WATT32 {"wdebug", ARG_BOOL, ' ', C_WDEBUG}, #endif @@ -380,24 +377,25 @@ static const struct LongShort aliases[]= { /* Split the argument of -E to 'certname' and 'passphrase' separated by colon. * We allow ':' and '\' to be escaped by '\' so that we can use certificate * nicknames containing ':'. See - * for details. */ -#ifndef UNITTESTS -static -#endif -void parse_cert_parameter(const char *cert_parameter, - char **certname, - char **passphrase) + * for details. + * + * Unit test 1394 + */ +UNITTEST ParameterError parse_cert_parameter(const char *cert_parameter, + char **certname, + char **passphrase) { size_t param_length = strlen(cert_parameter); size_t span; const char *param_place = NULL; char *certname_place = NULL; + ParameterError err = PARAM_OK; *certname = NULL; *passphrase = NULL; /* most trivial assumption: cert_parameter is empty */ if(param_length == 0) - return; + return PARAM_BLANK_STRING; /* next less trivial: cert_parameter starts 'pkcs11:' and thus * looks like a RFC7512 PKCS#11 URI which can be used as-is. @@ -405,13 +403,17 @@ void parse_cert_parameter(const char *cert_parameter, * means no passphrase was given and no characters escaped */ if(curl_strnequal(cert_parameter, "pkcs11:", 7) || !strpbrk(cert_parameter, ":\\")) { - *certname = strdup(cert_parameter); - return; + *certname = curlx_strdup(cert_parameter); + if(!*certname) + return PARAM_NO_MEM; + return PARAM_OK; } /* deal with escaped chars; find unescaped colon if it exists */ - certname_place = malloc(param_length + 1); - if(!certname_place) - return; + certname_place = curlx_malloc(param_length + 1); + if(!certname_place) { + err = PARAM_NO_MEM; + goto done; + } *certname = certname_place; param_place = cert_parameter; @@ -420,7 +422,7 @@ void parse_cert_parameter(const char *cert_parameter, memcpy(certname_place, param_place, span); param_place += span; certname_place += span; - /* we just ate all the non-special chars. now we are on either a special + /* we ate all the non-special chars. now we are on either a special * char or the end of the string. */ switch(*param_place) { case '\0': @@ -428,22 +430,22 @@ void parse_cert_parameter(const char *cert_parameter, case '\\': param_place++; switch(*param_place) { - case '\0': - *certname_place++ = '\\'; - break; - case '\\': - *certname_place++ = '\\'; - param_place++; - break; - case ':': - *certname_place++ = ':'; - param_place++; - break; - default: - *certname_place++ = '\\'; - *certname_place++ = *param_place; - param_place++; - break; + case '\0': + *certname_place++ = '\\'; + break; + case '\\': + *certname_place++ = '\\'; + param_place++; + break; + case ':': + *certname_place++ = ':'; + param_place++; + break; + default: + *certname_place++ = '\\'; + *certname_place++ = *param_place; + param_place++; + break; } break; case ':': @@ -455,7 +457,7 @@ void parse_cert_parameter(const char *cert_parameter, #ifdef _WIN32 if((param_place == &cert_parameter[1]) && (cert_parameter[2] == '\\' || cert_parameter[2] == '/') && - (ISALPHA(cert_parameter[0])) ) { + ISALPHA(cert_parameter[0])) { /* colon in the second column, followed by a backslash, and the first character is an alphabetic letter: @@ -469,13 +471,20 @@ void parse_cert_parameter(const char *cert_parameter, * above; if we are still here, this is a separating colon */ param_place++; if(*param_place) { - *passphrase = strdup(param_place); + *passphrase = curlx_strdup(param_place); + if(!*passphrase) + err = PARAM_NO_MEM; } goto done; } } done: - *certname_place = '\0'; + if(err) { + curlx_safefree(*certname); + } + else + *certname_place = '\0'; + return err; } /* Replace (in-place) '%20' by '+' according to RFC1866 */ @@ -492,7 +501,7 @@ static size_t replace_url_encoded_space_by_plus(char *url) url[new_index] = '+'; orig_index += 3; } - else{ + else { if(new_index != orig_index) { url[new_index] = url[orig_index]; } @@ -506,68 +515,111 @@ static size_t replace_url_encoded_space_by_plus(char *url) return new_index; /* new size */ } -static void -GetFileAndPassword(const char *nextarg, char **file, char **password) +static ParameterError GetFileAndPassword(const char *nextarg, char **file, + char **password) { char *certname, *passphrase; + ParameterError err; /* nextarg is never NULL here */ - parse_cert_parameter(nextarg, &certname, &passphrase); - free(*file); - *file = certname; - if(passphrase) { - free(*password); - *password = passphrase; + err = parse_cert_parameter(nextarg, &certname, &passphrase); + if(!err) { + curlx_free(*file); + *file = certname; + if(passphrase) { + curlx_free(*password); + *password = passphrase; + } } + return err; +} + +struct sizeunit { + char unit; /* single lowercase ASCII letter */ + curl_off_t mul; + size_t mlen; /* number of digits in 'mul', when written in decimal */ +}; + +static const struct sizeunit *getunit(char unit) +{ + static const struct sizeunit list[] = { + {'p', (curl_off_t)1125899906842624, 16 }, /* Peta */ + {'t', (curl_off_t)1099511627776, 13 }, /* Tera */ + {'g', 1073741824, 10 }, /* Giga */ + {'m', 1048576, 7 }, /* Mega */ + {'k', 1024, 4 }, /* Kilo */ + }; + + size_t i; + for(i = 0; i < CURL_ARRAYSIZE(list); i++) + if((unit | 0x20) == list[i].unit) + return &list[i]; + return NULL; } /* Get a size parameter for '--limit-rate' or '--max-filesize'. - * We support a 'G', 'M' or 'K' suffix too. - */ -static ParameterError GetSizeParameter(const char *arg, - const char *which, - curl_off_t *value_out) + We support P, T, G, M and K (case insensitive) suffixes. + + Unit test 1623 + */ +UNITTEST ParameterError GetSizeParameter(const char *arg, curl_off_t *out) { const char *unit = arg; curl_off_t value; + curl_off_t prec = 0; + size_t plen = 0; + curl_off_t add = 0; + curl_off_t mul = 1; + int rc; - if(curlx_str_number(&unit, &value, CURL_OFF_T_MAX)) { - warnf("invalid number specified for %s", which); - return PARAM_BAD_USE; + rc = curlx_str_number(&unit, &value, CURL_OFF_T_MAX); + if(rc == STRE_OVERFLOW) + return PARAM_NUMBER_TOO_LARGE; + else if(rc) + return PARAM_BAD_NUMERIC; + + if(!curlx_str_single(&unit, '.')) { + const char *s = unit; + if(curlx_str_number(&unit, &prec, CURL_OFF_T_MAX)) + return PARAM_BAD_NUMERIC; + plen = unit - s; } - if(!*unit) - unit = "b"; - else if(strlen(unit) > 1) - unit = "w"; /* unsupported */ - - switch(*unit) { - case 'G': - case 'g': - if(value > (CURL_OFF_T_MAX / (1024*1024*1024))) - return PARAM_NUMBER_TOO_LARGE; - value *= 1024*1024*1024; - break; - case 'M': - case 'm': - if(value > (CURL_OFF_T_MAX / (1024*1024))) - return PARAM_NUMBER_TOO_LARGE; - value *= 1024*1024; - break; - case 'K': - case 'k': - if(value > (CURL_OFF_T_MAX / 1024)) - return PARAM_NUMBER_TOO_LARGE; - value *= 1024; - break; - case 'b': - case 'B': - /* for plain bytes, leave as-is */ - break; - default: - warnf("unsupported %s unit. Use G, M, K or B", which); + if(strlen(unit) > 1) return PARAM_BAD_USE; + else if(!*unit || ((*unit | 0x20) == 'b')) { + if(plen) + /* cannot handle partial bytes */ + return PARAM_BAD_USE; } - *value_out = value; + else { + const struct sizeunit *su = getunit(*unit); + if(!su) + return PARAM_BAD_USE; + mul = su->mul; + + if(prec) { + /* precision was provided */ + curl_off_t frac = 1; + + /* too many precision digits, trim them */ + while(su->mlen <= plen) { + prec /= 10; + plen--; + } + + while(plen--) + frac *= 10; + + if((CURL_OFF_T_MAX / mul) > prec) + add = mul * prec / frac; + else + add = (mul / frac) * prec; + } + } + if(value > ((CURL_OFF_T_MAX - add) / mul)) + return PARAM_NUMBER_TOO_LARGE; + + *out = (value * mul) + add; return PARAM_OK; } @@ -587,7 +639,7 @@ static void cleanarg(char *str) #endif /* the maximum size we allow the dynbuf generated string */ -#define MAX_DATAURLENCODE (500*1024*1024) +#define MAX_DATAURLENCODE (500 * 1024 * 1024) /* --data-urlencode */ static ParameterError data_urlencode(const char *nextarg, @@ -624,10 +676,10 @@ static ParameterError data_urlencode(const char *nextarg, /* a '@' letter, it means that a filename or - (stdin) follows */ if(!strcmp("-", p)) { file = stdin; - CURLX_SET_BINMODE(stdin); + CURL_BINMODE(stdin); } else { - file = fopen(p, "rb"); + file = curlx_fopen(p, "rb"); if(!file) { errorf("Failed to open %s", p); return PARAM_READ_ERROR; @@ -637,7 +689,7 @@ static ParameterError data_urlencode(const char *nextarg, err = file2memory(&postdata, &size, file); if(file && (file != stdin)) - fclose(file); + curlx_fclose(file); if(err) return err; } @@ -651,14 +703,14 @@ static ParameterError data_urlencode(const char *nextarg, if(!postdata) { /* no data from the file, point to a zero byte string to make this get sent as a POST anyway */ - postdata = strdup(""); + postdata = curlx_strdup(""); if(!postdata) return PARAM_NO_MEM; size = 0; } else { char *enc = curl_easy_escape(NULL, postdata, (int)size); - tool_safefree(postdata); /* no matter if it worked or not */ + curlx_safefree(postdata); /* no matter if it worked or not */ if(enc) { char *n; replace_url_encoded_space_by_plus(enc); @@ -676,7 +728,11 @@ static ParameterError data_urlencode(const char *nextarg, size = curlx_dyn_len(&dyn); } else { - n = enc; + /* make sure we return "our memory" */ + n = curlx_strdup(enc); + curl_free(enc); + if(!n) + return PARAM_NO_MEM; size = strlen(n); } postdata = n; @@ -691,8 +747,7 @@ error: return err; } -static void sethttpver(struct OperationConfig *config, - long httpversion) +static void sethttpver(struct OperationConfig *config, long httpversion) { if(config->httpversion && (config->httpversion != httpversion)) @@ -717,26 +772,29 @@ static CURLcode set_trace_config(const char *token) len = strlen(token); switch(*token) { - case '-': - toggle = FALSE; - name = token + 1; - len--; - break; - case '+': - toggle = TRUE; - name = token + 1; - len--; - break; - default: - toggle = TRUE; - name = token; - break; + case '-': + toggle = FALSE; + name = token + 1; + len--; + break; + case '+': + toggle = TRUE; + name = token + 1; + len--; + break; + default: + toggle = TRUE; + name = token; + break; } if((len == 3) && curl_strnequal(name, "all", 3)) { global->traceids = toggle; global->tracetime = toggle; - result = curl_global_trace(token); + if(toggle) + result = curl_global_trace("all,-lib-ids"); + else + result = curl_global_trace(token); if(result) goto out; } @@ -747,9 +805,9 @@ static CURLcode set_trace_config(const char *token) global->tracetime = toggle; } else { - char buffer[32]; - msnprintf(buffer, sizeof(buffer), "%c%.*s", toggle ? '+' : '-', - (int)len, name); + char buffer[64]; + curl_msnprintf(buffer, sizeof(buffer), "%c%.*s,-lib-ids", + toggle ? '+' : '-', (int)len, name); result = curl_global_trace(buffer); if(result) goto out; @@ -798,37 +856,37 @@ struct TOSEntry { }; static const struct TOSEntry tos_entries[] = { - {"AF11", 0x28}, - {"AF12", 0x30}, - {"AF13", 0x38}, - {"AF21", 0x48}, - {"AF22", 0x50}, - {"AF23", 0x58}, - {"AF31", 0x68}, - {"AF32", 0x70}, - {"AF33", 0x78}, - {"AF41", 0x88}, - {"AF42", 0x90}, - {"AF43", 0x98}, - {"CE", 0x03}, - {"CS0", 0x00}, - {"CS1", 0x20}, - {"CS2", 0x40}, - {"CS3", 0x60}, - {"CS4", 0x80}, - {"CS5", 0xa0}, - {"CS6", 0xc0}, - {"CS7", 0xe0}, - {"ECT0", 0x02}, - {"ECT1", 0x01}, - {"EF", 0xb8}, - {"LE", 0x04}, - {"LOWCOST", 0x02}, - {"LOWDELAY", 0x10}, - {"MINCOST", 0x02}, - {"RELIABILITY", 0x04}, - {"THROUGHPUT", 0x08}, - {"VOICE-ADMIT", 0xb0} + { "AF11", 0x28 }, + { "AF12", 0x30 }, + { "AF13", 0x38 }, + { "AF21", 0x48 }, + { "AF22", 0x50 }, + { "AF23", 0x58 }, + { "AF31", 0x68 }, + { "AF32", 0x70 }, + { "AF33", 0x78 }, + { "AF41", 0x88 }, + { "AF42", 0x90 }, + { "AF43", 0x98 }, + { "CE", 0x03 }, + { "CS0", 0x00 }, + { "CS1", 0x20 }, + { "CS2", 0x40 }, + { "CS3", 0x60 }, + { "CS4", 0x80 }, + { "CS5", 0xa0 }, + { "CS6", 0xc0 }, + { "CS7", 0xe0 }, + { "ECT0", 0x02 }, + { "ECT1", 0x01 }, + { "EF", 0xb8 }, + { "LE", 0x04 }, + { "LOWCOST", 0x02 }, + { "LOWDELAY", 0x10 }, + { "MINCOST", 0x02 }, + { "RELIABILITY", 0x04 }, + { "THROUGHPUT", 0x08 }, + { "VOICE-ADMIT", 0xb0 } }; static int find_tos(const void *a, const void *b) @@ -850,7 +908,7 @@ static ParameterError url_query(const char *nextarg, if(*nextarg == '+') { /* use without encoding */ - query = strdup(&nextarg[1]); + query = curlx_strdup(&nextarg[1]); if(!query) err = PARAM_NO_MEM; } @@ -860,11 +918,11 @@ static ParameterError url_query(const char *nextarg, if(!err) { if(config->query) { CURLcode result = curlx_dyn_addf(&dyn, "%s&%s", config->query, query); - free(query); + curlx_free(query); if(result) err = PARAM_NO_MEM; else { - free(config->query); + curlx_free(config->query); config->query = curlx_dyn_ptr(&dyn); } } @@ -896,10 +954,10 @@ static ParameterError set_data(cmdline_t cmd, if(!strcmp("-", nextarg)) { file = stdin; if(cmd == C_DATA_BINARY) /* forced data-binary */ - CURLX_SET_BINMODE(stdin); + CURL_BINMODE(stdin); } else { - file = fopen(nextarg, "rb"); + file = curlx_fopen(nextarg, "rb"); if(!file) { errorf("Failed to open %s", nextarg); return PARAM_READ_ERROR; @@ -917,14 +975,14 @@ static ParameterError set_data(cmdline_t cmd, } if(file && (file != stdin)) - fclose(file); + curlx_fclose(file); if(err) return err; if(!postdata) { /* no data from the file, point to a zero byte string to make this get sent as a POST anyway */ - postdata = strdup(""); + postdata = curlx_strdup(""); if(!postdata) return PARAM_NO_MEM; } @@ -948,7 +1006,7 @@ static ParameterError set_data(cmdline_t cmd, if(!err && curlx_dyn_addn(&config->postdata, postdata, size)) err = PARAM_NO_MEM; - tool_safefree(postdata); + curlx_safefree(postdata); config->postfields = curlx_dyn_ptr(&config->postdata); return err; @@ -963,43 +1021,35 @@ static ParameterError set_rate(const char *nextarg) /m == per minute /h == per hour (default) /d == per day (24 hours) - */ + */ ParameterError err = PARAM_OK; - const char *div = strchr(nextarg, '/'); - char number[26]; - long denominator; - long numerator = 60*60*1000; /* default per hour */ - size_t numlen = div ? (size_t)(div - nextarg) : strlen(nextarg); - if(numlen > sizeof(number) -1) - return PARAM_NUMBER_TOO_LARGE; + const char *p = nextarg; + curl_off_t denominator; + curl_off_t numerator = 60 * 60 * 1000; /* default per hour */ - memcpy(number, nextarg, numlen); - number[numlen] = 0; - err = str2unum(&denominator, number); - if(err) - return err; + if(curlx_str_number(&p, &denominator, CURL_OFF_T_MAX)) + return PARAM_BAD_NUMERIC; if(denominator < 1) return PARAM_BAD_USE; - if(div) { + if(!curlx_str_single(&p, '/')) { curl_off_t numunits; - div++; - if(curlx_str_number(&div, &numunits, CURL_OFF_T_MAX)) + if(curlx_str_number(&p, &numunits, CURL_OFF_T_MAX)) numunits = 1; - switch(*div) { + switch(*p) { case 's': /* per second */ numerator = 1000; break; case 'm': /* per minute */ - numerator = 60*1000; + numerator = 60 * 1000; break; case 'h': /* per hour */ break; case 'd': /* per day */ - numerator = 24*60*60*1000; + numerator = 24 * 60 * 60 * 1000; break; default: errorf("unsupported --rate unit"); @@ -1007,13 +1057,14 @@ static ParameterError set_rate(const char *nextarg) break; } - if((LONG_MAX / numerator) < numunits) { + if((CURL_OFF_T_MAX / numerator) < numunits) { /* overflow, too large number */ errorf("too large --rate unit"); err = PARAM_NUMBER_TOO_LARGE; } - /* this typecast is okay based on the check above */ - numerator *= (long)numunits; + else + /* this typecast is okay based on the check above */ + numerator *= numunits; } if(err) @@ -1021,7 +1072,7 @@ static ParameterError set_rate(const char *nextarg) else if(denominator > numerator) err = PARAM_NUMBER_TOO_LARGE; else - global->ms_per_transfer = numerator/denominator; + global->ms_per_transfer = numerator / denominator; return err; } @@ -1093,7 +1144,7 @@ static ParameterError parse_url(struct OperationConfig *config, if(fromstdin) f = stdin; else - f = fopen(&nextarg[1], FOPEN_READTEXT); + f = curlx_fopen(&nextarg[1], FOPEN_READTEXT); if(f) { curlx_dyn_init(&line, 8092); while(my_get_line(f, &line, &error)) { @@ -1103,7 +1154,7 @@ static ParameterError parse_url(struct OperationConfig *config, break; } if(!fromstdin) - fclose(f); + curlx_fclose(f); curlx_dyn_free(&line); if(error || err) return PARAM_READ_ERROR; @@ -1114,7 +1165,6 @@ static ParameterError parse_url(struct OperationConfig *config, return add_url(config, nextarg, FALSE); } - static ParameterError parse_localport(struct OperationConfig *config, const char *nextarg) { @@ -1136,7 +1186,7 @@ static ParameterError parse_localport(struct OperationConfig *config, if(ISBLANK(*pp)) pp++; } - msnprintf(buffer, sizeof(buffer), "%.*s", (int)plen, nextarg); + curl_msnprintf(buffer, sizeof(buffer), "%.*s", (int)plen, nextarg); if(str2unummax(&config->localport, buffer, 65535)) return PARAM_BAD_USE; if(!pp) @@ -1144,7 +1194,7 @@ static ParameterError parse_localport(struct OperationConfig *config, else { if(str2unummax(&config->localportrange, pp, 65535)) return PARAM_BAD_USE; - config->localportrange -= (config->localport-1); + config->localportrange -= (config->localport - 1); if(config->localportrange < 1) return PARAM_BAD_USE; } @@ -1205,27 +1255,33 @@ static ParameterError parse_ech(struct OperationConfig *config, file = stdin; } else { - file = fopen(nextarg, FOPEN_READTEXT); + file = curlx_fopen(nextarg, FOPEN_READTEXT); } if(!file) { - warnf("Couldn't read file \"%s\" " + warnf("Could not read file \"%s\" " "specified for \"--ech ecl:\" option", nextarg); - return PARAM_BAD_USE; /* */ + return PARAM_BAD_USE; } err = file2string(&tmpcfg, file); if(file != stdin) - fclose(file); + curlx_fclose(file); if(err) return err; - config->ech_config = aprintf("ecl:%s",tmpcfg); - free(tmpcfg); - if(!config->ech_config) - return PARAM_NO_MEM; + { + char *tmp = curl_maprintf("ecl:%s", tmpcfg); + curlx_free(tmpcfg); + if(!tmp) + return PARAM_NO_MEM; + config->ech_config = curlx_strdup(tmp); + curl_free(tmp); + if(!config->ech_config) + return PARAM_NO_MEM; + } } /* file done */ } else { - /* Simple case: just a string, with a keyword */ + /* Simple case: a string, with a keyword */ err = getstr(&config->ech, nextarg, DENY_BLANK); } return err; @@ -1241,7 +1297,7 @@ static ParameterError parse_header(struct OperationConfig *config, if(nextarg[0] == '@') { /* read many headers from a file or stdin */ bool use_stdin = !strcmp(&nextarg[1], "-"); - FILE *file = use_stdin ? stdin : fopen(&nextarg[1], FOPEN_READTEXT); + FILE *file = use_stdin ? stdin : curlx_fopen(&nextarg[1], FOPEN_READTEXT); if(!file) { errorf("Failed to open %s", &nextarg[1]); err = PARAM_READ_ERROR; @@ -1249,7 +1305,7 @@ static ParameterError parse_header(struct OperationConfig *config, else { struct dynbuf line; bool error = FALSE; - curlx_dyn_init(&line, 1024*100); + curlx_dyn_init(&line, 1024 * 100); while(my_get_line(file, &line, &error)) { const char *ptr = curlx_dyn_ptr(&line); err = add2list(cmd == C_PROXY_HEADER ? /* --proxy-header? */ @@ -1262,10 +1318,14 @@ static ParameterError parse_header(struct OperationConfig *config, err = PARAM_READ_ERROR; curlx_dyn_free(&line); if(!use_stdin) - fclose(file); + curlx_fclose(file); } } else { + if(!strchr(nextarg, ':') && !strchr(nextarg, ';')) { + warnf("The provided %s header '%s' does not look like a header?", + (cmd == C_PROXY_HEADER) ? "proxy" : "HTTP", nextarg); + } if(cmd == C_PROXY_HEADER) /* --proxy-header */ err = add2list(&config->proxyheaders, nextarg); else @@ -1366,7 +1426,7 @@ static ParameterError parse_quote(struct OperationConfig *config, err = add2list(&config->postquote, nextarg); break; case '+': - /* prefixed with a plus makes it a just-before-transfer one */ + /* prefixed with a plus makes it an immediately-before-transfer one */ nextarg++; err = add2list(&config->prequote, nextarg); break; @@ -1390,17 +1450,17 @@ static ParameterError parse_range(struct OperationConfig *config, } if(!curlx_str_number(&nextarg, &value, CURL_OFF_T_MAX) && curlx_str_single(&nextarg, '-')) { - /* Specifying a range WITHOUT A DASH will create an illegal HTTP range - (and will not actually be range by definition). The manpage previously + /* Specifying a range WITHOUT A DASH does create an illegal HTTP range + (and does not actually be range by definition). The man page previously claimed that to be a good way, why this code is added to work-around it. */ char buffer[32]; warnf("A specified range MUST include at least one dash (-). " "Appending one for you"); - msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", - value); - free(config->range); - config->range = strdup(buffer); + curl_msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", + value); + curlx_free(config->range); + config->range = curlx_strdup(buffer); if(!config->range) err = PARAM_NO_MEM; } @@ -1475,17 +1535,17 @@ static ParameterError parse_verbose(bool toggle) return err; } else if(!verbose_nopts) { - /* fist `-v` in an argument resets to base verbosity */ + /* first `-v` in an argument resets to base verbosity */ global->verbosity = 0; if(!global->trace_set && set_trace_config("-all")) return PARAM_NO_MEM; } - /* the '%' thing here will cause the trace get sent to stderr */ + /* the '%' thing here causes the trace get sent to stderr */ switch(global->verbosity) { case 0: global->verbosity = 1; - free(global->trace_dump); - global->trace_dump = strdup("%"); + curlx_free(global->trace_dump); + global->trace_dump = curlx_strdup("%"); if(!global->trace_dump) err = PARAM_NO_MEM; else { @@ -1535,16 +1595,16 @@ static ParameterError parse_writeout(struct OperationConfig *config, } else { fname = nextarg; - file = fopen(fname, FOPEN_READTEXT); + file = curlx_fopen(fname, FOPEN_READTEXT); if(!file) { errorf("Failed to open %s", fname); return PARAM_READ_ERROR; } } - tool_safefree(config->writeout); + curlx_safefree(config->writeout); err = file2string(&config->writeout, file); if(file && (file != stdin)) - fclose(file); + curlx_fclose(file); if(err) return err; if(!config->writeout) @@ -1606,12 +1666,12 @@ struct flagmap { }; static const struct flagmap flag_table[] = { - {"answered", 8, CURLULFLAG_ANSWERED}, - {"deleted", 7, CURLULFLAG_DELETED}, - {"draft", 5, CURLULFLAG_DRAFT}, - {"flagged", 7, CURLULFLAG_FLAGGED}, - {"seen", 4, CURLULFLAG_SEEN}, - {NULL, 0, 0} + { "answered", 8, CURLULFLAG_ANSWERED }, + { "deleted", 7, CURLULFLAG_DELETED }, + { "draft", 5, CURLULFLAG_DRAFT }, + { "flagged", 7, CURLULFLAG_FLAGGED }, + { "seen", 4, CURLULFLAG_SEEN }, + { NULL, 0, 0 } }; static ParameterError parse_upload_flags(struct OperationConfig *config, @@ -1623,7 +1683,7 @@ static ParameterError parse_upload_flags(struct OperationConfig *config, bool negate; const struct flagmap *map; size_t len; - char *next = strchr(flag, ','); /* Find next comma or end */ + const char *next = strchr(flag, ','); /* Find next comma or end */ if(next) len = next - flag; else @@ -1645,15 +1705,15 @@ static ParameterError parse_upload_flags(struct OperationConfig *config, } } - if(!map->name) { - err = PARAM_OPTION_UNKNOWN; - break; - } + if(!map->name) { + err = PARAM_OPTION_UNKNOWN; + break; + } - if(next) - /* move over the comma */ - next++; - flag = next; + if(next) + /* move over the comma */ + next++; + flag = next; } return err; @@ -1700,7 +1760,7 @@ static ParameterError opt_none(struct OperationConfig *config, case C_DUMP_CA_EMBED: /* --dump-ca-embed */ return PARAM_CA_EMBED_REQUESTED; case C_FTP_PASV: /* --ftp-pasv */ - tool_safefree(config->ftpport); + curlx_safefree(config->ftpport); break; case C_HTTP1_0: /* --http1.0 */ @@ -2028,14 +2088,6 @@ static ParameterError opt_bool(struct OperationConfig *config, case C_MAIL_RCPT_ALLOWFAILS: /* --mail-rcpt-allowfails */ config->mail_rcpt_allowfails = toggle; break; - case C_FAIL_WITH_BODY: /* --fail-with-body */ - config->failwithbody = toggle; - if(config->failonerror && config->failwithbody) { - errorf("You must select either --fail or " - "--fail-with-body, not both."); - return PARAM_BAD_USE; - } - break; case C_REMOVE_ON_ERROR: /* --remove-on-error */ if(config->use_resume && toggle) { errorf("--continue-at is mutually exclusive with --remove-on-error"); @@ -2043,13 +2095,15 @@ static ParameterError opt_bool(struct OperationConfig *config, } config->rm_partial = toggle; break; - case C_FAIL: /* --fail */ - config->failonerror = toggle; - if(config->failonerror && config->failwithbody) { - errorf("You must select either --fail or " - "--fail-with-body, not both."); - return PARAM_BAD_USE; - } + case C_FAIL: /* --fail without body */ + if(toggle && (config->fail == FAIL_WITH_BODY)) + warnf("--fail deselects --fail-with-body here"); + config->fail = toggle ? FAIL_WO_BODY : FAIL_NONE; + break; + case C_FAIL_WITH_BODY: /* --fail-with-body */ + if(toggle && (config->fail == FAIL_WO_BODY)) + warnf("--fail-with-body deselects --fail here"); + config->fail = toggle ? FAIL_WITH_BODY : FAIL_NONE; break; case C_GLOBOFF: /* --globoff */ config->globoff = toggle; @@ -2081,7 +2135,7 @@ static ParameterError opt_bool(struct OperationConfig *config, config->doh_insecure_ok = toggle; break; case C_LIST_ONLY: /* --list-only */ - config->dirlistonly = toggle; /* only list the names of the FTP dir */ + config->dirlistonly = toggle; /* only list names of the FTP directory */ break; case C_MANUAL: /* --manual */ if(toggle) /* --no-manual shows no manual... */ @@ -2108,7 +2162,6 @@ static ParameterError opt_bool(struct OperationConfig *config, break; case C_REMOTE_NAME: /* --remote-name */ return parse_remote_name(config, toggle); - break; case C_PROXYTUNNEL: /* --proxytunnel */ config->proxytunnel = toggle; break; @@ -2130,7 +2183,6 @@ static ParameterError opt_bool(struct OperationConfig *config, break; case C_VERBOSE: /* --verbose */ return parse_verbose(toggle); - break; case C_VERSION: /* --version */ if(toggle) /* --no-version yields no output! */ return PARAM_VERSION_INFO_REQUESTED; @@ -2163,15 +2215,249 @@ static ParameterError opt_bool(struct OperationConfig *config, return PARAM_OK; } +static ParameterError existingfile(char **store, + const struct LongShort *a, + const char *filename) +{ + curlx_struct_stat info; + if(curlx_stat(filename, &info)) { + errorf("The file '%s' provided to --%s does not exist", + filename, a->lname); + return PARAM_BAD_USE; + } + return getstr(store, filename, DENY_BLANK); +} -/* opt_filestring handles string and file options */ -static ParameterError opt_filestring(struct OperationConfig *config, - const struct LongShort *a, - const char *nextarg) +/* opt_file handles file options */ +static ParameterError opt_file(struct OperationConfig *config, + const struct LongShort *a, + const char *nextarg, + int max_recursive) +{ + ParameterError err = PARAM_OK; + if((nextarg[0] == '-') && nextarg[1]) { + /* if the filename looks like a command line option */ + warnf("The filename argument '%s' looks like a flag.", nextarg); + } + switch(a->cmd) { + case C_ABSTRACT_UNIX_SOCKET: /* --abstract-unix-socket */ + config->abstract_unix_socket = TRUE; + err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK); + break; + case C_CACERT: /* --cacert */ + err = existingfile(&config->cacert, a, nextarg); + break; + case C_CAPATH: /* --capath */ + err = getstr(&config->capath, nextarg, DENY_BLANK); + break; + case C_CERT: /* --cert */ + err = GetFileAndPassword(nextarg, &config->cert, &config->key_passwd); + break; + case C_CONFIG: /* --config */ + if(--max_recursive < 0) { + errorf("Max config file recursion level reached (%u)", + CONFIG_MAX_LEVELS); + err = PARAM_BAD_USE; + } + else { + err = parseconfig(nextarg, max_recursive, NULL); + } + break; + case C_CRLFILE: /* --crlfile */ + err = existingfile(&config->crlfile, a, nextarg); + break; + case C_DUMP_HEADER: /* --dump-header */ + err = getstr(&config->headerfile, nextarg, DENY_BLANK); + break; + case C_ETAG_SAVE: /* --etag-save */ + if(config->num_urls > 1) { + errorf("The etag options only work on a single URL"); + err = PARAM_BAD_USE; + } + else + err = getstr(&config->etag_save_file, nextarg, DENY_BLANK); + break; + case C_ETAG_COMPARE: /* --etag-compare */ + if(config->num_urls > 1) { + errorf("The etag options only work on a single URL"); + err = PARAM_BAD_USE; + } + else + err = getstr(&config->etag_compare_file, nextarg, DENY_BLANK); + break; + case C_KEY: /* --key */ + err = getstr(&config->key, nextarg, DENY_BLANK); + break; + case C_KNOWNHOSTS: /* --knownhosts */ + err = existingfile(&config->knownhosts, a, nextarg); + break; + case C_NETRC_FILE: /* --netrc-file */ + err = existingfile(&config->netrc_file, a, nextarg); + break; + case C_OUTPUT: /* --output */ + err = parse_output(config, nextarg); + break; + case C_PROXY_CACERT: /* --proxy-cacert */ + err = existingfile(&config->proxy_cacert, a, nextarg); + break; + case C_PROXY_CAPATH: /* --proxy-capath */ + err = getstr(&config->proxy_capath, nextarg, DENY_BLANK); + break; + case C_PROXY_CERT: /* --proxy-cert */ + err = GetFileAndPassword(nextarg, &config->proxy_cert, + &config->proxy_key_passwd); + break; + case C_PROXY_CRLFILE: /* --proxy-crlfile */ + err = existingfile(&config->proxy_crlfile, a, nextarg); + break; + case C_PROXY_KEY: /* --proxy-key */ + err = getstr(&config->proxy_key, nextarg, ALLOW_BLANK); + break; + case C_SSL_SESSIONS: /* --ssl-sessions */ + if(feature_ssls_export) + err = getstr(&global->ssl_sessions, nextarg, DENY_BLANK); + else + err = PARAM_LIBCURL_DOESNT_SUPPORT; + break; + case C_STDERR: /* --stderr */ + tool_set_stderr_file(nextarg); + break; + case C_TRACE: /* --trace */ + err = getstr(&global->trace_dump, nextarg, DENY_BLANK); + if(!err) { + if(global->tracetype && (global->tracetype != TRACE_BIN)) + warnf("--trace overrides an earlier trace/verbose option"); + global->tracetype = TRACE_BIN; + } + break; + case C_TRACE_ASCII: /* --trace-ascii */ + err = getstr(&global->trace_dump, nextarg, DENY_BLANK); + if(!err) { + if(global->tracetype && (global->tracetype != TRACE_ASCII)) + warnf("--trace-ascii overrides an earlier trace/verbose option"); + global->tracetype = TRACE_ASCII; + } + break; + case C_UNIX_SOCKET: /* --unix-socket */ + config->abstract_unix_socket = FALSE; + err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK); + break; + case C_UPLOAD_FILE: /* --upload-file */ + err = parse_upload_file(config, nextarg); + break; + } + return err; +} + +/* options that accept unsigned numbers */ +static ParameterError opt_unum(struct OperationConfig *config, + const struct LongShort *a, + const char *nextarg) +{ + ParameterError err = PARAM_OK; + long val; + err = str2unum(&val, nextarg); + if(err) + return err; + switch(a->cmd) { + case C_VLAN_PRIORITY: /* --vlan-priority */ + if(val > 7) + return PARAM_NUMBER_TOO_LARGE; + config->vlan_priority = val; + break; + case C_RETRY: /* --retry */ + config->req_retry = val; + break; + case C_KEEPALIVE_TIME: /* --keepalive-time */ + config->alivetime = val; + break; + case C_KEEPALIVE_CNT: /* --keepalive-cnt */ + config->alivecnt = val; + break; + case C_TFTP_BLKSIZE: /* --tftp-blksize */ + config->tftp_blksize = val; + break; + case C_HAPPY_EYEBALLS_TIMEOUT_MS: /* --happy-eyeballs-timeout-ms */ + config->happy_eyeballs_timeout_ms = val; + /* 0 is a valid value for this timeout */ + break; + case C_SPEED_TIME: /* --speed-time */ + /* low speed time */ + config->low_speed_time = val; + if(!config->low_speed_limit) + config->low_speed_limit = 1; + break; + case C_SPEED_LIMIT: /* --speed-limit */ + /* low speed limit */ + config->low_speed_limit = val; + if(!config->low_speed_time) + config->low_speed_time = 30; + break; + case C_PARALLEL_HOST: /* --parallel-max-host */ + if(val > MAX_PARALLEL_HOST) + global->parallel_host = MAX_PARALLEL_HOST; + else if(val < 1) + global->parallel_host = PARALLEL_HOST_DEFAULT; + else + global->parallel_host = (unsigned short)val; + break; + case C_PARALLEL_MAX: /* --parallel-max */ + if(val > MAX_PARALLEL) + global->parallel_max = MAX_PARALLEL; + else if(val < 1) + global->parallel_max = PARALLEL_DEFAULT; + else + global->parallel_max = (unsigned short)val; + break; + default: + DEBUGASSERT(0); + return PARAM_OPTION_UNKNOWN; + } + return err; +} + +/* handle options that accept seconds */ +static ParameterError opt_secs(struct OperationConfig *config, + const struct LongShort *a, + const char *nextarg) +{ + long val; + ParameterError err = secs2ms(&val, nextarg); + if(err) + return err; + switch(a->cmd) { + case C_CONNECT_TIMEOUT: /* --connect-timeout */ + config->connecttimeout_ms = val; + break; + case C_RETRY_DELAY: /* --retry-delay */ + if(val >= INT32_MAX) + val = INT32_MAX; + config->retry_delay_ms = (uint32_t)val; + break; + case C_RETRY_MAX_TIME: /* --retry-max-time */ + config->retry_maxtime_ms = val; + break; + case C_EXPECT100_TIMEOUT: /* --expect100-timeout */ + config->expect100timeout_ms = val; + break; + case C_MAX_TIME: /* --max-time */ + /* specified max time */ + config->timeout_ms = val; + break; + default: + DEBUGASSERT(0); + return PARAM_OPTION_UNKNOWN; + } + return err; +} + +/* opt_string handles string options */ +static ParameterError opt_string(struct OperationConfig *config, + const struct LongShort *a, + const char *nextarg) { ParameterError err = PARAM_OK; curl_off_t value; - long val; static const char *redir_protos[] = { "http", "https", @@ -2199,14 +2485,11 @@ static ParameterError opt_filestring(struct OperationConfig *config, config->authtype |= CURLAUTH_BEARER; return getstr(&config->oauth_bearer, nextarg, DENY_BLANK); - case C_CONNECT_TIMEOUT: /* --connect-timeout */ - return secs2ms(&config->connecttimeout_ms, nextarg); - case C_DOH_URL: /* --doh-url */ err = getstr(&config->doh_url, nextarg, ALLOW_BLANK); if(!err && config->doh_url && !config->doh_url[0]) /* if given a blank string, make it NULL again */ - tool_safefree(config->doh_url); + curlx_safefree(config->doh_url); break; case C_CIPHERS: /* -- ciphers */ @@ -2227,24 +2510,8 @@ static ParameterError opt_filestring(struct OperationConfig *config, /* IP addrs of DNS servers */ err = getstr(&config->dns_servers, nextarg, DENY_BLANK); break; - case C_TRACE: /* --trace */ - err = getstr(&global->trace_dump, nextarg, DENY_BLANK); - if(!err) { - if(global->tracetype && (global->tracetype != TRACE_BIN)) - warnf("--trace overrides an earlier trace/verbose option"); - global->tracetype = TRACE_BIN; - } - break; - case C_TRACE_ASCII: /* --trace-ascii */ - err = getstr(&global->trace_dump, nextarg, DENY_BLANK); - if(!err) { - if(global->tracetype && (global->tracetype != TRACE_ASCII)) - warnf("--trace-ascii overrides an earlier trace/verbose option"); - global->tracetype = TRACE_ASCII; - } - break; case C_LIMIT_RATE: /* --limit-rate */ - err = GetSizeParameter(nextarg, "rate", &value); + err = GetSizeParameter(nextarg, &value); if(!err) { config->recvpersecond = value; config->sendpersecond = value; @@ -2267,30 +2534,20 @@ static ParameterError opt_filestring(struct OperationConfig *config, case C_IPFS_GATEWAY: /* --ipfs-gateway */ err = getstr(&config->ipfs_gateway, nextarg, DENY_BLANK); break; -#endif /* !CURL_DISABLE_IPFS */ +#endif case C_AWS_SIGV4: /* --aws-sigv4 */ config->authtype |= CURLAUTH_AWS_SIGV4; err = getstr(&config->aws_sigv4, nextarg, ALLOW_BLANK); break; - case C_STDERR: /* --stderr */ - tool_set_stderr_file(nextarg); - break; case C_INTERFACE: /* --interface */ /* interface */ err = getstr(&config->iface, nextarg, DENY_BLANK); break; - case C_KRB: /* --krb */ - /* kerberos level string */ - if(!feature_spnego) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->krblevel, nextarg, DENY_BLANK); - break; case C_HAPROXY_CLIENTIP: /* --haproxy-clientip */ err = getstr(&config->haproxy_clientip, nextarg, DENY_BLANK); break; case C_MAX_FILESIZE: /* --max-filesize */ - err = GetSizeParameter(nextarg, "max-filesize", &value); + err = GetSizeParameter(nextarg, &value); if(!err) config->max_filesize = value; break; @@ -2328,18 +2585,6 @@ static ParameterError opt_filestring(struct OperationConfig *config, err = str2unummax(&config->ip_tos, nextarg, 0xFF); break; } - case C_VLAN_PRIORITY: /* --vlan-priority */ - err = str2unummax(&config->vlan_priority, nextarg, 7); - break; - case C_RETRY: /* --retry */ - err = str2unum(&config->req_retry, nextarg); - break; - case C_RETRY_DELAY: /* --retry-delay */ - err = secs2ms(&config->retry_delay_ms, nextarg); - break; - case C_RETRY_MAX_TIME: /* --retry-max-time */ - err = secs2ms(&config->retry_maxtime_ms, nextarg); - break; case C_FTP_ACCOUNT: /* --ftp-account */ err = getstr(&config->ftp_account, nextarg, DENY_BLANK); break; @@ -2360,12 +2605,6 @@ static ParameterError opt_filestring(struct OperationConfig *config, err = getstr(&global->libcurl, nextarg, DENY_BLANK); #endif break; - case C_KEEPALIVE_TIME: /* --keepalive-time */ - err = str2unum(&config->alivetime, nextarg); - break; - case C_KEEPALIVE_CNT: /* --keepalive-cnt */ - err = str2unum(&config->alivecnt, nextarg); - break; case C_NOPROXY: /* --noproxy */ /* This specifies the noproxy list */ err = getstr(&config->noproxy, nextarg, ALLOW_BLANK); @@ -2375,9 +2614,6 @@ static ParameterError opt_filestring(struct OperationConfig *config, err = getstr(&config->proxy, nextarg, DENY_BLANK); config->proxyver = CURLPROXY_HTTP_1_0; break; - case C_TFTP_BLKSIZE: /* --tftp-blksize */ - err = str2unum(&config->tftp_blksize, nextarg); - break; case C_MAIL_FROM: /* --mail-from */ err = getstr(&config->mail_from, nextarg, DENY_BLANK); break; @@ -2406,10 +2642,6 @@ static ParameterError opt_filestring(struct OperationConfig *config, case C_SASL_AUTHZID: /* --sasl-authzid */ err = getstr(&config->sasl_authzid, nextarg, DENY_BLANK); break; - case C_UNIX_SOCKET: /* --unix-socket */ - config->abstract_unix_socket = FALSE; - err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK); - break; case C_PROXY_SERVICE_NAME: /* --proxy-service-name */ err = getstr(&config->proxy_service_name, nextarg, DENY_BLANK); break; @@ -2421,16 +2653,9 @@ static ParameterError opt_filestring(struct OperationConfig *config, if(!err) err = check_protocol(config->proto_default); break; - case C_EXPECT100_TIMEOUT: /* --expect100-timeout */ - err = secs2ms(&config->expect100timeout_ms, nextarg); - break; case C_CONNECT_TO: /* --connect-to */ err = add2list(&config->connect_to, nextarg); break; - case C_ABSTRACT_UNIX_SOCKET: /* --abstract-unix-socket */ - config->abstract_unix_socket = TRUE; - err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK); - break; case C_TLS_MAX: /* --tls-max */ err = str2tls_max(&config->ssl_version_max, nextarg); if(!err && (config->ssl_version_max < config->ssl_version)) { @@ -2438,10 +2663,6 @@ static ParameterError opt_filestring(struct OperationConfig *config, err = PARAM_BAD_USE; } break; - case C_HAPPY_EYEBALLS_TIMEOUT_MS: /* --happy-eyeballs-timeout-ms */ - err = str2unum(&config->happy_eyeballs_timeout_ms, nextarg); - /* 0 is a valid value for this timeout */ - break; case C_TRACE_CONFIG: /* --trace-config */ global->trace_set = TRUE; if(set_trace_config(nextarg)) @@ -2499,9 +2720,6 @@ static ParameterError opt_filestring(struct OperationConfig *config, case C_URL_QUERY: /* --url-query */ err = url_query(nextarg, config); break; - case C_DUMP_HEADER: /* --dump-header */ - err = getstr(&config->headerfile, nextarg, DENY_BLANK); - break; case C_REFERER: { /* --referer */ size_t len = strlen(nextarg); /* does it end with ;auto ? */ @@ -2517,21 +2735,12 @@ static ParameterError opt_filestring(struct OperationConfig *config, if(len) err = getstrn(&config->referer, nextarg, len, ALLOW_BLANK); else - tool_safefree(config->referer); + curlx_safefree(config->referer); } break; - case C_CERT: /* --cert */ - GetFileAndPassword(nextarg, &config->cert, &config->key_passwd); - break; - case C_CACERT: /* --cacert */ - err = getstr(&config->cacert, nextarg, DENY_BLANK); - break; case C_CERT_TYPE: /* --cert-type */ err = getstr(&config->cert_type, nextarg, DENY_BLANK); break; - case C_KEY: /* --key */ - err = getstr(&config->key, nextarg, DENY_BLANK); - break; case C_KEY_TYPE: /* --key-type */ err = getstr(&config->key_type, nextarg, DENY_BLANK); break; @@ -2548,9 +2757,6 @@ static ParameterError opt_filestring(struct OperationConfig *config, case C_ECH: /* --ech */ err = parse_ech(config, nextarg); break; - case C_CAPATH: /* --capath */ - err = getstr(&config->capath, nextarg, DENY_BLANK); - break; case C_PUBKEY: /* --pubkey */ err = getstr(&config->pubkey, nextarg, DENY_BLANK); break; @@ -2567,9 +2773,6 @@ static ParameterError opt_filestring(struct OperationConfig *config, else err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK); break; - case C_CRLFILE: /* --crlfile */ - err = getstr(&config->crlfile, nextarg, DENY_BLANK); - break; case C_TLSUSER: /* --tlsuser */ if(!feature_tls_srp) err = PARAM_LIBCURL_DOESNT_SUPPORT; @@ -2597,12 +2800,6 @@ static ParameterError opt_filestring(struct OperationConfig *config, case C_PROXY_PINNEDPUBKEY: /* --proxy-pinnedpubkey */ err = getstr(&config->proxy_pinnedpubkey, nextarg, DENY_BLANK); break; - case C_SSL_SESSIONS: /* --ssl-sessions */ - if(feature_ssls_export) - err = getstr(&global->ssl_sessions, nextarg, DENY_BLANK); - else - err = PARAM_LIBCURL_DOESNT_SUPPORT; - break; case C_PROXY_TLSUSER: /* --proxy-tlsuser */ if(!feature_tls_srp) err = PARAM_LIBCURL_DOESNT_SUPPORT; @@ -2625,16 +2822,9 @@ static ParameterError opt_filestring(struct OperationConfig *config, err = PARAM_LIBCURL_DOESNT_SUPPORT; /* only support TLS-SRP */ } break; - case C_PROXY_CERT: /* --proxy-cert */ - GetFileAndPassword(nextarg, &config->proxy_cert, - &config->proxy_key_passwd); - break; case C_PROXY_CERT_TYPE: /* --proxy-cert-type */ err = getstr(&config->proxy_cert_type, nextarg, DENY_BLANK); break; - case C_PROXY_KEY: /* --proxy-key */ - err = getstr(&config->proxy_key, nextarg, ALLOW_BLANK); - break; case C_PROXY_KEY_TYPE: /* --proxy-key-type */ err = getstr(&config->proxy_key_type, nextarg, DENY_BLANK); break; @@ -2644,34 +2834,9 @@ static ParameterError opt_filestring(struct OperationConfig *config, case C_PROXY_CIPHERS: /* --proxy-ciphers */ err = getstr(&config->proxy_cipher_list, nextarg, DENY_BLANK); break; - case C_PROXY_CRLFILE: /* --proxy-crlfile */ - err = getstr(&config->proxy_crlfile, nextarg, DENY_BLANK); - break; case C_LOGIN_OPTIONS: /* --login-options */ err = getstr(&config->login_options, nextarg, ALLOW_BLANK); break; - case C_PROXY_CACERT: /* --proxy-cacert */ - err = getstr(&config->proxy_cacert, nextarg, DENY_BLANK); - break; - case C_PROXY_CAPATH: /* --proxy-capath */ - err = getstr(&config->proxy_capath, nextarg, DENY_BLANK); - break; - case C_ETAG_SAVE: /* --etag-save */ - if(config->num_urls > 1) { - errorf("The etag options only work on a single URL"); - err = PARAM_BAD_USE; - } - else - err = getstr(&config->etag_save_file, nextarg, DENY_BLANK); - break; - case C_ETAG_COMPARE: /* --etag-compare */ - if(config->num_urls > 1) { - errorf("The etag options only work on a single URL"); - err = PARAM_BAD_USE; - } - else - err = getstr(&config->etag_compare_file, nextarg, DENY_BLANK); - break; case C_CURVES: /* --curves */ err = getstr(&config->ssl_ec_curves, nextarg, DENY_BLANK); break; @@ -2695,31 +2860,15 @@ static ParameterError opt_filestring(struct OperationConfig *config, case C_PROXY_HEADER: /* --proxy-header */ err = parse_header(config, (cmdline_t)a->cmd, nextarg); break; - case C_CONFIG: /* --config */ - if(parseconfig(nextarg)) { - errorf("cannot read config from '%s'", nextarg); - err = PARAM_READ_ERROR; - } - break; - case C_MAX_TIME: /* --max-time */ - /* specified max time */ - err = secs2ms(&config->timeout_ms, nextarg); - break; - case C_NETRC_FILE: /* --netrc-file */ - err = getstr(&config->netrc_file, nextarg, DENY_BLANK); - break; case C_OUTPUT_DIR: /* --output-dir */ err = getstr(&config->output_dir, nextarg, DENY_BLANK); break; - case C_OUTPUT: /* --output */ - err = parse_output(config, nextarg); - break; case C_FTP_PORT: /* --ftp-port */ /* This makes the FTP sessions use PORT instead of PASV */ /* use or <192.168.10.10> style addresses. Anything except - this will make us try to get the "default" address. + this makes us try to get the "default" address. NOTE: this is a changed behavior since the released 4.1! - */ + */ err = getstr(&config->ftpport, nextarg, DENY_BLANK); break; case C_FTP_SSL_CCC_MODE: /* --ftp-ssl-ccc-mode */ @@ -2736,15 +2885,12 @@ static ParameterError opt_filestring(struct OperationConfig *config, /* Telnet options */ err = add2list(&config->telnet_options, nextarg); break; - case C_UPLOAD_FILE: /* --upload-file */ - err = parse_upload_file(config, nextarg); - break; case C_USER: /* --user */ - /* user:password */ + /* user:password */ err = getstr(&config->userpwd, nextarg, ALLOW_BLANK); break; case C_PROXY_USER: /* --proxy-user */ - /* Proxy user:password */ + /* Proxy user:password */ err = getstr(&config->proxyuserpwd, nextarg, ALLOW_BLANK); break; case C_WRITE_OUT: /* --write-out */ @@ -2763,41 +2909,6 @@ static ParameterError opt_filestring(struct OperationConfig *config, /* set custom request */ err = getstr(&config->customrequest, nextarg, DENY_BLANK); break; - case C_SPEED_TIME: /* --speed-time */ - /* low speed time */ - err = str2unum(&config->low_speed_time, nextarg); - if(!err && !config->low_speed_limit) - config->low_speed_limit = 1; - break; - case C_SPEED_LIMIT: /* --speed-limit */ - /* low speed limit */ - err = str2unum(&config->low_speed_limit, nextarg); - if(!err && !config->low_speed_time) - config->low_speed_time = 30; - break; - case C_PARALLEL_HOST: /* --parallel-max-host */ - err = str2unum(&val, nextarg); - if(err) - break; - if(val > MAX_PARALLEL_HOST) - global->parallel_host = MAX_PARALLEL_HOST; - else if(val < 1) - global->parallel_host = PARALLEL_HOST_DEFAULT; - else - global->parallel_host = (unsigned short)val; - break; - break; - case C_PARALLEL_MAX: /* --parallel-max */ - err = str2unum(&val, nextarg); - if(err) - break; - if(val > MAX_PARALLEL) - global->parallel_max = MAX_PARALLEL; - else if(val < 1) - global->parallel_max = PARALLEL_DEFAULT; - else - global->parallel_max = (unsigned short)val; - break; case C_TIME_COND: /* --time-cond */ err = parse_time_cond(config, nextarg); break; @@ -2811,7 +2922,7 @@ static ParameterError opt_filestring(struct OperationConfig *config, /* detect e2 80 80 - e2 80 ff */ static bool has_leading_unicode(const unsigned char *arg) { - return ((arg[0] == 0xe2) && (arg[1] == 0x80) && (arg[2] & 0x80)); + return (arg[0] == 0xe2) && (arg[1] == 0x80) && (arg[2] & 0x80); } /* the longest command line option, excluding the leading -- */ @@ -2821,7 +2932,8 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ const char *nextarg, /* NULL if unset */ bool *usedarg, /* set to TRUE if the arg has been used */ - struct OperationConfig *config) + struct OperationConfig *config, + int max_recursive) { const char *parse = NULL; bool longopt = FALSE; @@ -2859,8 +2971,8 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ p = word; /* is there an '=' ? */ if(!curlx_str_until(&p, &out, MAX_OPTION_LEN, '=') && - !curlx_str_single(&p, '=') ) { - /* there's an equal sign */ + !curlx_str_single(&p, '=')) { + /* there is an equal sign */ char tempword[MAX_OPTION_LEN + 1]; memcpy(tempword, curlx_str(&out), curlx_strlen(&out)); tempword[curlx_strlen(&out)] = 0; @@ -2887,9 +2999,8 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ struct dynbuf nbuf; bool replaced; - if((ARGTYPE(a->desc) != ARG_STRG) && - (ARGTYPE(a->desc) != ARG_FILE)) { - /* --expand on an option that is not a string or a filename */ + if(ARGTYPE(a->desc) < ARG_STRG) { + /* --expand on an option that does not take an argument */ err = PARAM_EXPAND_ERROR; goto error; } @@ -2947,18 +3058,28 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ break; } - if((ARGTYPE(a->desc) == ARG_FILE) && - (nextarg[0] == '-') && nextarg[1]) { - /* if the filename looks like a command line option */ - warnf("The filename argument '%s' looks like a flag.", - nextarg); - } - else if(has_leading_unicode((const unsigned char *)nextarg)) { + if(has_leading_unicode((const unsigned char *)nextarg)) { warnf("The argument '%s' starts with a Unicode character. " "Maybe ASCII was intended?", nextarg); } - /* ARG_FILE | ARG_STRG */ - err = opt_filestring(config, a, nextarg); + switch(ARGTYPE(a->desc)) { + case ARG_FILE: + err = opt_file(config, a, nextarg, max_recursive); + break; + case ARG_STRG: + err = opt_string(config, a, nextarg); + break; + case ARG_SECS: + err = opt_secs(config, a, nextarg); + break; + case ARG_UNUM: + err = opt_unum(config, a, nextarg); + break; + default: + DEBUGASSERT(0); + err = PARAM_OPTION_UNKNOWN; + break; + } if(a->desc & ARG_CLEAR) cleanarg(CURL_UNCONST(nextarg)); } @@ -2980,7 +3101,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ error: if(nextalloc) - free(CURL_UNCONST(nextarg)); + curlx_free(CURL_UNCONST(nextarg)); return err; } @@ -2989,10 +3110,11 @@ ParameterError parse_args(int argc, argv_item_t argv[]) int i; bool stillflags; const char *orig_opt = NULL; - ParameterError result = PARAM_OK; + ParameterError err = PARAM_OK; struct OperationConfig *config = global->first; - for(i = 1, stillflags = TRUE; i < argc && !result; i++) { + stillflags = TRUE; + for(i = 1; i < argc && !err; i++) { orig_opt = convert_tchar_to_UTF8(argv[i]); if(!orig_opt) return PARAM_NO_MEM; @@ -3009,19 +3131,20 @@ ParameterError parse_args(int argc, argv_item_t argv[]) if(i < (argc - 1)) { nextarg = convert_tchar_to_UTF8(argv[i + 1]); if(!nextarg) { - unicodefree(orig_opt); + unicodefree(CURL_UNCONST(orig_opt)); return PARAM_NO_MEM; } } - result = getparameter(orig_opt, nextarg, &passarg, config); + err = getparameter(orig_opt, nextarg, &passarg, config, + CONFIG_MAX_LEVELS); - unicodefree(nextarg); + unicodefree(CURL_UNCONST(nextarg)); config = global->last; - if(result == PARAM_NEXT_OPERATION) { - /* Reset result as PARAM_NEXT_OPERATION is only used here and not + if(err == PARAM_NEXT_OPERATION) { + /* Reset err as PARAM_NEXT_OPERATION is only used here and not returned from this function */ - result = PARAM_OK; + err = PARAM_OK; if(config->url_list && config->url_list->url) { /* Allocate the next config */ @@ -3035,41 +3158,42 @@ ParameterError parse_args(int argc, argv_item_t argv[]) config = config->next; } else - result = PARAM_NO_MEM; + err = PARAM_NO_MEM; } else { errorf("missing URL before --next"); - result = PARAM_BAD_USE; + err = PARAM_BAD_USE; } } - else if(!result && passarg) + else if(!err && passarg) i++; /* we are supposed to skip this */ } } else { bool used; - /* Just add the URL please */ - result = getparameter("--url", orig_opt, &used, config); + /* add the URL please */ + err = getparameter("--url", orig_opt, &used, config, 0); } - if(!result) { - unicodefree(orig_opt); + if(!err) { + unicodefree(CURL_UNCONST(orig_opt)); orig_opt = NULL; } } - if(!result && config->content_disposition) { + if(!err && config->content_disposition) { if(config->resume_from_current) - result = PARAM_CONTDISP_RESUME_FROM; + err = PARAM_CONTDISP_RESUME_FROM; } - if(result && result != PARAM_HELP_REQUESTED && - result != PARAM_MANUAL_REQUESTED && - result != PARAM_VERSION_INFO_REQUESTED && - result != PARAM_ENGINES_REQUESTED && - result != PARAM_CA_EMBED_REQUESTED) { - const char *reason = param2text(result); + if(err && + err != PARAM_HELP_REQUESTED && + err != PARAM_MANUAL_REQUESTED && + err != PARAM_VERSION_INFO_REQUESTED && + err != PARAM_ENGINES_REQUESTED && + err != PARAM_CA_EMBED_REQUESTED) { + const char *reason = param2text(err); if(orig_opt && strcmp(":", orig_opt)) helpf("option %s: %s", orig_opt, reason); @@ -3077,6 +3201,6 @@ ParameterError parse_args(int argc, argv_item_t argv[]) helpf("%s", reason); } - unicodefree(orig_opt); - return result; + unicodefree(CURL_UNCONST(orig_opt)); + return err; } diff --git a/src/tool_getparam.h b/src/tool_getparam.h index a08bbac7b6..e137cc322f 100644 --- a/src/tool_getparam.h +++ b/src/tool_getparam.h @@ -139,6 +139,7 @@ typedef enum { C_KEEPALIVE_TIME, C_KEY, C_KEY_TYPE, + C_KNOWNHOSTS, C_KRB, C_KRB4, C_LIBCURL, @@ -316,14 +317,16 @@ typedef enum { #define ARG_BOOL 1 /* accepts a --no-[name] prefix */ #define ARG_STRG 2 /* requires an argument */ #define ARG_FILE 3 /* requires an argument, usually a filename */ +#define ARG_SECS 4 /* requires a time in seconds */ +#define ARG_UNUM 5 /* requires a non-negative number */ -#define ARG_TYPEMASK 0x03 -#define ARGTYPE(x) ((x) & ARG_TYPEMASK) +#define ARG_TYPEMASK 0x07 +#define ARGTYPE(x) ((x) & ARG_TYPEMASK) -#define ARG_DEPR 0x10 /* deprecated option */ +#define ARG_DEPR 0x10 /* deprecated option */ #define ARG_CLEAR 0x20 /* clear cmdline argument */ -#define ARG_TLS 0x40 /* requires TLS support */ -#define ARG_NO 0x80 /* set if the option is documented as --no-* */ +#define ARG_TLS 0x40 /* requires TLS support */ +#define ARG_NO 0x80 /* set if the option is documented as --no-* */ struct LongShort { const char *lname; /* long name option */ @@ -334,8 +337,8 @@ struct LongShort { typedef enum { PARAM_OK = 0, - PARAM_OPTION_AMBIGUOUS, PARAM_OPTION_UNKNOWN, + PARAM_CONFIG_OPTION_UNKNOWN, PARAM_REQUIRES_PARAMETER, PARAM_BAD_USE, PARAM_HELP_REQUESTED, @@ -357,6 +360,7 @@ typedef enum { PARAM_EXPAND_ERROR, /* --expand problem */ PARAM_BLANK_STRING, PARAM_VAR_SYNTAX, /* --variable syntax error */ + PARAM_RECURSION, PARAM_LAST } ParameterError; @@ -367,27 +371,29 @@ const struct LongShort *findshortopt(char letter); ParameterError getparameter(const char *flag, const char *nextarg, bool *usedarg, - struct OperationConfig *config); + struct OperationConfig *config, + int max_recursive); #ifdef UNITTESTS -void parse_cert_parameter(const char *cert_parameter, - char **certname, - char **passphrase); +UNITTEST ParameterError parse_cert_parameter(const char *cert_parameter, + char **certname, + char **passphrase); +UNITTEST ParameterError GetSizeParameter(const char *arg, curl_off_t *out); #endif ParameterError parse_args(int argc, argv_item_t argv[]); -#if defined(UNICODE) && defined(_WIN32) && !defined(UNDER_CE) +#if defined(UNICODE) && defined(_WIN32) -#define convert_UTF8_to_tchar(ptr) curlx_convert_UTF8_to_wchar((ptr)) -#define convert_tchar_to_UTF8(ptr) curlx_convert_wchar_to_UTF8((ptr)) -#define unicodefree(ptr) curlx_unicodefree(ptr) +#define convert_UTF8_to_tchar(ptr) curlx_convert_UTF8_to_wchar(ptr) +#define convert_tchar_to_UTF8(ptr) curlx_convert_wchar_to_UTF8(ptr) +#define unicodefree(ptr) curlx_free(ptr) #else #define convert_UTF8_to_tchar(ptr) (const char *)(ptr) #define convert_tchar_to_UTF8(ptr) (const char *)(ptr) -#define unicodefree(ptr) do {} while(0) +#define unicodefree(ptr) do {} while(0) #endif diff --git a/src/tool_getpass.c b/src/tool_getpass.c index fc59accc07..68a16cab3e 100644 --- a/src/tool_getpass.c +++ b/src/tool_getpass.c @@ -30,10 +30,6 @@ #ifndef HAVE_GETPASS_R /* this file is only for systems without getpass_r() */ -#ifdef HAVE_FCNTL_H -# include -#endif - #ifdef HAVE_TERMIOS_H # include #elif defined(HAVE_TERMIO_H) @@ -46,7 +42,7 @@ # include iodef #endif -#if defined(_WIN32) && !defined(UNDER_CE) +#ifdef _WIN32 # include #endif @@ -55,8 +51,6 @@ #endif #include "tool_getpass.h" -#include "memdebug.h" /* keep this as LAST include */ - #ifdef __VMS /* VMS implementation */ char *getpass_r(const char *prompt, char *buffer, size_t buflen) @@ -64,15 +58,12 @@ char *getpass_r(const char *prompt, char *buffer, size_t buflen) long sts; short chan; - /* MSK, 23-JAN-2004, iosbdef.h was not in VAX V7.2 or CC 6.4 */ - /* distribution so I created this. May revert back later to */ - /* struct _iosb iosb; */ - struct _iosb - { - short int iosb$w_status; /* status */ - short int iosb$w_bcnt; /* byte count */ - int unused; /* unused */ - } iosb; + /* iosbdef.h was not in VAX V7.2 or CC 6.4 */ + struct _isb { + short int iosb$w_status; /* status */ + short int iosb$w_bcnt; /* byte count */ + int unused; /* unused */ + } iosb; $DESCRIPTOR(ttdesc, "TT"); @@ -95,7 +86,6 @@ char *getpass_r(const char *prompt, char *buffer, size_t buflen) #endif /* __VMS */ #ifdef _WIN32 - char *getpass_r(const char *prompt, char *buffer, size_t buflen) { size_t i; @@ -107,22 +97,21 @@ char *getpass_r(const char *prompt, char *buffer, size_t buflen) buffer[i] = '\0'; break; } - else - if(buffer[i] == '\b') - /* remove this letter and if this is not the first key, remove the - previous one as well */ - i = i - (i >= 1 ? 2 : 1); + else if(buffer[i] == '\b') + /* remove this letter and if this is not the first key, remove the + previous one as well */ + i = i - (i >= 1 ? 2 : 1); } /* since echo is disabled, print a newline */ fputs("\n", tool_stderr); /* if user did not hit ENTER, terminate buffer */ if(i == buflen) - buffer[buflen-1] = '\0'; + buffer[buflen - 1] = '\0'; return buffer; /* we always return success */ } #define DONE -#endif /* _WIN32 && !UNDER_CE */ +#endif /* _WIN32 */ #ifndef DONE /* not previously provided */ @@ -173,23 +162,23 @@ static bool ttyecho(bool enable, int fd) } char *getpass_r(const char *prompt, /* prompt to display */ - char *password, /* buffer to store password in */ + char *buffer, /* buffer to store password in */ size_t buflen) /* size of buffer to store password in */ { ssize_t nread; bool disabled; - int fd = open("/dev/tty", O_RDONLY); + int fd = curlx_open("/dev/tty", O_RDONLY); if(fd == -1) fd = STDIN_FILENO; /* use stdin if the tty could not be used */ disabled = ttyecho(FALSE, fd); /* disable terminal echo */ fputs(prompt, tool_stderr); - nread = read(fd, password, buflen); + nread = read(fd, buffer, buflen); if(nread > 0) - password[--nread] = '\0'; /* null-terminate where enter is stored */ + buffer[--nread] = '\0'; /* null-terminate where enter is stored */ else - password[0] = '\0'; /* got nothing */ + buffer[0] = '\0'; /* got nothing */ if(disabled) { /* if echo actually was disabled, add a newline */ @@ -198,9 +187,9 @@ char *getpass_r(const char *prompt, /* prompt to display */ } if(STDIN_FILENO != fd) - close(fd); + curlx_close(fd); - return password; /* return pointer to buffer */ + return buffer; /* return pointer to buffer */ } #endif /* DONE */ diff --git a/src/tool_getpass.h b/src/tool_getpass.h index 0a4d6d5a83..f04fefb4bc 100644 --- a/src/tool_getpass.h +++ b/src/tool_getpass.h @@ -30,7 +30,7 @@ also found in one of the standard headers. */ /* - * Returning NULL will abort the continued operation! + * Returning NULL aborts the continued operation! */ char *getpass_r(const char *prompt, char *buffer, size_t buflen); #endif diff --git a/src/tool_help.c b/src/tool_help.c index 58d3a2c1f2..212972f382 100644 --- a/src/tool_help.c +++ b/src/tool_help.c @@ -27,14 +27,11 @@ #include "tool_libinfo.h" #include "tool_util.h" #include "tool_version.h" -#include "tool_cb_prg.h" #include "tool_hugehelp.h" #include "tool_getparam.h" #include "tool_cfgable.h" #include "terminal.h" -#include "memdebug.h" /* keep this as LAST include */ - struct category_descriptors { const char *opt; const char *desc; @@ -43,31 +40,31 @@ struct category_descriptors { static const struct category_descriptors categories[] = { /* important is left out because it is the default help page */ - {"auth", "Authentication methods", CURLHELP_AUTH}, - {"connection", "Manage connections", CURLHELP_CONNECTION}, - {"curl", "The command line tool itself", CURLHELP_CURL}, - {"deprecated", "Legacy", CURLHELP_DEPRECATED}, - {"dns", "Names and resolving", CURLHELP_DNS}, - {"file", "FILE protocol", CURLHELP_FILE}, - {"ftp", "FTP protocol", CURLHELP_FTP}, - {"global", "Global options", CURLHELP_GLOBAL}, - {"http", "HTTP and HTTPS protocol", CURLHELP_HTTP}, - {"imap", "IMAP protocol", CURLHELP_IMAP}, - {"ldap", "LDAP protocol", CURLHELP_LDAP}, - {"output", "File system output", CURLHELP_OUTPUT}, - {"pop3", "POP3 protocol", CURLHELP_POP3}, - {"post", "HTTP POST specific", CURLHELP_POST}, - {"proxy", "Options for proxies", CURLHELP_PROXY}, - {"scp", "SCP protocol", CURLHELP_SCP}, - {"sftp", "SFTP protocol", CURLHELP_SFTP}, - {"smtp", "SMTP protocol", CURLHELP_SMTP}, - {"ssh", "SSH protocol", CURLHELP_SSH}, - {"telnet", "TELNET protocol", CURLHELP_TELNET}, - {"tftp", "TFTP protocol", CURLHELP_TFTP}, - {"timeout", "Timeouts and delays", CURLHELP_TIMEOUT}, - {"tls", "TLS/SSL related", CURLHELP_TLS}, - {"upload", "Upload, sending data", CURLHELP_UPLOAD}, - {"verbose", "Tracing, logging etc", CURLHELP_VERBOSE} + { "auth", "Authentication methods", CURLHELP_AUTH }, + { "connection", "Manage connections", CURLHELP_CONNECTION }, + { "curl", "The command line tool itself", CURLHELP_CURL }, + { "deprecated", "Legacy", CURLHELP_DEPRECATED }, + { "dns", "Names and resolving", CURLHELP_DNS }, + { "file", "FILE protocol", CURLHELP_FILE }, + { "ftp", "FTP protocol", CURLHELP_FTP }, + { "global", "Global options", CURLHELP_GLOBAL }, + { "http", "HTTP and HTTPS protocol", CURLHELP_HTTP }, + { "imap", "IMAP protocol", CURLHELP_IMAP }, + { "ldap", "LDAP protocol", CURLHELP_LDAP }, + { "output", "File system output", CURLHELP_OUTPUT }, + { "pop3", "POP3 protocol", CURLHELP_POP3 }, + { "post", "HTTP POST specific", CURLHELP_POST }, + { "proxy", "Options for proxies", CURLHELP_PROXY }, + { "scp", "SCP protocol", CURLHELP_SCP }, + { "sftp", "SFTP protocol", CURLHELP_SFTP }, + { "smtp", "SMTP protocol", CURLHELP_SMTP }, + { "ssh", "SSH protocol", CURLHELP_SSH }, + { "telnet", "TELNET protocol", CURLHELP_TELNET }, + { "tftp", "TFTP protocol", CURLHELP_TFTP }, + { "timeout", "Timeouts and delays", CURLHELP_TIMEOUT }, + { "tls", "TLS/SSL related", CURLHELP_TLS }, + { "upload", "Upload, sending data", CURLHELP_UPLOAD }, + { "verbose", "Tracing, logging etc", CURLHELP_VERBOSE } }; static void print_category(unsigned int category, unsigned int cols) @@ -87,20 +84,24 @@ static void print_category(unsigned int category, unsigned int cols) if(len > longdesc) longdesc = len; } - if(longopt + longdesc > cols) + + if(longdesc > cols) + longopt = 0; /* avoid wrap-around */ + else if(longopt + longdesc > cols) longopt = cols - longdesc; for(i = 0; helptext[i].opt; ++i) if(helptext[i].categories & category) { size_t opt = longopt; size_t desclen = strlen(helptext[i].desc); - if(opt + desclen >= (cols - 2)) { + /* avoid wrap-around */ + if(cols >= 2 && opt + desclen >= (cols - 2)) { if(desclen < (cols - 2)) opt = (cols - 3) - desclen; else opt = 0; } - printf(" %-*s %s\n", (int)opt, helptext[i].opt, helptext[i].desc); + curl_mprintf(" %-*s %s\n", (int)opt, helptext[i].opt, helptext[i].desc); } } @@ -110,7 +111,7 @@ static int get_category_content(const char *category, unsigned int cols) unsigned int i; for(i = 0; i < CURL_ARRAYSIZE(categories); ++i) if(curl_strequal(categories[i].opt, category)) { - printf("%s: %s\n", categories[i].opt, categories[i].desc); + curl_mprintf("%s: %s\n", categories[i].opt, categories[i].desc); print_category(categories[i].category, cols); return 0; } @@ -122,7 +123,7 @@ static void get_categories(void) { unsigned int i; for(i = 0; i < CURL_ARRAYSIZE(categories); ++i) - printf(" %-11s %s\n", categories[i].opt, categories[i].desc); + curl_mprintf(" %-11s %s\n", categories[i].opt, categories[i].desc); } /* Prints all categories as a comma-separated list of given width */ @@ -135,18 +136,18 @@ static void get_categories_list(unsigned int width) if(i == CURL_ARRAYSIZE(categories) - 1) { /* final category */ if(col + len + 1 < width) - printf("%s.\n", categories[i].opt); + curl_mprintf("%s.\n", categories[i].opt); else /* start a new line first */ - printf("\n%s.\n", categories[i].opt); + curl_mprintf("\n%s.\n", categories[i].opt); } else if(col + len + 2 < width) { - printf("%s, ", categories[i].opt); + curl_mprintf("%s, ", categories[i].opt); col += len + 2; } else { /* start a new line first */ - printf("\n%s, ", categories[i].opt); + curl_mprintf("\n%s, ", categories[i].opt); col = len + 2; } } @@ -226,7 +227,8 @@ void tool_help(const char *category) unsigned int cols = get_terminal_columns(); /* If no category was provided */ if(!category) { - const char *category_note = "\nThis is not the full help; this " + const char *category_note = + "\nThis is not the full help; this " "menu is split into categories.\nUse \"--help category\" to get " "an overview of all categories, which are:"; const char *category_note2 = @@ -268,28 +270,26 @@ void tool_help(const char *category) else if(!category[2]) a = findshortopt(category[1]); if(!a) { - fprintf(tool_stderr, "Incorrect option name to show help for," - " see curl -h\n"); + curl_mfprintf(tool_stderr, "Incorrect option name to show help for," + " see curl -h\n"); } else { char cmdbuf[80]; if(a->letter != ' ') - msnprintf(cmdbuf, sizeof(cmdbuf), "\n -%c, --", a->letter); + curl_msnprintf(cmdbuf, sizeof(cmdbuf), "\n -%c, --", a->letter); else if(a->desc & ARG_NO) - msnprintf(cmdbuf, sizeof(cmdbuf), "\n --no-%s", a->lname); + curl_msnprintf(cmdbuf, sizeof(cmdbuf), "\n --no-%s", a->lname); else - msnprintf(cmdbuf, sizeof(cmdbuf), "\n %s", category); -#ifdef USE_MANUAL + curl_msnprintf(cmdbuf, sizeof(cmdbuf), "\n %s", category); if(a->cmd == C_XATTR) /* this is the last option, which then ends when FILES starts */ showhelp("\nALL OPTIONS\n", cmdbuf, "\nFILES"); else showhelp("\nALL OPTIONS\n", cmdbuf, "\n -"); -#endif } #else - fprintf(tool_stderr, "Cannot comply. " - "This curl was built without built-in manual\n"); + curl_mfprintf(tool_stderr, "Cannot comply. " + "This curl was built without built-in manual\n"); #endif } /* Otherwise print category and handle the case if the cat was not found */ @@ -301,7 +301,7 @@ void tool_help(const char *category) static bool is_debug(void) { - const char *const *builtin; + const char * const *builtin; for(builtin = feature_names; *builtin; ++builtin) if(curl_strequal("debug", *builtin)) return TRUE; @@ -310,17 +310,17 @@ static bool is_debug(void) void tool_version_info(void) { - const char *const *builtin; + const char * const *builtin; if(is_debug()) - fprintf(tool_stderr, "WARNING: this libcurl is Debug-enabled, " - "do not use in production\n\n"); + curl_mfprintf(tool_stderr, "WARNING: this libcurl is Debug-enabled, " + "do not use in production\n\n"); - printf(CURL_ID "%s\n", curl_version()); + curl_mprintf(CURL_ID "%s\n", curl_version()); #ifdef CURL_PATCHSTAMP - printf("Release-Date: %s, security patched: %s\n", - LIBCURL_TIMESTAMP, CURL_PATCHSTAMP); + curl_mprintf("Release-Date: %s, security patched: %s\n", + LIBCURL_TIMESTAMP, CURL_PATCHSTAMP); #else - printf("Release-Date: %s\n", LIBCURL_TIMESTAMP); + curl_mprintf("Release-Date: %s\n", LIBCURL_TIMESTAMP); #endif if(built_in_protos[0]) { #ifndef CURL_DISABLE_IPFS @@ -328,7 +328,7 @@ void tool_version_info(void) /* we have ipfs and ipns support if libcurl has http support */ for(builtin = built_in_protos; *builtin; ++builtin) { if(insert) { - /* update insertion so ipfs will be printed in alphabetical order */ + /* update insertion so ipfs is printed in alphabetical order */ if(strcmp(*builtin, "ipfs") < 0) insert = *builtin; else @@ -339,15 +339,12 @@ void tool_version_info(void) } } #endif /* !CURL_DISABLE_IPFS */ - printf("Protocols:"); + curl_mprintf("Protocols:"); for(builtin = built_in_protos; *builtin; ++builtin) { - /* Special case: do not list rtmp?* protocols. - They may only appear together with "rtmp" */ - if(!curl_strnequal(*builtin, "rtmp", 4) || !builtin[0][4]) - printf(" %s", *builtin); + curl_mprintf(" %s", *builtin); #ifndef CURL_DISABLE_IPFS if(insert && insert == *builtin) { - printf(" ipfs ipns"); + curl_mprintf(" ipfs ipns"); insert = NULL; } #endif /* !CURL_DISABLE_IPFS */ @@ -360,27 +357,33 @@ void tool_version_info(void) #ifdef CURL_CA_EMBED ++feat_ext_count; #endif - feat_ext = malloc(sizeof(*feature_names) * (feat_ext_count + 1)); +#ifdef CURL_DEBUG_GLOBAL_MEM + ++feat_ext_count; +#endif + feat_ext = curlx_malloc(sizeof(*feature_names) * (feat_ext_count + 1)); if(feat_ext) { memcpy((void *)feat_ext, feature_names, sizeof(*feature_names) * feature_count); feat_ext_count = feature_count; #ifdef CURL_CA_EMBED feat_ext[feat_ext_count++] = "CAcert"; +#endif +#ifdef CURL_DEBUG_GLOBAL_MEM + feat_ext[feat_ext_count++] = "global-mem-debug"; #endif feat_ext[feat_ext_count] = NULL; qsort((void *)feat_ext, feat_ext_count, sizeof(*feat_ext), struplocompare4sort); - printf("Features:"); + curl_mprintf("Features:"); for(builtin = feat_ext; *builtin; ++builtin) - printf(" %s", *builtin); + curl_mprintf(" %s", *builtin); puts(""); /* newline */ - free((void *)feat_ext); + curlx_free((void *)feat_ext); } } if(strcmp(CURL_VERSION, curlinfo->version)) { - printf("WARNING: curl and libcurl versions do not match. " - "Functionality may be affected.\n"); + curl_mprintf("WARNING: curl and libcurl versions do not match. " + "Functionality may be affected.\n"); } } @@ -395,7 +398,7 @@ void tool_list_engines(void) puts("Build-time engines:"); if(engines) { for(; engines; engines = engines->next) - printf(" %s\n", engines->data); + curl_mprintf(" %s\n", engines->data); } else { puts(" "); diff --git a/src/tool_help.h b/src/tool_help.h index ec763c5d5a..6a2ecdb062 100644 --- a/src/tool_help.h +++ b/src/tool_help.h @@ -54,39 +54,39 @@ struct helptxt { }; /* - * The bitmask output is generated with the following command - ------------------------------------------------------------ + The bitmask output is generated with the following command: + ------------------------------------------------------------ make -C docs/cmdline-opts listcats */ -#define CURLHELP_AUTH (1u << 0u) -#define CURLHELP_CONNECTION (1u << 1u) -#define CURLHELP_CURL (1u << 2u) -#define CURLHELP_DEPRECATED (1u << 3u) -#define CURLHELP_DNS (1u << 4u) -#define CURLHELP_FILE (1u << 5u) -#define CURLHELP_FTP (1u << 6u) -#define CURLHELP_GLOBAL (1u << 7u) -#define CURLHELP_HTTP (1u << 8u) -#define CURLHELP_IMAP (1u << 9u) -#define CURLHELP_IMPORTANT (1u << 10u) -#define CURLHELP_LDAP (1u << 11u) -#define CURLHELP_OUTPUT (1u << 12u) -#define CURLHELP_POP3 (1u << 13u) -#define CURLHELP_POST (1u << 14u) -#define CURLHELP_PROXY (1u << 15u) -#define CURLHELP_SCP (1u << 16u) -#define CURLHELP_SFTP (1u << 17u) -#define CURLHELP_SMTP (1u << 18u) -#define CURLHELP_SSH (1u << 19u) -#define CURLHELP_TELNET (1u << 20u) -#define CURLHELP_TFTP (1u << 21u) -#define CURLHELP_TIMEOUT (1u << 22u) -#define CURLHELP_TLS (1u << 23u) -#define CURLHELP_UPLOAD (1u << 24u) -#define CURLHELP_VERBOSE (1u << 25u) +#define CURLHELP_AUTH (1 << 0) +#define CURLHELP_CONNECTION (1 << 1) +#define CURLHELP_CURL (1 << 2) +#define CURLHELP_DEPRECATED (1 << 3) +#define CURLHELP_DNS (1 << 4) +#define CURLHELP_FILE (1 << 5) +#define CURLHELP_FTP (1 << 6) +#define CURLHELP_GLOBAL (1 << 7) +#define CURLHELP_HTTP (1 << 8) +#define CURLHELP_IMAP (1 << 9) +#define CURLHELP_IMPORTANT (1 << 10) +#define CURLHELP_LDAP (1 << 11) +#define CURLHELP_OUTPUT (1 << 12) +#define CURLHELP_POP3 (1 << 13) +#define CURLHELP_POST (1 << 14) +#define CURLHELP_PROXY (1 << 15) +#define CURLHELP_SCP (1 << 16) +#define CURLHELP_SFTP (1 << 17) +#define CURLHELP_SMTP (1 << 18) +#define CURLHELP_SSH (1 << 19) +#define CURLHELP_TELNET (1 << 20) +#define CURLHELP_TFTP (1 << 21) +#define CURLHELP_TIMEOUT (1 << 22) +#define CURLHELP_TLS (1 << 23) +#define CURLHELP_UPLOAD (1 << 24) +#define CURLHELP_VERBOSE (1 << 25) -#define CURLHELP_ALL (0xfffffffu) +#define CURLHELP_ALL 0xfffffffU extern const struct helptxt helptext[]; diff --git a/src/tool_helpers.c b/src/tool_helpers.c index fbf3f1c932..a23c3a80e7 100644 --- a/src/tool_helpers.c +++ b/src/tool_helpers.c @@ -27,7 +27,6 @@ #include "tool_msgs.h" #include "tool_getparam.h" #include "tool_helpers.h" -#include "memdebug.h" /* keep this as LAST include */ /* ** Helper functions that are used from more than one source file. @@ -40,8 +39,8 @@ const char *param2text(ParameterError error) return "had unsupported trailing garbage"; case PARAM_OPTION_UNKNOWN: return "is unknown"; - case PARAM_OPTION_AMBIGUOUS: - return "is ambiguous"; + case PARAM_CONFIG_OPTION_UNKNOWN: + return "found an unknown config option"; case PARAM_REQUIRES_PARAMETER: return "requires parameter"; case PARAM_BAD_USE: @@ -78,7 +77,7 @@ const char *param2text(ParameterError error) int SetHTTPrequest(HttpReq req, HttpReq *store) { /* this mirrors the HttpReq enum in tool_sdecls.h */ - const char *reqname[]= { + const char *reqname[] = { "", /* unspec */ "GET (-G, --get)", "HEAD (-I, --head)", @@ -99,10 +98,10 @@ int SetHTTPrequest(HttpReq req, HttpReq *store) return 1; } -void customrequest_helper(HttpReq req, char *method) +void customrequest_helper(HttpReq req, const char *method) { /* this mirrors the HttpReq enum in tool_sdecls.h */ - const char *dflt[]= { + const char *dflt[] = { "GET", "GET", "HEAD", diff --git a/src/tool_helpers.h b/src/tool_helpers.h index fab026b05d..cbfa168523 100644 --- a/src/tool_helpers.h +++ b/src/tool_helpers.h @@ -27,6 +27,6 @@ const char *param2text(ParameterError error); int SetHTTPrequest(HttpReq req, HttpReq *store); -void customrequest_helper(HttpReq req, char *method); +void customrequest_helper(HttpReq req, const char *method); #endif /* HEADER_CURL_TOOL_HELPERS_H */ diff --git a/src/tool_ipfs.c b/src/tool_ipfs.c index dd030f09bc..2abfcfd3db 100644 --- a/src/tool_ipfs.c +++ b/src/tool_ipfs.c @@ -28,102 +28,71 @@ #include "tool_cfgable.h" #include "tool_msgs.h" #include "tool_ipfs.h" -#include "memdebug.h" /* keep this as LAST include */ -/* ensure input ends in slash */ -static CURLcode ensure_trailing_slash(char **input) +/* input string ends in slash? */ +static bool has_trailing_slash(const char *input) { - if(*input && **input) { - size_t len = strlen(*input); - if(((*input)[len - 1] != '/')) { - struct dynbuf dyn; - curlx_dyn_init(&dyn, len + 2); - - if(curlx_dyn_addn(&dyn, *input, len)) { - tool_safefree(*input); - return CURLE_OUT_OF_MEMORY; - } - - tool_safefree(*input); - - if(curlx_dyn_addn(&dyn, "/", 1)) - return CURLE_OUT_OF_MEMORY; - - *input = curlx_dyn_ptr(&dyn); - } - } - - return CURLE_OK; + size_t len = strlen(input); + return len && input[len - 1] == '/'; } static char *ipfs_gateway(void) { - char *ipfs_path = NULL; - char *gateway_composed_file_path = NULL; - FILE *gateway_file = NULL; - char *gateway = curl_getenv("IPFS_GATEWAY"); + char *ipfs_path_c = NULL; + char *gateway_composed_c = NULL; + FILE *gfile = NULL; + char *gateway_env = getenv("IPFS_GATEWAY"); - /* Gateway is found from environment variable. */ - if(gateway) { - if(ensure_trailing_slash(&gateway)) - goto fail; - return gateway; - } + if(gateway_env) + return curlx_strdup(gateway_env); /* Try to find the gateway in the IPFS data folder. */ - ipfs_path = curl_getenv("IPFS_PATH"); + ipfs_path_c = curl_getenv("IPFS_PATH"); - if(!ipfs_path) { + if(!ipfs_path_c) { char *home = getenv("HOME"); - if(home && *home) - ipfs_path = aprintf("%s/.ipfs/", home); /* fallback to "~/.ipfs", as that is the default location. */ + if(home && *home) + ipfs_path_c = curl_maprintf("%s/.ipfs/", home); + if(!ipfs_path_c) + goto fail; } - if(!ipfs_path || ensure_trailing_slash(&ipfs_path)) + gateway_composed_c = + curl_maprintf("%s%sgateway", ipfs_path_c, + has_trailing_slash(ipfs_path_c) ? "" : "/"); + + if(!gateway_composed_c) goto fail; - gateway_composed_file_path = aprintf("%sgateway", ipfs_path); + gfile = curlx_fopen(gateway_composed_c, FOPEN_READTEXT); + curl_free(gateway_composed_c); - if(!gateway_composed_file_path) - goto fail; - - gateway_file = fopen(gateway_composed_file_path, FOPEN_READTEXT); - tool_safefree(gateway_composed_file_path); - - if(gateway_file) { + if(gfile) { int c; struct dynbuf dyn; + char *gateway = NULL; curlx_dyn_init(&dyn, MAX_GATEWAY_URL_LEN); /* get the first line of the gateway file, ignore the rest */ - while((c = getc(gateway_file)) != EOF && c != '\n' && c != '\r') { + while((c = getc(gfile)) != EOF && c != '\n' && c != '\r') { char c_char = (char)c; if(curlx_dyn_addn(&dyn, &c_char, 1)) goto fail; } - fclose(gateway_file); - gateway_file = NULL; - if(curlx_dyn_len(&dyn)) gateway = curlx_dyn_ptr(&dyn); - if(gateway) - ensure_trailing_slash(&gateway); - - if(!gateway) - goto fail; - - tool_safefree(ipfs_path); + curl_free(ipfs_path_c); + curlx_fclose(gfile); return gateway; } fail: - if(gateway_file) - fclose(gateway_file); - tool_safefree(gateway); - tool_safefree(ipfs_path); + if(gfile) + curlx_fclose(gfile); + curl_free(ipfs_path_c); return NULL; } @@ -158,23 +127,16 @@ CURLcode ipfs_url_rewrite(CURLU *uh, const char *protocol, char **url, goto clean; /* We might have a --ipfs-gateway argument. Check it first and use it. Error - * if we do have something but if it is an invalid url. + * if we do have something but if it is an invalid URL. */ if(config->ipfs_gateway) { - /* ensure the gateway ends in a trailing / */ - if(ensure_trailing_slash(&config->ipfs_gateway) != CURLE_OK) { - result = CURLE_OUT_OF_MEMORY; - goto clean; - } - if(!curl_url_set(gatewayurl, CURLUPART_URL, config->ipfs_gateway, - CURLU_GUESS_SCHEME)) { - gateway = strdup(config->ipfs_gateway); + CURLU_GUESS_SCHEME)) { + gateway = curlx_strdup(config->ipfs_gateway); if(!gateway) { result = CURLE_URL_MALFORMAT; goto clean; } - } else { result = CURLE_BAD_FUNCTION_ARGUMENT; @@ -182,7 +144,6 @@ CURLcode ipfs_url_rewrite(CURLU *uh, const char *protocol, char **url, } } else { - /* this is ensured to end in a trailing / within ipfs_gateway() */ gateway = ipfs_gateway(); if(!gateway) { result = CURLE_FILE_COULDNT_READ_FILE; @@ -196,61 +157,51 @@ CURLcode ipfs_url_rewrite(CURLU *uh, const char *protocol, char **url, } /* check for unsupported gateway parts */ - if(curl_url_get(gatewayurl, CURLUPART_QUERY, &gwquery, 0) - != CURLUE_NO_QUERY) { + if(curl_url_get(gatewayurl, CURLUPART_QUERY, &gwquery, 0) != + CURLUE_NO_QUERY) { result = CURLE_URL_MALFORMAT; goto clean; } /* get gateway parts */ - if(curl_url_get(gatewayurl, CURLUPART_HOST, - &gwhost, CURLU_URLDECODE)) { + if(curl_url_get(gatewayurl, CURLUPART_HOST, &gwhost, CURLU_URLDECODE) || + curl_url_get(gatewayurl, CURLUPART_SCHEME, &gwscheme, CURLU_URLDECODE) || + curl_url_get(gatewayurl, CURLUPART_PORT, &gwport, + CURLU_URLDECODE | CURLU_DEFAULT_PORT) || + curl_url_get(gatewayurl, CURLUPART_PATH, &gwpath, CURLU_URLDECODE)) goto clean; - } - - if(curl_url_get(gatewayurl, CURLUPART_SCHEME, - &gwscheme, CURLU_URLDECODE)) { - goto clean; - } - - curl_url_get(gatewayurl, CURLUPART_PORT, &gwport, CURLU_URLDECODE); - curl_url_get(gatewayurl, CURLUPART_PATH, &gwpath, CURLU_URLDECODE); /* get the path from user input */ - curl_url_get(uh, CURLUPART_PATH, &inputpath, CURLU_URLDECODE); + if(curl_url_get(uh, CURLUPART_PATH, &inputpath, CURLU_URLDECODE)) + goto clean; /* inputpath might be NULL or a valid pointer now */ - /* set gateway parts in input url */ + /* set gateway parts in input URL */ if(curl_url_set(uh, CURLUPART_SCHEME, gwscheme, CURLU_URLENCODE) || curl_url_set(uh, CURLUPART_HOST, gwhost, CURLU_URLENCODE) || curl_url_set(uh, CURLUPART_PORT, gwport, CURLU_URLENCODE)) goto clean; - /* if the input path is just a slash, clear it */ + /* if the input path is a slash, clear it */ if(inputpath && (inputpath[0] == '/') && !inputpath[1]) *inputpath = '\0'; - /* ensure the gateway path ends with a trailing slash */ - ensure_trailing_slash(&gwpath); - - pathbuffer = aprintf("%s%s/%s%s", gwpath, protocol, cid, - inputpath ? inputpath : ""); - if(!pathbuffer) { + pathbuffer = curl_maprintf("%s%s%s/%s%s", gwpath, + has_trailing_slash(gwpath) ? "" : "/", + protocol, cid, + inputpath ? inputpath : ""); + if(!pathbuffer || + curl_url_set(uh, CURLUPART_PATH, pathbuffer, CURLU_URLENCODE)) goto clean; - } - - if(curl_url_set(uh, CURLUPART_PATH, pathbuffer, CURLU_URLENCODE)) { - goto clean; - } /* Free whatever it has now, rewriting is next */ - tool_safefree(*url); + curlx_safefree(*url); if(curl_url_get(uh, CURLUPART_URL, &cloneurl, CURLU_URLENCODE)) { goto clean; } - /* we need to strdup the URL so that we can call free() on it later */ - *url = strdup(cloneurl); + /* we need to strdup the URL so that we can call curlx_free() on it later */ + *url = curlx_strdup(cloneurl); curl_free(cloneurl); if(!*url) goto clean; @@ -258,7 +209,7 @@ CURLcode ipfs_url_rewrite(CURLU *uh, const char *protocol, char **url, result = CURLE_OK; clean: - free(gateway); + curlx_free(gateway); curl_free(gwhost); curl_free(gwpath); curl_free(gwquery); diff --git a/src/tool_ipfs.h b/src/tool_ipfs.h index 31d8019fa2..caa959b607 100644 --- a/src/tool_ipfs.h +++ b/src/tool_ipfs.h @@ -27,9 +27,8 @@ #ifndef CURL_DISABLE_IPFS #define MAX_GATEWAY_URL_LEN 10000 - CURLcode ipfs_url_rewrite(CURLU *uh, const char *protocol, char **url, struct OperationConfig *config); +#endif #endif /* HEADER_CURL_TOOL_IPFS_H */ -#endif /* !CURL_DISABLE_IPFS */ diff --git a/src/tool_libinfo.c b/src/tool_libinfo.c index d755532652..5a5382c007 100644 --- a/src/tool_libinfo.c +++ b/src/tool_libinfo.c @@ -24,7 +24,6 @@ #include "tool_setup.h" #include "tool_libinfo.h" -#include "memdebug.h" /* keep this as LAST include */ /* global variable definitions, for libcurl runtime info */ @@ -47,7 +46,7 @@ const char *proto_tftp = NULL; #ifndef CURL_DISABLE_IPFS const char *proto_ipfs = "ipfs"; const char *proto_ipns = "ipns"; -#endif /* !CURL_DISABLE_IPFS */ +#endif static struct proto_name_tokenp { const char *proto_name; @@ -62,7 +61,7 @@ static struct proto_name_tokenp { { "scp", &proto_scp }, { "sftp", &proto_sftp }, { "tftp", &proto_tftp }, - { NULL, NULL } + { NULL, NULL } }; bool feature_altsvc = FALSE; @@ -88,38 +87,37 @@ static struct feature_name_presentp { int feature_bitmask; } const maybe_feature[] = { /* Keep alphabetically sorted. */ - {"alt-svc", &feature_altsvc, CURL_VERSION_ALTSVC}, - {"AsynchDNS", NULL, CURL_VERSION_ASYNCHDNS}, - {"brotli", &feature_brotli, CURL_VERSION_BROTLI}, - {"CharConv", NULL, CURL_VERSION_CONV}, - {"Debug", NULL, CURL_VERSION_DEBUG}, - {"ECH", &feature_ech, 0}, - {"gsasl", NULL, CURL_VERSION_GSASL}, - {"GSS-API", NULL, CURL_VERSION_GSSAPI}, - {"HSTS", &feature_hsts, CURL_VERSION_HSTS}, - {"HTTP2", &feature_http2, CURL_VERSION_HTTP2}, - {"HTTP3", &feature_http3, CURL_VERSION_HTTP3}, - {"HTTPS-proxy", &feature_httpsproxy, CURL_VERSION_HTTPS_PROXY}, - {"IDN", NULL, CURL_VERSION_IDN}, - {"IPv6", NULL, CURL_VERSION_IPV6}, - {"Kerberos", NULL, CURL_VERSION_KERBEROS5}, - {"Largefile", NULL, CURL_VERSION_LARGEFILE}, - {"libz", &feature_libz, CURL_VERSION_LIBZ}, - {"MultiSSL", NULL, CURL_VERSION_MULTI_SSL}, - {"NTLM", &feature_ntlm, CURL_VERSION_NTLM}, - {"NTLM_WB", &feature_ntlm_wb, CURL_VERSION_NTLM_WB}, - {"PSL", NULL, CURL_VERSION_PSL}, - {"SPNEGO", &feature_spnego, CURL_VERSION_SPNEGO}, - {"SSL", &feature_ssl, CURL_VERSION_SSL}, - {"SSPI", NULL, CURL_VERSION_SSPI}, - {"SSLS-EXPORT", &feature_ssls_export, 0}, - {"threadsafe", NULL, CURL_VERSION_THREADSAFE}, - {"TLS-SRP", &feature_tls_srp, CURL_VERSION_TLSAUTH_SRP}, - {"TrackMemory", NULL, CURL_VERSION_CURLDEBUG}, - {"Unicode", NULL, CURL_VERSION_UNICODE}, - {"UnixSockets", NULL, CURL_VERSION_UNIX_SOCKETS}, - {"zstd", &feature_zstd, CURL_VERSION_ZSTD}, - {NULL, NULL, 0} + { "alt-svc", &feature_altsvc, CURL_VERSION_ALTSVC }, + { "AsynchDNS", NULL, CURL_VERSION_ASYNCHDNS }, + { "brotli", &feature_brotli, CURL_VERSION_BROTLI }, + { "CharConv", NULL, CURL_VERSION_CONV }, + { "Debug", NULL, CURL_VERSION_DEBUG }, + { "ECH", &feature_ech, 0 }, + { "gsasl", NULL, CURL_VERSION_GSASL }, + { "GSS-API", NULL, CURL_VERSION_GSSAPI }, + { "HSTS", &feature_hsts, CURL_VERSION_HSTS }, + { "HTTP2", &feature_http2, CURL_VERSION_HTTP2 }, + { "HTTP3", &feature_http3, CURL_VERSION_HTTP3 }, + { "HTTPS-proxy", &feature_httpsproxy, CURL_VERSION_HTTPS_PROXY }, + { "IDN", NULL, CURL_VERSION_IDN }, + { "IPv6", NULL, CURL_VERSION_IPV6 }, + { "Kerberos", NULL, CURL_VERSION_KERBEROS5 }, + { "Largefile", NULL, CURL_VERSION_LARGEFILE }, + { "libz", &feature_libz, CURL_VERSION_LIBZ }, + { "MultiSSL", NULL, CURL_VERSION_MULTI_SSL }, + { "NTLM", &feature_ntlm, CURL_VERSION_NTLM }, + { "NTLM_WB", &feature_ntlm_wb, CURL_VERSION_NTLM_WB }, + { "PSL", NULL, CURL_VERSION_PSL }, + { "SPNEGO", &feature_spnego, CURL_VERSION_SPNEGO }, + { "SSL", &feature_ssl, CURL_VERSION_SSL }, + { "SSPI", NULL, CURL_VERSION_SSPI }, + { "SSLS-EXPORT", &feature_ssls_export, 0 }, + { "threadsafe", NULL, CURL_VERSION_THREADSAFE }, + { "TLS-SRP", &feature_tls_srp, CURL_VERSION_TLSAUTH_SRP }, + { "Unicode", NULL, CURL_VERSION_UNICODE }, + { "UnixSockets", NULL, CURL_VERSION_UNIX_SOCKETS }, + { "zstd", &feature_zstd, CURL_VERSION_ZSTD }, + { NULL, NULL, 0 } }; static const char *fnames[CURL_ARRAYSIZE(maybe_feature)]; @@ -134,11 +132,10 @@ size_t feature_count; * the latter is not returned by curl_version_info(), it is built from * the returned features bit mask. */ - CURLcode get_libcurl_info(void) { CURLcode result = CURLE_OK; - const char *const *builtin; + const char * const *builtin; /* Pointer to libcurl's runtime version information */ curlinfo = curl_version_info(CURLVERSION_NOW); @@ -186,8 +183,9 @@ CURLcode get_libcurl_info(void) ++feature_count; } - feature_libssh2 = curlinfo->libssh_version && - !strncmp("libssh2", curlinfo->libssh_version, 7); + feature_libssh2 = curlinfo->age >= CURLVERSION_FOURTH && + curlinfo->libssh_version && + !strncmp("libssh2", curlinfo->libssh_version, 7); return CURLE_OK; } @@ -198,7 +196,6 @@ CURLcode get_libcurl_info(void) * a given protocol and thus allows comparing pointers rather than strings. * In addition, the returned pointer is not deallocated until the program ends. */ - const char *proto_token(const char *proto) { const char * const *builtin; diff --git a/src/tool_libinfo.h b/src/tool_libinfo.h index 5e6e1de24c..ddc41a1338 100644 --- a/src/tool_libinfo.h +++ b/src/tool_libinfo.h @@ -27,7 +27,6 @@ /* global variable declarations, for libcurl runtime info */ - extern curl_version_info_data *curlinfo; extern const char * const *built_in_protos; diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index ecbd8bf459..864771bfba 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -22,6 +22,7 @@ * ***************************************************************************/ #include "tool_setup.h" + #include "tool_help.h" /* @@ -33,828 +34,831 @@ */ const struct helptxt helptext[] = { - {" --abstract-unix-socket ", - "Connect via abstract Unix domain socket", - CURLHELP_CONNECTION}, - {" --alt-svc ", - "Enable alt-svc with this cache file", - CURLHELP_HTTP}, - {" --anyauth", - "Pick any authentication method", - CURLHELP_HTTP | CURLHELP_PROXY | CURLHELP_AUTH}, - {"-a, --append", - "Append to target file when uploading", - CURLHELP_FTP | CURLHELP_SFTP}, - {" --aws-sigv4 ", - "AWS V4 signature auth", - CURLHELP_AUTH | CURLHELP_HTTP}, - {" --basic", - "HTTP Basic Authentication", - CURLHELP_AUTH}, - {" --ca-native", - "Load CA certs from the OS", - CURLHELP_TLS}, - {" --cacert ", - "CA certificate to verify peer against", - CURLHELP_TLS}, - {" --capath ", - "CA directory to verify peer against", - CURLHELP_TLS}, - {"-E, --cert ", - "Client certificate file and password", - CURLHELP_TLS}, - {" --cert-status", - "Verify server cert status OCSP-staple", - CURLHELP_TLS}, - {" --cert-type ", - "Certificate type (DER/PEM/ENG/PROV/P12)", - CURLHELP_TLS}, - {" --ciphers ", - "TLS 1.2 (1.1, 1.0) ciphers to use", - CURLHELP_TLS}, - {" --compressed", - "Request compressed response", - CURLHELP_HTTP}, - {" --compressed-ssh", - "Enable SSH compression", - CURLHELP_SCP | CURLHELP_SSH}, - {"-K, --config ", - "Read config from a file", - CURLHELP_CURL}, - {" --connect-timeout ", - "Maximum time allowed to connect", - CURLHELP_CONNECTION | CURLHELP_TIMEOUT}, - {" --connect-to ", - "Connect to host2 instead of host1", - CURLHELP_CONNECTION | CURLHELP_DNS}, - {"-C, --continue-at ", - "Resumed transfer offset", - CURLHELP_CONNECTION}, - {"-b, --cookie ", - "Send cookies from string/load from file", - CURLHELP_HTTP}, - {"-c, --cookie-jar ", - "Save cookies to after operation", - CURLHELP_HTTP}, - {" --create-dirs", - "Create necessary local directory hierarchy", - CURLHELP_OUTPUT}, - {" --create-file-mode ", - "File mode for created files", - CURLHELP_SFTP | CURLHELP_SCP | CURLHELP_FILE | CURLHELP_UPLOAD}, - {" --crlf", - "Convert LF to CRLF in upload", - CURLHELP_FTP | CURLHELP_SMTP}, - {" --crlfile ", - "Certificate Revocation list", - CURLHELP_TLS}, - {" --curves ", - "(EC) TLS key exchange algorithms to request", - CURLHELP_TLS}, - {"-d, --data ", - "HTTP POST data", - CURLHELP_IMPORTANT | CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD}, - {" --data-ascii ", - "HTTP POST ASCII data", - CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD}, - {" --data-binary ", - "HTTP POST binary data", - CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD}, - {" --data-raw ", - "HTTP POST data, '@' allowed", - CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD}, - {" --data-urlencode ", - "HTTP POST data URL encoded", - CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD}, - {" --delegation ", - "GSS-API delegation permission", - CURLHELP_AUTH}, - {" --digest", - "HTTP Digest Authentication", - CURLHELP_PROXY | CURLHELP_AUTH | CURLHELP_HTTP}, - {"-q, --disable", - "Disable .curlrc", - CURLHELP_CURL}, - {" --disable-eprt", - "Inhibit using EPRT or LPRT", - CURLHELP_FTP}, - {" --disable-epsv", - "Inhibit using EPSV", - CURLHELP_FTP}, - {" --disallow-username-in-url", - "Disallow username in URL", - CURLHELP_CURL}, - {" --dns-interface ", - "Interface to use for DNS requests", - CURLHELP_DNS}, - {" --dns-ipv4-addr
", - "IPv4 address to use for DNS requests", - CURLHELP_DNS}, - {" --dns-ipv6-addr
", - "IPv6 address to use for DNS requests", - CURLHELP_DNS}, - {" --dns-servers ", - "DNS server addrs to use", - CURLHELP_DNS}, - {" --doh-cert-status", - "Verify DoH server cert status OCSP-staple", - CURLHELP_DNS | CURLHELP_TLS}, - {" --doh-insecure", - "Allow insecure DoH server connections", - CURLHELP_DNS | CURLHELP_TLS}, - {" --doh-url ", - "Resolve hostnames over DoH", - CURLHELP_DNS}, - {" --dump-ca-embed", - "Write the embedded CA bundle to standard output", - CURLHELP_HTTP | CURLHELP_PROXY | CURLHELP_TLS}, - {"-D, --dump-header ", - "Write the received headers to ", - CURLHELP_HTTP | CURLHELP_FTP}, - {" --ech ", - "Configure ECH", - CURLHELP_TLS}, - {" --egd-file ", - "EGD socket path for random data", - CURLHELP_DEPRECATED}, - {" --engine ", - "Crypto engine to use", - CURLHELP_TLS}, - {" --etag-compare ", - "Load ETag from file", - CURLHELP_HTTP}, - {" --etag-save ", - "Parse incoming ETag and save to a file", - CURLHELP_HTTP}, - {" --expect100-timeout ", - "How long to wait for 100-continue", - CURLHELP_HTTP | CURLHELP_TIMEOUT}, - {"-f, --fail", - "Fail fast with no output on HTTP errors", - CURLHELP_IMPORTANT | CURLHELP_HTTP}, - {" --fail-early", - "Fail on first transfer error", - CURLHELP_CURL | CURLHELP_GLOBAL}, - {" --fail-with-body", - "Fail on HTTP errors but save the body", - CURLHELP_HTTP | CURLHELP_OUTPUT}, - {" --false-start", - "Enable TLS False Start", - CURLHELP_DEPRECATED}, - {" --follow", - "Follow redirects per spec", - CURLHELP_HTTP}, - {"-F, --form ", - "Specify multipart MIME data", - CURLHELP_HTTP | CURLHELP_UPLOAD | CURLHELP_POST | CURLHELP_IMAP | - CURLHELP_SMTP}, - {" --form-escape", - "Escape form fields using backslash", - CURLHELP_HTTP | CURLHELP_UPLOAD | CURLHELP_POST}, - {" --form-string ", - "Specify multipart MIME data", - CURLHELP_HTTP | CURLHELP_UPLOAD | CURLHELP_POST | CURLHELP_SMTP | - CURLHELP_IMAP}, - {" --ftp-account ", - "Account data string", - CURLHELP_FTP | CURLHELP_AUTH}, - {" --ftp-alternative-to-user ", - "String to replace USER [name]", - CURLHELP_FTP}, - {" --ftp-create-dirs", - "Create the remote dirs if not present", - CURLHELP_FTP | CURLHELP_SFTP}, - {" --ftp-method ", - "Control CWD usage", - CURLHELP_FTP}, - {" --ftp-pasv", - "Send PASV/EPSV instead of PORT", - CURLHELP_FTP}, - {"-P, --ftp-port
", - "Send PORT instead of PASV", - CURLHELP_FTP}, - {" --ftp-pret", - "Send PRET before PASV", - CURLHELP_FTP}, - {" --ftp-skip-pasv-ip", - "Skip the IP address for PASV", - CURLHELP_FTP}, - {" --ftp-ssl-ccc", - "Send CCC after authenticating", - CURLHELP_FTP | CURLHELP_TLS}, - {" --ftp-ssl-ccc-mode ", - "Set CCC mode", - CURLHELP_FTP | CURLHELP_TLS}, - {" --ftp-ssl-control", - "Require TLS for login, clear for transfer", - CURLHELP_FTP | CURLHELP_TLS}, - {"-G, --get", - "Put the post data in the URL and use GET", - CURLHELP_HTTP}, - {"-g, --globoff", - "Disable URL globbing with {} and []", - CURLHELP_CURL}, - {" --happy-eyeballs-timeout-ms ", - "Time for IPv6 before IPv4", - CURLHELP_CONNECTION | CURLHELP_TIMEOUT}, - {" --haproxy-clientip ", - "Set address in HAProxy PROXY", - CURLHELP_HTTP | CURLHELP_PROXY}, - {" --haproxy-protocol", - "Send HAProxy PROXY protocol v1 header", - CURLHELP_HTTP | CURLHELP_PROXY}, - {"-I, --head", - "Show document info only", - CURLHELP_HTTP | CURLHELP_FTP | CURLHELP_FILE}, - {"-H, --header
", - "Pass custom header(s) to server", - CURLHELP_HTTP | CURLHELP_IMAP | CURLHELP_SMTP}, - {"-h, --help ", - "Get help for commands", - CURLHELP_IMPORTANT | CURLHELP_CURL}, - {" --hostpubmd5 ", - "Acceptable MD5 hash of host public key", - CURLHELP_SFTP | CURLHELP_SCP | CURLHELP_SSH}, - {" --hostpubsha256 ", - "Acceptable SHA256 hash of host public key", - CURLHELP_SFTP | CURLHELP_SCP | CURLHELP_SSH}, - {" --hsts ", - "Enable HSTS with this cache file", - CURLHELP_HTTP}, - {" --http0.9", - "Allow HTTP/0.9 responses", - CURLHELP_HTTP}, - {"-0, --http1.0", - "Use HTTP/1.0", - CURLHELP_HTTP}, - {" --http1.1", - "Use HTTP/1.1", - CURLHELP_HTTP}, - {" --http2", - "Use HTTP/2", - CURLHELP_HTTP}, - {" --http2-prior-knowledge", - "Use HTTP/2 without HTTP/1.1 Upgrade", - CURLHELP_HTTP}, - {" --http3", - "Use HTTP/3", - CURLHELP_HTTP}, - {" --http3-only", - "Use HTTP/3 only", - CURLHELP_HTTP}, - {" --ignore-content-length", - "Ignore the size of the remote resource", - CURLHELP_HTTP | CURLHELP_FTP}, - {"-k, --insecure", - "Allow insecure server connections", - CURLHELP_TLS | CURLHELP_SFTP | CURLHELP_SCP | CURLHELP_SSH}, - {" --interface ", - "Use network interface", - CURLHELP_CONNECTION}, - {" --ip-tos ", - "Set IP Type of Service or Traffic Class", - CURLHELP_CONNECTION}, - {" --ipfs-gateway ", - "Gateway for IPFS", - CURLHELP_CURL}, - {"-4, --ipv4", - "Resolve names to IPv4 addresses", - CURLHELP_CONNECTION | CURLHELP_DNS}, - {"-6, --ipv6", - "Resolve names to IPv6 addresses", - CURLHELP_CONNECTION | CURLHELP_DNS}, - {" --json ", - "HTTP POST JSON", - CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD}, - {"-j, --junk-session-cookies", - "Ignore session cookies read from file", - CURLHELP_HTTP}, - {" --keepalive-cnt ", - "Maximum number of keepalive probes", - CURLHELP_CONNECTION}, - {" --keepalive-time ", - "Interval time for keepalive probes", - CURLHELP_CONNECTION | CURLHELP_TIMEOUT}, - {" --key ", - "Private key filename", - CURLHELP_TLS | CURLHELP_SSH}, - {" --key-type ", - "Private key file type (DER/PEM/ENG)", - CURLHELP_TLS}, - {" --krb ", - "Enable Kerberos with security ", - CURLHELP_FTP}, - {" --libcurl ", - "Generate libcurl code for this command line", - CURLHELP_CURL | CURLHELP_GLOBAL}, - {" --limit-rate ", - "Limit transfer speed to RATE", - CURLHELP_CONNECTION}, - {"-l, --list-only", - "List only mode", - CURLHELP_FTP | CURLHELP_POP3 | CURLHELP_SFTP | CURLHELP_FILE}, - {" --local-port ", - "Use a local port number within RANGE", - CURLHELP_CONNECTION}, - {"-L, --location", - "Follow redirects", - CURLHELP_HTTP}, - {" --location-trusted", - "As --location, but send secrets to other hosts", - CURLHELP_HTTP | CURLHELP_AUTH}, - {" --login-options ", - "Server login options", - CURLHELP_IMAP | CURLHELP_POP3 | CURLHELP_SMTP | CURLHELP_AUTH | - CURLHELP_LDAP}, - {" --mail-auth
", - "Originator address of the original email", - CURLHELP_SMTP}, - {" --mail-from
", - "Mail from this address", - CURLHELP_SMTP}, - {" --mail-rcpt
", - "Mail to this address", - CURLHELP_SMTP}, - {" --mail-rcpt-allowfails", - "Allow RCPT TO command to fail", - CURLHELP_SMTP}, - {"-M, --manual", - "Display the full manual", - CURLHELP_CURL}, - {" --max-filesize ", - "Maximum file size to download", - CURLHELP_CONNECTION}, - {" --max-redirs ", - "Maximum number of redirects allowed", - CURLHELP_HTTP}, - {"-m, --max-time ", - "Maximum time allowed for transfer", - CURLHELP_CONNECTION | CURLHELP_TIMEOUT}, - {" --metalink", - "Process given URLs as metalink XML file", - CURLHELP_DEPRECATED}, - {" --mptcp", - "Enable Multipath TCP", - CURLHELP_CONNECTION}, - {" --negotiate", - "Use HTTP Negotiate (SPNEGO) authentication", - CURLHELP_AUTH | CURLHELP_HTTP}, - {"-n, --netrc", - "Must read .netrc for username and password", - CURLHELP_AUTH}, - {" --netrc-file ", - "Specify FILE for netrc", - CURLHELP_AUTH}, - {" --netrc-optional", - "Use either .netrc or URL", - CURLHELP_AUTH}, - {"-:, --next", - "Make next URL use separate options", - CURLHELP_CURL}, - {" --no-alpn", - "Disable the ALPN TLS extension", - CURLHELP_TLS | CURLHELP_HTTP}, - {"-N, --no-buffer", - "Disable buffering of the output stream", - CURLHELP_OUTPUT}, - {" --no-clobber", - "Do not overwrite files that already exist", - CURLHELP_OUTPUT}, - {" --no-keepalive", - "Disable TCP keepalive on the connection", - CURLHELP_CONNECTION}, - {" --no-npn", - "Disable the NPN TLS extension", - CURLHELP_DEPRECATED}, - {" --no-progress-meter", - "Do not show the progress meter", - CURLHELP_VERBOSE}, - {" --no-sessionid", - "Disable SSL session-ID reusing", - CURLHELP_TLS}, - {" --noproxy ", - "List of hosts which do not use proxy", - CURLHELP_PROXY}, - {" --ntlm", - "HTTP NTLM authentication", - CURLHELP_AUTH | CURLHELP_HTTP}, - {" --ntlm-wb", - "HTTP NTLM authentication with winbind", - CURLHELP_DEPRECATED}, - {" --oauth2-bearer ", - "OAuth 2 Bearer Token", - CURLHELP_AUTH | CURLHELP_IMAP | CURLHELP_POP3 | CURLHELP_SMTP | - CURLHELP_LDAP}, - {" --out-null", - "Discard response data into the void", - CURLHELP_OUTPUT}, - {"-o, --output ", - "Write to file instead of stdout", - CURLHELP_IMPORTANT | CURLHELP_OUTPUT}, - {" --output-dir ", - "Directory to save files in", - CURLHELP_OUTPUT}, - {"-Z, --parallel", - "Perform transfers in parallel", - CURLHELP_CONNECTION | CURLHELP_CURL | CURLHELP_GLOBAL}, - {" --parallel-immediate", - "Do not wait for multiplexing", - CURLHELP_CONNECTION | CURLHELP_CURL | CURLHELP_GLOBAL}, - {" --parallel-max ", - "Maximum concurrency for parallel transfers", - CURLHELP_CONNECTION | CURLHELP_CURL | CURLHELP_GLOBAL}, - {" --parallel-max-host ", - "Maximum connections to a single host", - CURLHELP_CONNECTION | CURLHELP_CURL | CURLHELP_GLOBAL}, - {" --pass ", - "Passphrase for the private key", - CURLHELP_SSH | CURLHELP_TLS | CURLHELP_AUTH}, - {" --path-as-is", - "Do not squash .. sequences in URL path", - CURLHELP_CURL}, - {" --pinnedpubkey ", - "Public key to verify peer against", - CURLHELP_TLS}, - {" --post301", - "Do not switch to GET after a 301 redirect", - CURLHELP_HTTP | CURLHELP_POST}, - {" --post302", - "Do not switch to GET after a 302 redirect", - CURLHELP_HTTP | CURLHELP_POST}, - {" --post303", - "Do not switch to GET after a 303 redirect", - CURLHELP_HTTP | CURLHELP_POST}, - {" --preproxy <[protocol://]host[:port]>", - "Use this proxy first", - CURLHELP_PROXY}, - {"-#, --progress-bar", - "Display transfer progress as a bar", - CURLHELP_VERBOSE | CURLHELP_GLOBAL}, - {" --proto ", - "Enable/disable PROTOCOLS", - CURLHELP_CONNECTION | CURLHELP_CURL}, - {" --proto-default ", - "Use PROTOCOL for any URL missing a scheme", - CURLHELP_CONNECTION | CURLHELP_CURL}, - {" --proto-redir ", - "Enable/disable PROTOCOLS on redirect", - CURLHELP_CONNECTION | CURLHELP_CURL}, - {"-x, --proxy <[protocol://]host[:port]>", - "Use this proxy", - CURLHELP_PROXY}, - {" --proxy-anyauth", - "Pick any proxy authentication method", - CURLHELP_PROXY | CURLHELP_AUTH}, - {" --proxy-basic", - "Use Basic authentication on the proxy", - CURLHELP_PROXY | CURLHELP_AUTH}, - {" --proxy-ca-native", - "Load CA certs from the OS to verify proxy", - CURLHELP_TLS}, - {" --proxy-cacert ", - "CA certificates to verify proxy against", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-capath ", - "CA directory to verify proxy against", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-cert ", - "Set client certificate for proxy", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-cert-type ", - "Client certificate type for HTTPS proxy", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-ciphers ", - "TLS 1.2 (1.1, 1.0) ciphers to use for proxy", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-crlfile ", - "Set a CRL list for proxy", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-digest", - "Digest auth with the proxy", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-header
", - "Pass custom header(s) to proxy", - CURLHELP_PROXY}, - {" --proxy-http2", - "Use HTTP/2 with HTTPS proxy", - CURLHELP_HTTP | CURLHELP_PROXY}, - {" --proxy-insecure", - "Skip HTTPS proxy cert verification", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-key ", - "Private key for HTTPS proxy", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-key-type ", - "Private key file type for proxy", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-negotiate", - "HTTP Negotiate (SPNEGO) auth with the proxy", - CURLHELP_PROXY | CURLHELP_AUTH}, - {" --proxy-ntlm", - "NTLM authentication with the proxy", - CURLHELP_PROXY | CURLHELP_AUTH}, - {" --proxy-pass ", - "Passphrase for private key for HTTPS proxy", - CURLHELP_PROXY | CURLHELP_TLS | CURLHELP_AUTH}, - {" --proxy-pinnedpubkey ", - "FILE/HASHES public key to verify proxy with", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-service-name ", - "SPNEGO proxy service name", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-ssl-allow-beast", - "Allow this security flaw for HTTPS proxy", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-ssl-auto-client-cert", - "Auto client certificate for proxy", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-tls13-ciphers ", - "TLS 1.3 proxy cipher suites", - CURLHELP_PROXY | CURLHELP_TLS}, - {" --proxy-tlsauthtype ", - "TLS authentication type for HTTPS proxy", - CURLHELP_PROXY | CURLHELP_TLS | CURLHELP_AUTH}, - {" --proxy-tlspassword ", - "TLS password for HTTPS proxy", - CURLHELP_PROXY | CURLHELP_TLS | CURLHELP_AUTH}, - {" --proxy-tlsuser ", - "TLS username for HTTPS proxy", - CURLHELP_PROXY | CURLHELP_TLS | CURLHELP_AUTH}, - {" --proxy-tlsv1", - "TLSv1 for HTTPS proxy", - CURLHELP_PROXY | CURLHELP_TLS | CURLHELP_AUTH}, - {"-U, --proxy-user ", - "Proxy user and password", - CURLHELP_PROXY | CURLHELP_AUTH}, - {" --proxy1.0 ", - "Use HTTP/1.0 proxy on given port", - CURLHELP_PROXY}, - {"-p, --proxytunnel", - "HTTP proxy tunnel (using CONNECT)", - CURLHELP_PROXY}, - {" --pubkey ", - "SSH Public key filename", - CURLHELP_SFTP | CURLHELP_SCP | CURLHELP_SSH | CURLHELP_AUTH}, - {"-Q, --quote ", - "Send command(s) to server before transfer", - CURLHELP_FTP | CURLHELP_SFTP}, - {" --random-file ", - "File for reading random data from", - CURLHELP_DEPRECATED}, - {"-r, --range ", - "Retrieve only the bytes within RANGE", - CURLHELP_HTTP | CURLHELP_FTP | CURLHELP_SFTP | CURLHELP_FILE}, - {" --rate ", - "Request rate for serial transfers", - CURLHELP_CONNECTION | CURLHELP_GLOBAL}, - {" --raw", - "Do HTTP raw; no transfer decoding", - CURLHELP_HTTP}, - {"-e, --referer ", - "Referrer URL", - CURLHELP_HTTP}, - {"-J, --remote-header-name", - "Use the header-provided filename", - CURLHELP_OUTPUT}, - {"-O, --remote-name", - "Write output to file named as remote file", - CURLHELP_IMPORTANT | CURLHELP_OUTPUT}, - {" --remote-name-all", - "Use the remote filename for all URLs", - CURLHELP_OUTPUT}, - {"-R, --remote-time", - "Set remote file's time on local output", - CURLHELP_OUTPUT}, - {" --remove-on-error", - "Remove output file on errors", - CURLHELP_OUTPUT}, - {"-X, --request ", - "Specify request method to use", - CURLHELP_CONNECTION | CURLHELP_POP3 | CURLHELP_FTP | CURLHELP_IMAP | - CURLHELP_SMTP}, - {" --request-target ", - "Specify the target for this request", - CURLHELP_HTTP}, - {" --resolve <[+]host:port:addr[,addr]...>", - "Resolve host+port to address", - CURLHELP_CONNECTION | CURLHELP_DNS}, - {" --retry ", - "Retry request if transient problems occur", - CURLHELP_CURL}, - {" --retry-all-errors", - "Retry all errors (with --retry)", - CURLHELP_CURL}, - {" --retry-connrefused", - "Retry on connection refused (with --retry)", - CURLHELP_CURL}, - {" --retry-delay ", - "Wait time between retries", - CURLHELP_CURL | CURLHELP_TIMEOUT}, - {" --retry-max-time ", - "Retry only within this period", - CURLHELP_CURL | CURLHELP_TIMEOUT}, - {" --sasl-authzid ", - "Identity for SASL PLAIN authentication", - CURLHELP_AUTH}, - {" --sasl-ir", - "Initial response in SASL authentication", - CURLHELP_AUTH}, - {" --service-name ", - "SPNEGO service name", - CURLHELP_AUTH}, - {"-S, --show-error", - "Show error even when -s is used", - CURLHELP_CURL | CURLHELP_GLOBAL}, - {"-i, --show-headers", - "Show response headers in output", - CURLHELP_IMPORTANT | CURLHELP_VERBOSE | CURLHELP_OUTPUT}, - {" --sigalgs ", - "TLS signature algorithms to use", - CURLHELP_TLS}, - {"-s, --silent", - "Silent mode", - CURLHELP_IMPORTANT | CURLHELP_VERBOSE}, - {" --skip-existing", - "Skip download if local file already exists", - CURLHELP_CURL | CURLHELP_OUTPUT}, - {" --socks4 ", - "SOCKS4 proxy on given host + port", - CURLHELP_PROXY}, - {" --socks4a ", - "SOCKS4a proxy on given host + port", - CURLHELP_PROXY}, - {" --socks5 ", - "SOCKS5 proxy on given host + port", - CURLHELP_PROXY}, - {" --socks5-basic", - "Username/password auth for SOCKS5 proxies", - CURLHELP_PROXY | CURLHELP_AUTH}, - {" --socks5-gssapi", - "Enable GSS-API auth for SOCKS5 proxies", - CURLHELP_PROXY | CURLHELP_AUTH}, - {" --socks5-gssapi-nec", - "Compatibility with NEC SOCKS5 server", - CURLHELP_PROXY | CURLHELP_AUTH}, - {" --socks5-gssapi-service ", - "SOCKS5 proxy service name for GSS-API", - CURLHELP_PROXY | CURLHELP_AUTH}, - {" --socks5-hostname ", - "SOCKS5 proxy, pass hostname to proxy", - CURLHELP_PROXY}, - {"-Y, --speed-limit ", - "Stop transfers slower than this", - CURLHELP_CONNECTION}, - {"-y, --speed-time ", - "Trigger 'speed-limit' abort after this time", - CURLHELP_CONNECTION | CURLHELP_TIMEOUT}, - {" --ssl", - "Try enabling TLS", - CURLHELP_TLS | CURLHELP_IMAP | CURLHELP_POP3 | CURLHELP_SMTP | - CURLHELP_LDAP}, - {" --ssl-allow-beast", - "Allow security flaw to improve interop", - CURLHELP_TLS}, - {" --ssl-auto-client-cert", - "Use auto client certificate (Schannel)", - CURLHELP_TLS}, - {" --ssl-no-revoke", - "Disable cert revocation checks (Schannel)", - CURLHELP_TLS}, - {" --ssl-reqd", - "Require SSL/TLS", - CURLHELP_TLS | CURLHELP_IMAP | CURLHELP_POP3 | CURLHELP_SMTP | - CURLHELP_LDAP}, - {" --ssl-revoke-best-effort", - "Ignore missing cert CRL dist points", - CURLHELP_TLS}, - {" --ssl-sessions ", - "Load/save SSL session tickets from/to this file", - CURLHELP_TLS}, - {"-2, --sslv2", - "SSLv2", - CURLHELP_DEPRECATED}, - {"-3, --sslv3", - "SSLv3", - CURLHELP_DEPRECATED}, - {" --stderr ", - "Where to redirect stderr", - CURLHELP_VERBOSE | CURLHELP_GLOBAL}, - {" --styled-output", - "Enable styled output for HTTP headers", - CURLHELP_VERBOSE | CURLHELP_GLOBAL}, - {" --suppress-connect-headers", - "Suppress proxy CONNECT response headers", - CURLHELP_PROXY}, - {" --tcp-fastopen", - "Use TCP Fast Open", - CURLHELP_CONNECTION}, - {" --tcp-nodelay", - "Set TCP_NODELAY", - CURLHELP_CONNECTION}, - {"-t, --telnet-option ", - "Set telnet option", - CURLHELP_TELNET}, - {" --tftp-blksize ", - "Set TFTP BLKSIZE option", - CURLHELP_TFTP}, - {" --tftp-no-options", - "Do not send any TFTP options", - CURLHELP_TFTP}, - {"-z, --time-cond